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,84 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-visual-editor
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class TCB_Post_List_Author_Image
*/
class TCB_Post_List_Author_Image {
const PLACEHOLDER_URL = 'editor/css/images/author_image.png';
/**
* Get the default author image url, or the author image url for a specific post
*
* @param $post_id
*
* @return string
*/
public static function get_default_url( $post_id = null ) {
$url = tve_editor_url( static::PLACEHOLDER_URL );
if ( ! empty( $post_id ) ) {
$post = get_post( $post_id );
if ( $post ) {
$avatar_data = get_avatar_data( $post->post_author, [ 'size' => 256 ] );
if ( ! empty( $avatar_data['url'] ) && ! is_wp_error( $avatar_data['url'] ) ) {
$url = $avatar_data['url'];
}
}
}
return $url;
}
/**
* Returns post author avatar
*
* @return false|string
*/
public static function author_avatar() {
global $post;
if ( empty( $post ) ) {
$avatar_url = '';
} else {
// check if author option is turned "on" under settings.
if ( ! get_option( 'show_avatars' ) ) {
return tve_editor_url( static::PLACEHOLDER_URL );
}
/**
* Allow dynamic implementations for post_authors.
*
* @param string $post_author
*/
$post_author = apply_filters( 'tcb_dynamic_field_author', $post->post_author );
$avatar = get_avatar( $post_author, 256 );
preg_match( '/src=\'([^\']*)\'/m', $avatar, $matches );
if ( empty( $matches[1] ) ) {
$avatar_url = get_avatar_url( $post->post_author, [ 'size' => 256 ] );
} else {
$avatar_url = html_entity_decode( $matches[1] );
}
/* if we're in the editor, append a dynamic flag at the end so we can recognize that the URL is dynamic */
if ( TCB_Utils::in_editor_render( true ) ) {
$avatar_url = add_query_arg( [
'dynamic_author' => 1,
], $avatar_url );
}
}
return $avatar_url;
}
}

View File

