is_from_partner( $product ) ) { return false; } $this->debug = apply_filters( 'themeisle_sdk_promo_debug', $this->debug ); $promotions_to_load = apply_filters( $product->get_key() . '_load_promotions', array() ); $promotions_to_load[] = 'optimole'; $promotions_to_load[] = 'rop'; $this->promotions = $this->get_promotions(); foreach ( $this->promotions as $slug => $data ) { if ( ! in_array( $slug, $promotions_to_load, true ) ) { unset( $this->promotions[ $slug ] ); } } add_action( 'init', array( $this, 'register_settings' ), 99 ); add_action( 'admin_init', array( $this, 'register_reference' ), 99 ); return ! empty( $this->promotions ); } /** * Registers the hooks. * * @param Product $product Product to load. */ public function load( $product ) { if ( ! $this->is_writeable() || ! current_user_can( 'install_plugins' ) ) { return; } $this->product = $product; $last_dismiss = $this->get_last_dismiss_time(); if ( ! $this->debug && $last_dismiss && ( time() - $last_dismiss ) < 7 * DAY_IN_SECONDS ) { return; } add_filter( 'attachment_fields_to_edit', array( $this, 'add_attachment_field' ), 10, 2 ); add_action( 'current_screen', [ $this, 'load_available' ] ); add_action( 'elementor/editor/after_enqueue_scripts', array( $this, 'enqueue' ) ); add_filter( 'themeisle_sdk_ran_promos', '__return_true' ); } /** * Load available promotions. */ public function load_available() { $this->promotions = $this->filter_by_screen_and_merge(); if ( empty( $this->promotions ) ) { return; } $this->load_promotion( $this->promotions[ array_rand( $this->promotions ) ] ); } /** * Register plugin reference. * * @return void */ public function register_reference() { if ( isset( $_GET['reference_key'] ) ) { update_option( 'otter_reference_key', sanitize_key( $_GET['reference_key'] ) ); } if ( isset( $_GET['optimole_reference_key'] ) ) { update_option( 'optimole_reference_key', sanitize_key( $_GET['optimole_reference_key'] ) ); } if ( isset( $_GET['rop_reference_key'] ) ) { update_option( 'rop_reference_key', sanitize_key( $_GET['rop_reference_key'] ) ); } } /** * Register Settings */ public function register_settings() { $default = get_option( 'themeisle_sdk_promotions_otter', '{}' ); register_setting( 'themeisle_sdk_settings', $this->option_main, array( 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'show_in_rest' => true, 'default' => $default, ) ); register_setting( 'themeisle_sdk_settings', $this->option_otter, array( 'type' => 'boolean', 'sanitize_callback' => 'rest_sanitize_boolean', 'show_in_rest' => true, 'default' => false, ) ); register_setting( 'themeisle_sdk_settings', $this->option_optimole, array( 'type' => 'boolean', 'sanitize_callback' => 'rest_sanitize_boolean', 'show_in_rest' => true, 'default' => false, ) ); register_setting( 'themeisle_sdk_settings', $this->option_rop, array( 'type' => 'boolean', 'sanitize_callback' => 'rest_sanitize_boolean', 'show_in_rest' => true, 'default' => false, ) ); } /** * Get the SDK base url. * * @return string */ private function get_sdk_uri() { global $themeisle_sdk_max_path; /** * $themeisle_sdk_max_path can point to the theme when the theme version is higher. * hence we also need to check that the path does not point to the theme else this will break the URL. * References: https://github.com/Codeinwp/neve-pro-addon/issues/2403 */ if ( $this->product->is_plugin() && false === strpos( $themeisle_sdk_max_path, get_template_directory() ) ) { return plugins_url( '/', $themeisle_sdk_max_path . '/themeisle-sdk/' ); }; return get_template_directory_uri() . '/vendor/codeinwp/themeisle-sdk/'; } /** * Check if the path is writable. * * @return boolean * @access public */ public function is_writeable() { include_once ABSPATH . 'wp-admin/includes/file.php'; $filesystem_method = get_filesystem_method(); if ( 'direct' === $filesystem_method ) { return true; } return false; } /** * Get the Otter Blocks plugin status. * * @param string $plugin Plugin slug. * * @return bool */ private function is_plugin_installed( $plugin ) { static $allowed_keys = [ 'otter-blocks' => 'otter-blocks/otter-blocks.php', 'optimole-wp' => 'optimole-wp/optimole-wp.php', 'tweet-old-post' => 'tweet-old-post/tweet-old-post.php', ]; if ( ! isset( $allowed_keys[ $plugin ] ) ) { return false; } if ( file_exists( WP_CONTENT_DIR . '/plugins/' . $allowed_keys[ $plugin ] ) ) { return true; } return false; } /** * Get promotions. * * @return array */ private function get_promotions() { $has_otter = defined( 'OTTER_BLOCKS_VERSION' ) || $this->is_plugin_installed( 'otter-blocks' ); $had_otter_from_promo = get_option( $this->option_otter, false ); $has_optimole = defined( 'OPTIMOLE_VERSION' ) || $this->is_plugin_installed( 'optimole-wp' ); $had_optimole_from_promo = get_option( $this->option_optimole, false ); $has_rop = defined( 'ROP_LITE_VERSION' ) || $this->is_plugin_installed( 'tweet-old-post' ); $had_rop_from_promo = get_option( $this->option_rop, false ); $is_min_req_v = version_compare( get_bloginfo( 'version' ), '5.8', '>=' ); $has_enough_attachments = $this->has_min_media_attachments(); $has_enough_old_posts = $this->has_old_posts(); $all = [ 'optimole' => [ 'om-editor' => [ 'env' => ! $has_optimole && $is_min_req_v && ! $had_optimole_from_promo, 'screen' => 'editor', ], 'om-image-block' => [ 'env' => ! $has_optimole && $is_min_req_v && ! $had_optimole_from_promo, 'screen' => 'editor', ], 'om-attachment' => [ 'env' => ! $has_optimole && ! $had_optimole_from_promo, 'screen' => 'media-editor', ], 'om-media' => [ 'env' => ! $has_optimole && ! $had_optimole_from_promo && $has_enough_attachments, 'screen' => 'media', ], 'om-elementor' => [ 'env' => ! $has_optimole && ! $had_optimole_from_promo && defined( 'ELEMENTOR_VERSION' ), 'screen' => 'elementor', ], ], 'otter' => [ 'blocks-css' => [ 'env' => ! $has_otter && $is_min_req_v && ! $had_otter_from_promo, 'screen' => 'editor', ], 'blocks-animation' => [ 'env' => ! $has_otter && $is_min_req_v && ! $had_otter_from_promo, 'screen' => 'editor', ], 'blocks-conditions' => [ 'env' => ! $has_otter && $is_min_req_v && ! $had_otter_from_promo, 'screen' => 'editor', ], ], 'rop' => [ 'rop-posts' => [ 'env' => ! $has_rop && ! $had_rop_from_promo && $has_enough_old_posts, 'screen' => 'edit-post', ], ], ]; foreach ( $all as $slug => $data ) { foreach ( $data as $key => $conditions ) { if ( ! $conditions['env'] ) { unset( $all[ $slug ][ $key ] ); continue; } if ( $this->get_upsells_dismiss_time( $key ) ) { unset( $all[ $slug ][ $key ] ); } } if ( empty( $all[ $slug ] ) ) { unset( $all[ $slug ] ); } } return $all; } /** * Get the upsell dismiss time. * * @param string $key The upsell key. If empty will return all dismiss times. * * @return false | string */ private function get_upsells_dismiss_time( $key = '' ) { $old = get_option( 'themeisle_sdk_promotions_otter', '{}' ); $data = get_option( $this->option_main, $old ); $data = json_decode( $data, true ); if ( empty( $key ) ) { return $data; } return isset( $data[ $key ] ) ? $data[ $key ] : false; } /** * Get the last dismiss time of a promotion. * * @return false */ private function get_last_dismiss_time() { $dismissed = $this->get_upsells_dismiss_time(); if ( empty( $dismissed ) ) { return false; } $last_dismiss = max( array_values( $dismissed ) ); return $last_dismiss; } /** * Filter by screen & merge into single array of keys. * * @return array */ private function filter_by_screen_and_merge() { $current_screen = get_current_screen(); $is_elementor = isset( $_GET['action'] ) && $_GET['action'] === 'elementor'; $is_media = isset( $current_screen->id ) && $current_screen->id === 'upload'; $is_posts = isset( $current_screen->id ) && $current_screen->id === 'edit-post'; $is_editor = method_exists( $current_screen, 'is_block_editor' ) && $current_screen->is_block_editor(); $return = []; foreach ( $this->promotions as $slug => $promos ) { foreach ( $promos as $key => $data ) { switch ( $data['screen'] ) { case 'media-editor': if ( ! $is_media && ! $is_editor ) { unset( $this->promotions[ $slug ][ $key ] ); } break; case 'media': if ( ! $is_media ) { unset( $this->promotions[ $slug ][ $key ] ); } break; case 'editor': if ( ! $is_editor || $is_elementor ) { unset( $this->promotions[ $slug ][ $key ] ); } break; case 'elementor': if ( ! $is_elementor ) { unset( $this->promotions[ $slug ][ $key ] ); } break; case 'edit-post': if ( ! $is_posts ) { unset( $this->promotions[ $slug ][ $key ] ); } break; } } $return = array_merge( $return, $this->promotions[ $slug ] ); } return array_keys( $return ); } /** * Load single promotion. * * @param string $slug slug of the promotion. */ private function load_promotion( $slug ) { $this->loaded_promo = $slug; if ( $this->debug ) { add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue' ] ); add_action( 'admin_enqueue_scripts', [ $this, 'enqueue' ] ); if ( $this->get_upsells_dismiss_time( 'om-media' ) === false ) { add_action( 'admin_notices', [ $this, 'render_optimole_dash_notice' ] ); } if ( $this->get_upsells_dismiss_time( 'rop-posts' ) === false ) { add_action( 'admin_notices', [ $this, 'render_rop_dash_notice' ] ); } return; } switch ( $slug ) { case 'om-editor': case 'om-image-block': case 'blocks-css': case 'blocks-animation': case 'blocks-conditions': add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue' ] ); break; case 'om-attachment': add_action( 'admin_enqueue_scripts', [ $this, 'enqueue' ] ); break; case 'om-media': add_action( 'admin_enqueue_scripts', [ $this, 'enqueue' ] ); add_action( 'admin_notices', [ $this, 'render_optimole_dash_notice' ] ); break; case 'rop-posts': add_action( 'admin_enqueue_scripts', [ $this, 'enqueue' ] ); add_action( 'admin_notices', [ $this, 'render_rop_dash_notice' ] ); break; } } /** * Render dashboard notice. */ public function render_optimole_dash_notice() { $screen = get_current_screen(); if ( ! isset( $screen->id ) || $screen->id !== 'upload' ) { return; } echo '
'; } /** * Enqueue the assets. */ public function enqueue() { global $themeisle_sdk_max_path; $handle = 'ti-sdk-promo'; $saved = $this->get_upsells_dismiss_time(); $themeisle_sdk_src = $this->get_sdk_uri(); $user = wp_get_current_user(); $asset_file = require $themeisle_sdk_max_path . '/assets/js/build/index.asset.php'; $deps = array_merge( $asset_file['dependencies'], [ 'updates' ] ); wp_register_script( $handle, $themeisle_sdk_src . 'assets/js/build/index.js', $deps, $asset_file['version'], true ); wp_localize_script( $handle, 'themeisleSDKPromotions', [ 'debug' => $this->debug, 'email' => $user->user_email, 'showPromotion' => $this->loaded_promo, 'optionKey' => $this->option_main, 'product' => $this->product->get_name(), 'option' => empty( $saved ) ? new \stdClass() : $saved, 'nonce' => wp_create_nonce( 'wp_rest' ), 'assets' => $themeisle_sdk_src . 'assets/images/', 'optimoleApi' => esc_url( rest_url( 'optml/v1/register_service' ) ), 'optimoleActivationUrl' => $this->get_plugin_activation_link( 'optimole-wp' ), 'otterActivationUrl' => $this->get_plugin_activation_link( 'otter-blocks' ), 'ropActivationUrl' => $this->get_plugin_activation_link( 'tweet-old-post' ), 'optimoleDash' => esc_url( add_query_arg( [ 'page' => 'optimole' ], admin_url( 'upload.php' ) ) ), 'ropDash' => esc_url( add_query_arg( [ 'page' => 'TweetOldPost' ], admin_url( 'admin.php' ) ) ), // translators: %s is the product name. 'title' => esc_html( sprintf( __( 'Recommended by %s', 'textdomain' ), $this->product->get_name() ) ), ] ); wp_enqueue_script( $handle ); wp_enqueue_style( $handle, $themeisle_sdk_src . 'assets/js/build/style-index.css', [ 'wp-components' ], $asset_file['version'] ); } /** * Render rop notice. */ public function render_rop_dash_notice() { $screen = get_current_screen(); if ( ! isset( $screen->id ) || $screen->id !== 'edit-post' ) { return; } echo '
'; } /** * Add promo to attachment modal. * * @param array $fields Fields array. * @param \WP_Post $post Post object. * * @return array */ public function add_attachment_field( $fields, $post ) { if ( $post->post_type !== 'attachment' ) { return $fields; } if ( ! isset( $post->post_mime_type ) || strpos( $post->post_mime_type, 'image' ) === false ) { return $fields; } $meta = wp_get_attachment_metadata( $post->ID ); if ( isset( $meta['filesize'] ) && $meta['filesize'] < 200000 ) { return $fields; } $fields['optimole'] = array( 'input' => 'html', 'html' => '
', 'label' => '', ); if ( count( $fields ) < 2 ) { add_filter( 'wp_required_field_message', '__return_empty_string' ); } return $fields; } /** * Get plugin activation link. * * @param string $slug The plugin slug. * * @return string */ private function get_plugin_activation_link( $slug ) { $reference_key = $slug === 'otter-blocks' ? 'reference_key' : 'optimole_reference_key'; return esc_url( add_query_arg( array( 'plugin_status' => 'all', 'paged' => '1', 'action' => 'activate', $reference_key => $this->product->get_key(), 'plugin' => rawurlencode( $slug . '/' . $slug . '.php' ), '_wpnonce' => wp_create_nonce( 'activate-plugin_' . $slug . '/' . $slug . '.php' ), ), admin_url( 'plugins.php' ) ) ); } /** * Check if has 50 image media items. * * @return bool */ private function has_min_media_attachments() { if ( $this->debug ) { return true; } $attachment_count = get_transient( 'tsk_attachment_count' ); if ( false === $attachment_count ) { $args = array( 'post_type' => 'attachment', 'posts_per_page' => 51, 'fields' => 'ids', 'post_status' => 'inherit', 'no_found_rows' => true, ); $query = new \WP_Query( $args ); $attachment_count = $query->post_count; set_transient( 'tsk_attachment_count', $attachment_count, DAY_IN_SECONDS ); } return $attachment_count > 50; } /** * Check if the website has more than 100 posts and over 10 are over a year old. * * @return bool */ private function has_old_posts() { if ( $this->debug ) { return true; } $posts_count = get_transient( 'tsk_posts_count' ); // Create a new WP_Query object to get all posts $args = array( 'post_type' => 'post', 'posts_per_page' => 101, //phpcs:ignore WordPress.WP.PostsPerPage.posts_per_page_posts_per_page 'fields' => 'ids', 'no_found_rows' => true, ); if ( false === $posts_count ) { $query = new \WP_Query( $args ); $total_posts = $query->post_count; wp_reset_postdata(); // Count the number of posts older than 1 year $one_year_ago = gmdate( 'Y-m-d H:i:s', strtotime( '-1 year' ) ); $args['date_query'] = array( array( 'before' => $one_year_ago, 'inclusive' => true, ), ); $query = new \WP_Query( $args ); $old_posts = $query->post_count; wp_reset_postdata(); $posts_count = array( 'total_posts' => $total_posts, 'old_posts' => $old_posts, ); set_transient( 'tsk_posts_count', $posts_count, DAY_IN_SECONDS ); } // Check if there are more than 100 posts and more than 10 old posts return $posts_count['total_posts'] > 100 && $posts_count['old_posts'] > 10; } }