Commit inicial - WordPress Análisis de Precios Unitarios

- WordPress core y plugins
- Tema Twenty Twenty-Four configurado
- Plugin allow-unfiltered-html.php simplificado
- .gitignore configurado para excluir wp-config.php y uploads

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-11-03 21:04:30 -06:00
commit a22573bf0b
24068 changed files with 4993111 additions and 0 deletions

View File

@@ -0,0 +1,402 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
use function TVD\Cache\content_set_cache;
use function TVD\Cache\meta_cache;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class TVD_Content_Sets
*
* @project: thrive-dashboard
*/
class TVD_Content_Sets {
/**
* Nonce that should be checked for meta boxes request
*/
const META_BOX_NONCE = 'thrive-dashboard-sets-meta-box-nonce';
/**
* Singleton
*/
use TD_Singleton;
/**
* TVD_Content_Sets constructor.
*/
private function __construct() {
//Maybe do something like on smart site add check before enqueue & output templates
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
add_action( 'admin_print_footer_scripts', array( $this, 'backbone_templates' ) );
add_action( 'rest_api_init', array( $this, 'admin_create_rest_routes' ) );
/* cache-related actions */
add_action( 'save_post', array( $this, 'on_save_post_clear_cache' ), 10, 2 );
add_action( 'saved_term', array( $this, 'on_save_term_clear_cache' ) );
add_action( 'delete_post', array( $this, 'on_delete_post_clear_cache' ), 10, 2 );
if ( $this->show_ui() ) {
add_action( 'wp_after_insert_post', array( $this, 'after_save_post' ), 10, 2 );
add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) );
add_action( 'admin_print_scripts-post-new.php', array( $this, 'edit_post_admin_script' ) );
add_action( 'admin_print_scripts-post.php', array( $this, 'edit_post_admin_script' ) );
}
require_once __DIR__ . '/content-set/class-utils.php';
require_once __DIR__ . '/content-set/class-set.php';
require_once __DIR__ . '/content-set/class-rule.php';
require_once __DIR__ . '/content-set/class-post-rule.php';
require_once __DIR__ . '/content-set/class-term-rule.php';
require_once __DIR__ . '/content-set/class-blog-rule.php';
require_once __DIR__ . '/content-set/class-search-result-rule.php';
require_once __DIR__ . '/content-set/class-archive-rule.php';
\TVD\Content_Sets\Set::init();
}
/**
* Adds the admin meta boxes
*/
public function add_meta_boxes() {
add_meta_box(
'tvd_content_sets',
esc_html__( 'Thrive Content Sets', 'thrive-dash' ),
array( $this, 'content_sets_meta_box' ),
null,
'advanced',
'high'
);
}
/**
* Callback for adding meta boxes for content sets
*
* @return mixed
*/
public function content_sets_meta_box() {
return require dirname( __FILE__ ) . '/meta-boxes/content-sets.php';
}
/**
* Edit post - scripts + styles
*/
public function edit_post_admin_script() {
$matches = \TVD\Content_Sets\Set::get_items_that_static_match( get_post() );
tve_dash_enqueue_style( 'thrive-admin-dashboard-edit-post', TVD_Smart_Const::url( 'assets/admin/css/styles-edit-post.css' ) );
tve_dash_enqueue_script( 'thrive-admin-dashboard-edit-post', TVD_Smart_Const::url( 'assets/admin/js/dist/edit-post.min.js' ), array( 'jquery' ), false, true );
wp_localize_script( 'thrive-admin-dashboard-edit-post', 'TD', array(
'sets' => \TVD\Content_Sets\Set::get_items_for_dropdown(),
'matches' => \TVD\Content_Sets\Set::get_items_for_dropdown( $matches ),
) );
}
/**
* Enqueue Content Sets scripts & styles
*/
public function enqueue_scripts() {
if ( $this->allow_enqueue_scripts( tve_get_current_screen_key() ) ) {
tve_dash_enqueue_style( 'tvd-content-sets-admin', TVD_Smart_Const::url( 'assets/admin/css/styles-content-sets.css' ) );
tve_dash_enqueue_script( 'tvd-content-sets-admin', TVD_Smart_Const::url( 'assets/admin/js/dist/content-sets-admin.min.js' ), array(
'jquery',
'backbone',
'tve-dash-main-js',
), false, true );
$public_post_types = \TVD\Content_Sets\Post_Rule::get_content_types();
wp_localize_script( 'tvd-content-sets-admin', 'TD_SETS', array(
'routes' => array(
'base' => get_rest_url() . 'tss/v1/content-sets',
),
'post_types' => array_keys( $public_post_types ),
'nonce' => wp_create_nonce( 'wp_rest' ),
'fields' => array(
'title' => \TVD\Content_Sets\Rule::FIELD_TITLE,
'tag' => \TVD\Content_Sets\Rule::FIELD_TAG,
'category' => \TVD\Content_Sets\Rule::FIELD_CATEGORY,
'published_date' => \TVD\Content_Sets\Rule::FIELD_PUBLISHED_DATE,
'topic' => \TVD\Content_Sets\Rule::FIELD_TOPIC,
'difficulty' => \TVD\Content_Sets\Rule::FIELD_DIFFICULTY,
'label' => \TVD\Content_Sets\Rule::FIELD_LABEL,
'author' => \TVD\Content_Sets\Rule::FIELD_AUTHOR,
),
'operators' => array(
'is' => \TVD\Content_Sets\Rule::OPERATOR_IS,
'not_is' => \TVD\Content_Sets\Rule::OPERATOR_NOT_IS,
'grater_equal' => \TVD\Content_Sets\Rule::OPERATOR_GRATER_EQUAL,
'lower_equal' => \TVD\Content_Sets\Rule::OPERATOR_LOWER_EQUAL,
'within_last' => \TVD\Content_Sets\Rule::OPERATOR_WITHIN_LAST,
),
'options' => array(
'general' => array(
'content' => $this->localize_content_values( $public_post_types ),
'field' => array(
array(
'value' => '',
'disabled' => true,
'label' => __( 'Select your field or taxonomy', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::FIELD_ALL,
'label' => __( 'All', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::FIELD_TITLE,
'label' => __( 'Title', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::FIELD_TAG,
'label' => __( 'Tag', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::FIELD_CATEGORY,
'label' => __( 'Category', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::FIELD_PUBLISHED_DATE,
'label' => __( 'Published', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::FIELD_AUTHOR,
'label' => __( 'Author', 'thrive-dash' ),
),
),
'operator' => array(
array(
'value' => '',
'disabled' => true,
'label' => __( 'Choose your condition', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::OPERATOR_IS,
'label' => __( 'Is', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::OPERATOR_NOT_IS,
'label' => __( 'Is not', 'thrive-dash' ),
),
),
),
/**
* Allow Protected files to hook here and modify the exceptions list
*
* @param array $exceptions
*/
'exceptions' => apply_filters( 'tvd_content_sets_get_content_types_exceptions', array(
'tva_courses' => array(
array(
'value' => '',
'disabled' => true,
'label' => __( 'Select your field or taxonomy', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::FIELD_ALL,
'label' => __( 'All', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::FIELD_TITLE,
'label' => __( 'Title', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::FIELD_TOPIC,
'label' => __( 'Topic', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::FIELD_DIFFICULTY,
'label' => __( 'Difficulty', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::FIELD_LABEL,
'label' => __( 'Label', 'thrive-dash' ),
),
),
'published_date' => array(
array(
'value' => '',
'disabled' => true,
'label' => __( 'Choose your condition', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::OPERATOR_LOWER_EQUAL,
'label' => __( 'On or before', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::OPERATOR_GRATER_EQUAL,
'label' => __( 'On or after', 'thrive-dash' ),
),
array(
'value' => \TVD\Content_Sets\Rule::OPERATOR_WITHIN_LAST,
'label' => __( 'Within the last', 'thrive-dash' ),
),
),
) ),
),
'sets' => \TVD\Content_Sets\Set::get_items(),
) );
}
}
/**
* Add backbone templates
*/
public function backbone_templates() {
if ( $this->allow_enqueue_scripts( tve_get_current_screen_key() ) ) {
$templates = tve_dash_get_backbone_templates( TVD_Smart_Const::path( 'views/admin/content-sets-templates' ), 'content-sets-templates' );
tve_dash_output_backbone_templates( $templates );
}
}
/**
* Register the rest route
*/
public function admin_create_rest_routes() {
require_once __DIR__ . '/endpoints/class-tvd-content-sets-controller.php';
$controller = new TVD_Content_Sets_Controller();
$controller->register_routes();
}
/**
* @param array $public_post_types
*
* @return array[]
*/
private function localize_content_values( $public_post_types = array() ) {
$values = array(
array(
'value' => '',
'disabled' => true,
'label' => __( 'Choose content type', 'thrive-dash' ),
),
);
foreach ( $public_post_types as $key => $name ) {
$values[] = array(
'value' => $key,
'label' => $name,
);
}
$values[] = array(
'value' => 'tva_courses',
'label' => __( 'Apprentice Course', 'thrive-dash' ),
);
$values[] = array(
'value' => \TVD\Content_Sets\Rule::HIDDEN_POST_TYPE_BLOG,
'label' => __( 'Blog Page', 'thrive-dash' ),
);
$values[] = array(
'value' => \TVD\Content_Sets\Rule::HIDDEN_POST_TYPE_SEARCH_RESULTS,
'label' => __( 'Search Results Page', 'thrive-dash' ),
);
$values[] = array(
'value' => 'archive',
'label' => __( 'Archive', 'thrive-dash' ),
);
return $values;
}
/**
* Checks if the system allows to include the content sets scripts
* Calls a filter that is implemented in other plugins (ex: Thrive Apprentice)
*
* @param string $screen_id
*
* @return boolean
*/
private function allow_enqueue_scripts( $screen_id ) {
return apply_filters( 'tvd_content_sets_allow_enqueue_scripts', $screen_id === 'admin_page_tve_dash_smart_site', $screen_id );
}
/**
* When a post is saved, clear the content sets cache associated with it
*
* @param int $post_id
* @param \WP_Post $post
*/
public function on_save_post_clear_cache( $post_id, $post ) {
if ( $post->post_type === \TVD\Content_Sets\Set::POST_TYPE ) {
// need to clear everything
content_set_cache( 'term' )->clear(); // delete any stored term meta
content_set_cache( 'post' )->clear(); // delete any stored post meta
} else {
content_set_cache( $post )->clear( $post_id );
}
}
/**
* When a term is saved, clear the content sets cache associated with it
*
* @param int $term_id
*/
public function on_save_term_clear_cache( $term_id ) {
content_set_cache( 'term' )->clear( $term_id ); // delete any stored term meta
}
/**
* When a content set is deleted, clear content set cache
*
* @param int $post_id
* @param \WP_Post $post
*/
public function on_delete_post_clear_cache( $post_id, $post ) {
if ( $post->post_type === \TVD\Content_Sets\Set::POST_TYPE ) {
content_set_cache( 'term' )->clear();
content_set_cache( 'post' )->clear();
}
}
/**
* After the post is saved, we update the content sets that have been modified from /wp-admin/post.php UI
*
* @param int $post_id
* @param WP_Post $post
*/
public function after_save_post( $post_id, $post ) {
// Security check: verify meta box nonce
if ( ! isset( $_POST['tvd_content_sets_meta_box'] ) || ! wp_verify_nonce( $_POST['tvd_content_sets_meta_box'], self::META_BOX_NONCE ) ) {
return;
}
$content_sets_ids = isset( $_POST['tvd_matched_content_sets_id'] ) ? $_POST['tvd_matched_content_sets_id'] : '';
\TVD\Content_Sets\Set::toggle_object_to_set_static_rules( $post, array_filter( explode( ',', $content_sets_ids ) ) );
}
/**
* Returns true if the system can show the content sets UI
*
* @return bool
*/
public function show_ui() {
return defined( 'TVD_CONTENT_SETS_SHOW_UI' ) && TVD_CONTENT_SETS_SHOW_UI;
}
}
/**
* @return TVD_Content_Sets
*/
function tvd_content_sets() {
return TVD_Content_Sets::get_instance();
}
add_action( 'init', 'tvd_content_sets', 8 );

View File

@@ -0,0 +1,737 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
/**
* Class TVD_Global_Shortcodes
*/
class TVD_Global_Shortcodes {
public static $dynamic_shortcodes = array(
'thrive_global_shortcode_url' => 'global_shortcode_url',
'thrv_dynamic_data_request' => 'request_data_shortcode',
'thrv_dynamic_data_date' => 'date_shortcode',
'thrv_dynamic_data_content' => 'content_shortcode',
'thrv_dynamic_data_user' => 'user_data_shortcode',
'thrv_dynamic_data_source' => 'source_shortcode',
'thrv_dynamic_data_user_acf' => 'acf_user_field',
);
public function __construct() {
add_filter( 'tcb_content_allowed_shortcodes', array( $this, 'allowed_shortcodes' ) );
add_filter( 'tcb_dynamiclink_data', array( $this, 'links_shortcodes' ) );
add_filter( 'tcb_inline_shortcodes', array( $this, 'tcb_inline_shortcodes' ), 99, 1 );
$this->add_shortcodes();
}
public function add_shortcodes() {
foreach ( static::$dynamic_shortcodes as $shortcode => $func ) {
$function = array( $this, $func );
add_shortcode( $shortcode, static function ( $attr ) use ( $function ) {
$output = call_user_func_array( $function, func_get_args() );
return TVD_Global_Shortcodes::maybe_link_wrap( $output, $attr );
} );
}
}
/**
* Checks if shortcode content needs to be wrapped in a link (if a custom json-encoded attribute exists in the shortcode)
*
* @param string $shortcode_content
* @param array $attr
*
* @return string
*/
public static function maybe_link_wrap( $shortcode_content, $attr ) {
/**
* If a static link is detected in config, we need to wrap $content in that link (only if a link doesn't already exist in $shortcode_content ..)
*/
if ( $shortcode_content && ! empty( $attr['static-link'] ) ) {
$link_attr = json_decode( htmlspecialchars_decode( $attr['static-link'] ), true );
if ( strpos( $shortcode_content, '</a>' ) === false ) {
if ( ! empty( $link_attr ) && ! empty( $link_attr['href'] ) ) {
/* replacement for shortcode open "[" */
if ( strpos( $link_attr['href'], '((' ) === 0 ) {
$link_attr['href'] = str_replace( array( '((', '))' ), array( '[', ']' ), $link_attr['href'] );
$link_attr['href'] = do_shortcode( $link_attr['href'] );
}
/* put the brackets back in the title attribute if they were replaced before */
if ( ! empty( $link_attr['title'] ) && strpos( $link_attr['title'], '((' ) !== false ) {
$link_attr['title'] = str_replace( array( '((', '))' ), array(
'[',
']',
), $link_attr['title'] );
}
$attributes = array();
foreach ( $link_attr as $attr_name => $value ) {
$attributes[] = ( $attr_name === 'className' ? 'class' : $attr_name ) . '="' . esc_attr( $value ) . '"';
}
$shortcode_content = '<a ' . implode( ' ', $attributes ) . '>' . $shortcode_content . '</a>';
}
} elseif ( extension_loaded( 'dom' ) && isset( $link_attr['className'] ) && function_exists( 'mb_convert_encoding' ) ) {
/**
* For elements already containing a link just add the old classes (e.g. global styles)
*/
$dom = new DOMDocument;
@$dom->loadHTML( mb_convert_encoding( $shortcode_content, 'HTML-ENTITIES', 'UTF-8' ) );
$dom->encoding = 'UTF-8';
$items = $dom->getElementsByTagName( 'a' );
for ( $i = 0; $i < $items->length; $i ++ ) {
$link = $items->item( $i );
if ( $link ) {
$link->setAttribute( 'class', $link_attr['className'] );
if ( isset( $link_attr['data-css'] ) ) {
$link->setAttribute( 'data-css', $link_attr['data-css'] );
}
}
}
$body = $dom->getElementsByTagName( 'body' );
if ( $body && $body->length ) {
$shortcode_content = '';
foreach ( $body[0]->childNodes as $child ) {
$shortcode_content .= $dom->saveHTML( $child );
}
} else {
$shortcode_content = $dom->saveHTML();
}
}
}
return $shortcode_content;
}
public function tcb_inline_shortcodes( $shortcodes ) {
return array_merge_recursive( TVD_Global_Shortcodes::get_inline_shortcodes(), $shortcodes );
}
public static function get_inline_shortcodes() {
$inline_shortcodes = array();
$shortcodes_without_params = array(
/* Content */
'the_ID' => array(
'name' => __( 'Post ID', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_content',
'fn' => 'content_shortcode',
'group' => 'Content',
),
'the_title' => array(
'name' => __( 'Post title', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_content',
'fn' => 'content_shortcode',
'group' => 'Content',
),
'post_type' => array(
'name' => __( 'Post type', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_content',
'fn' => 'content_shortcode',
'group' => 'Content',
),
'permalink' => array(
'name' => __( 'Post URL', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_content',
'fn' => 'content_shortcode',
'group' => 'Content',
),
/* Time & date*/
'd M Y' => array(
'name' => esc_html__( 'Date (14 Aug 2029)', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_date',
'fn' => 'date_shortcode',
'group' => 'Time & date',
),
'd.n.Y' => array(
'name' => esc_html__( 'Date (14.8.2029)', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_date',
'fn' => 'date_shortcode',
'group' => 'Time & date',
),
'd-m-Y' => array(
'name' => esc_html__( 'Date (14-08-2029)', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_date',
'fn' => 'date_shortcode',
'group' => 'Time & date',
),
'd/m/Y' => array(
'name' => esc_html__( 'Date (14/08/2029)', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_date',
'fn' => 'date_shortcode',
'group' => 'Time & date',
),
'G:i:s' => array(
'name' => esc_html__( 'Time (23:59:59)', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_date',
'fn' => 'date_shortcode',
'group' => 'Time & date',
),
'G:i' => array(
'name' => esc_html__( 'Time (23:59)', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_date',
'fn' => 'date_shortcode',
'group' => 'Time & date',
),
'd' => array(
'name' => esc_html__( 'Day (0131)', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_date',
'fn' => 'date_shortcode',
'group' => 'Time & date',
),
'jS' => array(
'name' => esc_html__( 'Day (1st, 2nd, 15th - 31st)', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_date',
'fn' => 'date_shortcode',
'group' => 'Time & date',
),
'l' => array(
'name' => esc_html__( 'Day of the week (Monday)', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_date',
'fn' => 'date_shortcode',
'group' => 'Time & date',
),
'D' => array(
'name' => esc_html__( 'Day of the week (Mon)', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_date',
'fn' => 'date_shortcode',
'group' => 'Time & date',
),
'm' => array(
'name' => esc_html__( 'Month (01-12)', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_date',
'fn' => 'date_shortcode',
'group' => 'Time & date',
),
'F' => array(
'name' => esc_html__( 'Month (January - December)', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_date',
'fn' => 'date_shortcode',
'group' => 'Time & date',
),
'M' => array(
'name' => esc_html__( 'Month (Jan Dec)', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_date',
'fn' => 'date_shortcode',
'group' => 'Time & date',
),
'Y' => array(
'name' => esc_html__( 'Year (2029)', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_date',
'fn' => 'date_shortcode',
'group' => 'Time & date',
),
);
$resources = array(
'get' => __( 'URL QueryString', 'thrive-dash' ),
'post' => __( 'POST variable', 'thrive-dash' ),
'cookie' => __( 'Cookie', 'thrive-dash' ),
);
$shortcodes_with_default = array(
/* User Data */
'username' => array(
'name' => __( 'WordPress username', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_user',
'fn' => 'user_data_shortcode',
'group' => 'User data',
'link' => __( 'Link to user profile', 'thrive-dash' ),
),
'user_email' => array(
'name' => __( 'WordPress user email', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_user',
'fn' => 'user_data_shortcode',
'group' => 'User data',
),
'role' => array(
'name' => __( 'WordPress user role', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_user',
'fn' => 'user_data_shortcode',
'group' => 'User data',
),
'first_name' => array(
'name' => __( 'WordPress user first name', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_user',
'fn' => 'user_data_shortcode',
'group' => 'User data',
'link' => __( 'Link to user profile', 'thrive-dash' ),
),
'last_name' => array(
'name' => __( 'WordPress user last name', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_user',
'fn' => 'user_data_shortcode',
'group' => 'User data',
'link' => __( 'Link to user profile', 'thrive-dash' ),
),
'nickname' => array(
'name' => __( 'WordPress user nickname', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_user',
'fn' => 'user_data_shortcode',
'group' => 'User data',
),
'display_name' => array(
'name' => __( 'WordPress user public name', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_user',
'fn' => 'user_data_shortcode',
'group' => 'User data',
),
'website' => array(
'name' => __( 'WordPress user website', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_user',
'fn' => 'user_data_shortcode',
'group' => 'User data',
),
'user_bio' => array(
'name' => __( 'WordPress user bio', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_user',
'fn' => 'user_data_shortcode',
'group' => 'User data',
),
'ip' => array(
'name' => __( 'IP', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_user',
'fn' => 'user_data_shortcode',
'group' => 'User data',
),
'browser' => array(
'name' => __( 'Browser', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_user',
'fn' => 'user_data_shortcode',
'group' => 'User data',
),
/* Source */
'HTTP_REFERER' => array(
'name' => __( 'Referring URL', 'thrive-dash' ),
'shortcode' => 'thrv_dynamic_data_source',
'fn' => 'source_shortcode',
'group' => 'Source',
),
);
if ( tvd_has_external_fields_plugins() ) {
// if is acf plugin active, run through all of them and update the custom fields for the user
foreach ( tvd_get_acf_user_external_fields() as $field ) {
$shortcodes_with_default[ $field['name'] ] = array(
'name' => $field['label'],
'shortcode' => 'thrv_dynamic_data_user_acf',
'fn' => 'acf_user_field',
'group' => 'User data',
);
}
}
foreach ( $shortcodes_with_default as $key => $data ) {
$shortcode = array(
'name' => $data['name'],
'option' => $data['name'],
'value' => $data['shortcode'],
'extra_param' => $key,
'input' => array(
'default' => array(
'type' => 'input',
'label' => __( 'Default Value', 'thrive-dash' ),
'value' => '',
),
'id' => array(
'extra_options' => array(),
'real_data' => array(
$key => call_user_func( 'TVD_Global_Shortcodes::' . $data['fn'], array( 'id' => $key ) ),
),
'type' => 'hidden',
'value' => $key,
),
),
);
/* Support shortcodes link*/
if ( isset( $data['link'] ) ) {
$shortcode['input']['link'] = array(
'type' => 'checkbox',
'label' => $data['link'],
'value' => true,
);
}
$inline_shortcodes[ $data['group'] ][] = $shortcode;
}
foreach ( $shortcodes_without_params as $key => $data ) {
$shortcode = array(
'name' => $data['name'],
'option' => $data['name'],
'value' => $data['shortcode'],
'extra_param' => $key,
'input' => array(
'id' => array(
'extra_options' => array(),
'real_data' => array(
$key => call_user_func( 'TVD_Global_Shortcodes::' . $data['fn'], array( 'id' => $key ) ),
),
'type' => 'hidden',
'value' => $key,
),
),
);
$inline_shortcodes[ $data['group'] ][] = $shortcode;
}
foreach ( $resources as $key => $name ) {
$inline_shortcodes['Request data'][] = array(
'name' => $name,
'option' => $name,
'value' => 'thrv_dynamic_data_request',
'extra_param' => $key,
'input' => array(
'var_name' => array(
'type' => 'input',
'label' => __( 'Variable name', 'thrive-dash' ),
'value' => '',
),
'default' => array(
'type' => 'input',
'label' => __( 'Default Value', 'thrive-dash' ),
'value' => '',
),
'id' => array(
'extra_options' => array(),
'real_data' => array(
$key => TVD_Global_Shortcodes::request_data_shortcode( array(
'id' => $key,
'var_name' => '',
) ),
),
'type' => 'hidden',
'value' => $key,
),
),
);
}
return $inline_shortcodes;
}
/**
* Filter allowed shortcodes for tve_do_wp_shortcodes
*
* @param $shortcodes
*
* @return array
*/
public function allowed_shortcodes( $shortcodes ) {
return array_merge( $shortcodes, array_keys( TVD_Global_Shortcodes::$dynamic_shortcodes ) );
}
/**
* Add global shortcodes to be used in dynamic links
*
* @param $links
*
* @return mixed
*/
public function links_shortcodes( $links ) {
$global_links = array();
foreach ( $this->global_data() as $index => $value ) {
$value['id'] = $index;
$global_links[] = $value;
}
$links['Site'] = array(
'links' => array( $global_links ),
'shortcode' => 'thrive_global_shortcode_url',
);
return $links;
}
/**
* Global data related to the site
*
* @return array
*/
public function global_data() {
// phpcs:disable
return apply_filters( 'tvd_global_data', array(
array(
'name' => __( 'Homepage', 'thrive-dash' ),
'url' => get_home_url(),
'show' => true,
),
array(
'name' => __( 'Blog', 'thrive-dash' ),
'url' => get_option( 'page_for_posts' ) ? get_permalink( get_option( 'page_for_posts' ) ) : get_home_url(),
'show' => true,
),
array(
'name' => __( 'RSS Feed', 'thrive-dash' ),
'url' => get_home_url() . '/feed',
'show' => true,
),
array(
'name' => __( 'Login', 'thrive-dash' ),
'url' => wp_login_url(),
'show' => true,
),
array(
'name' => __( 'Logout', 'thrive-dash' ),
'url' => wp_logout_url(),
'show' => true,
),
) );
// phpcs:enable
}
/**
* Replace the shortcode with its content
*
* @param $args
*
* @return mixed|string
*/
public function global_shortcode_url( $args ) {
$data = '';
if ( isset( $args['id'] ) ) {
$groups = $this->global_data();
$id = (int) $args['id'];
$data = empty( $groups[ $id ] ) ? '' : $groups[ $id ]['url'];
}
if ( isset( $args['logout-redirect'] ) ) {
$data .= '&redirect_to=' . $args['logout-redirect'];
}
return $data;
}
/**
* Shortcode render for user data
*
* @param $args
*
* @return mixed|string
*/
public static function user_data_shortcode( $args ) {
$user_data = tve_current_user_data();
$value = '';
if ( isset( $args['id'] ) ) {
if ( $args['id'] === 'browser' ) {
$value = '<span class="tve-browser-data"></span >'; /* Replace this with JS because PHP get_browser doesnt work all the time */
}
if ( isset( $user_data[ $args['id'] ] ) ) {
$value = $user_data[ $args['id'] ];
if ( $args['id'] === 'website' ) {
/* create link with user website */
$value = sprintf( '<a href="%s" target="_blank">%s</a>', $value, $value );
} elseif ( isset( $args['link'] ) && $args['link'] === '1' ) {
$value = sprintf( '<a href="%s" target="_blank">%s</a>', $user_data['edit_url'], $value );
}
}
}
if ( empty( $value ) && isset( $args['default'] ) ) {
$value = $args['default'];
}
return $value;
}
/**
* Shortcode render for user data from acf
*
* @param $args
*
* @return mixed|string
*/
public static function acf_user_field( $args ) {
$value = '';
if ( tvd_has_external_fields_plugins() ) {
$value = get_field( $args['id'], 'user_' . get_current_user_id() );
if ( empty( $value ) && isset( $args['default'] ) ) {
$value = $args['default'];
}
}
return $value;
}
/**
* Shortcode render for post data
*
* @param $args
*
* @return mixed|string
*/
public static function content_shortcode( $args ) {
$value = '';
global $post;
if ( is_singular() || ( wp_doing_ajax() && ! empty( $post ) ) ) {
if ( isset( $args['id'] ) ) {
$func = "get_{$args['id']}";
if ( function_exists( $func ) ) {
$value = $func();
}
}
if ( empty( $value ) && isset( $args['default'] ) ) {
$value = $args['default'];
}
}
return $value;
}
public static function date_shortcode( $args ) {
/**
* The hour should be in 24h format
*/
$format = str_replace( 'g', 'G', $args['id'] );
if ( function_exists( 'wp_date' ) ) {
$result = wp_date( $format );
} else {
$result = date_i18n( $format );
}
return trim( $result );
}
/**
* Shortcode render for data from page source
*
* @param $args
*
* @return mixed|string
*/
public static function source_shortcode( $args ) {
$allowed = array( 'HTTP_REFERER' );
$value = '';
if ( isset( $args['id'], $_SERVER[ $args['id'] ] ) && in_array( $args['id'], $allowed, true ) ) {
$value = sanitize_text_field( $_SERVER[ $args['id'] ] );
}
if ( empty( $value ) && isset( $args['default'] ) ) {
$value = $args['default'];
}
return $value;
}
/**
* Try to get a value deep down from a variable
* e.g a[b][c]
*
* @param $original_key
* @param $params
*
* @return mixed|string
*/
public static function get_deep_value( $original_key, $params ) {
$original_key = str_replace( ']', '', $original_key );
$ref = $params;
foreach ( explode( '[', $original_key ) as $key ) {
if ( isset( $ref[ $key ] ) ) {
$ref = $ref[ $key ];
} else {
$ref = '';
}
}
return $ref;
}
/**
* Shortcode render for data from $_REQUEST
*
* @param array $args
* @param string $context
* @param string $tag
*
* @return mixed|string
*/
public static function request_data_shortcode( $args = array(), $context = '', $tag = '' ) {
$value = '';
$should_load_value = true;
if ( ! empty( $tag ) && function_exists( 'is_editor_page' ) && is_editor_page() ) {
//preserve shortcode in case someone adds it as dynamic link cuz why not
if ( empty( $args['inline'] ) ) {
$attributes = '';
foreach ( array( 'var_name', 'id', 'default', 'inline' ) as $key ) {
if ( ! empty( $args[ $key ] ) ) {
$attributes .= $key . '=' . $args[ $key ] . ' ';
}
}
return '[' . $tag . ' ' . trim( $attributes ) . ']';
}
$should_load_value = false;
}
if ( $should_load_value && ! empty( $args['var_name'] ) ) {
/**
* just in case the var_name has spaces check var_Name with underscore
* e.g test 01 comes as test_01
*/
$var_name = $args['var_name'];
$fallback_var = strpos( $var_name, ' ' ) !== false ? preg_replace( "/\s/", "_", $var_name ) : '';
if ( strpos( $var_name, '((' ) !== false ) {
$var_name = preg_replace( '#\\(\\(#', '[', $var_name );
$var_name = preg_replace( '#\\)\\)#', ']', $var_name );
}
$global = [];
switch ( $args['id'] ) {
case 'post':
$global = $_POST;
break;
case 'get':
$global = $_GET;
break;
case 'cookie':
$global = $_COOKIE;
break;
default:
break;
}
$value = isset( $global[ $var_name ] ) ? $global[ $var_name ] : '';
if ( empty( $value ) ) {
$value = static::get_deep_value( $var_name, $global );
}
if ( empty( $value ) && ! empty( $fallback_var ) ) {
$value = isset( $global[ $fallback_var ] ) ? $global[ $fallback_var ] : '';
}
if ( empty( $value ) ) {
$value = static::get_deep_value( $fallback_var, $global );
}
$value = sanitize_text_field( $value );
}
if ( empty( $value ) && isset( $args['default'] ) ) {
$value = $args['default'];
}
return esc_html( stripslashes( $value ) );
}
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
/**
* Base class for extending the WP_REST_Controller
*/
class TVD_REST_Controller {
public static $version = 1;
public static $namespace = 'tss/v';
public $base = '';
/**
* Register the routes for the objects of the controller.
*/
public function register_routes() {}
}

View File

@@ -0,0 +1,781 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
/**
* Class TVD_Smart_DB
*/
class TVD_Smart_DB {
/**
* Groups table name
*
* @var string
*/
private $groups_table_name;
/**
* Fields table name
*
* @var string
*/
private $fields_table_name;
/**
* WordPress Database
*
* @var wpdb
*/
private $wpdb;
/**
* Default fields and data
*
* @var array
*/
private $default_groups;
/**
* Icons specific to type
*/
public static $icons;
/**
* Types of fields
*/
public static $types
= array(
'text' => 0,
'address' => 1,
'phone' => 2,
'email' => 3,
'link' => 4,
// 'location' => 5,
);
/**
* TVD_Smart_DB constructor.
*/
public function __construct() {
global $wpdb;
$this->wpdb = $wpdb;
$this->groups_table_name = $this->wpdb->prefix . 'td_groups';
$this->fields_table_name = $this->wpdb->prefix . 'td_fields';
$this->default_groups = $this->groups();
static::$icons = $this->icons();
}
/**
* Default icons
*
* return array
*/
public function icons() {
return array(
static::$types['text'] => 'text-new',
static::$types['address'] => 'address-new',
static::$types['phone'] => 'phone-new',
static::$types['email'] => 'envelope-new',
static::$types['link'] => 'link-new',
// TVD_Smart_DB::$types['location'] => 'map-marker-solid',
);
}
/**
* Default types
*
* return array
*/
public static function field_types() {
$types = array();
foreach ( static::$types as $key => $type ) {
$types[ static::$types[ $key ] ] = array(
'name' => $key,
'icon' => static::field_icon( static::$types[ $key ] ),
'key' => static::$types[ $key ],
);
}
return $types;
}
/**
* Default fields and data
*
* return array
*/
public function groups() {
return array(
'Company' => array(
array(
'name' => 'Name',
'type' => static::$types['text'],
'identifier' => 'name',
),
array(
'name' => 'Address',
'type' => static::$types['address'],
'identifier' => 'addr',
),
array(
'name' => 'Phone number',
'type' => static::$types['phone'],
'data' => [ 'text' => 'Call', 'url' => '' ],
'identifier' => 'phone',
),
array(
'name' => 'Alternative phone number',
'type' => static::$types['phone'],
'data' => [ 'text' => 'Call', 'url' => '' ],
'identifier' => 'alt_phone',
),
array(
'name' => 'Email address',
'type' => static::$types['email'],
'data' => [ 'text' => 'Email', 'url' => '' ],
'identifier' => 'mail',
),
// array(
// 'name' => 'Map Location',
// 'type' => TVD_Smart_DB::$types['location'],
// ),
),
'Legal' => array(
array(
'name' => 'Privacy policy',
'type' => static::$types['link'],
'data' => array( 'text' => 'Privacy policy', 'url' => '' ),
'identifier' => 'priv',
),
array(
'name' => 'Disclaimer',
'type' => static::$types['link'],
'data' => array( 'text' => 'Disclaimer', 'url' => '' ),
'identifier' => 'disc',
),
array(
'name' => 'Terms and conditions',
'type' => static::$types['link'],
'data' => array( 'text' => 'Terms and conditions', 'url' => '' ),
'identifier' => 'toc',
),
array(
'name' => 'Contact',
'type' => static::$types['link'],
'data' => array( 'text' => 'Contact', 'url' => '' ),
'identifier' => 'contact',
),
),
'Social' => array(
array(
'name' => 'Facebook page',
'icon' => 'facebook-brands-new',
'type' => static::$types['link'],
'identifier' => 'fb',
),
array(
'name' => 'YouTube',
'icon' => 'youtube-brands-new',
'type' => static::$types['link'],
'identifier' => 'yt',
),
array(
'name' => 'LinkedIn',
'icon' => 'linkedin-brands-new',
'type' => static::$types['link'],
'identifier' => 'in',
),
array(
'name' => 'Pinterest',
'icon' => 'pinterest-brands-new',
'type' => static::$types['link'],
'identifier' => 'pin',
),
array(
'name' => 'Instagram',
'icon' => 'instagram-brands-new',
'type' => static::$types['link'],
'identifier' => 'ig',
),
array(
'name' => 'Xing',
'icon' => 'xing-brands-new',
'type' => static::$types['link'],
'identifier' => 'xing',
),
array(
'name' => 'X',
'icon' => 'x-brands-new',
'type' => static::$types['link'],
'identifier' => 't',
),
array(
'name' => 'TikTok',
'icon' => 'tiktok-brands-new',
'type' => static::$types['link'],
'identifier' => 'tiktok',
),
array(
'name' => 'Bluesky',
'icon' => 'bluesky-brands-new',
'type' => static::$types['link'],
'identifier' => 'bluesky',
),
),
);
}
/**
* Insert the default data in the db
*/
public function insert_default_data() {
/**
* We can't use the migration queries in the migration file because we have relationships, so we insert the data here
*/
$result = $this->wpdb->get_row( "SELECT `id` FROM $this->groups_table_name LIMIT 0,1", ARRAY_A );
/* if there's no entries in the table, this is the first ( 1.0.0 ) db update */
if ( empty( $result ) ) {
foreach ( $this->default_groups as $group => $fields ) {
/**
* Insert the group
*/
$result = $this->wpdb->insert(
$this->groups_table_name,
array(
'name' => $group,
'is_default' => 1,
),
array(
'%s',
)
);
$id = $this->wpdb->insert_id;
if ( $result ) {
/**
* Insert the fields
*/
foreach ( $fields as $field ) {
$this->wpdb->insert(
$this->fields_table_name,
array(
'name' => $field['name'],
'type' => $field['type'],
'identifier' => $field['identifier'],
'data' => empty( $field['data'] ) ? null : maybe_serialize( $field['data'] ),
'is_default' => 1,
'group_id' => $id,
),
array(
'%s',
'%d',
)
);
}
}
}
} else {
/*
* there are entries in the table => this is the 1.0.X -> 1.0.(X+1) db upgrade
* Insert 'Xing' and 'Twitter' manually, and populate the newly added 'identifier' column for each default field
*/
$xing_twitter_row = $this->wpdb->get_row( "SELECT * FROM $this->fields_table_name WHERE `identifier`='xing' OR `identifier`='t' LIMIT 0,1", ARRAY_A );
/* only continue if xing and twitter aren't already inserted */
if ( empty( $xing_twitter_row ) ) {
$this->insert_xing_and_twitter_default_fields();
$this->add_string_identifier_to_fields();
}
$tiktok_row = $this->wpdb->get_row( "SELECT * FROM $this->fields_table_name WHERE `identifier`='tiktok' LIMIT 0,1", ARRAY_A );
/* only continue if tiktok isn't inserted */
if ( empty( $tiktok_row ) ) {
$this->add_tiktok_field();
}
$bluesky_row = $this->wpdb->get_row( "SELECT * FROM $this->fields_table_name WHERE `identifier`='bluesky' LIMIT 0,1", ARRAY_A );
/* only continue if bluesky isn't inserted */
if ( empty( $bluesky_row ) ) {
$this->add_bluesky_field();
}
}
}
public function add_tiktok_field() {
$format = array(
'%s',
'%d',
);
$this->wpdb->insert(
$this->fields_table_name,
array(
'name' => 'TikTok',
'type' => static::$types['link'],
'identifier' => 'tiktok',
'is_default' => 1,
'group_id' => 3,
),
$format
);
}
public function add_bluesky_field() {
$format = array(
'%s',
'%d',
);
$this->wpdb->insert(
$this->fields_table_name,
array(
'name' => 'Bluesky',
'type' => static::$types['link'],
'identifier' => 'bluesky',
'is_default' => 1,
'group_id' => 3,
),
$format
);
}
/**
* Insert the xing and twitter social fields and add their IDs to the 'id - string identifier' map
*/
private function insert_xing_and_twitter_default_fields() {
$format = array(
'%s',
'%d',
);
$this->wpdb->insert(
$this->fields_table_name,
array(
'name' => 'Xing',
'type' => static::$types['link'],
'identifier' => 'xing',
'is_default' => 1,
'group_id' => 3,
),
$format
);
$this->wpdb->insert(
$this->fields_table_name,
array(
'name' => 'X',
'type' => static::$types['link'],
'identifier' => 't',
'is_default' => 1,
'group_id' => 3,
),
$format
);
}
/**
* Add a string 'identifier' to the proper column for all the default field rows
*/
private function add_string_identifier_to_fields() {
foreach ( $this->type_id_map as $id => $identifier ) {
$this->wpdb->update( $this->fields_table_name, array( 'identifier' => $identifier ), array( 'id' => $id ) );
}
}
/**
* Get groups with fields
*
* @param int $id
* @param boolean $with_fields
*
* @return array|object|null
*/
public function get_groups( $id = 0, $with_fields = true ) {
$args = array();
$query = 'SELECT * FROM ' . $this->groups_table_name;
if ( $id ) {
$where = ' WHERE id = %d';
$args[] = $id;
} else {
/**
* We need this so WPDB won't complain about not preparing the data correctly
*/
$where = ' WHERE 1 = %d';
$args[] = 1;
}
$query .= $where;
$results = $this->wpdb->get_results( $this->wpdb->prepare( $query, $args ), ARRAY_A );
/* I did not write this, I only rewrote it a bit, please don't assassinate me */
if ( $results && $with_fields ) {
foreach ( $results as $group_index => $group ) {
$group_name = $results[ $group_index ]['name'];
$group_config = empty( $this->default_groups[ $group_name ] ) ? array() : $this->default_groups[ $group_name ];
$fields = $this->get_fields( $group );
if ( ! empty( $fields ) ) {
foreach ( $fields as $index => $field ) {
$field['group_name'] = $group['name'];
if ( ! empty( $fields[ $index ]['name'] ) ) {
$fields[ $index ]['name'] = $this->ensure_lowercase_label( $fields[ $index ]['name'] );
}
$fields[ $index ]['formated_data'] = empty( $field['data'] ) ? '' : static::format_field_data( maybe_unserialize( $field['data'] ), $field );
$fields[ $index ]['data'] = empty( $field['data'] ) ? '' : maybe_unserialize( $field['data'] );
$field_config = empty( $field['identifier'] ) ? array() : $this->get_config_for_identifier( $group_config, $field['identifier'] );
if ( empty( $field_config ) || empty( $field_config['icon'] ) ) {
$icon = static::field_icon( $field['type'] );
} else {
$icon = dashboard_icon( $field_config['icon'], true );
}
$fields[ $index ]['icon'] = $icon;
$fields[ $index ]['default_field'] = empty( $field_config ) ? 0 : 1;
}
}
$results[ $group_index ]['default_field'] = empty( $group_config ) ? 0 : 1;
$results[ $group_index ]['fields'] = $fields;
}
}
return $results;
}
/**
* For the current identifier, search the array of configs and return the right one
*
* @param $default_group
* @param $identifier
*
* @return array|mixed
*/
private function get_config_for_identifier( $default_group, $identifier ) {
$config = array();
foreach ( $default_group as $index => $field ) {
if ( ! empty( $field['identifier'] ) && $field['identifier'] === $identifier ) {
$config = $field;
break;
}
}
return $config;
}
/**
* Make sure some specific default labels ( that are stored in the DB ) are lowercase
*
* @param string $label
*
* @return string
*/
private function ensure_lowercase_label( $label ) {
if ( $label === 'Terms and Conditions' ) {
$label = 'Terms and conditions';
} else if ( $label === 'Facebook Page' ) {
$label = 'Facebook page';
}
return $label;
}
/**
* @param $field_type
*
* @return mixed
*/
public static function field_icon( $field_type ) {
return dashboard_icon( static::$icons[ $field_type ], true );
}
public static function format_field_data( $field_data, $field, $args = array() ) {
$data = '';
$unavailable = '';
if ( apply_filters( 'td_smartsite_shortcode_tooltip', false ) ) {
$unavailable_name = empty( $field['group_name'] ) ? $field['name'] : '[' . $field['group_name'] . '] ' . $field['name'];
$unavailable = '<span class="thrive-inline-shortcode-unavailable">' .
'<span class="thrive-shortcode-notice">' .
'!' .
'</span>' .
$unavailable_name .
'</span>';
if ( empty( $args['hide-tooltip'] ) ) {
$unavailable .= '<span class="thrive-tooltip-wrapper">' .
'<span class="thrive-shortcode-tooltip">' .
__( 'This global variable hasn\'t been set. Define your global variables in the', 'thrive-dash' ) .
'<br>' .
'<a><span onClick=window.open("' . add_query_arg( 'page', 'tve_dash_smart_site', admin_url( 'admin.php' ) ) . '","_blank") >' . ' ' . __( ' Global Fields dashboard', 'thrive-dash' ) . '</span></a>' .
'</span>' .
'</span>';
}
}
switch ( (int) $field['type'] ) {
// text field
case TVD_Smart_DB::$types['text']:
$data = empty( $field_data['text'] ) ? $unavailable : $field_data['text'];
break;
//address field
case TVD_Smart_DB::$types['address']:
if ( empty( $field_data['address1'] ) ) {
$data = $unavailable;
} else {
$address_fields = [ 'address1', 'address2', 'city', 'state', 'zip', 'country' ];
$field_data = array_replace( array_fill_keys( $address_fields, null ), $field_data );
$data = implode( empty( $args['multiline'] ) ? ', ' : '<br>', array_filter( $field_data ) );
}
break;
// phone field
case TVD_Smart_DB::$types['phone']:
$data = empty( $field_data['phone'] ) ? $unavailable : $field_data['phone'];
break;
// email field
case TVD_Smart_DB::$types['email']:
$data = empty( $field_data['email'] ) ? $unavailable : $field_data['email'];
break;
//link field
case TVD_Smart_DB::$types['link']:
if ( empty( $field_data['url'] ) ) {
$data = $unavailable;
} else {
if ( empty( $args['prevent-link'] ) ) {
$link_attr = TVD_Smart_Shortcodes::tvd_decode_link_attributes( $args );
$field_value = '<a ' . ( ! empty( $args['link-css-attr'] ) ? 'data-css="' . $args['link-css-attr'] . '"' : '' ) . ' href="' . $field_data['url'] . '" target="' . ( ! empty( $link_attr['target'] ) ? $link_attr['target'] : '' ) . '">' . $field_data['text'] . '</a>';
} else {
$field_value = $field_data['text'];
}
$data = $field_value;
}
break;
// location field
case TVD_Smart_DB::$types['location']:
$url = 'https://maps.google.com/maps?q=' . urlencode( empty( $field_data['location'] ) ? 'New York' : $field_data['location'] ) . '&t=m&z=10&output=embed&iwloc=near';
$data = '<iframe frameborder="0" marginheight="0" marginwidth="0" src="' . $url . '"></iframe>';
break;
}
return $data;
}
/**
* Get fields for group or by ID
*
* @param array $group
* @param int $id
*
* @return array|object|null
*/
public function get_fields( $group = array(), $id = 0 ) {
if ( $group ) {
$where = ' WHERE group_id = %d';
$args[] = $group['id'];
} else {
/**
* We need this so WPDB won't complain about not preparing the data correctly
*/
$where = ' WHERE 1 = %d';
$args[] = 1;
}
if ( $id ) {
if ( is_numeric( $id ) ) {
$where .= ' AND id = %d';
} else {
$where .= ' AND identifier = %s';
}
$args[] = $id;
}
$query = $this->wpdb->prepare( 'SELECT * FROM ' . $this->fields_table_name . $where, $args );
if ( ! $id ) {
$results = $this->wpdb->get_results( $query, ARRAY_A );
} else {
$results = $this->wpdb->get_row( $query, ARRAY_A );
}
return $results;
}
/**
* Get fields of a specific type which have some data
*
* @param array $group_id
* @param int $type
*
* @return array|object|null
*/
public function get_fields_by_type( $group_id = array(), $type = 0 ) {
if ( $type ) {
$where = ' WHERE type = %d';
$args[] = $type;
} else {
return array();
}
if ( $group_id ) {
$where .= ' AND group_id = %d';
$args[] = $group_id;
} else {
$where .= ' AND 1 = %d';
$args[] = 1;
}
$query = $this->wpdb->prepare( 'SELECT * FROM ' . $this->fields_table_name . $where, $args );
return $this->wpdb->get_results( $query, ARRAY_A );
}
public static function save_field( $model, $action ) {
$rep = array(
'%d',
'%s',
'%s',
'%s',
'%s',
);
global $wpdb;
// Add new field
if ( $action === 'insert' ) {
$model['created_at'] = date( 'Y-m-d h:i:s' );
$result = $wpdb->insert(
$wpdb->prefix . 'td_fields',
array(
'group_id' => $model['group_id'],
'name' => $model['name'],
'type' => $model['type'],
'data' => maybe_serialize( $model['data'] ),
'created_at' => $model['created_at'],
),
$rep
);
$model['id'] = (string) $wpdb->insert_id;
} else {
// Update existing field
$model['updated_at'] = date( 'Y-m-d h:i:s' );
$result = $wpdb->update(
$wpdb->prefix . 'td_fields',
array(
'group_id' => (int) $model['group_id'],
'name' => $model['name'],
'type' => $model['type'],
'data' => maybe_serialize( $model['data'] ),
'updated_at' => $model['updated_at'],
),
array( 'id' => $model['id'] ),
$rep,
array( '%d' )
);
}
$model['formated_data'] = static::format_field_data( $model['data'], $model );
$model['icon'] = empty( $model['icon'] ) ? static::field_icon( $model['type'] ) : $model['icon'];
return $result ? $model : false;
}
public static function delete_field( $id ) {
global $wpdb;
return $wpdb->delete( $wpdb->prefix . 'td_fields', array( 'id' => $id ) );
}
public static function insert_group( $model ) {
$model['created_at'] = date( 'Y-m-d h:i:s' );
global $wpdb;
$result = $wpdb->insert(
$wpdb->prefix . 'td_groups',
array(
'name' => $model['name'],
'created_at' => $model['created_at'],
),
array(
'%s',
'%s',
)
);
$model['id'] = (string) $wpdb->insert_id;
return $result ? $model : false;
}
public static function update_group( $model ) {
global $wpdb;
$model['updated_at'] = date( 'Y-m-d h:i:s' );
$result = $wpdb->update(
$wpdb->prefix . 'td_groups',
array(
'name' => $model['name'],
'updated_at' => $model['updated_at'],
),
array( 'id' => $model['id'] ),
array( '%s', '%s', ),
array( '%d' )
);
return $result ? $model : false;
}
public static function delete_group( $id ) {
global $wpdb;
$result = $wpdb->delete( $wpdb->prefix . 'td_groups', array( 'id' => $id ) );
if ( $result ) {
$wpdb->delete( $wpdb->prefix . 'td_fields', array( 'group_id' => $id ), array( '%d' ) );
}
return $result;
}
/*
* we're certain that these IDs have these identifiers because they were there from the start and were inserted in this specific order
* the 'identifier' is the same as the one from the groups() function
* in theory this should never be modified again, it's used only for backwards compatibility when upgrading the DB from 1.0.0 to 1.0.1
*/
public $type_id_map = array(
1 => 'name',
2 => 'addr',
3 => 'phone',
4 => 'alt_phone',
5 => 'mail',
6 => 'priv',
7 => 'disc',
8 => 'toc',
9 => 'contact',
10 => 'fb',
11 => 'yt',
12 => 'in',
13 => 'pin',
14 => 'ig',
);
}

View File

@@ -0,0 +1,116 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
/**
* Class TVD_Smart_Shortcodes
*/
final class TVD_Smart_Shortcodes {
/**
* Database instance for Smart Site
*
* @var TVD_Smart_DB
*/
private $db;
public static $smart_shortcodes = array(
TVD_Smart_Site::GLOBAL_FIELDS_SHORTCODE => 'tvd_tss_smart_fields',
TVD_Smart_Site::GLOBAL_FIELDS_SHORTCODE_URL => 'tvd_tss_smart_url',
);
/**
* TVD_Smart_Shortcodes constructor.
*/
public function __construct() {
$this->db = new TVD_Smart_DB();
foreach ( static::$smart_shortcodes as $shortcode => $func ) {
$function = array( $this, $func );
add_shortcode( $shortcode, static function ( $attr ) use ( $function ) {
$output = call_user_func_array( $function, func_get_args() );
return TVD_Global_Shortcodes::maybe_link_wrap( $output, $attr );
} );
}
}
/**
* Execute smart fields shortcode
*
* @param $args
*
* @return string
*/
public function tvd_tss_smart_fields( $args ) {
$data = '';
if ( $args['id'] ) {
$field = $this->db->get_fields( array(), $args['id'] );
if ( ! empty( $field ) ) {
$groups = $this->db->get_groups( $field['group_id'], false );
$group = array_pop( $groups );
$field['group_name'] = $group['name'];
$field_data = maybe_unserialize( $field['data'] );
$data = TVD_Smart_DB::format_field_data( $field_data, $field, $args );
}
}
return $data;
}
/**
* Execute smart url shortcode
*
* @param $args
*
* @return string
*/
public function tvd_tss_smart_url( $args ) {
$data = '';
if ( ! empty( $args['id'] ) ) {
$field = $this->db->get_fields( array(), $args['id'] );
if ( ! empty( $field['data'] ) ) {
$field_data = maybe_unserialize( $field['data'] );
if ( isset( $field_data['phone'] ) ) {
$data = 'tel:' . $field_data['phone'];
} elseif ( isset( $field_data['email'] ) ) {
$data = 'mailto:' . $field_data['email'];
} else {
$data = $field_data['url'];
}
}
}
return ( ! empty( $field_data ) ) ? $data : '';
}
/**
* Decode the link settings attributes into an array
*
* @param $link_attr
*
* @return array|mixed
*/
public static function tvd_decode_link_attributes( $link_attr ) {
$data = [];
if ( ! empty( $link_attr['static-link'] ) ) {
$data = json_decode( htmlspecialchars_decode( $link_attr['static-link'] ), true );
}
return $data;
}
}

View File

@@ -0,0 +1,432 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
if ( ! class_exists( 'TVD_Smart_Site' ) ) :
final class TVD_Smart_Site {
/**
* Global fields shortcode key.
*/
const GLOBAL_FIELDS_SHORTCODE = 'thrive_global_fields';
/**
* Global fields url shortcode key.
*/
const GLOBAL_FIELDS_SHORTCODE_URL = 'thrive_global_fields_url';
/**
* Database instance for Smart Site
*
* @var TVD_Smart_DB
*/
private $db;
/**
* @var string
*/
private $_dashboard_page = 'tve_dash_smart_site';
/**
* @var TVD_Smart_Shortcodes
*/
public $shortcodes;
public $global_shortcodes;
/**
* TVD_Smart_Site constructor.
*
* @throws Exception
*/
public function __construct() {
$this->db = new TVD_Smart_DB();
$this->action_filters();
$this->shortcodes = new TVD_Smart_Shortcodes();
$this->global_shortcodes = new TVD_Global_Shortcodes();
}
/**
* Add actiions and filters
*/
private function action_filters() {
add_action( 'current_screen', array( $this, 'conditional_hooks' ) );
add_action( 'admin_menu', array( $this, 'admin_menu' ) );
add_action( 'tve_after_db_migration', array( $this, 'insert_default_data' ), 10, 1 );
add_action( 'rest_api_init', array( $this, 'create_initial_rest_routes' ) );
add_filter( 'tve_dash_filter_features', array( $this, 'smart_site_feature' ) );
add_filter( 'tcb_inline_shortcodes', array( $this, 'smart_site_tcb_shortcodes' ), 10, 1 );
add_filter( 'tcb_dynamiclink_data', array( $this, 'smart_site_links_shortcodes' ) );
add_filter( 'tcb_content_allowed_shortcodes', array( $this, 'allowed_shotcodes' ) );
add_action( 'wp_enqueue_scripts', array( $this, 'frontend_enqueue_scripts' ), 11 );
}
/**
* Filter allowed shortcodes for tve_do_wp_shortcodes
*
* @param $shortcodes
*
* @return array
*/
public function allowed_shotcodes( $shortcodes ) {
return array_merge( $shortcodes, array( TVD_Smart_Site::GLOBAL_FIELDS_SHORTCODE, TVD_Smart_Site::GLOBAL_FIELDS_SHORTCODE_URL ) );
}
/**
* Hooks for the edit screen
*
*/
public function conditional_hooks() {
$screen = tve_get_current_screen_key();
/**
* Main Dashboard section
*/
if ( $screen === 'toplevel_page_tve_dash_section' ) {
add_filter( 'tve_dash_filter_features', array( $this, 'smart_site_feature' ) );
}
/**
* Smart Site Dashboard
*/
if ( $screen === 'admin_page_' . $this->_dashboard_page ) {
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_styles' ) );
add_action( 'admin_print_footer_scripts', array( $this, 'backbone_templates' ) );
}
}
/**
* Add Card to dashboard
*
* @param $features
*
* @return mixed
*/
public function smart_site_feature( $features ) {
$features['smart_site'] = array(
'icon' => 'tvd-smart-site',
'title' => 'Smart Site',
'description' => __( 'Define variables and values sitewide to use when building content', 'thrive-dash' ),
'btn_link' => add_query_arg( 'page', $this->_dashboard_page, admin_url( 'admin.php' ) ),
'btn_text' => __( 'Smart Settings', 'thrive-dash' ),
);
return $features;
}
/**
* Add to admin menu
*/
public function admin_menu() {
add_submenu_page( '', __( 'Smart Site', 'thrive-dash' ), __( 'Smart Site', 'thrive-dash' ), TVE_DASH_CAPABILITY, $this->_dashboard_page, array(
$this,
'admin_dashboard',
) );
}
/**
* Main Smart Site page content
*/
public function admin_dashboard() {
include TVE_DASH_PATH . '/inc/smart-site/views/admin/templates/dashboard.phtml';
}
/**
* Insert the default data in the DB
*
* @param string $option_name
*/
public function insert_default_data( $option_name ) {
if ( $option_name === 'tve_td_db_version' ) {
$db = new TVD_Smart_DB();
$db->insert_default_data();
}
}
/**
* Enqueue admin styles
*/
public function enqueue_styles() {
tve_dash_enqueue_style( 'tvd-ss-admin', TVD_Smart_Const::url( 'assets/admin/css/styles.css' ) );
}
/**
* Enqueue admin scripts
*/
public function enqueue_scripts() {
if ( tve_get_current_screen_key() === 'admin_page_' . $this->_dashboard_page ) {
remove_all_actions( 'admin_notices' );
tve_dash_enqueue();
wp_enqueue_script( 'jquery' );
wp_enqueue_script( 'backbone' );
tve_dash_enqueue_script( 'tvd-ss-admin', TVD_Smart_Const::url( 'assets/admin/js/dist/admin.min.js' ), array(
'jquery',
'backbone',
'tve-dash-main-js',
), false, true );
wp_localize_script( 'tvd-ss-admin', 'SmartSite', $this->localize() );
}
}
/**
* Enqueue scripts in the frontend
*/
public function frontend_enqueue_scripts() {
if ( function_exists( 'is_editor_page' ) && is_editor_page() ) {
tve_dash_enqueue_script( 'tvd-ss-tcb-hooks', TVD_Smart_Const::url( 'assets/js/tcb_hooks.js' ), array(
'jquery',
), false, true );
}
}
/**
* Localize admin data
*
* @return array
*/
public function localize() {
return array(
't' => include TVD_Smart_Const::path( 'i18n.php' ),
'dash_url' => admin_url( 'admin.php?page=tve_dash_section' ),
'url' => TVD_Smart_Const::url(),
'nonce' => wp_create_nonce( 'wp_rest' ),
'routes' => array(
'groups' => $this->get_route_url( 'groups' ),
'fields' => $this->get_route_url( 'fields' ),
),
'data' => array(
'groups' => $this->db->get_groups(),
'fieldTypes' => TVD_Smart_DB::field_types(),
),
);
}
/**
* Add backbone templates
*/
public function backbone_templates() {
$templates = tve_dash_get_backbone_templates( TVD_Smart_Const::path( 'views/admin/templates' ), 'templates' );
tve_dash_output_backbone_templates( $templates );
}
/**
* Create the rest routes
*/
public function create_initial_rest_routes() {
$endpoints = array(
'TVD_Groups_Controller',
'TVD_Fields_Controller',
);
foreach ( $endpoints as $e ) {
/** @var TVD_REST_Controller $controller */
$controller = new $e();
$controller->register_routes();
}
}
/**
* Get the route url
*
* @param $endpoint
* @param int $id
* @param array $args
*
* @return string
*/
private function get_route_url( $endpoint, $id = 0, $args = array() ) {
$url = get_rest_url() . TVD_Smart_Const::REST_NAMESPACE . '/' . $endpoint;
if ( ! empty( $id ) && is_numeric( $id ) ) {
$url .= '/' . $id;
}
if ( ! empty( $args ) ) {
add_query_arg( $args, $url );
}
return $url;
}
public function add_phone_link( $fields_phone ) {
foreach ( $fields_phone as &$field ) {
$field['name'] .= ' (Click to call)';
if ( ! empty( $field['data'] ) ) {
$field_data = maybe_unserialize( $field['data'] );
if ( ! empty( $field_data['phone'] ) ) {
$field_data['url'] = 'tel:' . $field_data['phone'];
$field['data'] = maybe_serialize( $field_data );
}
}
}
return $fields_phone;
}
public function add_email_link( $field_email ) {
foreach ( $field_email as &$field ) {
$field['name'] .= ' (Click to email)';
if ( ! empty( $field['data'] ) ) {
$field_data = maybe_unserialize( $field['data'] );
if ( ! empty( $field_data['email'] ) ) {
$field_data['url'] = 'mailto:' . $field_data['email'];
$field['data'] = maybe_serialize( $field_data );
}
}
}
return $field_email;
}
/**
* Get the data from global fields for the links that have a value set
*
* @param $dynamic_links
*
* @return mixed
*/
public function smart_site_links_shortcodes( $dynamic_links ) {
if ( ! empty( $_REQUEST[ TVE_FRAME_FLAG ] ) ) {
$groups = $this->db->get_groups( 0, false );
$links = array();
foreach ( $groups as $group ) {
$fields_link = $this->db->get_fields_by_type( $group['id'], TVD_Smart_DB::$types['link'] );
$fields_phone = $this->db->get_fields_by_type( $group['id'], TVD_Smart_DB::$types['phone'] );
$field_email = $this->db->get_fields_by_type( $group['id'], TVD_Smart_DB::$types['email'] );
$fields_phone = $this->add_phone_link( $fields_phone );
$field_email = $this->add_email_link( $field_email );
$new_fields = array_merge( $fields_phone, $field_email );
$fields = array_merge( $new_fields, $fields_link );
$name = $group['name'];
/* Add the fields in the group only when the group is empty */
if ( ! empty( $fields ) && empty( $links[ $name ] ) ) {
$links[ $name ] = array();
foreach ( $fields as $global_field ) {
$data = maybe_unserialize( $global_field['data'] );
$inserted_id = $global_field['id'];
$links[ $name ][] = array(
'name' => $global_field['name'],
'id' => empty( $global_field['identifier'] ) ? $inserted_id : $global_field['identifier'],
'inserted_id' => $inserted_id, /* used for backwards compatibility, mostly */
'identifier' => $global_field['identifier'],
'url' => empty( $data['url'] ) ? '' : $data['url'],
);
}
}
if ( empty( $links[ $name ] ) ) {
unset( $links[ $name ] );
}
}
if ( ! empty( $links ) ) {
$dynamic_links['Global Fields'] = array(
'links' => $links,
'shortcode' => TVD_Smart_Site::GLOBAL_FIELDS_SHORTCODE_URL,
);
}
}
return $dynamic_links;
}
/**
* Add the shortcodes to the froala editor
*
* @param $shortcodes
*
* @return array
*/
public function smart_site_tcb_shortcodes( $shortcodes ) {
if ( ! empty( $_REQUEST[ TVE_FRAME_FLAG ] ) ) {
$groups = $this->db->get_groups();
$result_shortcodes = array();
foreach ( $groups as $group ) {
if ( ! empty( $group['fields'] ) ) {
$result_shortcodes[ $group['id'] ] = array(
'name' => $group['name'],
'option' => $group['name'],
'value' => TVD_Smart_Site::GLOBAL_FIELDS_SHORTCODE,
'extra_param' => $group['id'],
'input' => array(
'id' => array(
'type' => 'select',
'label' => __( 'Field', 'thrive-dash' ),
'value' => array(),
'extra_options' => array(
'multiline' => array(
'available_for' => array( TVD_Smart_DB::$types['address'] ),
'type' => 'checkbox',
'label' => __( 'Include line breaks', 'thrive-dash' ),
'value' => false,
),
'enable_call' => array(
'available_for' => array( TVD_Smart_DB::$types['phone'] ),
'type' => 'checkbox',
'label' => __( 'Enable click to call', 'thrive-dash' ),
'value' => false,
),
'enable_email' => array(
'available_for' => array( TVD_Smart_DB::$types['email'] ),
'type' => 'checkbox',
'label' => __( 'Enable click to email', 'thrive-dash' ),
'value' => false,
),
),
),
),
);
foreach ( $group['fields'] as $field ) {
$result_shortcodes[ $group['id'] ]['input']['id']['value'][ $field['id'] ] = $field['name'];
$result_shortcodes[ $group['id'] ]['input']['id']['value_type'][ $field['id'] ] = $field['type'];
$result_shortcodes[ $group['id'] ]['input']['id']['real_data'][ $field['id'] ] = $this->shortcodes->tvd_tss_smart_fields( array( 'id' => $field['id'] ) );
}
}
}
$shortcode['Global fields'] = array_values( $result_shortcodes );
$shortcodes = array_merge( $shortcodes, $shortcode );
}
return $shortcodes;
}
}
endif;
/**
* Start Smart Site
*/
new TVD_Smart_Site();

View File

@@ -0,0 +1,79 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
namespace TVD\Content_Sets;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class Archive_Rule
*
* @package TVD\Content_Sets
* @project : thrive-dashboard
*/
class Archive_Rule extends Rule {
/**
* Returns true if the rule is valid
*
* NOTE: for now the archive rules is only supported for Authors
*
* @return bool
*/
public function is_valid() {
$valid = parent::is_valid();
if ( $this->field !== 'author' ) {
/**
* For now we only support Author field for Archive Rules
*/
$valid = false;
}
return $valid;
}
/**
* Should be extended in child classes
*
* @param string $query_string
* @param bool|int $paged if non-false, it will return limited results
* @param int $per_page number of results per page. ignored if $paged = false
*
* @return array
*/
public function get_items( $query_string = '', $paged = false, $per_page = 15 ) {
$this->paged = $paged;
$this->per_page = $per_page;
$this->query_string = $query_string;
$items = array();
if ( $this->field === 'author' ) {
$items = parent::search_users();
}
return $items;
}
/**
* Test if a rule matches the given params
*
* @param \WP_User $user
*
* @return bool
*/
public function matches( $user ) {
if ( is_author() ) {
return $this->match_value( $user->ID, $user );
}
return false;
}
}

View File

@@ -0,0 +1,39 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
namespace TVD\Content_Sets;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class Blog_Rule
*
* @package TVD\Content_Sets
* @project : thrive-dashboard
*/
class Blog_Rule extends Rule {
/**
* @return bool
*/
public function is_valid() {
return true;
}
/**
* Returns true if the active query is for the blog homepage.
*
* @param \WP_Post|\WP_Term $post_or_term
*
* @return bool
*/
public function matches( $post_or_term ) {
return is_home();
}
}

View File

@@ -0,0 +1,305 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
namespace TVD\Content_Sets;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class Post_Rule
*
* @package TVD\Content_Sets
* @project : thrive-dashboard
*/
class Post_Rule extends Rule {
/**
* @var string[]
*/
private $tax_fields
= array(
self::FIELD_CATEGORY => 'category',
self::FIELD_TAG => 'post_tag',
self::FIELD_AUTHOR => 'author',
);
/**
* Should be extended in child classes
*
* @param string $query_string
* @param bool|int $paged if non-false, it will return limited results
* @param int $per_page number of results per page. ignored if $paged = false
*
* @return array
*/
public function get_items( $query_string = '', $paged = false, $per_page = 15 ) {
/**
* Needed for the filter
*/
$this->query_string = $query_string;
$this->paged = $paged;
$this->per_page = $per_page;
return $this->should_search_terms() ? $this->search_terms() : $this->search_posts();
}
/**
* posts_where hook callback
* Searches particular post by title
*
* @param string $where
* @param \WP_Query $wp_query
*
* @return string
*/
public function title_filter( $where, $wp_query ) {
if ( $this->field === self::FIELD_TITLE ) {
global $wpdb;
$operation = $this->operator === self::OPERATOR_IS ? 'LIKE' : 'NOT LIKE';
$where .= ' AND ' . $wpdb->posts . '.post_title ' . $operation . ' \'%' . esc_sql( $wpdb->esc_like( $this->query_string ) ) . '%\'';
}
return $where;
}
/**
* terms_clauses hook callback
* Searches for a particular term by name
*
* @param $pieces
* @param $taxonomies
* @param $args
*
* @return array
*/
public function name_filter( $pieces, $taxonomies, $args ) {
if ( $this->should_search_terms() ) {
global $wpdb;
$operation = 'LIKE';
$pieces['where'] .= ' AND name ' . $operation . ' ' . ' \'%' . esc_sql( $wpdb->esc_like( $this->query_string ) ) . '%\' ';
}
return $pieces;
}
/**
* Prepares the item for front-end
*
* @param int $item
* @param bool $is_term
*
* @return array
*/
public function get_frontend_item( $item ) {
if ( $this->should_search_terms() ) {
return parent::get_frontend_item( $item );
}
$post = get_post( $item );
if ( empty( $post ) ) {
return array();
}
return array(
'id' => $post->ID,
'text' => $this->alter_frontend_title( $post->post_title, $post->post_status ),
);
}
/**
* Used to get all public content types needed for the content sets
*
* @return array
*/
public static function get_content_types() {
$ignored_types = apply_filters( 'thrive_ignored_post_types', array(
'attachment',
'tcb_lightbox',
'tcb_symbol',
'tva-acc-restriction',
) );
$all = get_post_types( array( 'public' => true ) );
$post_types = array();
foreach ( $all as $key => $post_type ) {
if ( in_array( $key, $ignored_types, true ) ) {
continue;
}
$post_types[ $key ] = tvd_get_post_type_label( $key );
}
/**
* Allow other functionality to be injected here
* Used to inject the Protected File post_type in the list so it can be protected by an apprentice product
*
* @param array $post_types
*/
return apply_filters( 'tvd_content_sets_get_content_types', $post_types );
}
/**
* Returns true if the UI needs to perform a terms search
*
* @return bool
*/
private function should_search_terms() {
return array_key_exists( $this->field, $this->tax_fields );
}
/**
* Returns the terms (category|post_tags) needed for the UI
* called from get_items method
*
* @return array
*/
private function search_terms() {
$response = array();
$taxonomy = $this->tax_fields[ $this->field ];
if ( $this->field === self::FIELD_AUTHOR ) {
$response = parent::search_users();
} else {
add_filter( 'terms_clauses', array( $this, 'name_filter' ), 10, 3 );
$query = new \WP_Term_Query();
$args = array(
'taxonomy' => $taxonomy,
'hide_empty' => false,
);
if ( $this->paged !== false ) {
$args['number'] = $this->per_page;
$args['offset'] = ( $this->paged - 1 ) * $this->per_page;
}
$terms = $query->query( $args );
remove_filter( 'terms_clauses', array( $this, 'name_filter' ), 10 );
foreach ( $terms as $term ) {
$response[] = array(
'id' => $term->term_id,
'text' => $term->name,
);
}
}
return $response;
}
/**
* Returns the posts needed for the UI
* called from get_items method
*
* @return array
*/
private function search_posts() {
$response = array();
add_filter( 'posts_where', array( $this, 'title_filter' ), 10, 2 );
$query = new \WP_Query;
$args = array(
'post_type' => $this->content,
'post_status' => array( 'draft', 'publish' ),
'posts_per_page' => $this->paged !== false ? $this->per_page : - 1,
);
if ( $this->paged !== false ) {
$args['paged'] = $this->paged;
}
$posts = $query->query( $args );
remove_filter( 'posts_where', array( $this, 'title_filter' ), 10 );
foreach ( $posts as $post ) {
/**
* Allow other plugins to hook here and remove the post from the content set dropdown
*
* @param boolean return value
* @param \WP_Post $post
*/
if ( ! apply_filters( 'tvd_content_sets_allow_select_post', true, $post ) ) {
continue;
}
$response[] = array(
'id' => $post->ID,
'text' => $this->alter_frontend_title( $post->post_title, $post->post_status ),
);
}
return $response;
}
/**
* Test if a rule matches the given params
*
* @param int|string $value
* @param \WP_Post|\WP_Term $post_or_term
*
* @return bool
*/
public function match_value( $value, $post_or_term ) {
if ( $this->field === self::FIELD_PUBLISHED_DATE ) {
$post_published_date = get_the_date( 'Y-m-d', $post_or_term );
switch ( $this->operator ) {
case self::OPERATOR_LOWER_EQUAL:
return strtotime( $post_published_date ) <= strtotime( $this->value );
case self::OPERATOR_GRATER_EQUAL:
return strtotime( $post_published_date ) >= strtotime( $this->value );
case self::OPERATOR_WITHIN_LAST:
return strtotime( $post_published_date ) >= strtotime( '-' . $this->value );
default:
break;
}
return false;
}
if ( $this->field === self::FIELD_TAG ) {
$common = count( array_intersect( $this->value, wp_get_post_tags( $post_or_term->ID, array( 'fields' => 'ids' ) ) ) );
if ( $this->operator === self::OPERATOR_IS ) {
return $common > 0;
}
return $common === 0;
}
if ( $this->field === self::FIELD_CATEGORY ) {
$common = count( array_intersect( $this->value, wp_get_post_categories( $post_or_term->ID, array( 'fields' => 'ids' ) ) ) );
if ( $this->operator === self::OPERATOR_IS ) {
return $common > 0;
}
return $common === 0;
}
if ( $this->field === self::FIELD_AUTHOR ) {
return in_array( (int) $post_or_term->post_author, $this->value, true );
}
return parent::match_value( $value, $post_or_term );
}
}

View File

@@ -0,0 +1,445 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
namespace TVD\Content_Sets;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class Rule
*
* @package TVD\Content_Sets
* @project : thrive-dashboard
*/
class Rule implements \JsonSerializable {
const HIDDEN_POST_TYPE_SEARCH_RESULTS = 'tvd_search_result_page';
const HIDDEN_POST_TYPE_BLOG = 'tvd_blog_page';
const OPERATOR_IS = '===';
const OPERATOR_NOT_IS = '!==';
const OPERATOR_GRATER_EQUAL = '>=';
const OPERATOR_LOWER_EQUAL = '<=';
const OPERATOR_WITHIN_LAST = 'within_last';
const FIELD_ALL = '-1';
const FIELD_TITLE = 'title';
const FIELD_TAG = 'tag';
const FIELD_CATEGORY = 'category';
const FIELD_PUBLISHED_DATE = 'published_date';
const FIELD_TOPIC = 'topic';
const FIELD_DIFFICULTY = 'difficulty';
const FIELD_LABEL = 'label';
const FIELD_AUTHOR = 'author';
/**
* @var string Query String needed to for post search
*/
protected $query_string = '';
protected $paged = false;
protected $per_page = 15;
protected $user_fields = array(
self::FIELD_AUTHOR,
);
/**
* @var mixed
*/
public $type;
/**
* @var mixed
*/
public $value;
/**
* @var mixed
*/
public $content;
/**
* @var mixed
*/
public $field;
/**
* @var mixed
*/
public $operator;
/**
* Rule constructor.
*
* @param array $data
*/
public function __construct( $data ) {
$this->type = $data['content_type'];
$this->content = $data['content'];
$this->field = $data['field'];
$this->operator = $data['operator'];
$this->value = $data['value'];
}
/**
* Factory for Rule Classes
*
* @param array $data
*
* @return Term_Rule|Post_Rule
*/
public static function factory( $data ) {
switch ( $data['content_type'] ) {
case 'term':
$class_name = 'Term_Rule';
break;
case self::HIDDEN_POST_TYPE_SEARCH_RESULTS:
$class_name = 'Search_Result_Rule';
break;
case self::HIDDEN_POST_TYPE_BLOG:
$class_name = 'Blog_Rule';
break;
case 'archive':
$class_name = 'Archive_Rule';
break;
default:
$class_name = 'Post_Rule';
break;
}
$class_name = __NAMESPACE__ . '\\' . $class_name;
return new $class_name( $data );
}
/**
* Returns true if the rule is valid
*
* @return bool
*/
public function is_valid() {
return ! empty( $this->type ) && in_array( $this->type, array( 'post', 'term', 'archive' ) ) &&
! empty( $this->content ) &&
! empty( $this->field ) &&
( (int) $this->field === - 1 || ( ! empty( $this->operator ) && ( is_array( $this->value ) || ! empty( $this->value ) || is_numeric( $this->value ) ) ) );
}
/**
* Return true if the active rule is equal to the rule provided as parameter
*
* @param Rule $rule
*
* @return boolean
*/
public function is_equal_to( $rule ) {
return serialize( $rule->jsonSerialize( false ) ) === serialize( $this->jsonSerialize( false ) );
}
/**
* Returns true if rule has value equal to parameter or value in array of values
*
* @param scalar $value
*
* @return boolean
*/
public function has_value( $value ) {
if ( $this->value === $value || ( is_array( $this->value ) && in_array( $value, $this->value, true ) ) ) {
return true;
}
return false;
}
/**
* Should be extended in child classes
*
* @param string $query_string
* @param bool|int $paged if non-false, it will return limited results
* @param int $per_page number of results per page. ignored if $paged = false
*
* @return array
*/
public function get_items( $query_string = '', $paged = false, $per_page = 15 ) {
return array();
}
/**
* Test if a rule matches the given params
*
* @param \WP_Post|\WP_Term $post_or_term
*
* @return bool
*/
public function matches( $post_or_term ) {
list( $content, $value ) = Utils::get_post_or_term_parts( $post_or_term );
if ( ! $this->match_content( $content ) ) {
return false;
}
if ( (int) $this->field === - 1 ) {
return true;
}
return $this->match_value( $value, $post_or_term );
}
/**
* @param \WP_Post|\WP_Term $post_or_term
*
* @used in Thrive Apprentice - edit-post view
* @return bool
*/
public function matches_static_value( $post_or_term ) {
list( $content, $value ) = Utils::get_post_or_term_parts( $post_or_term );
if ( ! $this->match_content( $content ) ) {
return false;
}
if ( $this->field !== self::FIELD_TITLE ) {
return false;
}
return $this->match_value( $value, $post_or_term );
}
/**
* @param string $content
*
* @return bool
*/
public function match_content( $content ) {
return $this->content === $content;
}
/**
* @param string|array $value
* @param \WP_Post|\WP_Term|\WP_User $post_or_term
*
* @return bool
*/
public function match_value( $value, $post_or_term ) {
if ( is_array( $this->value ) ) {
if ( $this->operator === self::OPERATOR_IS ) {
return in_array( $value, $this->value );
}
if ( $this->operator === self::OPERATOR_NOT_IS ) {
return ! in_array( $value, $this->value );
}
}
return $value === $this->value;
}
/**
* Constructs the item needed for front-end
* Needs to be extended in child classes
*
* @param int $item
*
* @return array
*/
public function get_frontend_item( $item ) {
if ( $this->should_search_users() ) {
return $this->get_frontend_user( $item );
}
return $this->get_frontend_term( $item );
}
/**
* Constructs the item from a term, needed for front-end
*
* @param int $item
*
* @return array
*/
public function get_frontend_user( $item ) {
$user = get_user_by( 'id', $item );
if ( empty( $user ) || ! $user instanceof \WP_User ) {
return array();
}
return array(
'id' => (int) $item,
'text' => $user->display_name,
);
}
/**
* Constructs the item from a term, needed for front-end
*
* @param int $item
*
* @return array
*/
public function get_frontend_term( $item ) {
$term = get_term( $item );
if ( empty( $term ) ) {
return array();
}
return array(
'id' => $term->term_id,
'text' => $term->name,
);
}
/**
* @return string
*/
public function get_content() {
return $this->content;
}
/**
* @return array
*/
public function get_value() {
return $this->value;
}
/**
* @return array
*/
#[\ReturnTypeWillChange]
public function jsonSerialize( $frontend = true ) {
$value = $this->value;
if ( is_array( $this->value ) ) {
if ( $frontend ) {
$value = array_map( function ( $item ) {
if ( isset( $item ) && is_int( $item ) && $front_item = $this->get_frontend_item( $item ) ) {
return $front_item;
}
}, $this->value );
//Remove empty values for UI
//Solves the case when we have a content set that contains a course or a post and that post is no longer available
//Also reset the indexes of the array
$value = array_values( array_filter( $value ) );
} else {
$value = $this->value = array_map( static function ( $item ) {
if ( is_numeric( $item ) ) {
return (int) $item;
}
if ( ! empty( $item['id'] ) || is_numeric( $item['id'] ) ) {
return (int) $item['id'];
}
}, $this->value );
}
}
$return = array(
'content_type' => $this->type,
'content' => $this->content,
'field' => $this->field,
'operator' => $this->operator,
'value' => $value,
);
if ( $frontend ) {
$return['content_label'] = array(
'singular' => 'Post',
'plural' => 'Posts',
);
if ( $return['content_type'] === 'term' ) {
/**
* @var \WP_Taxonomy
*/
$taxonomy = get_taxonomy( $return['content'] );
if ( $taxonomy instanceof \WP_Taxonomy ) {
$return['content_label']['singular'] = $taxonomy->labels->singular_name;
$return['content_label']['plural'] = $taxonomy->labels->name;
}
} elseif ( $return['content_type'] === 'post' ) {
/**
* @var \WP_Post_Type
*/
$postType = get_post_type_object( $return['content'] );
if ( $postType instanceof \WP_Post_Type ) {
$return['content_label']['singular'] = $postType->labels->singular_name;
$return['content_label']['plural'] = $postType->labels->name;
}
} elseif ( $return['content_type'] === self::HIDDEN_POST_TYPE_SEARCH_RESULTS ) {
$return['content_label']['plural'] = $return['content_label']['singular'] = 'Search result page';
} elseif ( $return['content_type'] === self::HIDDEN_POST_TYPE_BLOG ) {
$return['content_label']['plural'] = $return['content_label']['singular'] = 'Blog page';
} elseif ( $return['content_type'] === 'archive' ) {
$return['content_label']['plural'] = $return['content_label']['singular'] = 'Archive page';
}
}
return $return;
}
/**
* Alter the title that is shown in the UI depending on the status
*
* @param string $title
* @param string $status
*
* @return string
*/
protected function alter_frontend_title( $title, $status = 'publish' ) {
if ( $status !== 'publish' ) {
$title .= ' [' . $status . ']';
}
return $title;
}
/**
* @return array
*/
public function search_users() {
$search_string = esc_attr( trim( $this->query_string ) );
$response = array();
$users = new \WP_User_Query( array(
'search' => '*' . $search_string . '*',
'search_columns' => array(
'display_name',
),
'number' => $this->paged !== false ? $this->per_page : - 1,
'offset' => $this->paged !== false ? ( $this->paged - 1 ) * $this->per_page : 0,
)
);
/**
* @var \WP_User $user
*/
foreach ( $users->get_results() as $user ) {
$response[] = array(
'id' => (int) $user->data->ID,
'text' => (string) $user->data->display_name,
);
}
return $response;
}
/**
* Returns true if the system should search in user tables for values
*
* @return bool
*/
protected function should_search_users() {
return ! empty( $this->user_fields ) && in_array( $this->field, $this->user_fields );
}
}

View File

@@ -0,0 +1,39 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
namespace TVD\Content_Sets;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class Search_Result_Rule
*
* @package TVD\Content_Sets
* @project : thrive-dashboard
*/
class Search_Result_Rule extends Rule {
/**
* @return bool
*/
public function is_valid() {
return true;
}
/**
* Returns true if the active query is for a search.
*
* @param \WP_Post|\WP_Term $post_or_term
*
* @return bool
*/
public function matches( $post_or_term ) {
return is_search();
}
}

View File

@@ -0,0 +1,669 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
namespace TVD\Content_Sets;
use TVD\Cache\Cache_Exception;
use function TVD\Cache\content_set_cache;
use function TVD\Cache\meta_cache;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class Set
*
* @property int ID
* @property string $post_title
* @property array $post_content
*
* @package TVD\Content_Sets
* @project : thrive-dashboard
*/
class Set implements \JsonSerializable {
/**
* Stores the identified content set
*
* @var array
*/
private static $matched;
use \TD_Magic_Methods;
/**
* @var string Post type name
*/
const POST_TYPE = 'tvd_content_set';
/**
* default properties for a TA Course
*
* @var array
*/
protected $_defaults
= array(
'ID' => 0,
'post_title' => '',
'post_content' => array(),
);
private $rules = array();
/**
* Set constructor.
*
* @param int|array $data
*/
public function __construct( $data ) {
if ( is_int( $data ) ) {
$data = get_post( (int) $data, ARRAY_A );
$data['post_content'] = thrive_safe_unserialize( $data['post_content'] );
} elseif ( is_array( $data ) && ! empty( $data['post_content'] ) ) {
$data['post_content'] = thrive_safe_unserialize( $data['post_content'] );
}
$this->_data = array_merge( $this->_defaults, (array) $data );
foreach ( $data['post_content'] as $rule_data ) {
$this->rules[] = Rule::factory( $rule_data );
}
}
/**
* Register post type
*/
public static function init() {
register_post_type( static::POST_TYPE, array(
'labels' => array(
'name' => 'Content Set',
),
'publicly_queryable' => true, //Needs to be queryable on front-end for products
'public' => false,
'query_var' => false,
'rewrite' => false,
'show_in_nav_menus' => false,
'show_in_menu' => false,
'show_ui' => false,
'exclude_from_search' => true,
'show_in_rest' => true,
'has_archive' => false,
'map_meta_cap' => true,
) );
}
/**
* @param array $args
*
* @return Set[]
*/
public static function get_items( $args = array() ) {
$posts = get_posts( array_merge( array(
'posts_per_page' => - 1,
'post_type' => static::POST_TYPE,
), $args ) );
$sets = array();
foreach ( $posts as $post ) {
$sets[] = new Set( $post->to_array() );
}
return $sets;
}
/**
* Return all content sets as key => name
* Used for adding into a select element
*
* @param array|null $sets
*
* @return array
*/
public static function get_items_for_dropdown( $sets = null ) {
$dropdown = array();
if ( ! is_array( $sets ) ) {
$sets = static::get_items();
}
foreach ( $sets as $set ) {
$dropdown[] = array( 'id' => $set->ID, 'text' => $set->post_title );
}
return $dropdown;
}
/**
* @param \WP_Post|\WP_Term $post_or_term
* @param string $return_type can be objects or ids
*
* @return array
*/
public static function get_items_that_static_match( $post_or_term, $return_type = 'objects' ) {
$return = array();
foreach ( static::get_items() as $set ) {
if ( $set->has_matching_static_rules( $post_or_term ) ) {
$return[] = $return_type === 'ids' ? $set->ID : $set;
}
}
return $return;
}
/**
* @param $post_or_term
* @param array $sets_ids_for_object
*
* @return string|void
*/
public static function toggle_object_to_set_static_rules( $post_or_term, $sets_ids_for_object = array() ) {
list( $content, $value ) = Utils::get_post_or_term_parts( $post_or_term );
if ( $post_or_term instanceof \WP_Post && ! array_key_exists( $content, Post_Rule::get_content_types() ) ) {
return;
}
$current_matches = static::get_items_that_static_match( $post_or_term, 'ids' );
sort( $current_matches );
sort( $sets_ids_for_object );
if ( $current_matches == $sets_ids_for_object ) {
//No modifications have been done to the content sets$ty
return;
}
$rule = Rule::factory( array(
'content_type' => 'post',
'content' => $content,
'field' => Rule::FIELD_TITLE,
'operator' => Rule::OPERATOR_IS,
'value' => array( $value ),
) );
//Remove rule from content set
$sets_to_remove = array_diff( $current_matches, $sets_ids_for_object );
foreach ( $sets_to_remove as $id ) {
$set = new Set( (int) $id );
$set->remove_rule( $rule )->remove_rule_value( $value )->update();
}
//Add rule to content set
$sets_to_add = array_diff( $sets_ids_for_object, $current_matches );
foreach ( $sets_to_add as $id ) {
$set = new Set( (int) $id );
$set->add_rule( $rule )->update();
}
}
/**
* @param \WP_Post|\WP_Term|\WP_User $post_or_term
*
* @return bool
*/
public function has_matching_rules( $post_or_term ) {
$return = false;
/**
* @var Rule $rule
*/
foreach ( $this->rules as $rule ) {
if ( $rule->matches( $post_or_term ) ) {
$return = true;
break;
}
}
return $return;
}
/**
* @param \WP_Post|\WP_Term $post_or_term
*
* @return bool
*/
public function has_matching_static_rules( $post_or_term ) {
$return = false;
/**
* @var Rule $rule
*/
foreach ( $this->rules as $rule ) {
if ( $rule->matches_static_value( $post_or_term ) ) {
$return = true;
break;
}
}
return $return;
}
/**
* @param Rule $rule
*
* @return $this
*/
public function remove_rule( $rule ) {
$index_to_remove = null;
/**
* @var Rule $r
*/
foreach ( $this->rules as $index => $r ) {
if ( $r->is_equal_to( $rule ) ) {
$index_to_remove = $index;
break;
}
}
if ( ! is_numeric( $index_to_remove ) ) {
return $this;
}
unset( $this->rules[ $index_to_remove ] );
return $this;
}
/**
* Remove value from the rules
*
* @param scalar $value
*
* @return $this
*/
public function remove_rule_value( $value ) {
if ( empty( $value ) ) {
return $this;
}
/**
* @var Rule $r
*/
foreach ( $this->rules as $index => $r ) {
if ( $r->has_value( $value ) ) {
if ( is_array( $r->value ) && ( $key = array_search( $value, $r->value ) ) !== false ) {
unset( $this->rules[ $index ]->value[ $key ] );
//Reset the indexes
$this->rules[ $index ]->value = array_values( $this->rules[ $index ]->value );
} elseif ( is_scalar( $r->value ) ) {
$this->rules[ $index ]->value = '';
}
}
}
return $this;
}
/**
* @param Rule $rule
*
* @return $this
*/
public function add_static_rule( $rule ) {
if ( ! $rule->is_valid() ) {
return $this;
}
$child_added = false;
/**
* @var Rule $r
*/
foreach ( $this->rules as $r ) {
if ( $rule->get_content() === $r->get_content() && $rule->field === Rule::FIELD_TITLE && $r->field === Rule::FIELD_TITLE ) {
if ( is_array( $rule->value ) && is_array( $r->value ) ) {
$r->value = array_merge( $rule->value, $r->value );
$child_added = true;
break;
}
}
}
if ( ! $child_added ) {
$this->rules[] = $rule;
}
return $this;
}
/**
* @param Rule $rule
*
* @return $this
*/
public function add_rule( $rule ) {
if ( ! $rule->is_valid() ) {
return $this;
}
/**
* @var Rule $r
*/
foreach ( $this->rules as $r ) {
if ( $r->is_valid() && $r->is_equal_to( $rule ) ) {
return $this;
}
}
$this->rules[] = $rule;
return $this;
}
/**
* Identify all the sets that contain the given object
*
* @param \WP_Post|\WP_Term $post_or_term
* @param string $return_type what to return - objects or IDs
*
* @return array
*/
public static function identify_from_object( $post_or_term, $return_type = 'objects' ) {
$sets = array();
if ( ! $post_or_term instanceof \WP_Post && ! $post_or_term instanceof \WP_Term ) {
return $sets;
}
/**
* @var Set $set
*/
foreach ( static::get_items() as $set ) {
if ( $set->has_matching_rules( $post_or_term ) ) {
$sets[] = $return_type === 'objects' ? $set : $set->ID;
}
}
return $sets;
}
/**
* @return int|\WP_Error
*/
public function create() {
$rules = $this->prepare_rules_for_db();
$valid = ! empty( $this->post_title ) && is_array( $rules );
if ( ! $valid ) {
return 0;
}
/**
* We need to make sure that fire_after_hooks is false because other plugins also call the create method on save_post hooks
*/
return wp_insert_post( array(
'post_title' => $this->post_title,
'post_content' => serialize( $rules ),
'post_type' => static::POST_TYPE,
'post_status' => 'publish',
), false, false );
}
/**
* @return array|false|\WP_Post|null
*/
public function delete() {
/**
* Fired before completely deleting a content set from the database.
*
* @param Set $instance the content set instance
*/
do_action( 'tvd_content_set_before_delete', $this );
return wp_delete_post( $this->ID, true );
}
/**
* @return int|\WP_Error
*/
public function update() {
/**
* Triggered before a content set is updated
*
* @param Set $instance the content set instance
*/
do_action( 'tvd_content_set_before_update', new static( $this->ID ) );
$rules = $this->prepare_rules_for_db();
$valid = ! empty( $this->post_title ) && is_array( $rules );
if ( ! $valid ) {
return 0;
}
/**
* We need to make sure that fire_after_hooks is false because other plugins also call the update method on save_post hooks
*/
$result = wp_update_post( array(
'ID' => $this->ID,
'post_title' => $this->post_title,
'post_content' => serialize( $rules ),
), false, false );
if ( ! is_wp_error( $result ) ) {
/**
* Triggered before a content set is updated
*
* @param Set $instance the content set instance
*/
do_action( 'tvd_content_set_after_update', $this );
}
return $result;
}
/**
* Returns the rules if the rules are valid or false otherwise
* Prepares the rules for database
*
* @return bool|array
*/
private function prepare_rules_for_db() {
$valid = true;
$rules = array();
/**
* @var Post_Rule|Term_Rule $rule
*/
foreach ( $this->rules as $rule ) {
if ( ! $rule->is_valid() ) {
$valid = false;
break;
}
$rules[] = $rule->jsonSerialize( false );
}
if ( $valid ) {
return $rules;
}
return false;
}
public function get_tva_courses_ids() {
$id_pairs = array();
foreach ( $this->rules as $rule ) {
if ( true === $rule instanceof Term_Rule && $rule->get_content() === 'tva_courses' ) {
if ( $rule->field === Rule::FIELD_TITLE ) {
$entries = $rule->get_value();
} else {
//Dynamic stuff
//Fetching courses based on dynamic properties -> such as difficulty, label, topic or author
$course_terms = get_terms( [
'taxonomy' => 'tva_courses',
'hide_empty' => false,
'meta_query' => [
'tva_status' => [
'key' => 'tva_status',
'value' => 'private',
'compare' => '!=',
],
],
] );
$entries = [];
foreach ( $course_terms as $course_term ) {
if ( $rule->matches( $course_term ) ) {
$entries[] = [ 'id' => $course_term->term_id ];
}
}
}
if ( ! empty( $entries ) && is_array( $entries ) && is_array( $entries[0] ) && array_key_exists( 'id', $entries[0] ) ) {
$entries = array_column( $entries, 'id' );
}
$id_pairs [] = empty( $entries ) || ! is_array( $entries ) ? [] : $entries;
}
}
if ( empty( $id_pairs ) ) {
return array();
}
return array_merge( ...$id_pairs );
}
/**
* @return array
*/
#[\ReturnTypeWillChange]
public function jsonSerialize() {
return array(
'ID' => $this->ID,
'post_title' => $this->post_title,
'post_content' => $this->rules,
);
}
/**
* Identify the content sets that have rules matching the current request and store it in a local cache for further calls during this request
*
* @return int[]
*/
public static function get_for_request() {
if ( ! did_action( 'parse_query' ) && ! wp_doing_ajax() ) {
trigger_error( 'Content Sets: get_for_request() called incorrectly. It must be called after the `parse_query` hook', E_USER_WARNING );
return [];
}
if ( wp_doing_ajax() ) {
// TODO find a way to reliably match the main request
}
/* search in local cache first */
if ( static::$matched !== null ) {
return static::$matched;
}
/* nothing in the local cache, compute it */
static::$matched = [];
/* currently, only supports taxonomy terms and posts */
if ( Utils::is_context_supported() ) {
$queried_object = get_queried_object();
static::$matched = empty( $queried_object ) ? static::get_for_non_object() : static::get_for_object( $queried_object );
}
return static::$matched;
}
/**
* @param \WP_Post|\WP_Term $object
* @param int|null $id
*/
public static function get_for_object( $object, $id = null ) {
if ( $id === null ) {
$id = get_queried_object_id();
}
try {
$cache = content_set_cache( $object );
} catch ( Cache_Exception $e ) {
/* when receiving invalid data, make sure the execution continues */
return [];
}
$matched = $cache->get_or_store( $id, static function () use ( $object ) {
return static::identify_from_object( $object, 'ids' );
} );
if ( $cache->hit() ) {
/**
* This generates unused queries
* The purpose of this functionality is to fetch content sets with dynamic data - such as pusblied_date
* This functionality was removed from the initial content sets release
*/
// /**
// * We need to find all sets that are matched and the rule contains the time related rules
// */
// $sets = static::get_items( array( 'post__in' => $matched, 's' => Rule::FIELD_PUBLISHED_DATE ) );
//
// /**
// * For time related rules we apply again the match logic
// *
// * @var $set Set
// */
// foreach ( $sets as $set ) {
// if ( ! $set->has_matching_rules( $object ) ) {
// /**
// * If the rules has no matches, we exclude it from the matched list
// */
// $index = array_search( $set->ID, $matched );
// unset( $matched[ $index ] );
// }
// }
$matched = array_values( $matched ); //We need this to reset the indexes
}
return $matched;
}
/**
* Returns sets with dynamic contexts
* Ex: sets that have Search Result Page or Blog Page as rules.
*
* @return array
*/
public static function get_for_non_object() {
$sets = array();
/**
* @var null|\WP_User $maybe_user
*/
$maybe_user = null;
if ( is_author() ) {
$maybe_user = get_user_by( 'slug', get_query_var( 'author_name' ) );
}
foreach ( static::get_items() as $set ) {
if ( $set->has_matching_rules( $maybe_user ) ) {
$sets[] = $set->ID;
}
}
return $sets;
}
}

View File

@@ -0,0 +1,227 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
namespace TVD\Content_Sets;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class Term_Rule
*
* @package TVD\Content_Sets
* @project : thrive-dashboard
*/
class Term_Rule extends Rule {
private $option_fields
= array(
'tva_courses' => array(
self::FIELD_DIFFICULTY,
self::FIELD_TOPIC,
self::FIELD_LABEL,
),
);
/**
* Should be extended in child classes
*
* @param string $query_string
* @param bool|int $paged if non-false, it will return limited results
* @param int $per_page number of results per page. ignored if $paged = false
*
* @return array
*/
public function get_items( $query_string = '', $paged = false, $per_page = 15 ) {
/**
* Needed for the filter
*/
$this->query_string = $query_string;
$response = array();
if ( $this->should_search_options() ) {
$response = $this->search_option_fields();
} elseif ( $this->should_search_users() ) {
$response = parent::search_users();
} else {
add_filter( 'terms_clauses', array( $this, 'filter_terms_clauses' ), 10, 3 );
$query = new \WP_Term_Query();
$args = array(
'taxonomy' => $this->content,
'hide_empty' => 0,
);
if ( $paged !== false ) {
$args['number'] = $per_page;
$args['offset'] = ( $paged - 1 ) * $per_page;
}
$terms = $query->query( $args );
remove_filter( 'terms_clauses', array( $this, 'filter_terms_clauses' ), 10 );
foreach ( $terms as $term ) {
/**
* Allow other plugins to hook here and remove the term from the content set dropdown
*
* @param boolean return value
* @param \WP_Term $term
*/
if ( ! apply_filters( 'tvd_content_sets_allow_select_term', true, $term ) ) {
continue;
}
$response[] = array(
'id' => $term->term_id,
'text' => $this->alter_frontend_title( $term->name, $this->get_term_status( $term ) ),
);
}
}
return $response;
}
/**
* @param \WP_Term $term
*
* @return string
*/
private function get_term_status( $term ) {
/**
* Returns the status for the term
*
* @param string $status
* @param \WP_Term $term
*/
return apply_filters( 'tvd_content_sets_get_term_status', 'publish', $term );
}
/**
* @param $pieces
* @param $taxonomies
* @param $args
*
* @return mixed
*/
public function filter_terms_clauses( $pieces, $taxonomies, $args ) {
global $wpdb;
if ( $this->field === self::FIELD_TITLE ) {
$operation = $this->operator === self::OPERATOR_IS ? 'LIKE' : 'NOT LIKE';
$pieces['where'] .= ' AND lower(name) ' . $operation . ' ' . '\'%' . esc_sql( $wpdb->esc_like( strtolower( $this->query_string ) ) ) . '%\' ';
}
return $pieces;
}
/**
* Prepares the item for front-end
*
* @param int $item
*
* @return array
*/
public function get_frontend_item( $item ) {
if ( $this->should_search_options() ) {
$items = $this->get_option_fields();
if ( empty( $items ) || empty( $items[ $item ] ) ) {
return array();
}
return array(
'id' => (string) $item,
'text' => $items[ $item ],
);
}
if ( $this->should_search_users() ) {
return parent::get_frontend_user( $item );
}
return $this->get_frontend_term( $item );
}
/**
* Constructs the item from a term, needed for front-end
*
* @param int $item
*
* @return array
*/
public function get_frontend_term( $item ) {
$term = get_term( $item );
if ( empty( $term ) || is_wp_error( $term ) ) {
return array();
}
return array(
'id' => $term->term_id,
'text' => $this->alter_frontend_title( $term->name, $this->get_term_status( $term ) ),
);
}
/**
* @return array
*/
private function search_option_fields() {
$items = $this->get_option_fields();
$response = array();
foreach ( $items as $ID => $title ) {
if ( stripos( $title, $this->query_string ) !== false ) {
$response[] = array(
'id' => (string) $ID, //can be also 0 from the DB
'text' => $title,
);
}
}
return $response;
}
/**
* @return array
*/
private function get_option_fields() {
return apply_filters( 'tvd_content_sets_get_option_fields', [], $this );
}
/**
* Returns true if the system should search the option table for values
*
* @return bool
*/
private function should_search_options() {
return ! empty( $this->option_fields[ $this->content ] ) && in_array( $this->field, $this->option_fields[ $this->content ] );
}
/**
* Test if a rule matches the given params
*
* @param int|string $value
* @param \WP_Post|\WP_Term $post_or_term
*
* @return bool
*/
public function match_value( $value, $post_or_term ) {
if ( $this->should_search_options() || $this->should_search_users() ) {
$field_value = apply_filters( 'tvd_content_sets_field_value', '', $this, $post_or_term );
if ( $this->operator === self::OPERATOR_IS ) {
return in_array( $field_value, $this->value );
}
return ! in_array( $field_value, $this->value );
}
return parent::match_value( $value, $post_or_term );
}
}

View File

@@ -0,0 +1,52 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
namespace TVD\Content_Sets;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class Set_Utils
*
* @package TVD\Content_Sets
* @project : thrive-dashboard
*/
class Utils {
/**
* Currently, only supports taxonomy terms and posts
*
* @return bool
*/
public static function is_context_supported() {
return is_singular() || is_tax() || is_category() || is_home() || is_search() || is_author();
}
/**
* @param \WP_Post| \WP_Term $post_or_term
*
* @return array
*/
public static function get_post_or_term_parts( $post_or_term ) {
$type = '';
$id = 0;
if ( $post_or_term instanceof \WP_Post ) {
$type = $post_or_term->post_type;
$id = $post_or_term->ID;
} else if ( $post_or_term instanceof \WP_Term ) {
$type = $post_or_term->taxonomy;
$id = $post_or_term->term_id;
}
return array(
$type,
$id,
);
}
}

View File

@@ -0,0 +1,249 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class TVD_Content_Sets_Controller
*
* @project: thrive-dashboard
*/
class TVD_Content_Sets_Controller extends WP_REST_Controller {
/**
* @var string Base name
*/
public $rest_base = 'content-sets';
protected $version = 1;
protected $namespace = 'tss/v';
/**
* Register routes function
*/
public function register_routes() {
register_rest_route( $this->namespace . $this->version, $this->rest_base . '/normalize-rule', array(
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array( $this, 'normalize_rule' ),
'permission_callback' => array( $this, 'permission_check' ),
'args' => array(
'content_type' => array(
'type' => 'string',
'required' => true,
'enum' => array(
'post',
'term',
'archive',
\TVD\Content_Sets\Rule::HIDDEN_POST_TYPE_SEARCH_RESULTS,
\TVD\Content_Sets\Rule::HIDDEN_POST_TYPE_BLOG,
),
),
'content' => array(
'type' => 'string',
'required' => true,
),
'field' => array(
'type' => 'string',
'required' => true,
),
'operator' => array(
'type' => 'string',
'required' => true,
),
'value' => array(
'type' => array( 'string', 'array' ),
'default' => '',
),
),
),
) );
register_rest_route( $this->namespace . $this->version, $this->rest_base, array(
array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => array( $this, 'create_item' ),
'permission_callback' => array( $this, 'permission_check' ),
'args' => array(
'post_title' => array(
'type' => 'string',
'required' => true,
),
'post_content' => array(
'type' => 'array',
'required' => true,
),
),
),
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'search_items' ),
'permission_callback' => array( $this, 'permission_check' ),
'args' => array(
'content_type' => array(
'type' => 'string',
'required' => true,
'enum' => array( 'post', 'term', 'archive' ),
),
'content' => array(
'type' => 'string',
'required' => true,
),
'field' => array(
'type' => 'string',
'required' => true,
),
'operator' => array(
'type' => 'string',
'required' => true,
),
'value' => array(
'type' => array( 'string', 'array' ),
'default' => '',
),
'query_string' => array(
'type' => 'string',
'required' => true,
),
),
),
) );
register_rest_route( $this->namespace . $this->version, $this->rest_base . '/(?P<ID>.+)', array(
array(
'methods' => WP_REST_Server::DELETABLE,
'callback' => array( $this, 'delete_item' ),
'permission_callback' => array( $this, 'permission_check' ),
'args' => array(
'ID' => array(
'type' => 'integer',
'required' => true,
),
),
),
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array( $this, 'update_item' ),
'permission_callback' => array( $this, 'permission_check' ),
'args' => array(
'ID' => array(
'type' => 'integer',
'required' => true,
),
'post_title' => array(
'type' => 'string',
'required' => true,
),
'post_content' => array(
'type' => 'array',
'required' => true,
),
),
),
) );
}
/**
* rename this into search posts/ search items
*
* @param WP_REST_Request $request
*
* @return WP_REST_Response
*/
public function search_items( $request ) {
$rule = \TVD\Content_Sets\Rule::factory( $request->get_params() );
$paged = false;
if ( $request->get_param( 'page' ) ) {
$paged = (int) $request->get_param( 'page' );
$per_page = (int) $request->get_param( 'per_page' ) ?: 150;
}
return new WP_REST_Response( $rule->get_items( $request->get_param( 'query_string' ), $paged, $per_page ), 200 );
}
/**
* @param WP_REST_Request $request
*
* @return WP_Error|WP_REST_Response
*/
public function create_item( $request ) {
$set = new \TVD\Content_Sets\Set( $request->get_params() );
$new_set_id = $set->create();
$cache_plugin = tve_dash_detect_cache_plugin();
if ( $cache_plugin ) {
tve_dash_cache_plugin_clear( $cache_plugin );
}
if ( ! empty( $new_set_id ) ) {
return new WP_REST_Response( \TVD\Content_Sets\Set::get_items(), 200 );
}
return new WP_Error( 'cant-create', __( 'The request contains invalid rules', 'thrive-dash' ), array( 'status' => 422 ) );
}
/**
* @param WP_REST_Request $request
*
* @return WP_REST_Response
*/
public function delete_item( $request ) {
$set = new \TVD\Content_Sets\Set( $request->get_param( 'ID' ) );
$response = $set->delete();
return new WP_REST_Response( $response, 200 );
}
/**
* @param WP_REST_Request $request
*
* @return WP_Error|WP_REST_Response
*/
public function update_item( $request ) {
$set = new \TVD\Content_Sets\Set( $request->get_params() );
$set_id = $set->update();
$cache_plugin = tve_dash_detect_cache_plugin();
if ( $cache_plugin ) {
tve_dash_cache_plugin_clear( $cache_plugin );
}
if ( ! empty( $set_id ) ) {
return new WP_REST_Response( ! empty( $request->get_param( 'return_one' ) ) ? $set->jsonSerialize() : \TVD\Content_Sets\Set::get_items(), 200 );
}
return new WP_Error( 'cant-update', __( 'The request contains invalid rules', 'thrive-dash' ), array( 'status' => 422 ) );
}
/**
* Normalizing a rule so that the rule contains all information
*
* Mainly this is for content_label rule key.
*
* @param WP_REST_Request $request
*
* @return WP_REST_Response
*/
public function normalize_rule( $request ) {
$rule = \TVD\Content_Sets\Rule::factory( $request->get_params() );
return new WP_REST_Response( $rule, 200 );
}
/**
* If the user has access to the admin pages, then he is allowed to perform any operation on the scripts.
*
* @return bool
*/
public function permission_check() {
return current_user_can( TVE_DASH_CAPABILITY );
}
}

View File

@@ -0,0 +1,148 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
class TVD_Fields_Controller extends TVD_REST_Controller {
/**
* @var string Base name
*/
public $base = 'fields';
/**
* Register Routes
*/
public function register_routes() {
register_rest_route( static::$namespace . static::$version, '/' . $this->base, array(
array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => array( $this, 'add_field' ),
'permission_callback' => array( $this, 'fields_permissions_check' ),
'args' => array(),
),
) );
register_rest_route( static::$namespace . static::$version, '/' . $this->base . '/(?P<id>[\d]+)', array(
array(
'methods' => WP_REST_Server::DELETABLE,
'callback' => array( $this, 'delete_field' ),
'permission_callback' => array( $this, 'fields_permissions_check' ),
'args' => array(),
),
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array( $this, 'edit_field' ),
'permission_callback' => array( $this, 'fields_permissions_check' ),
'args' => array(),
),
) );
register_rest_route( static::$namespace . static::$version, '/' . $this->base . '/save_fields/', array(
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array( $this, 'save_fields' ),
'permission_callback' => array( $this, 'fields_permissions_check' ),
'args' => array(),
),
) );
}
/**
* Add multiple fields at once
*
* @param $request WP_REST_Request
*
* @return WP_Error|WP_REST_Response
*/
public function save_fields( $request ) {
$models = $request->get_param( 'models' );
$response = array();
foreach ( $models as $model ) {
$data = TVD_Smart_DB::save_field( $model, empty( $model['id'] )? 'insert': 'update' );
if ( $data ) {
$response[] = $data;
} else {
return new WP_Error( 'error', __( 'Something went wrong while saving the fields, please refresh and try again. If the problem persists please contact our support team.', 'thrive-dash' ) );
}
}
return new WP_REST_Response( $response, 200 );
}
/**
* @param $request WP_REST_Request
*
* @return WP_Error|WP_REST_Response
*/
public function add_field( $request ) {
$model = $request->get_params();
$model = TVD_Smart_DB::save_field( $model, 'insert' );
if ( $model ) {
return new WP_REST_Response( $model, 200 );
}
return new WP_Error( 'no-results', __( 'The group was not added, please try again !', 'thrive-dash' ) );
}
/**
* Delete a group and all it's fields
*
* @param $request
*
* @return WP_Error|WP_REST_Response
*/
public function delete_field( $request ) {
$id = $request->get_param( 'id' );
$result = TVD_Smart_DB::delete_field( $id );
if ( $result ) {
return new WP_REST_Response( true, 200 );
}
return new WP_Error( 'no-results', __( 'No field was deleted!', 'thrive-dash' ) );
}
/**
* Edit a group
*
* @param $request WP_REST_Request
*
* @return WP_Error|WP_REST_Response
*/
public function edit_field( $request ) {
$model = $request->get_params();
$model = TVD_Smart_DB::save_field( $model, 'update' );
if ( $model ) {
return new WP_REST_Response( $model, 200 );
}
return new WP_Error( 'no-results', __( 'No group was updated!', 'thrive-dash' ) );
}
/**
* Permissions check
*
* @param $request
*
* @return bool
*/
public function fields_permissions_check( $request ) {
return current_user_can( TVE_DASH_CAPABILITY );
}
}

View File

@@ -0,0 +1,116 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden
}
class TVD_Groups_Controller extends TVD_REST_Controller {
/**
* @var string Base name
*/
public $base = 'groups';
/**
* Register Routes
*/
public function register_routes() {
register_rest_route( static::$namespace . static::$version, '/' . $this->base, array(
array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => array( $this, 'add_group' ),
'permission_callback' => array( $this, 'groups_permissions_check' ),
'args' => array(),
),
) );
register_rest_route( static::$namespace . static::$version, '/' . $this->base . '/(?P<id>[\d]+)', array(
array(
'methods' => WP_REST_Server::DELETABLE,
'callback' => array( $this, 'delete_group' ),
'permission_callback' => array( $this, 'groups_permissions_check' ),
'args' => array(),
),
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array( $this, 'edit_group' ),
'permission_callback' => array( $this, 'groups_permissions_check' ),
'args' => array(),
),
) );
}
/**
* Add a group
*
* @param $request WP_REST_Request
*
* @return WP_Error|WP_REST_Response
*/
public function add_group( $request ) {
$model = $request->get_params();
$model = TVD_Smart_DB::insert_group( $model );
if ( $model ) {
return new WP_REST_Response( $model, 200 );
}
return new WP_Error( 'no-results', __( 'The group was not added, please try again !', 'thrive-dash' ) );
}
/**
* Delete a group and all it's fields
*
* @param $request
*
* @return WP_Error|WP_REST_Response
*/
public function delete_group( $request ) {
$id = $request->get_param( 'id' );
$result = TVD_Smart_DB::delete_group( $id );
if ( $result ) {
return new WP_REST_Response( true, 200 );
}
return new WP_Error( 'no-results', __( 'No group was deleted!', 'thrive-dash' ) );
}
/**
* Edit a group
*
* @param $request WP_REST_Request
*
* @return WP_Error|WP_REST_Response
*/
public function edit_group( $request ) {
$model = $request->get_params();
$model = TVD_Smart_DB::update_group( $model );
if ( $model ) {
return new WP_REST_Response( $model, 200 );
}
return new WP_Error( 'no-results', __( 'No group was updated!', 'thrive-dash' ) );
}
/**
* Permissions check
*
* @param $request
*
* @return bool
*/
public function groups_permissions_check( $request ) {
return current_user_can( TVE_DASH_CAPABILITY );
}
}

View File

@@ -0,0 +1,28 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
?>
<div id="tvd-contents-sets">
<?php wp_nonce_field( TVD_Content_Sets::META_BOX_NONCE, 'tvd_content_sets_meta_box' ); ?>
<p>
<?php echo sprintf( __( 'If this post has been added to a content set by matching a dynamic rule, the content set will not be displayed here. View your content set rules from %s.', 'thrive-dash' ),
'<strong>' . __( 'Thrive Dashboard > Smart Site', 'thrive-dash' ) . '</strong>' ); ?>
</p>
<p>
<?php echo esc_html__( 'This post has been added directly to the following content sets:', 'thrive-dash' ); ?>
</p>
<div>
<input placeholder="<?php echo esc_html__( 'Search content sets', 'thrive-dash' ) ?>" id="tvd-content-sets-autocomplete"/>
</div>
<div id="tvd-matched-content-sets"></div>
</div>