@@ -0,0 +1,358 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-visual-editor
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class TCB_Post_List_Content
*/
class TCB_Post_List_Content {
/* the default read more is blank */
public static $default_read_more = '';
/**
* Some patterns to remove in the case of excerpt and words.
*
* @var array
*/
static public $patterns_to_remove = array(
'/___TVE_SHORTCODE_RAW__(.+?)__TVE_SHORTCODE_RAW___/s',
'#__CONFIG_group_edit__(.+?)__CONFIG_group_edit__#',
'#__CONFIG_colors_palette__(.+?)__CONFIG_colors_palette__#',
'#__CONFIG_local_colors__(.+?)__CONFIG_local_colors__#',
/* some custom menus are saved without the __CONFIG_widget_menu key (in symbols, this is a problem) - we need to get rid of all json-encoded string from the content */
'#{"menu_id"(.+)(true|false|null|"|\'|\d|]|})}#',
/* Custom menu - REST API version - encoded json */
'@{&#8220;menu_id&#8221;(.+)(true|false|null|&#8221;|\d|]|})}@',
/* the audio shortcode doesn't really behave well, so it's better to just remove it */
'#<audio(.*?)><\/audio>#',
);
/**
* Return the post content after calculating excerpt / word counts.
*
* @param $attr
*
* @return mixed
*/
public static function get_content( $attr ) {
global $tcb_read_more_link;
global $post;
/**
* Do a wp-query post-data setup for the current post;
* If this is a post list and the loop finishes, we call setup_postdata again after the loop ( @see TCB_Post_List::render() ) in order to restore things to normal
*/
setup_postdata( $post );
/* if the post content is not inside the post list, it means this is called from TTB -> return the regular full content without changing anything */
if ( TCB_Post_List::is_outside_post_list_render() ) {
$element_class = 'tcb-post-content' . ' ' . TCB_SHORTCODE_CLASS;
if ( is_editor_page() ) {
$element_class .= ' tcb-no-delete tcb-selector-no_clone';
}
$content = TCB_Post_List_Shortcodes::shortcode_function_content( 'the_content' );
$content .= wp_link_pages(
array(
'before' => '<nav class="post-nav-links bg-light-background" aria-label="' . esc_attr__( 'Page', 'thrive-cb' ) . '">
<span class="label">' . __( 'Pages:', 'thrive-cb' ) . '</span>',
'after' => '</nav>',
'link_before' => '<span class="page-number">',
'link_after' => '</span>',
'echo' => false,
)
);
/**
* Allows other functionality to hook here
*
* Used in TA Visual builder to completely hide post content from frontend if is empty
*
* @param string $post_content
* @param string $content
*/
return apply_filters( 'tcb_get_post_content', TCB_Post_List_Shortcodes::before_wrap( [
'content' => $content,
'tag' => 'section',
'class' => $element_class,
], $attr ), trim( $content ) );
}
$attr = array_merge( static::default_attr(), $attr );
$is_editor_page = TCB_Utils::in_editor_render();
if ( $is_editor_page ) {
/* we don't add read more inside the editor so we can better handle the post_content and modify the read more text */
$tcb_read_more_link = ' ';
} else {
$tcb_read_more_link = TCB_Utils::wrap_content( $attr['read_more'], 'a', '', 'more-link', array( 'href' => get_the_permalink() . '#more-' . get_the_ID() ) );
}
/* if the post has a tcb read more element, display the content only until there. filter is called in functions.php */
add_filter( 'tcb_force_excerpt', '__return_true' );
/* remove the 'Continue reading' text added by the TAr more tag */
add_filter( 'the_content_more_link', [ __CLASS__, 'the_content_more_link_filter' ] );
$is_rest = TCB_Utils::is_rest();
/* if this is a REST request, we have to manually add the tve_editor_content to the content filter in order for it to cut off the excerpt */
if ( $is_rest && ! doing_filter( 'the_content' ) ) {
add_filter( 'the_content', 'tve_editor_content' );
/*
* This filter is added along with the content filter on the frontend, so we add it here too
* It seems to remove duplicated content ( if this is not added, the content is shown twice in the editor )
*/
add_filter( 'the_content', 'tve_clean_wp_editor_content', - 100 );
}
switch ( $attr['size'] ) {
case 'excerpt':
$content = static::get_excerpt( $tcb_read_more_link );
break;
case 'content':
$content = static::get_full_content( $attr );
break;
case 'words':
default:
$content = static::get_words( $attr['words'], $tcb_read_more_link );
}
/* remove all the filters that we added before */
if ( $is_rest && ! doing_filter( 'the_content' ) ) {
remove_filter( 'the_content', 'tve_editor_content' );
remove_filter( 'the_content', 'tve_clean_wp_editor_content', - 100 );
}
remove_filter( 'tcb_force_excerpt', '__return_true' );
remove_filter( 'the_content_more_link', [ __CLASS__, 'the_content_more_link_filter' ] );
$classes = 'tcb-post-content ' . TCB_SHORTCODE_CLASS . ( $is_editor_page ? ' tcb-post-content-placeholder' : '' );
return TCB_Post_List_Shortcodes::before_wrap( [
'content' => $content,
'tag' => 'section',
'class' => $classes,
], $attr );
}
/**
* Get the styles used by the current post.
*
* @param $post_id
*
* @return string
*/
public static function get_post_styles( $post_id ) {
$global_css_prefix = tcb_selection_root();
$selector = apply_filters( 'tcb_global_styles_selector', $global_css_prefix );
$post_css = tve_get_post_meta( $post_id, 'tve_custom_css', true );
return sprintf( '<style type="text/css" class="tve_custom_style">%s</style>', str_replace( $global_css_prefix, $selector, $post_css ) );
}
/**
* Get the excerpt.
*
* @param $tcb_read_more_link
*
* @return mixed|string|string[]|null
*/
public static function get_excerpt( $tcb_read_more_link ) {
/* we add this filter so functions will use our read more text and we remove it later */
add_filter( 'excerpt_more', [ __CLASS__, 'excerpt_more_filter' ] );
$content = TCB_Post_List_Shortcodes::shortcode_function_content( 'the_excerpt' );
$tcb_enabled = (int) get_post_meta( get_the_ID(), 'tcb_editor_enabled', true );
/* if there's no content for this post, and it is a WP Post, and we're in the editor, take the original WP content */
if ( empty( $content ) && is_editor_page() && ! $tcb_enabled ) {
global $post;
$content = $post->post_content;
}
$content = static::replace_tve_shortcodes( $content );
$content = str_replace( ']]>', ']]&gt;', $content );
$content = static::filter_content( $content );
/* replace these manually in case preg_replace only found the start or the end _config_ */
$content = str_replace( [ '___TVE_SHORTCODE_RAW__', '__TVE_SHORTCODE_RAW___' ], '', $content );
$content = strip_shortcodes( $content );
/* make sure we always display the read more link, and only add it if it doesn't exist in the content already */
if (
strpos( $content, $tcb_read_more_link ) === false &&
/* also check the 'decoded' version of the content in case '...' was turned into '&#8230;' */
strpos( str_replace( '&#8230;', '...', $content ), $tcb_read_more_link ) === false
) {
/* if there are no paragraphs in the content, just add the read more at the end */
if ( strpos( $content, '<\/p>' ) === false ) {
$content .= $tcb_read_more_link;
} else {
/* if there are paragraphs, insert the read more before the closing of the last paragraph tag */
$content = preg_replace( '/<\/p>$/', " {$tcb_read_more_link}$0", trim( $content ) );
}
}
remove_filter( 'excerpt_more', [ __CLASS__, 'excerpt_more_filter' ] );
$tcb_read_more_link = null;
return $content;
}
/**
* Get the full content and prepend the styles used for this content.
*
* @param $attr
*
* @return string
*/
public static function get_full_content( $attr ) {
$content = TCB_Post_List_Shortcodes::shortcode_function_content( 'the_content', [ $attr['read_more'] ] );
$content_text = trim( str_replace( $attr['read_more'], '', strip_tags( $content ) ) );
if ( empty( $content_text ) ) {
if ( TCB_Editor()->is_inner_frame() ) {
$content = TCB_Utils::wrap_content( __( 'No Post Content', 'thrive-cb' ), 'p' );
}
} else {
$queried_object = get_queried_object();
$post_id = get_the_ID();
/* get the styles used by this post, but only if it isn't on the post with the post list that we're rendering */
if ( ( ! empty( $queried_object ) && property_exists( $queried_object, 'ID' ) && $post_id !== $queried_object->ID ) || ! is_singular() ) {
$content = static::get_post_styles( $post_id ) . $content;
}
}
return $content;
}
/**
* Get the first x words of the content.
*
* @param $words
* @param $tcb_read_more_link
*
* @return mixed|string
*/
public static function get_words( $words, $tcb_read_more_link ) {
$tcb_enabled = (int) get_post_meta( get_the_ID(), 'tcb_editor_enabled', true );
/* if architect is enabled, the 'plain text' content is kept in post_content, so we use that */
if ( $tcb_enabled ) {
global $post;
$content = $post->post_content;
}
/* if the content is empty / architect is not enabled, get the content through some filters */
if ( empty( $content ) ) {
$content = TCB_Post_List_Shortcodes::shortcode_function_content( 'the_content' );
}
$content = strip_shortcodes( $content );
$content = static::replace_tve_shortcodes( $content );
$content = str_replace( ']]>', ']]&gt;', $content );
$content = static::filter_content( $content );
$content = wp_trim_words( $content, $words, '' ) . ' ' . $tcb_read_more_link;
$content = TCB_Utils::wrap_content( $content, 'p' );
return $content;
}
/**
* Remove some config text.
*
* @param $content
*
* @return string|string[]|null
*/
public static function filter_content( $content ) {
foreach ( static::$patterns_to_remove as $pattern ) {
$content = preg_replace( $pattern, '', $content );
}
return $content;
}
/**
* The TVE shortcodes have no brackets (they are stored with __CONFIG__ around them), so they cant be modified with the wordpress shortcode functions.
* (the tve_thrive_shortcodes() function cannot be used because the regex only works if the config is wrapped in something, and that doesn't happen in the editor)
*
* @param $content
*
* @return string|string[]|null
*/
public static function replace_tve_shortcodes( $content ) {
global $tve_thrive_shortcodes;
foreach ( $tve_thrive_shortcodes as $shortcode => $callback ) {
$content = preg_replace( '/__CONFIG_' . $shortcode . '__(.+?)__CONFIG_' . $shortcode . '__/', '', $content );
}
return $content;
}
/**
* Add read more text to the excerpt
*
* @param $read_more
*
* @return mixed
*/
public static function excerpt_more_filter( $read_more ) {
global $tcb_read_more_link;
if ( $tcb_read_more_link !== null ) {
$read_more = ' ' . $tcb_read_more_link;
}
return $read_more;
}
/**
* Remove the WP more tag. (it is added in post-template.php, line 349)
*
*
* @return string
*/
public static function the_content_more_link_filter() {
return '';
}
/**
* Default attributes for post list available with filter
*
* @return mixed|void
*/
private static function default_attr() {
return apply_filters( 'tcb_post_list_content_default_attr', [
'size' => 'words',
'read_more' => static::$default_read_more,
'words' => 12,
] );
}
}

