setup_white_label(); add_action( 'wp_ajax_skip_subscribe', array( $this, 'skip_subscribe' ) ); add_action( 'wp_ajax_nopriv_skip_subscribe', array( $this, 'skip_subscribe' ) ); add_action( 'wp_ajax_mark_onboarding_done', array( $this, 'mark_onboarding_done' ) ); add_action( 'wp_ajax_nopriv_mark_onboarding_done', array( $this, 'mark_onboarding_done' ) ); add_action( 'wp_ajax_tpc_get_logs', array( $this, 'external_get_logs' ) ); $this->register_feedback_settings(); $this->register_prevent_clone_hooks(); $this->get_font_parings(); } /** * Register hooks to prevent meta cloning for the templates. * This is needed because the template id is unique, and we don't want to clone it. * @return void */ public function register_prevent_clone_hooks() { $allowed_post_types = Editor::get_allowed_post_types(); if ( empty( $allowed_post_types ) ) { return; } foreach ( $allowed_post_types as $post_type ) { add_filter( 'update_' . $post_type . '_metadata', function ( $value, $post_id, $meta_key, $meta_value, $prev_value ) use ( $post_type ) { if ( $this->check_unique_template_id_on_meta_change( $post_id, $meta_key, $post_type, $meta_value ) ) { return true; } return $value; }, 10, 5 ); add_filter( 'add_' . $post_type . '_metadata', function ( $value, $post_id, $meta_key, $meta_value, $unique ) use ( $post_type ) { if ( $this->check_unique_template_id_on_meta_change( $post_id, $meta_key, $post_type, $meta_value ) ) { return true; } return $value; }, 10, 5 ); } } /** * Check that the meta value is unique for the allowed post types that support Templates Cloud. * * @param int $post_id The post ID. * @param string $meta_key The meta key. * @param string $meta_type The meta type. The post type ( post, page, neve_custom_layouts etc. ). * @param string $meta_value The meta value. * * @return bool */ public function check_unique_template_id_on_meta_change( $post_id, $meta_key, $meta_type, $meta_value ) { // Skip check if the meta key is not one of the allowed ones. if ( ! in_array( $meta_key, array( '_ti_tpc_template_sync', '_ti_tpc_template_id', '_ti_tpc_screenshot_url', '_ti_tpc_site_slug', '_ti_tpc_published', ), true ) ) { return false; } if ( empty( $meta_value ) ) { return false; } $template_id = get_post_meta( $post_id, '_ti_tpc_template_id', true ); if ( empty( $template_id ) && $meta_key === '_ti_tpc_template_id' ) { $template_id = $meta_value; } // Check if the template ID is used on any other posts or pages // exclude the current post from the query $args = array( 'post_type' => $meta_type, 'meta_key' => '_ti_tpc_template_id', 'meta_value' => $template_id, 'post__not_in' => array( $post_id ), 'posts_per_page' => 1, 'fields' => 'ids', ); $query = new \WP_Query( $args ); $duplicate_id = $query->get_posts(); if ( ! empty( $duplicate_id ) ) { // The template ID is already used on another post return true; } return false; } /** * Register feedback settings. * * @return void */ private function register_feedback_settings() { register_setting( 'tiob_feedback', self::IMPORTED_TEMPLATES_COUNT_OPT, array( 'type' => 'integer', 'show_in_rest' => true, 'default' => 0, ) ); register_setting( 'tiob_feedback', self::FEEDBACK_DISMISSED_OPT, array( 'type' => 'boolean', 'show_in_rest' => true, 'default' => false, ) ); } /** * Return the skip subscribe status. * Used to determine if email form should be displayed. * * @return bool */ private function get_skip_subscribe_status() { $status = false; if ( 'yes' === get_option( $this->skip_email_subscribe_namespace, 'no' ) ) { $status = true; } if ( get_transient( $this->skip_email_subscribe_namespace ) ) { $status = true; } return $status; } /** * Utility method to ensure proper response for ajax call. * * @param array $response */ private function ensure_ajax_response( $response ) { echo json_encode( $response ); die(); } /** * Handles the `skip_subscribe` ajax action. */ public function skip_subscribe() { $response = array( 'success' => false, 'code' => 'ti__ob_not_allowed', 'message' => 'Not allowed!', ); if ( ! isset( $_REQUEST['nonce'] ) ) { $this->ensure_ajax_response( $response ); return; } if ( ! wp_verify_nonce( $_REQUEST['nonce'], 'skip_subscribe_nonce' ) ) { $this->ensure_ajax_response( $response ); return; } unset( $response['code'] ); unset( $response['message'] ); $response['success'] = true; if ( isset( $_REQUEST['isTempSkip'] ) ) { set_transient( $this->skip_email_subscribe_namespace, 'yes', 7 * DAY_IN_SECONDS ); $this->ensure_ajax_response( $response ); return; } update_option( $this->skip_email_subscribe_namespace, 'yes' ); $this->ensure_ajax_response( $response ); } /** * Handles the `mark_onboarding_done` ajax action. */ public function mark_onboarding_done() { $response = array( 'success' => false, 'code' => 'ti__ob_not_allowed', 'message' => 'Not allowed!', ); if ( ! isset( $_REQUEST['nonce'] ) ) { $this->ensure_ajax_response( $response ); return; } if ( ! wp_verify_nonce( $_REQUEST['nonce'], 'onboarding_done_nonce' ) ) { $this->ensure_ajax_response( $response ); return; } unset( $response['code'] ); unset( $response['message'] ); update_option( 'tpc_onboarding_done', 'yes' ); $this->ensure_ajax_response( $response ); } /** * Hook into editor data to add Neve plan if available * or return the proper tier if stand-alone license is valid. * * @param array $data tiTpc exported data. * * @return array */ public function add_tpc_editor_data( $data ) { $plan = $this->neve_license_plan(); $data['tier'] = License::get_license_tier( $plan ); return $data; } /** * Redirect to onboarding if user is new. * * @return void */ public function activation_redirect() { $should_run_obd = get_option( 'tpc_maybe_run_onboarding', false ); if ( ! $should_run_obd ) { return; } if ( ! $this->should_load_onboarding() ) { return; } delete_option( 'tpc_maybe_run_onboarding' ); wp_safe_redirect( admin_url( 'admin.php?page=neve-onboarding' ) ); exit(); } /** * Use the Neve builtin compatibility to check for specific support. * * @return bool */ private function neve_theme_has_support( $feature ) { if ( defined( 'NEVE_COMPATIBILITY_FEATURES' ) ) { $features = NEVE_COMPATIBILITY_FEATURES; return isset( $features[ $feature ] ); } return false; } /** * Use the features defined in the TIOB plugin to check for specific support. * * @param string $feature The feature to check for. * * @return bool */ private function tiob_has_support( $feature ) { if ( defined( 'TIOB_FEATURES' ) ) { $features = TIOB_FEATURES; return isset( $features[ $feature ] ); } return false; } /** * Utility method to add a theme page from an array. * * @param array $page_data Page data. * @param int $offset Offset for the menu position. * * @return void */ private function add_theme_page_for_tiob( $page_data, $offset = 2 ) { if ( $this->neve_theme_has_support( 'theme_dedicated_menu' ) ) { global $submenu; $theme_page = 'neve-welcome'; $capability = 'activate_plugins'; add_submenu_page( $theme_page, $page_data['page_title'], $page_data['page_title'], $capability, $page_data['menu_slug'], $page_data['callback'] ); $item = array_pop( $submenu[ $theme_page ] ); array_splice( $submenu[ $theme_page ], $offset, 0, array( $item ) ); return; } // When using the new menu location we will not register items on the theme page anymore. if ( $this->tiob_has_support( 'new_menu' ) ) { return; } add_theme_page( $page_data['page_title'], $page_data['menu_title'], $page_data['capability'], $page_data['menu_slug'], $page_data['callback'] ); } /** * Utility method to add a plugin sub-page from an array. * * @param array $page_data Page data. * * @return void */ private function add_subpage_for_tiob( $page_data ) { $capability = 'manage_options'; add_submenu_page( $page_data['parent_slug'], $page_data['page_title'], $page_data['menu_title'], $capability, $page_data['menu_slug'], $page_data['callback'] ); } /** * Register theme options page. * * @return bool|void */ public function register() { $icon = 'data:image/svg+xml;base64,PHN2ZwogICAgICAgIHdpZHRoPSIxMDAiCiAgICAgICAgaGVpZ2h0PSIxMDAiCiAgICAgICAgdmlld0JveD0iMCAwIDEwMCAxMDAiCiAgICAgICAgZmlsbD0iI2YwZjBmMSIKICAgICAgICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCj4KICAgIDxwYXRoCiAgICAgICAgICAgIGQ9Ik05NS4wMjY0IDEwMEg0Ljk3MzU2QzIuMjI3OTcgMTAwIDAgOTcuNzcyIDAgOTUuMDI2NFY0Ljk3MzU2QzAgMi4yMjc5NyAyLjIyNzk3IDAgNC45NzM1NiAwSDk1LjAyNjRDOTcuNzcyIDAgMTAwIDIuMjI3OTcgMTAwIDQuOTczNTZWOTUuMDI2NEMxMDAgOTcuNzcyIDk3Ljc3MiAxMDAgOTUuMDI2NCAxMDBaIE04Mi42OTQxIDg2Ljc0NDhWMzAuODIwNVYxOC40NjUzSDcwLjM1MDJIMTQuNDE0NkwyNi43NTg0IDMwLjgyMDVINzAuMzUwMlY3NC40MDFMODIuNjk0MSA4Ni43NDQ4WiBNNDIuMjQxNiA1OC45MjkxTDQyLjI1MjggNzEuMTgzTDUzLjIzNTIgODIuMTY1M0w1My4xOTAyIDQ3Ljk4MDZMMTguOTk0MSA0Ny45MzU1TDI5Ljk3NjUgNTguOTA2Nkw0Mi4yNDE2IDU4LjkyOTFaIgogICAgICAgICAgICBmaWxsPSIjZjBmMGYxIgogICAgLz4KPC9zdmc+Cg=='; $priority = 61; // The position of the menu item, 60 is the position of the Appearance menu. $plugin_page = 'tiob-plugin'; $tpc_menu_page_data = array( 'page_title' => __( 'Templates Cloud', 'templates-patterns-collection' ), 'menu_title' => __( 'Templates Cloud', 'templates-patterns-collection' ), 'capability' => 'manage_options', 'menu_slug' => $plugin_page, 'callback' => array( $this, 'render_starter_sites', ), ); add_menu_page( $tpc_menu_page_data['page_title'], $tpc_menu_page_data['menu_title'], $tpc_menu_page_data['capability'], $tpc_menu_page_data['menu_slug'], $tpc_menu_page_data['callback'], $icon, $priority ); if ( $this->is_library_disabled() && $this->is_starter_sites_disabled() ) { return false; } $style = 'display:inline-block;'; if ( ! is_rtl() ) { $style .= 'transform:scaleX(-1);margin-right:5px;'; } else { $style .= 'margin-left:5px;'; } $prefix = defined( 'NEVE_VERSION' ) ? '↵' : ''; $starter_site_data = array( 'page_title' => __( 'Starter Sites', 'templates-patterns-collection' ), 'menu_title' => $prefix . __( 'Starter Sites', 'templates-patterns-collection' ), 'capability' => 'activate_plugins', 'menu_slug' => $this->page_slug, 'callback' => array( $this, 'render_starter_sites', ), ); $page_templates_data = array( 'page_title' => __( 'Page Templates', 'templates-patterns-collection' ), 'menu_title' => $prefix . __( 'Page Templates', 'templates-patterns-collection' ), 'capability' => 'activate_plugins', 'menu_slug' => ( $this->neve_theme_has_support( 'theme_dedicated_menu' ) ? 'admin.php' : 'themes.php' ) . '?page=' . $this->page_slug . '#pageTemplates', 'callback' => '', ); $library_data = array( 'parent_slug' => $plugin_page, 'page_title' => __( 'My Library', 'templates-patterns-collection' ), 'menu_title' => __( 'My Library', 'templates-patterns-collection' ), 'capability' => 'activate_plugins', 'menu_slug' => $plugin_page, 'callback' => $tpc_menu_page_data['callback'], ); $settings_data = array( 'parent_slug' => $plugin_page, 'page_title' => __( 'Settings', 'templates-patterns-collection' ), 'menu_title' => __( 'Settings', 'templates-patterns-collection' ), 'capability' => 'activate_plugins', 'menu_slug' => 'admin.php?page=' . $plugin_page . '#settings', 'callback' => '', ); if ( $this->is_starter_sites_disabled() && ! $this->is_library_disabled() ) { $library_data['menu_slug'] = $plugin_page; $library_data['callback'] = array( $this, 'render_starter_sites', ); $this->add_subpage_for_tiob( $library_data ); $this->add_subpage_for_tiob( $settings_data ); return false; } $this->add_theme_page_for_tiob( $starter_site_data, 2 ); $this->add_theme_page_for_tiob( $page_templates_data, 3 ); if ( $this->should_load_onboarding() ) { $onboarding_data = array( 'page_title' => __( 'Onboarding', 'templates-patterns-collection' ), 'menu_title' => $prefix . __( 'Onboarding', 'templates-patterns-collection' ), 'capability' => 'install_plugins', 'menu_slug' => 'neve-onboarding', 'callback' => array( $this, 'render_onboarding', ), ); $this->add_theme_page_for_tiob( $onboarding_data, 4 ); } if ( $this->is_library_disabled() ) { return false; } $this->add_subpage_for_tiob( $library_data ); $this->add_subpage_for_tiob( $settings_data ); } /** * Hide the onboarding item from Neve menu. * * @param $submenu_file string The submenu file. * * @return string */ public function hide_onboarding( $submenu_file ) { remove_submenu_page( 'neve-welcome', 'neve-onboarding' ); return $submenu_file; } /** * Map license plan from Neve if available. * * @return int */ private function neve_license_plan() { $category = apply_filters( 'product_neve_license_plan', -1 ); $category_mapping = License::NEVE_CATEGORY_MAPPING; return $category > -1 && isset( $category_mapping[ $category ] ) ? $category_mapping[ $category ] : -1; } /** * Check if current subscription is agency * or if we have a valid license for the standalone product. * * @return bool */ private function is_agency_plan() { $plan = $this->neve_license_plan(); $plan = License::get_license_tier( $plan ); return $plan === 3; } /** * Render method for the starter sites page. */ public function render_starter_sites() { echo '
'; } /** * Render method for the onboarding page. */ public function render_onboarding() { echo ''; } /** * Decide if the new onboarding should load * * @return bool */ private function should_load_onboarding() { if ( ! current_user_can( 'install_plugins' ) ) { return false; } if ( $this->is_starter_sites_disabled() ) { return false; } $current_theme = wp_get_theme(); $template = $current_theme->template === 'neve' ? $current_theme->template : $current_theme->parent(); if ( $template !== 'neve' ) { return false; } $onboarding_done = get_option( 'tpc_onboarding_done', 'no' ); if ( $onboarding_done === 'yes' ) { return false; } return get_option( 'tpc_obd_new_user', 'no' ) === 'yes'; } /** * Enqueue scripts and styles * * @return void */ public function enqueue() { $screen = get_current_screen(); if ( ! isset( $screen->id ) ) { return; } if ( $this->should_load_onboarding() && strpos( $screen->id, '_page_neve-onboarding' ) ) { wp_enqueue_media(); $onboarding_dependencies = ( include TIOB_PATH . 'onboarding/build/index.asset.php' ); wp_register_style( 'tiobObd', TIOB_URL . 'onboarding/build/style-index.css', array( 'wp-components' ), $onboarding_dependencies['version'] ); wp_style_add_data( 'tiobObd', 'rtl', 'replace' ); wp_enqueue_style( 'tiobObd' ); wp_register_script( 'tiobObd', TIOB_URL . 'onboarding/build/index.js', array_merge( $onboarding_dependencies['dependencies'], array( 'updates' ) ), $onboarding_dependencies['version'], true ); wp_localize_script( 'tiobObd', 'tiobDash', apply_filters( 'neve_dashboard_page_data', $this->get_localization() ) ); wp_enqueue_script( 'tiobObd' ); if ( ! empty( $this->google_fonts ) ) { $font_chunks = array_chunk( $this->google_fonts, absint( count( $this->google_fonts ) / 5 ) ); foreach ( $font_chunks as $index => $fonts_chunk ) { wp_enqueue_style( 'tiob-google-fonts-' . $index, 'https://fonts.googleapis.com/css?family=' . implode( '|', $fonts_chunk ) . '&display=swap"', array(), $onboarding_dependencies['version'] ); } } } $is_tiob_page = strpos( $screen->id, '_page_tiob-plugin' ) !== false; if ( strpos( $screen->id, '_page_' . $this->page_slug ) === false && ! $is_tiob_page ) { return; } $dismiss_notice = isset( $_GET['dismiss_notice'] ) && $_GET['dismiss_notice'] === 'yes'; if ( $dismiss_notice ) { update_option( self::VISITED_LIBRARY_OPT, 'yes' ); } $dependencies = ( include TIOB_PATH . 'assets/build/app.asset.php' ); wp_register_style( 'tiob', TIOB_URL . 'assets/build/style-app.css', array( 'wp-components' ), $dependencies['version'] ); wp_style_add_data( 'tiob', 'rtl', 'replace' ); wp_enqueue_style( 'tiob' ); wp_register_script( 'tiob', TIOB_URL . 'assets/build/app.js', array_merge( $dependencies['dependencies'], array( 'updates' ) ), $dependencies['version'], true ); $tiob_dash = apply_filters( 'neve_dashboard_page_data', $this->get_localization() ); if ( $is_tiob_page ) { $tiob_dash['hideStarterSites'] = true; } wp_localize_script( 'tiob', 'tiobDash', apply_filters( 'neve_dashboard_page_data', $tiob_dash ) ); wp_enqueue_script( 'tiob' ); } /** * Get localization data for the dashboard script. * * @return array */ private function get_localization() { $theme_name = apply_filters( 'ti_wl_theme_name', 'Neve' ); $user = wp_get_current_user(); $neve_upgrade_link = 'https://themeisle.com/themes/neve/upgrade/'; $upgrade_url = apply_filters( 'neve_upgrade_link_from_child_theme_filter', tsdk_utmify( $neve_upgrade_link, 'freevspro' ) ); $upgrade_url_tpc = tsdk_utmify( 'https://themeisle.com/plugins/templates-cloud', 'tcupgrade' ); if ( defined( 'NEVE_VERSION' ) ) { $upgrade_url_tpc = apply_filters( 'neve_upgrade_link_from_child_theme_filter', tsdk_utmify( $neve_upgrade_link, 'templatecloud' ) ); } return array( 'version' => TIOB_VERSION, 'nonce' => wp_create_nonce( 'wp_rest' ), 'assets' => TIOB_URL . 'assets/', 'upgradeURL' => $upgrade_url, 'upgradeURLTpc' => $upgrade_url_tpc, 'strings' => array( /* translators: %s - Theme name */ 'starterSitesTabDescription' => __( 'Choose from multiple unique demos, specially designed for you, that can be installed with a single click. You just need to choose your favorite, and we will take care of everything else.', 'templates-patterns-collection' ), ), 'cleanupAllowed' => ( ! empty( get_transient( Active_State::STATE_NAME ) ) ) ? 'yes' : 'no', 'onboarding' => array(), 'hasFileSystem' => WP_Filesystem(), 'themesURL' => admin_url( 'themes.php' ), 'themeAction' => $this->get_theme_action(), 'brandedTheme' => $this->get_whitelabel_name(), 'hideStarterSites' => $this->is_starter_sites_disabled(), 'hideMyLibrary' => $this->is_library_disabled(), 'fontParings' => $this->font_pairs_neve, 'endpoint' => ( defined( 'TPC_TEMPLATES_CLOUD_ENDPOINT' ) ) ? TPC_TEMPLATES_CLOUD_ENDPOINT : self::get_templates_cloud_endpoint(), 'params' => array( 'site_url' => get_site_url(), 'license_id' => License::get_license_data()->key, ), 'upsellNotifications' => $this->get_upsell_notifications(), 'isValidLicense' => $this->has_valid_addons(), 'licenseTIOB' => License::get_license_data(), 'emailSubscribe' => array( 'ajaxURL' => esc_url( admin_url( 'admin-ajax.php' ) ), 'nonce' => wp_create_nonce( 'skip_subscribe_nonce' ), 'skipStatus' => $this->get_skip_subscribe_status() ? 'yes' : 'no', 'email' => ( ! empty( $user->user_email ) ) ? $user->user_email : '', ), 'onboardingDone' => array( 'ajaxURL' => esc_url( admin_url( 'admin-ajax.php' ) ), 'nonce' => wp_create_nonce( 'onboarding_done_nonce' ), ), 'feedback' => array( 'count' => get_option( self::IMPORTED_TEMPLATES_COUNT_OPT, 0 ), 'dismissed' => get_option( self::FEEDBACK_DISMISSED_OPT, false ), ), 'onboardingUpsell' => array( 'dashboard' => tsdk_utmify( 'https://store.themeisle.com/', 'onboarding_upsell' ), 'contact' => tsdk_utmify( 'https://themeisle.com/contact/', 'onboarding_upsell' ), 'upgrade' => tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'onboarding_upsell' ), 'upgradeToast' => tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'onboarding_toast' ), ), 'onboardingAllowed' => $this->should_load_onboarding(), 'onboardingRedirect' => admin_url( 'admin.php?page=neve-onboarding' ), 'tiobSettings' => admin_url( 'admin.php?page=tiob-plugin#settings' ), 'links' => array( array( 'label' => __( 'Support', 'templates-patterns-collection' ), 'is_external' => true, 'url' => tsdk_utmify( 'https://themeisle.com/contact/', 'settings_page' ), ), array( 'label' => __( 'Feature request', 'templates-patterns-collection' ), 'target' => '_self', 'url' => admin_url( 'admin.php?page=tiob-plugin&tab=feedback#settings' ), ), array( 'label' => __( 'Leave a review', 'templates-patterns-collection' ), 'is_external' => true, 'url' => 'https://wordpress.org/support/plugin/templates-patterns-collection/reviews/#new-post', ), array( 'label' => __( 'Documentation', 'templates-patterns-collection' ), 'url' => tsdk_utmify( 'https://docs.themeisle.com/article/1354-neve-template-cloud-library', 'settings_page' ), 'is_button' => true, ), ), ); } /** * Neve Pro upsells. * @return array */ private function get_upsell_notifications() { $notifications['upsell_1'] = array( // We use these strings in Neve already so lets reuse the translations here. 'text' => esc_html__( 'Purchase the Business plan or higher to get instant access to all Premium Starter Site Templates — including Expert Sites — and much more.', 'neve' ), //phpcs:ignore WordPress.WP.I18n.TextDomainMismatch 'cta' => __( 'Get Neve Business', 'neve' ), //phpcs:ignore WordPress.WP.I18n.TextDomainMismatch 'url' => tsdk_utmify( 'https://themeisle.com/themes/neve/upgrade/', 'WP_Filesystem
' )
),
);
}
/**
* Check validity of addons plugin.
*
* @return bool
*/
private function has_valid_addons() {
if ( ! defined( 'NEVE_PRO_BASEFILE' ) ) {
return false;
}
$option_name = basename( dirname( NEVE_PRO_BASEFILE ) );
$option_name = str_replace( '-', '_', strtolower( trim( $option_name ) ) );
$status = get_option( $option_name . '_license_data' );
if ( ! $status ) {
return false;
}
if ( ! isset( $status->license ) ) {
return false;
}
if ( $status->license === 'not_active' || $status->license === 'invalid' ) {
return false;
}
return true;
}
/**
* Get logs from transient via ajax.
*/
public function external_get_logs() {
$nonce = $_POST['nonce'];
if ( ! wp_verify_nonce( $nonce, 'wp_rest' ) ) {
wp_die( __( 'Nonce verification failed', 'templates-patterns-collection' ) );
}
$data = get_transient( Logger::$log_transient_name );
if ( ! empty( $data ) ) {
echo $data;
wp_die();
}
wp_die( __( 'No logs found', 'templates-patterns-collection' ) );
}
}