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