View File

@@ -0,0 +1,182 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-theme
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class TCB_Post_List_Featured_Image
*/
class TCB_Post_List_Featured_Image {
const PLACEHOLDER_URL = 'editor/css/images/featured_image.png';
/**
* Get the html for the placeholder of the featured image or the real featured image if we send the post id
*
* @param $post_id
*
* @return string
*/
public static function get_default_url( $post_id = null ) {
$featured_image = tve_editor_url( static::PLACEHOLDER_URL );
if ( ! empty( $post_id ) && has_post_thumbnail( $post_id ) ) {
$post_featured_image = get_the_post_thumbnail_url( $post_id, 'full' );
if ( ! empty( $post_featured_image ) ) {
$featured_image = $post_featured_image;
}
}
return $featured_image;
}
/**
* Get the sizes of the post featured image existent in the website
*
* @param $post_id
*
* @return array
*/
public static function get_sizes( $post_id ) {
$sizes = [];
if ( has_post_thumbnail( $post_id ) ) {
$post_thumbnail_id = get_post_thumbnail_id( $post_id );
$sizes = static::get_image_sizes( $post_thumbnail_id );
}
return $sizes;
}
/**
* Get the available sizes for a certain image
*
* @param $thumb_id
*
* @return array
*/
public static function get_image_sizes( $thumb_id ) {
$sizes = [];
$filter_sizes = static::filter_available_sizes();
$post_thumbnail = get_post( $thumb_id );
$data['media_details'] = wp_get_attachment_metadata( $thumb_id );
if ( ! empty( $data['media_details']['sizes'] ) ) {
foreach ( $data['media_details']['sizes'] as $size => $size_data ) {
/**
* Avoid calling `wp_get_attachment_image_src` for each size if the result is not taken into account
*/
if ( ! isset( $filter_sizes[ $size ] ) ) {
continue;
}
$image_src = wp_get_attachment_image_src( $thumb_id, $size );
if ( ! $image_src ) {
continue;
}
$size_data['url'] = $image_src[0];
$sizes[ $size ] = $size_data;
}
}
/**
* We should always have the full size of the uploaded image
*/
$full_src = wp_get_attachment_image_src( $thumb_id, 'full' );
if ( ! empty( $full_src ) ) {
$sizes['full'] = array(
'file' => wp_basename( $full_src[0] ),
'width' => $full_src[1],
'height' => $full_src[2],
'mime_type' => $post_thumbnail->post_mime_type,
'ID' => $post_thumbnail->ID,
'url' => $full_src[0],
'title' => $post_thumbnail->post_name,
'caption' => $post_thumbnail->post_excerpt,
'alt' => get_post_meta( $post_thumbnail->ID, '_wp_attachment_image_alt', true ),
);
}
return $sizes;
}
/**
* Return only this specific values from the available sizes options
*
* @return array
*/
public static function filter_available_sizes() {
$sizes = apply_filters( 'image_size_names_choose', array(
'thumbnail' => __( 'Thumbnail', 'thrive-cb' ),
'medium' => __( 'Medium', 'thrive-cb' ),
'large' => __( 'Large', 'thrive-cb' ),
'full' => __( 'Full size', 'thrive-cb' ),
) );
/* MailPoet 3 adds an extra image size, but we're not using it in our products so we must remove it */
if ( ! empty( $sizes['mailpoet_newsletter_max'] ) ) {
unset ( $sizes['mailpoet_newsletter_max'] );
}
return $sizes;
}
/**
* Returns a normalized list of all currently registered image sub-sizes.
* wp_get_registered_image_subsizes it's available only from 5.3 so we are offering an alternative if the function it's not available
*
* @return array
*/
public static function get_registered_image_subsizes() {
$sizes = [];
if ( function_exists( 'wp_get_registered_image_subsizes' ) ) {
$sizes = wp_get_registered_image_subsizes();
} else {
$additional_sizes = wp_get_additional_image_sizes();
foreach ( get_intermediate_image_sizes() as $size_name ) {
$size_data = [
'width' => 0,
'height' => 0,
'crop' => false,
];
if ( isset( $additional_sizes[ $size_name ]['width'] ) ) {
// For sizes added by plugins and themes.
$size_data['width'] = intval( $additional_sizes[ $size_name ]['width'] );
} else {
// For default sizes set in options.
$size_data['width'] = intval( get_option( "{$size_name}_size_w" ) );
}
if ( isset( $additional_sizes[ $size_name ]['height'] ) ) {
$size_data['height'] = intval( $additional_sizes[ $size_name ]['height'] );
} else {
$size_data['height'] = intval( get_option( "{$size_name}_size_h" ) );
}
if ( empty( $size_data['width'] ) && empty( $size_data['height'] ) ) {
// This size isn't set.
continue;
}
$sizes[ $size_name ] = $size_data;
}
}
return $sizes;
}
}

