PluginProbe
Master Addons for Elementor – Elementor Addons, Widgets, Mega Menu Builder, Popup Builder, Widget Builder & Template Kits / trunk
Master Addons for Elementor – Elementor Addons, Widgets, Mega Menu Builder, Popup Builder, Widget Builder & Template Kits vtrunk
3.2.2 3.2.3 3.2.1 3.2.0 3.1.9 3.1.8 3.1.7 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.9 trunk 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.3 1.1.4 1.1.5 All 174 releases
master-addons / inc / classes / pro-upgrade.php

pro-upgrade.php in Master Addons for Elementor – Elementor Addons, Widgets, Mega Menu Builder, Popup Builder, Widget Builder & Template Kits trunk, at inc/classes/pro-upgrade.php

734 lines 18.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace MasterAddons\Inc\Classes;
4
5 use MasterAddons\Inc\Classes\Helper;
6 use MasterAddons\Inc\Classes\Notifications\Base\Date;
7
8
9 // No, Direct access Sir !!!
10 if (!defined('ABSPATH')) {
11 exit;
12 }
13
14 /**
15 * Upgrade to Pro Class
16 *
17 * Jewel Theme <support@jeweltheme.com>
18 */
19 if (!class_exists('MasterAddons\Inc\Classes\Pro_Upgrade')) {
20 class Pro_Upgrade
21 {
22
23 use Date;
24
25 public $slug;
26
27 protected $data = array();
28
29 /**
30 * Construct method
31 */
32 public function __construct()
33 {
34
35 $this->slug = Helper::jltma_slug_cleanup();
36
37 $this->maybe_sync_remote_data();
38 $this->register_sync_hook();
39 $this->set_data();
40
41 add_action('admin_footer', array($this, 'display_popup'));
42
43 // Add popup to Elementor editor (runs in iframe, needs separate hook)
44 add_action('elementor/editor/footer', array($this, 'display_popup'));
45
46 add_action('wp_dashboard_setup', array($this, 'dashboard_widget'), 999);
47 }
48
49 /**
50 * Register Dashboard widget
51 *
52 * @return void
53 * @author Jewel Theme <support@jeweltheme.com>
54 */
55 public function dashboard_widget()
56 {
57
58 wp_add_dashboard_widget(
59 'jltma_dashboard_widget',
60 esc_html__('Master Addons - News & Updates', 'master-addons'),
61 array($this, 'dashboard_widget_render')
62 );
63
64 // Force widget to very first position on the dashboard
65 global $wp_meta_boxes;
66
67 // Get the regular dashboard widgets array
68 // (which already has our new widget but appended at the end).
69 $default_dashboard = $wp_meta_boxes['dashboard']['normal']['core'];
70
71 // Backup and delete our new dashboard widget from the end of the array.
72 $default_widget_backup = array( 'jltma_dashboard_widget' => $default_dashboard['jltma_dashboard_widget'] );
73 unset( $default_dashboard['jltma_dashboard_widget'] );
74
75 // Merge the two arrays together so our widget is at the beginning.
76 $sorted_dashboard = array_merge( $default_widget_backup, $default_dashboard );
77
78 // Save the sorted array back into the original metaboxes .
79 $wp_meta_boxes['dashboard']['normal']['core'] = $sorted_dashboard;
80 }
81
82 /**
83 * Render dashboard widget
84 *
85 * @author Jewel Theme <support@jeweltheme.com>
86 */
87 public function dashboard_widget_render()
88 {
89 // Fetch feed with descriptions (master-addons.com/feed has content, jeweltheme doesn't)
90 $feed_items = $this->get_dashboard_feed_items(5);
91
92 echo '<div class="jltma-overview-widget">';
93
94 if (wp_validate_boolean($this->get_content('is_campaign'))) { ?>
95 <div class="jltma-dashboard-promo" style="--jltma-popup-color: <?php echo esc_attr($this->get_content('btn_color')); ?>;">
96 <a target="_blank" href="<?php echo esc_url($this->get_content('button_url')); ?>">
97 <img src="<?php echo esc_url($this->get_content('image_url')); ?>" alt="<?php esc_html__('Master Addons Promo Image', 'master-addons'); ?>" style="width: 100%; height: auto;">
98 </a>
99 <a class="jltma-popup-button" target="_blank" href="<?php echo esc_url($this->get_content('button_url')); ?>">
100 <?php echo esc_html($this->get_content('button_text')); ?>
101 </a>
102 </div>
103 <?php } ?>
104
105
106 <?php if (!empty($feed_items)) { ?>
107 <ul class="jltma-overview-list">
108 <?php foreach ($feed_items as $feed_item) { ?>
109 <li>
110 <a href="<?php echo esc_url($feed_item['link']); ?>" target="_blank">
111 <?php echo esc_html($feed_item['title']); ?>
112 </a>
113 </li>
114 <?php } ?>
115 </ul>
116 <?php } ?>
117
118 <div class="jltma-overview-footer">
119 <ul>
120 <li>
121 <a href="https://master-addons.com/blog/" target="_blank">
122 <?php echo esc_html__('Blog', 'master-addons'); ?>
123 <span aria-hidden="true" class="dashicons dashicons-external"></span>
124 </a>
125 </li>
126 <li>
127 <a href="https://master-addons.com/pricing" target="_blank">
128 <?php echo esc_html__('40% Off Bundle', 'master-addons'); ?>
129 <span aria-hidden="true" class="dashicons dashicons-external"></span>
130 </a>
131 </li>
132 <li class="jltma-overview-offers">
133 <a href="https://master-addons.com/pricing/" target="_blank">
134 <?php echo esc_html__('Latest Offers', 'master-addons'); ?>
135 <span aria-hidden="true" class="dashicons dashicons-external"></span>
136 </a>
137 </li>
138 </ul>
139 </div>
140
141 <style>
142 /* Master Addons Overview Widget — match WP core dashboard style */
143 #jltma_dashboard_widget .inside {
144 padding: 0;
145 margin: 0;
146 }
147
148 .jltma-overview-widget {
149 padding: 0;
150 }
151
152 .jltma-overview-subtitle {
153 margin: 0;
154 padding: 12px;
155 font-size: 14px;
156 font-weight: 600;
157 color: #1d2327;
158 border-bottom: 1px solid #f0f0f1;
159 }
160
161 .jltma-overview-list {
162 margin: 12px 0;
163 padding: 0;
164 list-style: none;
165 }
166
167 .jltma-overview-list li {
168 padding: 6px 12px;
169 margin: 0;
170 }
171
172 .jltma-overview-list li:last-child {
173 border-bottom: none;
174 }
175
176 .jltma-overview-list li a {
177 display: block;
178 font-size: 13px;
179 font-weight: 400;
180 line-height: 1.4;
181 color: #2271b1;
182 text-decoration: none;
183 margin-bottom: 4px;
184 }
185
186 .jltma-overview-list li a:hover {
187 color: #135e96;
188 text-decoration: underline;
189 }
190
191 .jltma-overview-excerpt {
192 margin: 0;
193 padding: 0;
194 font-size: 13px;
195 line-height: 1.5;
196 color: #50575e;
197 }
198
199 /* Footer */
200 .jltma-overview-footer {
201 margin: 0;
202 padding: 0;
203 border-top: 1px solid #f0f0f1;
204 background: #f6f7f7;
205 }
206
207 .jltma-overview-footer ul {
208 display: flex;
209 list-style: none;
210 margin: 0;
211 padding: 0;
212 }
213
214 .jltma-overview-footer ul li {
215 padding: 0;
216 margin: 0;
217 border-right: 1px solid #dcdcde;
218 }
219
220 .jltma-overview-footer ul li:last-child {
221 border-right: none;
222 }
223
224 .jltma-overview-footer ul li a {
225 display: inline-flex;
226 align-items: center;
227 gap: 3px;
228 padding: 10px 14px;
229 font-size: 13px;
230 font-weight: 400;
231 color: #2271b1;
232 text-decoration: none;
233 }
234
235 .jltma-overview-footer ul li a:hover {
236 color: #135e96;
237 text-decoration: underline;
238 }
239
240 .jltma-overview-footer ul li a .dashicons {
241 font-size: 16px;
242 width: 16px;
243 height: 16px;
244 text-decoration: none;
245 }
246
247 .jltma-overview-offers a {
248 color: #b32d2e !important;
249 font-weight: 500 !important;
250 }
251
252 .jltma-overview-offers a:hover {
253 color: #a00 !important;
254 }
255 </style>
256
257 <?php
258 echo '</div>';
259 }
260
261
262 /**
263 * Fetch RSS feed items for dashboard widget
264 * Handles master-addons.com/feed/ which has a script tag before XML declaration
265 *
266 * @param int $count Number of items to fetch
267 * @return array Array of items with title, link, excerpt keys
268 */
269 public function get_dashboard_feed_items($count = 5)
270 {
271 $transient_key = 'jltma_dashboard_feed_items';
272 $cached = get_transient($transient_key);
273
274 if (false !== $cached) {
275 return array_slice($cached, 0, $count);
276 }
277
278 $items = array();
279
280 // Try master-addons.com/feed/ first (has descriptions)
281 $response = wp_remote_get('https://master-addons.com/feed/', array('timeout' => 10));
282
283 if (!is_wp_error($response)) {
284 $body = wp_remote_retrieve_body($response);
285 // Strip script tag injected before XML declaration
286 $body = preg_replace('/^.*?(<\?xml)/s', '$1', $body);
287
288 // Parse with SimpleXML
289 libxml_use_internal_errors(true);
290 $xml = simplexml_load_string($body);
291 libxml_clear_errors();
292
293 if ($xml && isset($xml->channel->item)) {
294 foreach ($xml->channel->item as $xml_item) {
295 $title = (string) $xml_item->title;
296 $link = (string) $xml_item->link;
297 $desc = (string) $xml_item->description;
298 $desc = wp_strip_all_tags($desc);
299 $excerpt = mb_strlen($desc) > 200 ? mb_substr($desc, 0, 200) . '' : $desc;
300
301 $items[] = array(
302 'title' => $title,
303 'link' => $link,
304 'excerpt' => $excerpt,
305 );
306 }
307 }
308 }
309
310 // Fallback to jeweltheme feed (no descriptions)
311 if (empty($items)) {
312 include_once ABSPATH . WPINC . '/feed.php';
313 $rss = fetch_feed('https://master-addons.com/feed/');
314
315 if (!is_wp_error($rss)) {
316 $rss_items = $rss->get_items(0, 10);
317 foreach ($rss_items as $rss_item) {
318 $items[] = array(
319 'title' => $rss_item->get_title(),
320 'link' => $rss_item->get_permalink(),
321 'excerpt' => '',
322 );
323 }
324 }
325 }
326
327 if (!empty($items)) {
328 set_transient($transient_key, $items, 6 * HOUR_IN_SECONDS);
329 }
330
331 return array_slice($items, 0, $count);
332 }
333
334 /**
335 * Set merged data
336 *
337 * @return void
338 * @author Jewel Theme <support@jeweltheme.com>
339 */
340 public function set_data()
341 {
342 $this->data = Helper::get_merged_data(self::get_data());
343 }
344
345 /**
346 * Get Sheet data
347 *
348 * @author Jewel Theme <support@jeweltheme.com>
349 */
350 public static function get_data()
351 {
352 return get_option('jltma_sheet_promo_data');
353 }
354
355 /**
356 * Get Contents
357 *
358 * @param [type] $key .
359 *
360 * @author Jewel Theme <support@jeweltheme.com>
361 */
362 public function get_content($key)
363 {
364 if( empty( $this->data ) ) return;
365 return $this->data[$key] ?? null;
366 }
367
368 /**
369 * Get Option has data
370 *
371 * @author Jewel Theme <support@jeweltheme.com>
372 */
373 public function get_data_hash()
374 {
375 return get_option('jltma_sheet_promo_data_hash');
376 }
377
378 /**
379 * Sync to remote data
380 *
381 * @author Jewel Theme <support@jeweltheme.com>
382 */
383 public function maybe_sync_remote_data()
384 {
385 $data = self::get_data();
386
387 if (empty($data)) {
388 $this->sheet_data_remote_sync();
389 }
390 }
391
392 /**
393 * Register Sync hook
394 *
395 * @return void
396 * @author Jewel Theme <support@jeweltheme.com>
397 */
398 public function register_sync_hook()
399 {
400 $hook_action = 'jltma_sheet_promo_data_remote_sync';
401 add_action($hook_action, array($this, 'sheet_data_remote_sync'));
402
403 if (!wp_next_scheduled($hook_action)) {
404 wp_schedule_event(time(), 'daily', $hook_action);
405 }
406
407 register_deactivation_hook(JLTMA_FILE, array($this, 'clear_register_sync_hook'));
408 }
409
410 /**
411 * Clear register sync hook
412 *
413 * @return void
414 * @author Jewel Theme <support@jeweltheme.com>
415 */
416 public function clear_register_sync_hook()
417 {
418 wp_clear_scheduled_hook('jltma_sheet_promo_data_remote_sync');
419 }
420
421 /**
422 * Data sync with remote
423 *
424 * @return void
425 * @author Jewel Theme <support@jeweltheme.com>
426 */
427 public function sheet_data_remote_sync()
428 {
429 $data = self::get_data();
430 $force = empty( $data );
431
432 $remote_data = $this->get_update_remote_data();
433
434 // Don't clobber stored data when the API is unreachable / returned nothing.
435 if ( false === $remote_data ) {
436 return;
437 }
438
439 $stored_hash = $this->get_data_hash();
440 $remote_hash = base64_encode( json_encode( $remote_data ) );
441
442 if ( $force || $stored_hash !== $remote_hash ) {
443 update_option('jltma_sheet_promo_data', $remote_data);
444 update_option('jltma_sheet_promo_data_hash', $remote_hash);
445 do_action('jltma_sheet_promo_data_reset');
446 }
447 }
448
449 /**
450 * Update endpoint URL for this plugin
451 *
452 * @author Jewel Theme <support@jeweltheme.com>
453 */
454 public function get_update_url() {
455 return esc_url( Helper::api_endpoint() . 'api/plugin/update/' . Helper::jltma_slug_cleanup() );
456 }
457
458 /**
459 * Promotional remote data (REST API)
460 *
461 * Expects a single JSON object describing the update, e.g.
462 * { product_name, product_slug, image_url, start_date, end_date,
463 * button_text, button_url, btn_color, notice, show_for_premium, is_live }
464 *
465 * @return array|false
466 * @author Jewel Theme <support@jeweltheme.com>
467 */
468 public function get_update_remote_data() {
469 $response = wp_remote_get(
470 $this->get_update_url(),
471 array(
472 'timeout' => 15,
473 'headers' => array( 'Accept' => 'application/json' ),
474 )
475 );
476
477 if ( is_wp_error( $response ) || 200 !== (int) wp_remote_retrieve_response_code( $response ) ) {
478 return false;
479 }
480
481 $body = wp_remote_retrieve_body( $response );
482
483 if ( ! $body ) {
484 return false;
485 }
486
487 $data = json_decode( $body, true );
488
489 if ( ! is_array( $data ) || JSON_ERROR_NONE !== json_last_error() ) {
490 return false;
491 }
492
493 // API may return the update directly, or wrapped (e.g. { data: {...} }) / as a list.
494 if ( isset( $data['data'] ) && is_array( $data['data'] ) ) {
495 $data = $data['data'];
496 }
497
498 if ( isset( $data[0] ) && is_array( $data[0] ) ) {
499 $data = wp_list_filter( $data, array( 'product_slug' => Helper::jltma_slug_cleanup() ) );
500 $data = $data ? array_values( $data )[0] : array();
501 }
502
503 if ( empty( $data ) || empty( $data['product_slug'] ) ) {
504 return false;
505 }
506
507 // Only accept a update that targets this plugin.
508 if ( Helper::jltma_slug_cleanup() !== $data['product_slug'] ) {
509 return false;
510 }
511
512 // API sends dates as "d/m/Y h:i a" — normalise so PHP/JS date parsers don't choke.
513 foreach ( array( 'start_date', 'end_date' ) as $key ) {
514 if ( ! empty( $data[ $key ] ) ) {
515 $data[ $key ] = $this->normalize_date( $data[ $key ] );
516 }
517 }
518
519 return $data;
520 }
521
522 /**
523 * Display popup contents
524 *
525 * @author Jewel Theme <support@jeweltheme.com>
526 */
527 public function display_popup()
528 {
529 if (Helper::jltma_premium()) {
530 if (!$this->get_content('show_for_premium')) {
531 return;
532 }
533 }
534
535 if (empty( $this->get_content('is_live') )) {
536 $image_url = JLTMA_IMAGE_DIR . 'ma-fallback.png';
537 $notice = 'Use "SPECIAL40" to Get Flat 40% OFF';
538 $btn_url = 'https://master-addons.com/pricing/';
539 $btn_text = 'GET THE DEAL';
540 } else {
541 $image_url = $this->get_content('image_url');
542 $notice = $this->get_content('notice');
543 $btn_url = $this->get_content('button_url');
544 $btn_text = $this->get_content('button_text');
545 }
546
547 ?>
548
549 <div class="jltma-popup jltma-upgrade-popup" id="jltma-popup" data-plugin="<?php echo esc_attr($this->slug); ?>" tabindex="1" style="display: none;">
550
551 <div class="jltma-popup-overlay"></div>
552
553 <div class="jltma-popup-modal" style="background-image: url('<?php echo esc_url($image_url); ?>'); --jltma-popup-color: <?php echo esc_attr($this->get_content('btn_color')); ?>;">
554
555 <!-- close -->
556 <div class="jltma-popup-modal-close popup-dismiss">×</div>
557
558 <!-- content section -->
559 <div class="jltma-popup-modal-footer">
560
561 <!-- countdown -->
562 <div class="jltma-popup-countdown" style="display: none;">
563 <?php if (!empty($notice)) { ?>
564 <span data-counter="notice" style="color:#F4B740; font-size:14px; padding-bottom:20px; font-style:italic;">
565 <?php echo esc_html__('Notice:', 'master-addons'); ?> <?php echo esc_html( $notice ); ?>
566 </span>
567 <?php } ?>
568 <span class="jltma-popup-countdown-text"><?php echo esc_html__('Offer Ends In', 'master-addons'); ?></span>
569 <div class="jltma-popup-countdown-time">
570 <div>
571 <span data-counter="days">00</span>
572 <span><?php echo esc_html__('Days', 'master-addons'); ?></span>
573 </div>
574 <span>:</span>
575 <div>
576 <span data-counter="hours">00</span>
577 <span><?php echo esc_html__('Hours', 'master-addons'); ?></span>
578 </div>
579 <span>:</span>
580 <div>
581 <span data-counter="minutes">00</span>
582 <span><?php echo esc_html__('Minutes', 'master-addons'); ?></span>
583 </div>
584 <span>:</span>
585 <div>
586 <span data-counter="seconds">00</span>
587 <span><?php echo esc_html__('Seconds', 'master-addons'); ?></span>
588 </div>
589 </div>
590 </div>
591
592 <!-- button -->
593 <a class="jltma-popup-button" target="_blank" href="<?php echo esc_url($btn_url); ?>"><?php echo esc_html($btn_text); ?></a>
594 </div>
595 </div>
596 </div>
597
598 <script>
599 var $container = jQuery('#jltma-popup'),
600 plugin_data = <?php echo wp_json_encode( $this->data ); ?>,
601 events = {}; //Events
602
603 // Update Counter
604 function updateCounter(seconds) {
605 const $counter = $container.find(".jltma-popup-countdown-time");
606 const $days = $counter.find("[data-counter='days']");
607 const $hours = $counter.find("[data-counter='hours']");
608 const $minutes = $counter.find("[data-counter='minutes']");
609 const $seconds = $counter.find("[data-counter='seconds']");
610 const days = Math.floor(seconds / (3600 * 24));
611 seconds -= days * 3600 * 24;
612 const hrs = Math.floor(seconds / 3600);
613 seconds -= hrs * 3600;
614 const mnts = Math.floor(seconds / 60);
615 seconds -= mnts * 60;
616
617 $days.text(days);
618 $hours.text(hrs);
619 $minutes.text(mnts);
620 $seconds.text(seconds);
621 }
622
623 // Trigger Event
624 function trigger(event, args = []) {
625 if (typeof(events[event]) !== 'undefined') {
626 events[event].forEach(callback => {
627 callback.apply(this, args);
628 });
629 }
630 }
631
632 // initCounter
633 function initCounter(last_date) {
634 $container.find(".jltma-popup-countdown-time").show();
635
636 const countdown = () => {
637
638 // system time
639 const now = new Date().getTime();
640
641 // set end time to 11:59:59 PM
642 const endDate = new Date(last_date);
643 endDate.setHours(23);
644 endDate.setMinutes(59);
645 endDate.setSeconds(59);
646
647 const seconds = Math.floor((endDate.getTime() - now) / 1000);
648
649 if (seconds < 0) {
650 return false;
651 }
652
653 updateCounter(seconds);
654
655 return true;
656 }
657
658 let result = countdown();
659
660
661 if (result) {
662 trigger("countdownStart", [plugin_data]);
663 $container.find(".jltma-popup-countdown").show(0);
664 } else {
665 trigger("countdownFinish", [plugin_data]);
666 $container.find(".jltma-popup-countdown").hide(0);
667 }
668
669 // update counter every 1 second
670 const counter = setInterval(() => {
671
672 const result = countdown();
673
674 if (!result) {
675 clearInterval(counter);
676 trigger("counter_end", [plugin_data]);
677 $container.find(".jltma-popup-countdown").hide(0);
678 }
679
680 }, 1000);
681 }
682
683 initCounter('<?php echo esc_attr($this->counter_date()); ?>');
684 </script>
685
686 <?php
687 }
688
689 /**
690 * Counter Date
691 *
692 * @author Jewel Theme <support@jeweltheme.com>
693 */
694 public function counter_date()
695 {
696 $endDate = $this->get_content('end_date');
697
698 $is_active = $this->date_is_current_or_next($endDate);
699
700 if ($is_active) {
701 return $endDate;
702 }
703
704 return $this->date_increment($this->current_time(), 3);
705 }
706
707 /**
708 * Normalise a update date string to 'Y-m-d H:i:s'
709 *
710 * Accepts d/m/Y (optionally with 12h time) and a few common fallbacks.
711 * Returns the original value untouched if it cannot be parsed.
712 *
713 * @param string $value .
714 * @author Jewel Theme <support@jeweltheme.com>
715 */
716 public function normalize_date( $value ) {
717 $value = trim( (string) $value );
718 $formats = array( 'd/m/Y h:i a', 'd/m/Y H:i', 'd/m/Y', 'd-m-Y H:i:s', 'd-m-Y' );
719
720 foreach ( $formats as $format ) {
721 $dt = \DateTime::createFromFormat( $format, $value );
722
723 if ( $dt instanceof \DateTime ) {
724 return $dt->format( 'Y-m-d H:i:s' );
725 }
726 }
727
728 $ts = strtotime( $value );
729
730 return $ts ? gmdate( 'Y-m-d H:i:s', $ts ) : $value;
731 }
732 } // End class Pro_Upgrade
733 } // End class_exists check
734