self::ACTION,
];
return $data;
}
/**
* Init the object, during the AJAX request. Adds ajax handlers and verifies nonces
*/
public function init() {
add_action( 'wp_ajax_' . self::ACTION, [ $this, 'handle' ] );
}
/**
* Handles the ajax call
*/
public function handle() {
$post_id = $this->param('post_id');
if(empty($post_id)) {
$post_id = $this->param('page_id');
}
if ( wp_verify_nonce( $this->param( 'nonce' ), self::NONCE_KEY ) === false || ! TCB_Product::has_external_access( $post_id ) ) {
$this->error( __( 'This page has expired. Please reload and try again', 'thrive-cb' ), 403, 'nonce_expired' );
}
$custom = $this->param( 'custom' );
if ( empty( $custom ) || ! method_exists( $this, 'action_' . $custom ) ) {
$this->error( 'Invalid request.', 404 );
}
$action = 'action_' . $custom;
/* restore WAF-protected fields */
TCB_Utils::restore_post_waf_content();
/**
* Action triggered before any handler code is executed
* Allows setting up everything needed for the request, e.g. global objects
*
* @param TCB_Editor_Ajax $instance
*/
do_action( 'tcb_ajax_before', $this );
/**
* Action called just before the custom ajax callbacks.
*
* @param {TCB_Editor_Ajax} $this
*/
do_action( 'tcb_ajax_before_' . $custom, $this );
$response = call_user_func( [ $this, $action ] );
$response = apply_filters( 'tcb_ajax_response_' . $custom, $response, $this );
if ( $this->param( 'expect' ) === 'html' ) {
wp_die( $response ); // phpcs:ignore
}
$this->json( $response );
}
/**
* @param string $key
* @param mixed $default
* @param bool $sanitize whether or not to sanitize the returned value
*
* @return mixed
*/
protected function param( $key, $default = null, $sanitize = true ) {
if ( isset( $_POST[ $key ] ) ) {
$value = $_POST[ $key ]; //phpcs:ignore
} else {
$value = isset( $_REQUEST[ $key ] ) ? $_REQUEST[ $key ] : $default; //phpcs:ignore
}
return $sanitize ? map_deep( $value, 'sanitize_text_field' ) : $value;
}
/**
*
* @param string|WP_Error $message
* @param int $code
* @param string $str_code
*/
protected function error( $message, $code = 422, $str_code = '' ) {
if ( is_wp_error( $message ) ) {
$message = $message->get_error_message();
}
status_header( $code );
if ( $this->param( 'expect' ) === 'html' ) {
wp_die( esc_html( $message ), $code );
}
$json = [
'error' => true,
'message' => $message,
'tcb_default_error' => $code === 422,
];
if ( $str_code ) {
$json['code'] = $str_code;
}
wp_send_json( $json );
}
/**
* Send a json success response
*
* Makes sure the response always contain a 'message' and a success field
*
* @param array $data
*/
protected function json( $data ) {
if ( is_scalar( $data ) ) {
$data = [
'message' => $data,
];
}
if ( ! isset( $data['success'] ) ) {
$data['success'] = true;
}
wp_send_json( $data );
}
/** ------------------ AJAX endpoints after this point ------------------ **/
/**
* Saves the user-selected post_types to use in autocomplete search for links
*
* @return string success message
*/
public function action_save_link_post_types() {
/**
* Make sure there is no extra data
*/
$all_post_types = get_post_types();
$post_types = $this->param( 'post_types', [] );
update_option( 'tve_hyperlink_settings', array_intersect( $post_types, $all_post_types ) );
return __( 'Settings saved', 'thrive-cb' );
}
/**
* Search a post ( used in quick search for link elements )
* Will search in a range of post types, filterable
*
*/
public function action_post_search() {
$s = trim( wp_unslash( $this->param( 'q' ) ) );
$s = trim( $s );
$selected_post_types = [ 'post', 'page', 'product' ];
/**
* Add filter to allow hooking into the selected post types
*/
$selected_post_types = apply_filters( 'tcb_autocomplete_selected_post_types', $selected_post_types );
if ( ! $this->param( 'ignore_settings' ) ) {//do not ignore user settings
/**
* post types saved by the user
*/
$selected_post_types = maybe_unserialize( get_option( 'tve_hyperlink_settings', $selected_post_types ) );
}
if ( $this->param( 'search_lightbox' ) ) {
/**
* Filter that allows custom post types to be included in search results for site linking
*/
$post_types_data = apply_filters(
'tcb_link_search_post_types',
array(
'tcb_lightbox' => array(
'name' => __( 'TCB Lightbox', 'thrive-cb' ),
'event_action' => 'thrive_lightbox',
),
)
);
foreach ( $post_types_data as $key => $value ) {
/**
* if the key is numeric, the value is actually a post type, if not, the value is information for the post type
*/
$selected_post_types[] = is_numeric( $key ) ? $value : $key;
}
}
$args = [
'post_type' => $selected_post_types,
'post_status' => [ 'publish', 'inherit' ], //Inherit for the attachment post type
's' => $s,
'numberposts' => 20,
'fields' => 'ids', //we are taking ids because it resembles more with the results returned from wp search
];
$query = new WP_Query();
$found_ids = $query->query( $args );
$posts = [];
foreach ( $found_ids as $id ) {
$item = get_post( $id );
$title = $item->post_title;
if ( ! empty( $s ) ) {
$quoted = preg_quote( $s, '#' );
$item->post_title = preg_replace( "#($quoted)#i", '$0', $item->post_title );
}
$post = array(
'label' => $item->post_title,
'title' => $title,
'id' => $item->ID,
'value' => $item->post_title,
'url' => $item->post_type === 'attachment' ? wp_get_attachment_url( $item->ID ) : get_permalink( $item->ID ),
'type' => $item->post_type,
'is_popup' => isset( $post_types_data[ $item->post_type ] ) && ! empty( $post_types_data[ $item->post_type ]['event_action'] ),
);
if ( $post['is_popup'] ) {
$post['url'] = '#' . $post_types_data[ $item->post_type ]['name'] . ': ' . $title;
$post['event_action'] = $post_types_data[ $item->post_type ]['event_action'];
$post['post_type_name'] = $post_types_data[ $item->post_type ]['name'];
}
$posts [] = $post;
}
$posts = apply_filters( 'tcb_autocomplete_returned_posts', $posts, $s );
wp_send_json( $posts );
}
/**
* Saves a landing page thumbnail
*
* @return array
*/
public function action_save_landing_page_thumbnail() {
$lp_id = $this->param( 'id' );
$landing_page = $this->param( 'landing_page' );
$response = [];
if ( isset( $_FILES['img_data'] ) && is_numeric( $lp_id ) && ! empty( $landing_page ) ) {
$image_name = str_replace( '\\', '', $this->param( 'img_name' ) );
$image_width = $this->param( 'image_w' );
$image_height = $this->param( 'image_h' );
if ( ! function_exists( 'wp_handle_upload' ) ) {
require_once( ABSPATH . 'wp-admin/includes/file.php' );
}
add_filter( 'upload_dir', 'tve_filter_upload_user_template_location' );
$moved_file = wp_handle_upload(
$_FILES['img_data'],
array(
'action' => 'tcb_editor_ajax',
'unique_filename_callback' => sanitize_file_name( $image_name . '.png' ),
)
);
remove_filter( 'upload_dir', 'tve_filter_upload_user_template_location' );
if ( empty( $moved_file['url'] ) ) {
$this->error( __( 'Template could not be generated', 'thrive-cb' ) );
} else {
/* Resize the image so we won't have such big previews */
if ( ! empty( $moved_file['file'] ) ) {
$preview = wp_get_image_editor( $moved_file['file'] );
if ( ! is_wp_error( $preview ) ) {
/* resize to the given width while using the image's native height */
$preview->resize( 500, null );
$preview_sizes = $preview->get_size();
$preview->save( $moved_file['file'] );
}
}
$preview_data = [
'w' => isset( $preview_sizes['width'] ) ? $preview_sizes['width'] : $image_width,
'h' => isset( $preview_sizes['height'] ) ? $preview_sizes['height'] : $image_height,
'url' => $moved_file['url'],
];
/* Update the post meta of the saved lp with the preview */
update_post_meta( (int) $lp_id, TCB\SavedLandingPages\Saved_Lp::get_post_type_prefix() . 'preview_image', $preview_data );
$response['saved_lp_templates'] = TCB\SavedLandingPages\Saved_Lp::localize();
}
}
return $response;
}
public function action_save_page_preview() {
$image_name = str_replace( '\\', '', $this->param( 'img_name' ) );
$content_type = $this->param( 'content_type' );
if ( ! function_exists( 'wp_handle_upload' ) ) {
require_once( ABSPATH . 'wp-admin/includes/file.php' );
}
if ( function_exists( "tve_filter_{$content_type}_preview_location" ) ) {
/* Filter to change the default location of the uploaded file */
add_filter( 'upload_dir', "tve_filter_{$content_type}_preview_location" );
}
/* Callback for the file name(it's used for replacing the image instead of creating a new one) */
$unique_filename_callback = static function () use ( $image_name ) {
return $image_name;
};
$moved_file = wp_handle_upload(
$_FILES['img_data'],
array(
'action' => 'tcb_editor_ajax',
'unique_filename_callback' => $unique_filename_callback,
)
);
if ( ! empty( $moved_file['file'] ) ) {
$preview = wp_get_image_editor( $moved_file['file'] );
if ( ! is_wp_error( $preview ) ) {
/* resize to the given width while using the image's native height */
$preview->resize( 500, null );
$preview->save( $moved_file['file'] );
}
}
remove_filter( 'upload_dir', "tve_filter_{$content_type}_preview_location" );
}
/**
* Saves user template (code and picture)
*
* @return array
*/
public function action_save_user_template() {
$id = $this->param( 'id' );
$is_update = ! empty( $id ) && $id !== 'undefined';
$template_name = str_replace( '\\', '', $this->param( 'template_name' ) );
$new_template_data = [
'name' => $template_name,
'content' => $this->param( 'template_content', '', false ),
'type' => $this->param( 'template_type', '' ),
'id_category' => $this->param( 'template_category' ),
'css' => $this->param( 'custom_css_rules', '', false ),
'media_css' => json_decode( stripslashes( $this->param( 'media_rules', '', false ) ), true ),
];
if ( isset( $_FILES['img_data'] ) ) {
$preview_data = TCB\UserTemplates\Template::upload_preview_image( $_FILES['img_data'], $new_template_data );
if ( empty( $preview_data['url'] ) ) {
$this->error( __( 'Template could not be generated', 'thrive-cb' ) );
}
if ( file_exists( $preview_data['file'] ) ) {
TCB\UserTemplates\Template::resize_preview_image( $preview_data['file'] );
}
$new_template_data = tve_update_image_size( $preview_data['file'], $new_template_data, $preview_data['url'] );
}
$new_template_data = apply_filters( 'tcb_hook_save_user_template', $new_template_data );
if ( $is_update ) {
/* @var \TCB\UserTemplates\Template $template_instance */
$template_instance = \TCB\UserTemplates\Template::get_instance_with_id( $id );
$template_instance->update( $new_template_data );
} else {
TCB\UserTemplates\Template::insert( $new_template_data );
}
return [
'text' => $is_update ? __( 'Template updated!', 'thrive-cb' ) : __( 'Template saved!', 'thrive-cb' ),
'content_templates' => TCB\UserTemplates\Template::localize(),
];
}
public function action_save_user_template_category() {
$category_name = $this->param( 'category_name' );
if ( empty( $category_name ) ) {
$this->error( __( 'Invalid parameters!', 'thrive-cb' ) );
}
$new_category = TCB\UserTemplates\Category::add( $category_name );
$this->json( [
'text' => __( 'Category saved!', 'thrive-cb' ),
'response' => $new_category,
]
);
}
/**
* process and display wp editor contents
* used in "Insert Shortcode" element
*/
public function action_render_shortcode() {
$content = '';
if ( empty( $_POST['content'] ) ) {
$this->error( __( 'The content is empty. Please input some content.', 'thrive-cb' ) );
} else {
$content = stripslashes( $_POST['content'] ); // phpcs:ignore
}
/**
* ob_start makes sure no output is incorrectly sent to the browser during do_shortcode.
* There were instances where 3rd party shortcodes echo'd during do_shortcode call.
*/
ob_start();
$rendered = tcb_render_wp_shortcode( $content );
$rendered = ob_get_contents() . $rendered;
ob_end_clean();
$this->json(
array(
'text' => __( 'Success! Your content was added.', 'thrive-cb' ),
'response' => $rendered,
)
);
}
/**
* Update post visibility
*
* @return bool
*/
public function action_save_post_status() {
$post_id = (int) $this->param( 'ID' );
if ( empty( $post_id ) || ! current_user_can( 'edit_post', $post_id ) ) {
return false;
}
$post = get_post( $post_id );
if ( ! empty( $post ) ) {
$params = [];
$status = $this->param( 'post_status' );
if ( ! empty( $status ) ) {
$params = array_merge( $params, array(
'post_status' => $status,
'post_password' => $this->param( 'post_password' ),
) );
$params = array_merge( $params, array(
'ID' => $post_id,
'post_modified' => current_time( 'mysql' ),
'post_modified_gmt' => current_time( 'mysql' ),
'post_title' => get_the_title( $post_id ),
) );
wp_update_post( $params );
return true;
}
}
return false;
}
/**
* Update post title
*
* @return bool
*/
public function action_save_post_title() {
$post_id = (int) $this->param( 'ID' );
if ( empty( $post_id ) || ! current_user_can( 'edit_post', $post_id ) ) {
return false;
}
$post = get_post( $post_id );
if ( ! empty( $post ) ) {
$params = [];
$title = $this->param( 'post_title' );
$params = array_merge( $params, array(
'ID' => $post_id,
'post_modified' => current_time( 'mysql' ),
'post_modified_gmt' => current_time( 'mysql' ),
'post_title' => $title,
) );
wp_update_post( $params );
return true;
}
return false;
}
/**
* Update post format
*
* @return bool|array|mixed
*/
public function action_save_post_format() {
$post_id = (int) $this->param( 'ID' );
if ( empty( $post_id ) || ! current_user_can( 'edit_post', $post_id ) ) {
return false;
}
return set_post_format( $post_id, $this->param( 'post_format' ) );
}
/**
* Ajax listener to save the post in database. Handles "Save" and "Update" buttons together.
* If either button pressed, then write to saved field.
* If publish button pressed, then write to both save and published fields
*
* @return array
*/
public function action_save_post() {
@ini_set( 'memory_limit', TVE_EXTENDED_MEMORY_LIMIT ); //phpcs:ignore
if ( ! ( $post_id = $this->param( 'post_id' ) ) || ! current_user_can( 'edit_post', $post_id ) || ! tcb_has_external_cap() ) {
return array(
'success' => false,
'message' => __( 'You do not have the required permission for this action', 'thrive-cb' ),
);
}
$post_id = (int) $post_id;
$tcb_post = tcb_post( $post_id );
do_action( 'tcb_ajax_save_post', $post_id, $_POST );
$landing_page_template = $this->param( 'tve_landing_page', 0 );
$inline_rules = $this->param( 'inline_rules', null, false );
$clippath_pattern = '/clip-path:(.+?);/';
$inline_rules = preg_replace_callback( $clippath_pattern, [
$this,
'replace_clip_path',
], $inline_rules );
$response = [
'success' => true,
];
/**
* Post Constants - similar with tve_globals but do not depend on the Landing Page Key
*
* Usually stores flags for a particular post
*/
if ( ! empty( $_POST['tve_post_constants'] ) && is_array( $_POST['tve_post_constants'] ) ) {
update_post_meta( $post_id, '_tve_post_constants', map_deep( $_POST['tve_post_constants'], 'sanitize_text_field' ) );
}
if ( ( $custom_action = $this->param( 'custom_action' ) ) ) {
switch ( $custom_action ) {
case 'landing_page': //change or remove the landing page template for this post
$lp_id = $this->param( 'id' );
tcb_landing_page( $post_id )->change_template( $landing_page_template, $lp_id );
break;
case 'normal_page_reset':
tcb_landing_page( $post_id )->change_template( '', '' );
delete_post_meta( $post_id, 'tve_custom_css' );
delete_post_meta( $post_id, 'tve_updated_post' );
wp_update_post( array(
'ID' => $post_id,
'post_modified' => current_time( 'mysql' ),
'post_modified_gmt' => current_time( 'mysql' ),
'post_content' => '',
) );
break;
case 'cloud_landing_page':
$valid = tve_get_cloud_template_config( $landing_page_template );
if ( $valid === false ) { /* this is not a valid cloud landing page template - most likely, some of the files were deleted */
$current = tve_post_is_landing_page( $post_id );
return array(
'success' => false,
'current_template' => $current,
'error' => __( 'Some of the required files were not found. Please try re-downloading this template', 'thrive-cb' ),
'message' => __( 'Some of the required files were not found. Please try re-downloading this template', 'thrive-cb' ),
);
}
/* if valid, go on with the regular change of template */
tcb_landing_page( $post_id )->change_template( $landing_page_template );
$response['message'] = __( 'All changes saved.', 'thrive-cb' );
break;
case 'landing_page_reset':
/* clear the contents of the current landing page */
if ( ! ( $landing_page_template = tve_post_is_landing_page( $post_id ) ) ) {
break;
}
tcb_landing_page( $post_id, $landing_page_template )->reset();
$response['message'] = __( 'All changes saved.', 'thrive-cb' );
break;
case 'landing_page_delete':
/* @var \TCB\SavedLandingPages\Saved_Lp $saved_lp_instance */
$saved_lp_instance = TCB\SavedLandingPages\Saved_Lp::get_instance_with_id( $this->param( 'id' ) );
$saved_lp_instance->delete();
$response['saved_lp_templates'] = TCB\SavedLandingPages\Saved_Lp::localize();
break;
}
$response['revisions'] = tve_get_post_revisions( $post_id );
if ( isset( $_POST['header'] ) ) {
update_post_meta( $post_id, '_tve_header', (int) $_POST['header'] );
}
if ( isset( $_POST['footer'] ) ) {
update_post_meta( $post_id, '_tve_footer', (int) $_POST['footer'] );
}
return $response;
}
$key = $landing_page_template ? ( '_' . $landing_page_template ) : '';
$content = $this->param( 'tve_content', null, false );
/**
* Just in case someone whats to do stuff on content before we save it into db
*/
$content = apply_filters( 'tcb_save_post_content', $content, $post_id );
$content_split = tve_get_extended( $content );
$content = str_replace( [ '', '' ], '', $content );
update_post_meta( $post_id, "tve_content_before_more{$key}", $content_split['main'] );
update_post_meta( $post_id, "tve_content_more_found{$key}", $content_split['more_found'] );
update_post_meta( $post_id, "tve_custom_css{$key}", $inline_rules );
/**
* Store Lead generation forms data
*/
// add lead gen forms data
if ( ! empty( $_POST['lead_gen_forms'] ) && is_array( $_POST['lead_gen_forms'] ) ) {
foreach ( $_POST['lead_gen_forms'] as $lead_gen_form ) {
$form_identifier = $lead_gen_form['form_identifier'];
$inputs = $lead_gen_form['inputs'];
$apis = $lead_gen_form['apis'];
add_post_meta( $post_id, "_tve_lead_gen_form_{$form_identifier}", array( 'inputs' => $inputs, 'apis' => $apis ) );
}
}
/* user defined Custom CSS rules here, had to use different key because tve_custom_css was already used */
update_post_meta( $post_id, "tve_user_custom_css{$key}", $this->param( 'tve_custom_css', null, false ) );
tve_update_post_meta( $post_id, 'tve_page_events', $this->param( 'page_events', [], false ) );
if ( $this->param( 'update' ) === 'true' ) {
update_post_meta( $post_id, "tve_updated_post{$key}", $content );
/**
* If there is not WP content in the post, migrate it to TCB2-editor only mode
*/
$tcb_post->maybe_auto_migrate( false );
$tcb_post->enable_editor();
$tve_stripped_content = $this->param( 'tve_stripped_content', null, false );
$tve_stripped_content = str_replace( [
'',
'',
], '', $tve_stripped_content );
$tcb_post->update_plain_text_content( $tve_stripped_content );
}
/* global options for a post that are not included in the editor */
$tve_globals = empty( $_POST['tve_globals'] ) ? [] : map_deep( array_filter( $_POST['tve_globals'] ), 'sanitize_text_field' ); // phpcs:ignore
$tve_globals['font_cls'] = $this->param( 'custom_font_classes', [] );
update_post_meta( $post_id, "tve_globals{$key}", $tve_globals );
/* custom fonts used for this post */
tve_update_post_custom_fonts( $post_id, $tve_globals['font_cls'] );
if ( $landing_page_template ) {
update_post_meta( $post_id, 'tve_landing_page', $this->param( 'tve_landing_page' ) );
/* global Scripts for landing pages */
update_post_meta( $post_id, 'tve_global_scripts', $this->param( 'tve_global_scripts', [], false ) );
if ( ! empty( $_POST['tve_landing_page_save'] ) ) {
/* In the new version we add all data in post meta */
$template_data = [
'before_more' => $content_split['main'],
'more_found' => $content_split['more_found'],
'content' => $content,
'inline_css' => $this->param( 'inline_rules', null, false ),
'custom_css' => $this->param( 'tve_custom_css', null, false ),
'tve_globals' => $this->param( 'tve_globals', [], false ),
'tve_global_scripts' => $this->param( 'tve_global_scripts', [], false ),
'name' => $this->param( 'tve_landing_page_save' ),
'tags' => $this->param( 'template_tags' ),
'template' => $landing_page_template,
'theme_dependency' => get_post_meta( $post_id, 'tve_disable_theme_dependency', true ),
'tpl_colours' => get_post_meta( $post_id, 'thrv_lp_template_colours', true ),
'tpl_gradients' => get_post_meta( $post_id, 'thrv_lp_template_gradients', true ),
'tpl_button' => get_post_meta( $post_id, 'thrv_lp_template_button', true ),
'tpl_section' => get_post_meta( $post_id, 'thrv_lp_template_section', true ),
'tpl_contentbox' => get_post_meta( $post_id, 'thrv_lp_template_contentbox', true ),
'tpl_palettes' => get_post_meta( $post_id, 'thrv_lp_template_palettes', true ),
'tpl_palettes_v2' => get_post_meta( $post_id, TCB_LP_Palettes::LP_PALETTES, true ),
'tpl_palettes_config_v2' => get_post_meta( $post_id, TCB_LP_Palettes::LP_PALETTES_CONFIG, true ),
'tpl_skin_tag' => get_post_meta( $post_id, 'theme_skin_tag', true ),
'date' => date( 'Y-m-d' ),
];
/**
* if this is a cloud template, we need to store the thumbnail separately, as it has a different location
*/
$config = tve_get_cloud_template_config( $landing_page_template, false );
if ( $config !== false && ! empty( $config['thumb'] ) ) {
$template_data['thumbnail'] = $config['thumb'];
}
if ( empty( $template_data['more_found'] ) ) { // save some space
unset( $template_data['before_more'], $template_data['more_found'] ); // this is the same as the tve_save_post field
}
/**
* Export LPs sections too
*/
$template_data['sections']['header']['ID'] = get_post_meta( $post_id, '_tve_header', true );
$template_data['sections']['footer']['ID'] = get_post_meta( $post_id, '_tve_footer', true );
$saved_sections = get_post_meta( $post_id, 'sections', true );
if ( ! empty( $saved_sections ) ) {
$template_data['sections'] = array_merge( $template_data['sections'], $saved_sections );
}
TCB\SavedLandingPages\Saved_Lp::insert_post( $template_data );
$response['saved_lp_templates'] = TCB\SavedLandingPages\Saved_Lp::localize();
}
} else {
delete_post_meta( $post_id, 'tve_landing_page' );
}
tve_update_post_meta( $post_id, 'thrive_icon_pack', empty( $_POST['has_icons'] ) ? 0 : $_POST['has_icons'] );
tve_update_post_meta( $post_id, 'tve_has_masonry', empty( $_POST['tve_has_masonry'] ) ? 0 : 1 );
tve_update_post_meta( $post_id, 'tve_has_typefocus', empty( $_POST['tve_has_typefocus'] ) ? 0 : 1 );
tve_update_post_meta( $post_id, 'tve_has_wistia_popover', empty( $_POST['tve_has_wistia_popover'] ) ? 0 : 1 );
if ( isset( $_POST['header'] ) ) {
update_post_meta( $post_id, '_tve_header', (int) $_POST['header'] );
}
if ( isset( $_POST['footer'] ) ) {
update_post_meta( $post_id, '_tve_footer', (int) $_POST['footer'] );
}
/* Handle the css, js and additional saves */
\TCB\Lightspeed\Main::handle_optimize_saves( $post_id, $_POST );
/**
* Remove old unused meta
*/
tve_clean_up_meta_leftovers( $post_id );
/**
* trigger also a post / page update for the caching plugins to know there has been a save
* update post here so we can have access to its meta when a revision of it is saved
*
* @see tve_save_post_callback
*/
if ( ! empty( $content ) ) {
if ( $landing_page_template ) {
remove_all_filters( 'save_post' );
add_action( 'save_post', 'tve_save_post_callback' );
}
wp_update_post( array(
'ID' => $post_id,
'post_modified' => current_time( 'mysql' ),
'post_modified_gmt' => current_time( 'mysql' ),
) );
}
$response['revisions'] = tve_get_post_revisions( $post_id );
return $response;
}
/**
* Redirects the save post to an external method
*/
public function action_save_post_external() {
$external_action = $this->param( 'external_action' );
if ( ! $external_action ) {
$this->error( 'Invalid Request!' );
}
return apply_filters( 'tcb_ajax_' . $external_action, [], $_REQUEST );
}
/**
* Update wp options
*
* @return int
*/
public function action_update_option() {
$option_name = $this->param( 'option_name' );
$option_value = $this->param( 'option_value' );
$allowed = apply_filters( 'tcb_allowed_ajax_options', [
'tve_display_save_notification',
'tve_social_fb_app_id',
'tve_comments_disqus_shortname',
'tve_comments_facebook_admins',
'tcb_pinned_elements',
'tve_fa_kit',
] );
if ( ! in_array( $option_name, $allowed ) ) {
$this->error( 'Invalid', 403 );
}
if ( $option_name === 'tve_comments_facebook_admins' ) {
$tve_comments_facebook_admins_arr = explode( ';', $option_value );
$result = update_option( $option_name, $tve_comments_facebook_admins_arr );
} elseif ( $option_name === 'tcb_pinned_elements' ) {
$result = update_user_option( get_current_user_id(), $option_name, $option_value );
} else {
$result = update_option( $option_name, $option_value );
}
return (int) $result;
}
/**
* @return array
*/
public function action_get_api() {
$api_key = $this->param( 'api' );
$force = (bool) $this->param( 'force' );
$extra = $this->param( 'extra' );
$data = [];
if ( $api_key ) {
$connection = Thrive_Dash_List_Manager::connection_instance( $api_key );
$data = $connection->get_api_data( $extra, $force );
}
return $data;
}
/**
* Get extra fields from api
*
* @return array
*/
public function action_get_api_extra() {
$api = $this->param( 'api' );
$api_extra_data = [];
if ( $api && array_key_exists( $api, Thrive_Dash_List_Manager::available() ) ) {
$extra = $this->param( 'extra' );
$params = $this->param( 'params' );
$api_extra_data = Thrive_Dash_List_Manager::connection_instance( $api )->get_api_extra( $extra, $params );
}
return $api_extra_data;
}
public function action_custom_menu() {
ob_start();
include plugin_dir_path( __DIR__ ) . 'views/elements/menu-generated.php';
$content = ob_get_clean();
$this->json( [ 'response' => $content ] );
}
public function action_load_content_template() {
/**
* Allow things to happen before running do_shortcode below
*/
do_action( 'tcb_before_load_content_template' );
add_filter( 'tcb_is_editor_page_ajax', '__return_true' );
/* @var \TCB\UserTemplates\Template $template_instance */
$template_instance = \TCB\UserTemplates\Template::get_instance_with_id( $this->param( 'template_key' ) );
$template_data = $template_instance->load();
$template_data['html_code'] = tve_do_wp_shortcodes( tve_thrive_shortcodes( stripslashes( $template_data['html_code'] ), true ), true );
if ( ! empty( $template_data['media_css'] ) && is_array( $template_data['media_css'] ) ) {
if ( ! empty( $template_data['media_css'][0] ) ) {
$imports = explode( ';@import', $template_data['media_css'][0] );
foreach ( $imports as $key => $import ) {
if ( strpos( $import, '@import' ) === false ) {
$import = '@import' . $import;
}
$template_data['imports'][ $key ] = $import;
}
}
if ( ! empty( $template_data['media_css'][1] ) ) {
$template_data['inline_rules'] = $template_data['media_css'][1];
}
}
return $template_data;
}
public function action_delete_content_template() {
/* @var \TCB\UserTemplates\Template $template_instance */
$template_instance = \TCB\UserTemplates\Template::get_instance_with_id( $this->param( 'key' ) );
$template_instance->delete();
return [
'list' => TCB\UserTemplates\Template::localize(),
'message' => __( 'Content template deleted', 'thrive-cb' ),
];
}
/**
* Returns Current Post Revisions
*/
public function action_revisions() {
$post_id = $this->param( 'post_id' );
if ( empty( $post_id ) || ! is_numeric( $post_id ) ) {
$this->error( __( 'Invalid Post Parameter', 'thrive-cb' ) );
}
$revisions = tve_get_post_revisions( $post_id );
wp_send_json( $revisions );
}
/**
* Enables / Disables Theme CSS to Architect Page
*/
public function action_theme_css() {
$post_id = $this->param( 'post_id' );
$disable_it = $this->param( 'disabled' );
if ( empty( $post_id ) || ! is_numeric( $post_id ) ) {
$this->error( __( 'Invalid Post Parameter', 'thrive-cb' ) );
}
update_post_meta( $post_id, 'tve_disable_theme_dependency', $disable_it ? 1 : 0 );
$this->json( [] );
}
/**
* Updates the user wizard data
*/
public function action_user_settings() {
$config = $this->param( 'config', [] );
update_user_option( get_current_user_id(), 'tcb_u_settings', $config );
$this->json( $config );
}
/**
* Crud Operations on global gradients
*/
public function action_global_gradients() {
$name = $this->param( 'name' );
$gradient = $this->param( 'gradient', null, false );
$id = $this->param( 'id' );
$active = is_numeric( $this->param( 'active' ) ) ? 0 : 1;
$custom_name = (int) $this->param( 'custom_name' );
if ( empty( $custom_name ) || $custom_name !== 1 ) {
$custom_name = 0;
}
$max_name_characters = 50;
$global_gradients_option_name = apply_filters( 'tcb_global_gradients_option_name', 'thrv_global_gradients' );
if ( empty( $name ) || empty( $gradient ) || ! is_string( $gradient ) || ! is_string( $name ) ) {
/**
* The color has to have a name and it must be a valid string
*/
$this->error( 'Invalid Parameters! A gradient must contain a name and a gradient string!' );
}
if ( strlen( $name ) > $max_name_characters ) {
$this->error( 'Invalid color name! It must contain a maximum of ' . $max_name_characters . ' characters' );
}
$global_gradients = get_option( $global_gradients_option_name, [] );
if ( ! is_array( $global_gradients ) ) {
/**
* Security check: if the option is not empty and somehow the stored value is not an array, make it an array.
*/
$global_gradients = [];
}
if ( ! is_numeric( $id ) ) {
/**
* ADD Action
*/
$gradient_id = count( $global_gradients );
$global_gradients[] = [
'id' => $gradient_id,
'gradient' => $gradient,
'name' => $name,
'active' => $active,
'custom_name' => $custom_name,
];
} else {
/**
* Edit Gradient
*/
$index = - 1;
foreach ( $global_gradients as $key => $global_g ) {
if ( (int) $global_g['id'] === (int) $id ) {
$index = $key;
break;
}
}
if ( $index > - 1 ) {
$global_gradients[ $index ]['gradient'] = $gradient;
$global_gradients[ $index ]['name'] = $name;
$global_gradients[ $index ]['active'] = $active;
if ( $custom_name ) {
/**
* Update the custom name only if the value is 1
*/
$global_gradients[ $index ]['custom_name'] = $custom_name;
}
}
}
update_option( $global_gradients_option_name, $global_gradients );
/**
* Added possibility for external functionality to hook into here
*
* - Used in the landing page builder when a new gradient is added, to add it across all palettes
*/
do_action( 'tcb_action_global_gradients' );
$this->json( $global_gradients );
}
/**
* CRUD Operations on global colors
*/
public function action_global_colors() {
$name = $this->param( 'name' );
$color = $this->param( 'color', null, false );
$id = $this->param( 'id' );
$active = (int) $this->param( 'active', 1 );
$linked_vars = $this->param( 'linked_variables', [] );
$custom_name = (int) $this->param( 'custom_name' );
if ( empty( $custom_name ) || $custom_name !== 1 ) {
$custom_name = 0;
}
$max_name_characters = 50;
if ( empty( $name ) || empty( $color ) || ! is_string( $color ) || ! is_string( $name ) ) {
/**
* The color has to have a name and it must be a valid string
*/
$this->error( __( 'Invalid Parameters! A color must contain a name and a color string!', 'thrive-cb' ) );
}
if ( substr( $color, 0, 3 ) !== 'rgb' ) {
/**
* The color must be a valid RGB string
*/
$this->error( 'Invalid color format! It must be a valid RGB string!' );
}
if ( strlen( $name ) > $max_name_characters ) {
$this->error( 'Invalid color name! It must contain a maximum of ' . $max_name_characters . ' characters' );
}
$global_colors = tcb_color_manager()->get_list();
if ( ! is_array( $global_colors ) ) {
/**
* Security check: if the option is not empty and somehow the stored value is not an array, make it an array.
*/
$global_colors = [];
}
if ( ! is_numeric( $id ) ) {
/**
* ADD Action
*/
$color_id = count( $global_colors );
$global_colors[] = [
'id' => $color_id,
'color' => $color,
'name' => $name,
'active' => $active,
'custom_name' => $custom_name,
];
} else {
/**
* Edit Color
*/
$index = - 1;
foreach ( $global_colors as $key => $global_c ) {
if ( (int) $global_c['id'] === (int) $id ) {
$index = $key;
break;
}
}
if ( $index > - 1 ) {
$global_colors[ $index ]['color'] = $color;
$global_colors[ $index ]['name'] = $name;
$global_colors[ $index ]['active'] = $active;
if ( $custom_name ) {
/**
* Update the custom name only if the value is 1
*/
$global_colors[ $index ]['custom_name'] = $custom_name;
}
}
/**
* Process Linked Vars
*/
foreach ( $linked_vars as $var_id => $new_color ) {
$index = - 1;
foreach ( $global_colors as $key => $global_c ) {
if ( (int) $global_c['id'] === (int) $var_id ) {
$index = $key;
break;
}
}
if ( $index > - 1 ) {
$global_colors[ $index ]['color'] = $new_color;
}
}
}
tcb_color_manager()->update_list( $global_colors );
/**
* Added possibility for external functionality to hook into here
*
* - Used in the landing page builder when a new color is added, to add it across all palettes
*/
do_action( 'tcb_action_global_colors' );
$this->json( $global_colors );
}
/**
* Update Template Variables
*/
public function action_template_options() {
$name = $this->param( 'name' );
$type = $this->param( 'type', '' );
$value = $this->param( 'value', '', false );
$id = $this->param( 'id' );
$linked_vars = $this->param( 'linked_variables', [], false );
$custom_name = (int) $this->param( 'custom_name' );
if ( empty( $custom_name ) || $custom_name !== 1 ) {
$custom_name = 0;
}
if ( ! in_array( $type, [ 'color', 'gradient' ] ) ) {
$this->error( 'Invalid type' );
}
if ( empty( $name ) || empty( $value ) || ! is_string( $value ) || ! is_string( $name ) || ! is_numeric( $id ) ) {
/**
* The Gradient has to have a name and it must be a valid string
*/
$this->error( 'Invalid Parameters! A color must contain a name, an id and a color string!' );
}
$post_id = (int) $this->param( 'post_id', 0 );
if ( empty( $post_id ) ) {
$this->error( 'Something went wrong! Please contact the support team!' );
}
if ( tve_post_is_landing_page( $post_id ) ) {
tcb_landing_page( $post_id )->update_template_css_variable( $id, array(
'key' => $type,
'value' => $value,
'name' => $name,
'linked_variables' => $linked_vars,
'custom_name' => $custom_name,
'hsl_parent_dependency' => $this->param( 'hsl_parent_dependency', [], false ),
'hsl' => $this->param( 'hsl', [], false ),
) );
}
}
/**
* Function used to update custom options
*
* Used for updating the custom colors (Favorites Colors)
* Used for updating the custom gradients (Favorites Gradients)
*/
public function action_custom_options() {
$type = $this->param( 'type', '' );
$values = $this->param( 'values', [], false );
if ( ! in_array( $type, [ 'colours', 'gradients' ] ) ) {
$this->error( 'Invalid type' );
}
update_option( 'thrv_custom_' . $type, $values );
}
/**
* Lazy load data in the editor so we can improve the page load speed.
*/
public function action_lazy_load() {
$data = [];
$post_id = (int) $this->param( 'post_id', 0 );
tcb_editor()->set_post( $post_id, true );
if ( tcb_editor()->can_use_landing_pages() ) {
$data['lp_templates'] = class_exists( 'TCB_Landing_Page' ) ? TCB_Landing_Page::templates_v2() : [];
$data['saved_lp_templates'] = TCB\SavedLandingPages\Saved_Lp::localize( true );
$data['cloud_lp_templates'] = function_exists( 'tve_get_cloud_templates' ) ? tve_get_cloud_templates() : [];
}
$data['blocks'] = tcb_elements()->element_factory( 'contentblock' )->get_blocks();
$data['btn_default_templates'] = tcb_elements()->element_factory( 'button' )->get_default_templates();
$terms = get_terms( [ 'slug' => [ 'headers', 'footers' ] ] );
$terms = array_map( function ( $term ) {
return $term->term_id;
}, $terms );
$data['symbols'] = tcb_elements()->element_factory( 'symbol' )->get_all( [ 'category__not_in' => $terms ], true );
$data['content_templates'] = TCB\UserTemplates\Template::localize( true );
$data['custom_icons'] = TCB_Icon_Manager::get_custom_icons( $post_id );
/* Although this does not save request time, it caused issues on some servers if loaded during the main request in the editor, if there are