View File

@@ -0,0 +1,429 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-visual-editor
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class TCB_Post_List_REST
*/
class TCB_Post_List_REST {
public static $namespace = 'tcb/v1';
public static $route = '/posts';
public function __construct() {
$this->register_routes();
}
public function register_routes() {
register_rest_route( static::$namespace, static::$route, array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => [ $this, 'get_posts' ],
'permission_callback' => '__return_true',
),
) );
register_rest_route( static::$namespace, static::$route . '/html', array(
array(
/* This should be READABLE, but a lot of data is sent through this request, and it is appended in the request URL string.
* Because of the really long URL string, there were 414 errors for some users because the server can block requests like these.
* As a solution, we changed this to CREATABLE ( POST ) so the data is added inside the request */
'methods' => WP_REST_Server::CREATABLE,
'callback' => [ $this, 'get_html' ],
'permission_callback' => '__return_true',
),
) );
register_rest_route( static::$namespace, static::$route . '/terms', array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => [ $this, 'get_terms' ],
'permission_callback' => [ $this, 'route_permission' ],
),
) );
register_rest_route( static::$namespace, static::$route . '/authors', array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => [ $this, 'get_authors' ],
'permission_callback' => [ $this, 'route_permission' ],
),
) );
register_rest_route( static::$namespace, static::$route . '/taxonomies', array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => [ $this, 'get_taxonomies' ],
'permission_callback' => [ $this, 'route_permission' ],
),
) );
}
/**
* Check if a given request has access to route
*
* @param WP_REST_Request $request Full data about the request.
*
* @return WP_Error|bool
*/
public function route_permission( $request ) {
$post_id = isset( $request['post_id'] ) ? $request['post_id'] : null;
return \TCB_Product::has_external_access( $post_id );
}
/**
* Calculate the number of search results that we should return: 3 times the number of searched characters, minimum 20, max. 100.
*
* @param $search - should be a string, if it's not, we return a fixed number.
*
* @return int
*/
private static function get_results_count( $search = '' ) {
if ( is_string( $search ) ) {
$count = min( 100, max( 20, strlen( $search ) * 3 ) );
} else {
$count = 20;
}
return $count;
}
/**
* Get terms from taxonomy
*
* @param WP_REST_Request $request Full data about the request.
*
* @return WP_Error|WP_REST_Response
*/
public function get_terms( $request ) {
$taxonomy = $request->get_param( 'taxonomy' );
$search = $request->get_param( 'search' );
$terms = [];
if ( ! empty( $taxonomy ) ) {
$args = array(
'number' => static::get_results_count( $search ),
'taxonomy' => $taxonomy,
'hide_empty' => false,
);
if ( ! empty( $search ) ) {
$args['search'] = $search;
}
$specific = $request->get_param( 'specific' );
if ( ! empty( $specific ) ) {
$args = array(
'number' => 0,
'hide_empty' => false,
'include' => $request->get_param( 'terms' ),
);
}
$all = get_terms( $args );
$terms = array_map( function ( $item ) {
return [
'value' => $item->term_id,
'label' => $item->name,
];
}, $all );
}
$terms = array_values( $terms );
return new WP_REST_Response( $terms );
}
/**
* Get terms from taxonomy
*
* @param WP_REST_Request $request Full data about the request.
*
* @return WP_Error|WP_REST_Response
*/
public function get_posts( $request ) {
$post_type = $request->get_param( 'post_type' );
$search = $request->get_param( 'search' );
$args = array(
'posts_per_page' => static::get_results_count( $search ),
'orderby' => 'title',
'order' => 'ASC',
'post_type' => $post_type,
);
if ( ! empty( $search ) ) {
$args['s'] = $search;
}
/* this is for when we just want some specific results returned */
$specific = $request->get_param( 'specific' );
if ( ! empty( $specific ) ) {
$args = array(
'number' => 0,
'include' => $request->get_param( 'terms' ),
'post_type' => $post_type,
);
}
$all = get_posts( $args );
$all = apply_filters( 'tcb_filter_rest_products', $all, $request );
$posts = array_map( function ( $item ) {
return [
'value' => $item->ID,
'label' => $item->post_title,
];
}, $all );
$posts = array_values( $posts );
return new WP_REST_Response( $posts );
}
/**
* Get authors of the blog
*
* @param WP_REST_Request $request Full data about the request.
*
* @return WP_Error|WP_REST_Response
*/
public function get_authors( $request ) {
$search = $request->get_param( 'search' );
$args = array( 'number' => static::get_results_count( $search ) );
if ( ! empty( $search ) ) {
$args['search'] = '*' . $search . '*';
}
/* this is for when we just want some specific results returned */
$specific = $request->get_param( 'specific' );
if ( ! empty( $specific ) ) {
$args = array(
'number' => 0,
'include' => $request->get_param( 'terms' ),
);
}
$all = get_users( $args );
$authors = array_map( function ( $item ) {
return [
'value' => $item->ID,
'label' => $item->display_name,
];
}, $all );
$authors = array_values( $authors );
return new WP_REST_Response( $authors );
}
/**
* Get post type taxonomies
*
* @param WP_REST_Request $request Full data about the request.
*
* @return WP_Error|WP_REST_Response
*/
public function get_taxonomies( $request ) {
$post_type = $request->get_param( 'post_type' );
if ( empty( $post_type ) ) {
$taxonomies = [];
} else {
$all = get_object_taxonomies( $post_type, 'object' );
$taxonomies = array_map( function ( $item ) {
return [
'value' => $item->name,
'label' => $item->label,
];
}, $all );
$taxonomies = array_filter( $taxonomies, function ( $taxonomy ) {
$terms = get_terms( [
'taxonomy' => $taxonomy['value'],
'hide_empty' => false,
] );
/* we only return taxonomies that have terms inside them */
return count( $terms ) > 0;
} );
}
$taxonomies = array_values( $taxonomies );
return new WP_REST_Response( $taxonomies );
}
/**
* Get posts filtered by args for the post list
*
* @param WP_REST_Request $request Full data about the request.
*
* @return WP_Error|WP_REST_Response
*/
public function get_html( $request ) {
/* if we send a template parameter, we're going to print the post list after that one */
$content = $request->get_param( 'content' );
if ( ! empty( $content ) ) {
$content = str_replace( [ '{({', '})}' ], [ '[', ']' ], $content );
}
$args = $request->get_param( 'args' );
$args = array_merge(
array(
'attr' => [
'total_sticky_count' => 0,
],
'query' => [],
'identifier' => '',
),
empty( $args ) ? [] : $args
);
$total_posts = ! empty( $args['query']['posts_per_page'] ) ? $args['query']['posts_per_page'] : 10;
/* if the 'get_initial_posts' flag is not active, get the posts normally */
if ( empty( $args['query']['get_initial_posts'] ) ) {
if ( ! isset( $args['query']['rules'] ) ) {
$args['query']['rules'] = [];
}
$posts_per_page = (int) $args['query']['posts_per_page'];
if ( ! empty( $args['query']['sticky'] ) ) {
$query_args_sticky = TCB_Post_List::prepare_wp_query_args_sticky( $args['query'] );
$sticky_posts = get_posts( $query_args_sticky );
/*sticky posts from the current page*/
$number_of_sticky_posts = count( $sticky_posts );
if ( (int) $args['query']['posts_per_page'] !== - 1 ) {
if ( $number_of_sticky_posts > 0 ) {
/*compute the number of 'normal' posts that needs to be added on a page with sticky posts*/
$args['query']['posts_per_page'] -= $number_of_sticky_posts;
/*on a page with sticky posts the first 'normal' post will always have the offset 1*/
$args['query']['offset'] = 1;
} else {
/*compute the offset of the first normal post displayed on a page with only normal posts*/
$args['query']['offset'] = $args['query']['posts_per_page'] * ( $query_args_sticky['paged'] - 1 ) - $args['attr']['total_sticky_count'] + 1;
}
}
} else {
$sticky_posts = [];
$number_of_sticky_posts = 0;
}
/*if we need to add 'normal' posts on a page with sticky posts*/
if ( $posts_per_page !== $number_of_sticky_posts ) {
$query_args = TCB_Post_List::prepare_wp_query_args( $args['query'] );
$filters = ! empty( $args['filters'] ) ? $args['filters'] : [];
$query_args = TCB_Post_List_Filter::filter( $query_args, $filters );
$query = new WP_Query( $query_args );
$posts = $query->posts;
$total_posts = $query->found_posts;
$posts = array_merge( $sticky_posts, $posts );
} else {
$posts = $sticky_posts;
}
} else {
/* if the flag is active, use the default query to get the post info we need */
$post_ids = empty( $args['query']['post_ids'] ) ? [] : $args['query']['post_ids'];
$posts = $this->get_existing_posts( $post_ids );
}
global $post;
TCB_Post_List::enter_post_list_render();
$post_list = new TCB_Post_List( $args['attr'], $content );
$results = [];
foreach ( $posts as $key => $post ) {
if ( empty( $content ) ) {
/* posts are sent as key - value pairs, because it's easier to find them, but we send a parameter of order so we know how to display them */
$results[ get_the_ID() ] = TCB_Post_List::post_info( $key + 1 );
} else {
$results[ $key + 1 ] = $post_list->article_content();
}
}
TCB_Post_List::exit_post_list_render();
return new WP_REST_Response( array(
'total_post_count' => $total_posts,
'posts' => $results,
'count' => count( $results ),
) );
}
/**
* Get the first 7 posts and all the posts that exist in the current page.
*
* @param $post_ids
*
* @return array
*/
public function get_existing_posts( $post_ids ) {
$default_query = TCB_Post_List::get_default_query();
$default_query['offset'] = 0;
$default_query['posts_per_page'] = 7;
/* get the first 7 posts ( 6 + 1 to take into account excluding current post )*/
$first_posts = get_posts( $default_query );
/* no posts? get pages instead */
if ( empty( $first_posts ) ) {
$default_query['post_type'] = 'page';
$first_posts = get_posts( $default_query );
}
if ( ! empty( $post_ids ) ) {
$existing_posts_query = array(
'posts_per_page' => count( $post_ids ),
'post__in' => $post_ids,
'post_status' => 'any',
/* these can also be pages or custom post types */
'post_type' => 'any',
);
$extra_posts = get_posts( $existing_posts_query );
$extra_posts = apply_filters( 'tcb_localize_existing_post_list', $extra_posts, $post_ids );
/* also get the posts that are already in the page ( we have their IDs in 'get_initial_posts' ) */
$first_posts = array_merge( $first_posts, $extra_posts );
}
return $first_posts;
}
}
new TCB_Post_List_REST();

