PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.9.7
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.9.7
4.0.8 4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.10.1 3.10.10 3.10.11 3.10.12 3.10.13 3.10.14 3.10.15 3.10.2 3.10.3 All 149 releases
visualizer / vendor / codeinwp / themeisle-sdk / src / Modules / Promotions.php

Promotions.php in Visualizer – Tables & Charts Manager with Built-in AI Generator 3.9.7, at vendor/codeinwp/themeisle-sdk/src/Modules/Promotions.php

589 lines 15.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * The promotions model class for ThemeIsle SDK
4 *
5 * Here's how to hook it in your plugin: add_filter( 'menu_icons_load_promotions', function() { return array( 'otter' ); } );
6 *
7 * @package ThemeIsleSDK
8 * @subpackage Modules
9 * @copyright Copyright (c) 2017, Marius Cristea
10 * @license http://opensource.org/licenses/gpl-3.0.php GNU Public License
11 * @since 1.0.0
12 */
13
14 namespace ThemeisleSDK\Modules;
15
16 use ThemeisleSDK\Common\Abstract_Module;
17 use ThemeisleSDK\Product;
18
19 // Exit if accessed directly.
20 if ( ! defined( 'ABSPATH' ) ) {
21 exit;
22 }
23
24 /**
25 * Promotions module for ThemeIsle SDK.
26 */
27 class Promotions extends Abstract_Module {
28
29 /**
30 * Holds the promotions.
31 *
32 * @var array
33 */
34 private $promotions = array();
35
36 /**
37 * Option key for promos.
38 *
39 * @var string
40 */
41 private $option_main = 'themeisle_sdk_promotions';
42
43 /**
44 * Option key for otter promos.
45 *
46 * @var string
47 */
48 private $option_otter = 'themeisle_sdk_promotions_otter_installed';
49
50 /**
51 * Option key for optimole promos.
52 *
53 * @var string
54 */
55 private $option_optimole = 'themeisle_sdk_promotions_optimole_installed';
56
57 /**
58 * Loaded promotion.
59 *
60 * @var string
61 */
62 private $loaded_promo;
63
64 /**
65 * Debug mode.
66 *
67 * @var bool
68 */
69 private $debug = false;
70
71 /**
72 * Should we load this module.
73 *
74 * @param Product $product Product object.
75 *
76 * @return bool
77 */
78 public function can_load( $product ) {
79 if ( apply_filters( 'themeisle_sdk_ran_promos', false ) === true ) {
80 return false;
81 }
82 if ( $this->is_from_partner( $product ) ) {
83 return false;
84 }
85
86 $this->debug = apply_filters( 'themeisle_sdk_promo_debug', $this->debug );
87 $promotions_to_load = apply_filters( $product->get_key() . '_load_promotions', array() );
88 $promotions_to_load[] = 'optimole';
89
90 $this->promotions = $this->get_promotions();
91
92 foreach ( $this->promotions as $slug => $data ) {
93 if ( ! in_array( $slug, $promotions_to_load, true ) ) {
94 unset( $this->promotions[ $slug ] );
95 }
96 }
97
98 add_action( 'init', array( $this, 'register_settings' ), 99 );
99 add_action( 'admin_init', array( $this, 'register_reference' ), 99 );
100
101 return ! empty( $this->promotions );
102 }
103
104 /**
105 * Registers the hooks.
106 *
107 * @param Product $product Product to load.
108 */
109 public function load( $product ) {
110 if ( ! $this->is_writeable() || ! current_user_can( 'install_plugins' ) ) {
111 return;
112 }
113
114 $this->product = $product;
115
116 $last_dismiss = $this->get_last_dismiss_time();
117
118 if ( ! $this->debug && $last_dismiss && ( time() - $last_dismiss ) < 7 * DAY_IN_SECONDS ) {
119 return;
120 }
121
122 add_filter( 'attachment_fields_to_edit', array( $this, 'add_attachment_field' ), 10, 2 );
123 add_action( 'current_screen', [ $this, 'load_available' ] );
124 add_action( 'elementor/editor/after_enqueue_scripts', array( $this, 'enqueue' ) );
125 add_filter( 'themeisle_sdk_ran_promos', '__return_true' );
126 }
127
128 /**
129 * Load available promotions.
130 */
131 public function load_available() {
132 $this->promotions = $this->filter_by_screen_and_merge();
133
134 if ( empty( $this->promotions ) ) {
135 return;
136 }
137
138 $this->load_promotion( $this->promotions[ array_rand( $this->promotions ) ] );
139 }
140
141
142 /**
143 * Register plugin reference.
144 *
145 * @return void
146 */
147 public function register_reference() {
148 if ( isset( $_GET['reference_key'] ) ) {
149 update_option( 'otter_reference_key', sanitize_key( $_GET['reference_key'] ) );
150 }
151
152 if ( isset( $_GET['optimole_reference_key'] ) ) {
153 update_option( 'optimole_reference_key', sanitize_key( $_GET['optimole_reference_key'] ) );
154 }
155 }
156
157 /**
158 * Register Settings
159 */
160 public function register_settings() {
161 $default = get_option( 'themeisle_sdk_promotions_otter', '{}' );
162
163 register_setting(
164 'themeisle_sdk_settings',
165 $this->option_main,
166 array(
167 'type' => 'string',
168 'sanitize_callback' => 'sanitize_text_field',
169 'show_in_rest' => true,
170 'default' => $default,
171 )
172 );
173
174 register_setting(
175 'themeisle_sdk_settings',
176 $this->option_otter,
177 array(
178 'type' => 'boolean',
179 'sanitize_callback' => 'rest_sanitize_boolean',
180 'show_in_rest' => true,
181 'default' => false,
182 )
183 );
184 register_setting(
185 'themeisle_sdk_settings',
186 $this->option_optimole,
187 array(
188 'type' => 'boolean',
189 'sanitize_callback' => 'rest_sanitize_boolean',
190 'show_in_rest' => true,
191 'default' => false,
192 )
193 );
194 }
195
196 /**
197 * Get the SDK base url.
198 *
199 * @return string
200 */
201 private function get_sdk_uri() {
202 global $themeisle_sdk_max_path;
203
204 /**
205 * $themeisle_sdk_max_path can point to the theme when the theme version is higher.
206 * hence we also need to check that the path does not point to the theme else this will break the URL.
207 * References: https://github.com/Codeinwp/neve-pro-addon/issues/2403
208 */
209 if ( $this->product->is_plugin() && false === strpos( $themeisle_sdk_max_path, get_template_directory() ) ) {
210 return plugins_url( '/', $themeisle_sdk_max_path . '/themeisle-sdk/' );
211 };
212
213 return get_template_directory_uri() . '/vendor/codeinwp/themeisle-sdk/';
214 }
215
216 /**
217 * Check if the path is writable.
218 *
219 * @return boolean
220 * @access public
221 */
222 public function is_writeable() {
223
224 include_once ABSPATH . 'wp-admin/includes/file.php';
225 $filesystem_method = get_filesystem_method();
226
227 if ( 'direct' === $filesystem_method ) {
228 return true;
229 }
230
231 return false;
232 }
233
234 /**
235 * Get the Otter Blocks plugin status.
236 *
237 * @param string $plugin Plugin slug.
238 *
239 * @return bool
240 */
241 private function is_plugin_installed( $plugin ) {
242 static $allowed_keys = [
243 'otter-blocks' => 'otter-blocks/otter-blocks.php',
244 'optimole-wp' => 'optimole-wp/optimole-wp.php',
245 ];
246
247 if ( ! isset( $allowed_keys[ $plugin ] ) ) {
248 return false;
249 }
250
251 if ( file_exists( WP_CONTENT_DIR . '/plugins/' . $allowed_keys[ $plugin ] ) ) {
252 return true;
253 }
254
255 return false;
256 }
257
258 /**
259 * Get promotions.
260 *
261 * @return array
262 */
263 private function get_promotions() {
264 $has_otter = defined( 'OTTER_BLOCKS_VERSION' ) || $this->is_plugin_installed( 'otter-blocks' );
265 $had_otter_from_promo = get_option( $this->option_otter, false );
266 $has_optimole = defined( 'OPTIMOLE_VERSION' ) || $this->is_plugin_installed( 'optimole-wp' );
267 $had_optimole_from_promo = get_option( $this->option_optimole, false );
268 $is_min_req_v = version_compare( get_bloginfo( 'version' ), '5.8', '>=' );
269 $has_enough_attachments = $this->has_min_media_attachments();
270
271 $all = [
272 'optimole' => [
273 'om-editor' => [
274 'env' => ! $has_optimole && $is_min_req_v && ! $had_optimole_from_promo,
275 'screen' => 'editor',
276 ],
277 'om-image-block' => [
278 'env' => ! $has_optimole && $is_min_req_v && ! $had_optimole_from_promo,
279 'screen' => 'editor',
280 ],
281 'om-attachment' => [
282 'env' => ! $has_optimole && ! $had_optimole_from_promo,
283 'screen' => 'media-editor',
284 ],
285 'om-media' => [
286 'env' => ! $has_optimole && ! $had_optimole_from_promo && $has_enough_attachments,
287 'screen' => 'media',
288 ],
289 'om-elementor' => [
290 'env' => ! $has_optimole && ! $had_optimole_from_promo && defined( 'ELEMENTOR_VERSION' ),
291 'screen' => 'elementor',
292 ],
293 ],
294 'otter' => [
295 'blocks-css' => [
296 'env' => ! $has_otter && $is_min_req_v && ! $had_otter_from_promo,
297 'screen' => 'editor',
298 ],
299 'blocks-animation' => [
300 'env' => ! $has_otter && $is_min_req_v && ! $had_otter_from_promo,
301 'screen' => 'editor',
302 ],
303 'blocks-conditions' => [
304 'env' => ! $has_otter && $is_min_req_v && ! $had_otter_from_promo,
305 'screen' => 'editor',
306 ],
307 ],
308 ];
309
310 foreach ( $all as $slug => $data ) {
311 foreach ( $data as $key => $conditions ) {
312 if ( ! $conditions['env'] ) {
313 unset( $all[ $slug ][ $key ] );
314
315 continue;
316 }
317
318 if ( $this->get_upsells_dismiss_time( $key ) ) {
319 unset( $all[ $slug ][ $key ] );
320 }
321 }
322
323 if ( empty( $all[ $slug ] ) ) {
324 unset( $all[ $slug ] );
325 }
326 }
327
328 return $all;
329 }
330
331 /**
332 * Get the upsell dismiss time.
333 *
334 * @param string $key The upsell key. If empty will return all dismiss times.
335 *
336 * @return false | string
337 */
338 private function get_upsells_dismiss_time( $key = '' ) {
339 $old = get_option( 'themeisle_sdk_promotions_otter', '{}' );
340 $data = get_option( $this->option_main, $old );
341
342 $data = json_decode( $data, true );
343
344 if ( empty( $key ) ) {
345 return $data;
346 }
347
348 return isset( $data[ $key ] ) ? $data[ $key ] : false;
349 }
350
351 /**
352 * Get the last dismiss time of a promotion.
353 *
354 * @return false
355 */
356 private function get_last_dismiss_time() {
357 $dismissed = $this->get_upsells_dismiss_time();
358
359 if ( empty( $dismissed ) ) {
360 return false;
361 }
362
363 $last_dismiss = max( array_values( $dismissed ) );
364
365 return $last_dismiss;
366 }
367
368 /**
369 * Filter by screen & merge into single array of keys.
370 *
371 * @return array
372 */
373 private function filter_by_screen_and_merge() {
374 $current_screen = get_current_screen();
375
376 $is_elementor = isset( $_GET['action'] ) && $_GET['action'] === 'elementor';
377 $is_media = isset( $current_screen->id ) && $current_screen->id === 'upload';
378 $is_editor = method_exists( $current_screen, 'is_block_editor' ) && $current_screen->is_block_editor();
379
380 $return = [];
381
382 foreach ( $this->promotions as $slug => $promos ) {
383 foreach ( $promos as $key => $data ) {
384 switch ( $data['screen'] ) {
385 case 'media-editor':
386 if ( ! $is_media && ! $is_editor ) {
387 unset( $this->promotions[ $slug ][ $key ] );
388 }
389 break;
390 case 'media':
391 if ( ! $is_media ) {
392 unset( $this->promotions[ $slug ][ $key ] );
393 }
394 break;
395 case 'editor':
396 if ( ! $is_editor || $is_elementor ) {
397 unset( $this->promotions[ $slug ][ $key ] );
398 }
399 break;
400 case 'elementor':
401 if ( ! $is_elementor ) {
402 unset( $this->promotions[ $slug ][ $key ] );
403 }
404 break;
405 }
406 }
407
408 $return = array_merge( $return, $this->promotions[ $slug ] );
409 }
410
411 return array_keys( $return );
412 }
413
414 /**
415 * Load single promotion.
416 *
417 * @param string $slug slug of the promotion.
418 */
419 private function load_promotion( $slug ) {
420 $this->loaded_promo = $slug;
421
422 if ( $this->debug ) {
423 add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue' ] );
424 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue' ] );
425 if ( $this->get_upsells_dismiss_time( 'om-media' ) === false ) {
426 add_action( 'admin_notices', [ $this, 'render_optimole_dash_notice' ] );
427 }
428
429 return;
430 }
431
432 switch ( $slug ) {
433 case 'om-editor':
434 case 'om-image-block':
435 case 'blocks-css':
436 case 'blocks-animation':
437 case 'blocks-conditions':
438 add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue' ] );
439 break;
440 case 'om-attachment':
441 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue' ] );
442 break;
443 case 'om-media':
444 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue' ] );
445 add_action( 'admin_notices', [ $this, 'render_optimole_dash_notice' ] );
446 break;
447 }
448 }
449
450 /**
451 * Render dashboard notice.
452 */
453 public function render_optimole_dash_notice() {
454 $screen = get_current_screen();
455
456 if ( ! isset( $screen->id ) || $screen->id !== 'upload' ) {
457 return;
458 }
459
460 echo '<div id="ti-optml-notice" class="notice notice-info ti-sdk-om-notice"></div>';
461 }
462
463 /**
464 * Enqueue the assets.
465 */
466 public function enqueue() {
467 global $themeisle_sdk_max_path;
468 $handle = 'ti-sdk-promo';
469 $saved = $this->get_upsells_dismiss_time();
470 $themeisle_sdk_src = $this->get_sdk_uri();
471 $user = wp_get_current_user();
472 $asset_file = require $themeisle_sdk_max_path . '/assets/js/build/index.asset.php';
473 $deps = array_merge( $asset_file['dependencies'], [ 'updates' ] );
474
475 wp_register_script( $handle, $themeisle_sdk_src . 'assets/js/build/index.js', $deps, $asset_file['version'], true );
476 wp_localize_script(
477 $handle,
478 'themeisleSDKPromotions',
479 [
480 'debug' => $this->debug,
481 'email' => $user->user_email,
482 'showPromotion' => $this->loaded_promo,
483 'optionKey' => $this->option_main,
484 'product' => $this->product->get_name(),
485 'option' => empty( $saved ) ? new \stdClass() : $saved,
486 'nonce' => wp_create_nonce( 'wp_rest' ),
487 'assets' => $themeisle_sdk_src . 'assets/images/',
488 'optimoleApi' => esc_url( rest_url( 'optml/v1/register_service' ) ),
489 'optimoleActivationUrl' => $this->get_plugin_activation_link( 'optimole-wp' ),
490 'otterActivationUrl' => $this->get_plugin_activation_link( 'otter-blocks' ),
491 'optimoleDash' => esc_url( add_query_arg( [ 'page' => 'optimole' ], admin_url( 'upload.php' ) ) ),
492 // translators: %s is the product name.
493 'title' => esc_html( sprintf( __( 'Recommended by %s', 'textdomain' ), $this->product->get_name() ) ),
494 ]
495 );
496 wp_enqueue_script( $handle );
497 wp_enqueue_style( $handle, $themeisle_sdk_src . 'assets/js/build/style-index.css', [ 'wp-components' ], $asset_file['version'] );
498 }
499
500 /**
501 * Add promo to attachment modal.
502 *
503 * @param array $fields Fields array.
504 * @param \WP_Post $post Post object.
505 *
506 * @return array
507 */
508 public function add_attachment_field( $fields, $post ) {
509 if ( $post->post_type !== 'attachment' ) {
510 return $fields;
511 }
512
513 if ( ! isset( $post->post_mime_type ) || strpos( $post->post_mime_type, 'image' ) === false ) {
514 return $fields;
515 }
516
517 $meta = wp_get_attachment_metadata( $post->ID );
518
519 if ( isset( $meta['filesize'] ) && $meta['filesize'] < 200000 ) {
520 return $fields;
521 }
522
523 $fields['optimole'] = array(
524 'input' => 'html',
525 'html' => '<div id="ti-optml-notice-helper"></div>',
526 'label' => '',
527 );
528
529 if ( count( $fields ) < 2 ) {
530 add_filter( 'wp_required_field_message', '__return_empty_string' );
531 }
532
533 return $fields;
534 }
535
536 /**
537 * Get plugin activation link.
538 *
539 * @param string $slug The plugin slug.
540 *
541 * @return string
542 */
543 private function get_plugin_activation_link( $slug ) {
544 $reference_key = $slug === 'otter-blocks' ? 'reference_key' : 'optimole_reference_key';
545
546 return esc_url(
547 add_query_arg(
548 array(
549 'plugin_status' => 'all',
550 'paged' => '1',
551 'action' => 'activate',
552 $reference_key => $this->product->get_key(),
553 'plugin' => rawurlencode( $slug . '/' . $slug . '.php' ),
554 '_wpnonce' => wp_create_nonce( 'activate-plugin_' . $slug . '/' . $slug . '.php' ),
555 ),
556 admin_url( 'plugins.php' )
557 )
558 );
559 }
560
561 /**
562 * Check if has 50 image media items.
563 *
564 * @return bool
565 */
566 private function has_min_media_attachments() {
567 if ( $this->debug ) {
568 return true;
569 }
570 $attachment_count = get_transient( 'tsk_attachment_count' );
571 if ( false === $attachment_count ) {
572 $args = array(
573 'post_type' => 'attachment',
574 'posts_per_page' => 51,
575 'fields' => 'ids',
576 'post_status' => 'inherit',
577 'no_found_rows' => true,
578 );
579
580 $query = new \WP_Query( $args );
581 $attachment_count = $query->post_count;
582
583
584 set_transient( 'tsk_attachment_count', $attachment_count, DAY_IN_SECONDS );
585 }
586 return $attachment_count > 50;
587 }
588 }
589