View File

@@ -0,0 +1,124 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-theme
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class TCB_Post_List_Sub_Element_Abstract
*/
abstract class TCB_Post_List_Sub_Element_Abstract extends TCB_Element_Abstract {
/**
* Thrive_Theme_Element_Abstract constructor.
*
* @param string $tag
*/
public function __construct( $tag = '' ) {
parent::__construct( $tag );
add_filter( 'tcb_element_' . $this->tag() . '_config', [ $this, 'add_config' ] );
}
public function add_config( $config ) {
$config['shortcode'] = $this->shortcode();
$config['is_sub_element'] = $this->is_sub_element();
return $config;
}
/**
* Mark this as a sub-element
*
* @return bool
*/
public function is_sub_element() {
return true;
}
/**
* If an element has a shortcode tag (empty by default, override by children who have shortcode tags).
*
* @return bool
*/
public function shortcode() {
return '';
}
/**
* Element category that will be displayed in the sidebar
*
* @return string
*/
public function category() {
return TCB_Post_List::elements_group_label();
}
/**
* Default components that most post list sub-elements use
*
* @return array
*/
public function own_components() {
$prefix = tcb_selection_root() . ' ';
return [
'styles-templates' => [ 'hidden' => true ],
'animation' => [ 'disabled_controls' => [ '.btn-inline.anim-link' ] ],
'typography' => [
'disabled_controls' => [
'.tve-advanced-controls',
'p_spacing',
'h1_spacing',
'h2_spacing',
'h3_spacing',
],
'config' => [
'css_suffix' => '',
'css_prefix' => '',
'TextShadow' => [
'css_suffix' => '',
'css_prefix' => $prefix,
],
'FontColor' => [
'css_suffix' => '',
'css_prefix' => $prefix,
],
'FontSize' => [
'css_suffix' => '',
'css_prefix' => $prefix,
],
'TextStyle' => [
'css_suffix' => '',
'css_prefix' => $prefix,
],
'LineHeight' => [
'css_suffix' => '',
'css_prefix' => $prefix,
],
'FontFace' => [
'css_suffix' => '',
'css_prefix' => $prefix,
],
'LetterSpacing' => [
'css_suffix' => '',
'css_prefix' => $prefix,
],
'TextAlign' => [
'css_suffix' => '',
'css_prefix' => $prefix,
],
'TextTransform' => [
'css_suffix' => '',
'css_prefix' => $prefix,
],
],
],
];
}
}

View File

@@ -0,0 +1,125 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-visual-editor
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class TCB_Post_List_User_Image
*/
class TCB_Post_List_User_Image {
const PLACEHOLDER_URL = 'editor/css/images/author_image.png';
private static $instance;
public $user_id;
/**
* Singleton implementation for TCB_Post_List_User_Image
*
* @return TCB_Post_List_User_Image
*/
public static function get_instance( $user_id = null ) {
if ( self::$instance === null ) {
self::$instance = new self( $user_id );
}
return self::$instance;
}
public function __construct( $user_id = null ) {
$this->user_id = empty( $user_id ) ? get_current_user_id() : $user_id;
}
/**
* Get the default user image url, or the user image url
*
* @return string
*/
public function get_default_url() {
$url = static::get_placeholder_url();
if ( ! empty( $this->user_id ) ) {
$avatar_data = get_avatar_data( $this->user_id, [ 'size' => 256 ] );
if ( ! empty( $avatar_data['url'] ) && ! is_wp_error( $avatar_data['url'] ) ) {
$url = $avatar_data['url'];
}
}
return $url;
}
public function get_user_name() {
$name = '';
if ( ! empty( $this->user_id ) ) {
$user = get_userdata( $this->user_id );
if ( $user && $user->data ) {
$name = $user->data->display_name;
}
}
return $name;
}
/**
* Returns post user avatar
*
* @return false|string
*/
public function user_avatar() {
if ( empty( $this->user_id ) ) {
$avatar_url = '';
} else {
$avatar = get_avatar( $this->user_id, 256 );
preg_match( '/src=\'([^\']*)\'/m', $avatar, $matches );
if ( empty( $matches[1] ) ) {
$avatar_url = get_avatar_url( $this->user_id, [ 'size' => 256 ] );
} else {
$avatar_url = html_entity_decode( $matches[1] );
}
/* if we're in the editor, append a dynamic flag at the end so we can recognize that the URL is dynamic */
if ( TCB_Utils::in_editor_render( true ) ) {
$avatar_url = add_query_arg( [
'dynamic_user' => 1,
], $avatar_url );
}
}
return $avatar_url;
}
/**
* Returns user placeholder URL
*
* @return false|string
*/
public static function get_placeholder_url() {
return tve_editor_url( static::PLACEHOLDER_URL );
}
}
/**
* Returns the instance of the Custom Fields Shortcode Class
*
* @return TCB_Post_List_User_Image
*/
function tcb_dynamic_user_image_instance( $user_id ) {
return TCB_Post_List_User_Image::get_instance( $user_id );
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,32 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-visual-editor
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
class TCB_Pagination_Load_More extends TCB_Pagination {
const IDENTIFIER = 'tcb-pagination-load-more-button';
/**
* Get the pagination content for the current type.
*
* @return string|null
*/
public function get_content() {
return tcb_template( 'pagination/load-more-button.php', '', true );
}
/**
* Get the label for this type.
*
* @return string|void
*/
public function get_label() {
return __( 'Load More', 'thrive-cb' );
}
}

View File

@@ -0,0 +1,31 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-visual-editor
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
class TCB_Pagination_None extends TCB_Pagination {
/**
* Get the pagination content for the current type.
*
* @return string|null
*/
public function get_content() {
return '';
}
/**
* Get the label for this type.
*
* @return string|void
*/
public function get_label() {
return __( 'None', 'thrive-cb' );
}
}

View File

@@ -0,0 +1,126 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-visual-editor
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Class TCB_Pagination_Numeric
*/
class TCB_Pagination_Numeric extends TCB_Pagination {
const BUTTON_CLASS = 'tcb-pagination-button';
const PREV_NEXT_BUTTON_CLASS = 'tcb-pagination-prev-next-button';
const FIRST_LAST_BUTTON_CLASS = 'tcb-pagination-first-last-button';
private $default_attr = [
'hide_page_numbers' => false,
'hide_prev_next' => false,
'hide_first_last' => false,
'hide_label' => false,
];
/**
* Get the content for numeric pagination.
*
* @return string|null
*/
public function get_content() {
$default_attr = $this->default_attr;
/* structure: content = label + first + prev + page_numbers + next + last' */
$content = '';
if ( empty( $default_attr['hide_page_numbers'] ) ) {
$content = TCB_Utils::wrap_content( $content, 'div', '', 'tcb-pagination-numbers-wrapper' );
}
if ( empty( $default_attr['hide_prev_next'] ) ) {
$content = $this->get_button( 'prev' ) . $content . $this->get_button( 'next' );
}
if ( empty( $default_attr['hide_first_last'] ) ) {
$content = $this->get_button( 'first' ) . $content . $this->get_button( 'last' );
}
$content = TCB_Utils::wrap_content( $content, 'div', '', 'tcb-pagination-navigation-container ' . THRIVE_WRAPPER_CLASS );
if ( empty( $default_attr['hide_label'] ) ) {
$content = $this->get_navigation_label() . $content;
}
return $content;
}
/**
* Returns the pagination button html.
*
* @param $type
*
* @return string
*/
private function get_button( $type ) {
$attr = [];
$classes = [ static::BUTTON_CLASS, THRIVE_WRAPPER_CLASS, 'tcb-pagination-' . $type ];
$icon = tcb_template( 'pagination/' . $type . '-icon.php', null, true );
$name = ucfirst( $type );
switch ( $type ) {
/* prev and next are handled together */
case 'prev':
$name = __( 'Previous', 'thrive-cb' );
/* no break */
case 'next':
$classes[] = static::PREV_NEXT_BUTTON_CLASS;
$classes[] = 'tcb-with-icon';
$attr['data-button_layout'] = 'icon';
break;
case 'first':
case 'last':
$classes[] = static::FIRST_LAST_BUTTON_CLASS;
$attr['data-button_layout'] = 'text';
break;
default:
break;
}
/* 'Next' and 'Last' have their icons on the right by default */
if ( in_array( $type, [ 'next', 'last' ] ) ) {
$classes[] = 'tcb-flip';
}
$data = array(
'name' => $name,
'icon' => empty( $icon ) ? '' : TCB_Utils::wrap_content( $icon, 'span', '', 'tcb-button-icon' ),
);
$content = tcb_template( 'pagination/button.php', $data, true );
return TCB_Utils::wrap_content( $content, 'p', '', $classes, $attr );
}
private function get_navigation_label() {
$classes = [ 'tcb-pagination-label', THRIVE_WRAPPER_CLASS, 'tve_no_drag' ];
$content = tcb_template( 'pagination/label-pages.php', null, true );
return TCB_Utils::wrap_content( $content, 'div', '', $classes );
}
/**
* Get the label for this type.
*
* @return string|void
*/
public function get_label() {
return __( 'Numeric', 'thrive-cb' );
}
}

View File

@@ -0,0 +1,137 @@
<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-visual-editor
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
abstract class TCB_Pagination {
const IDENTIFIER = 'tcb-pagination';
/* possible types for pagination */
const LOAD_MORE = 'load_more';
const NONE = 'none';
public static $all_types = [ TCB_Pagination::NONE, 'numeric', TCB_Pagination::LOAD_MORE ];
/**
* The type of this pagination
*
* @var string
*/
private $type;
/**
* Array of attributes for this pagination.
*
* @var array
*/
private $attr;
/**
* TCB_Pagination constructor.
*
* @param $type
* @param $attr
*/
public function __construct( $type, $attr = [] ) {
$this->type = empty( $type ) ? static::NONE : $type;
$this->attr = $attr;
}
/**
* Render the pagination.
*
* @param $existing_content
*
* @return string
*/
public function render( $existing_content = '' ) {
/* if there is no existing content, call the render function */
if ( empty( $existing_content ) ) {
$content = $this->get_content();
} else {
/* if the pagination content already exists, we don't have to render again */
$content = $existing_content;
}
return TCB_Utils::wrap_content( $content, 'div', '', $this->get_classes(), $this->get_attr() );
}
/**
* Get/generate the classes for this pagination element.
*
* @return string
*/
private function get_classes() {
/* default classes - identifier, thrive wrapper. we also add a class to hide everything until it loads from JS in the frontend, ( the class is removed there ) */
$classes = [ self::IDENTIFIER, THRIVE_WRAPPER_CLASS, 'tcb-permanently-hidden' ];
$attr = $this->get_attr();
/* add the responsive classes, if they are present */
if ( ! empty( $attr['class'] ) ) {
$classes[] = $attr['class'];
}
return implode( ' ', $classes );
}
/**
* Get the pagination content for the current type. Implemented in the classes that extend this.
*
* @return string|null
*/
abstract public function get_content();
/**
* Get the label for this type. Implemented in the classes that extend this.
*
* @return string|void
*/
abstract public function get_label();
/**
* @return string
*/
public function get_type() {
return $this->type;
}
/**
* @return array
*/
public function get_attr() {
return $this->attr;
}
/**
* @param $type
* @param $attr
*
* @return TCB_Pagination|null
*/
public static function factory( $type, $attr = [] ) {
$class_name = "TCB_Pagination_{$type}";
/* check if the class exists and return an instance */
if ( ! empty( $type ) && class_exists( $class_name ) ) {
return ( new $class_name( $type, $attr ) );
}
return null;
}
}
/**
* @param $type
* @param $attr
*
* @return TCB_Pagination|null
*/
function tcb_pagination( $type, $attr = [] ) {
return TCB_Pagination::factory( $type, $attr );
}