PluginProbe
Boxzilla – WordPress Popup Builder / 3.4.7
Boxzilla – WordPress Popup Builder v3.4.7
3.4.11 3.4.10 3.4.9 3.4.3 3.4.4 3.4.5 3.4.6 3.4.7 3.4.8 trunk 3.0 3.0.1 3.0.2 3.0.3 3.1 3.1.1 3.1.10 3.1.11 3.1.12 3.1.13 3.1.14 3.1.15 3.1.16 3.1.17 3.1.18 All 72 releases
boxzilla / src / admin / class-admin.php

class-admin.php in Boxzilla – WordPress Popup Builder 3.4.7, at src/admin/class-admin.php

831 lines 23.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Boxzilla\Admin;
4
5 use Boxzilla\Plugin;
6 use Boxzilla\Box;
7 use Boxzilla\Boxzilla;
8 use WP_Post;
9 use WP_Screen;
10
11 class Admin
12 {
13 /**
14 * @var Plugin $plugin
15 */
16 private $plugin;
17
18 /**
19 * @var Boxzilla
20 */
21 protected $boxzilla;
22
23 /**
24 * @var ReviewNotice
25 */
26 protected $review_notice;
27
28 /**
29 * @param Plugin $plugin
30 * @param Boxzilla $boxzilla
31 */
32 public function __construct(Plugin $plugin, Boxzilla $boxzilla)
33 {
34 $this->plugin = $plugin;
35 $this->boxzilla = $boxzilla;
36 $this->review_notice = new ReviewNotice();
37 }
38
39 /**
40 * Initialise the all admin related stuff
41 */
42 public function init()
43 {
44 $this->add_hooks();
45 $this->run_migrations();
46 $this->review_notice->init();
47 }
48
49 /**
50 * Add necessary hooks
51 */
52 protected function add_hooks()
53 {
54 add_action('admin_init', [ $this, 'lazy_add_hooks' ]);
55 add_action('admin_init', [ $this, 'register' ]);
56 add_action('init', [ $this, 'listen_for_actions' ]);
57 add_action('admin_menu', [ $this, 'menu' ]);
58 add_action('admin_notices', [ $this, 'notices' ]);
59 add_action('save_post_boxzilla-box', [ $this, 'save_box_options' ], 20, 2);
60 add_action('trashed_post', [ $this, 'flush_rules' ]);
61 add_action('untrashed_post', [ $this, 'flush_rules' ]);
62 add_filter('bulk_actions-edit-boxzilla-box', [ $this, 'bulk_action_add' ]);
63 add_filter('handle_bulk_actions-edit-boxzilla-box', [ $this, 'bulk_action_handle' ], 10, 3);
64 }
65
66 /**
67 * Listen for admin actions.
68 */
69 public function listen_for_actions()
70 {
71 // triggered?
72 $vars = array_merge($_POST, $_GET);
73 if (empty($vars['_boxzilla_admin_action'])) {
74 return false;
75 }
76
77 // authorized?
78 if (! current_user_can('edit_posts')) {
79 return false;
80 }
81
82 // fire action
83 $action = $vars['_boxzilla_admin_action'];
84 do_action('boxzilla_admin_' . $action);
85
86 return true;
87 }
88
89 public function bulk_action_add($bulk_actions)
90 {
91 $bulk_actions['boxzilla_duplicate_box'] = esc_attr__('Duplicate box', 'boxzilla');
92 return $bulk_actions;
93 }
94
95 public function bulk_action_handle($redirect_to, $doaction, $post_ids)
96 {
97 if ($doaction !== 'boxzilla_duplicate_box' || empty($post_ids)) {
98 return $redirect_to;
99 }
100
101 foreach ($post_ids as $post_id) {
102 $post = get_post($post_id);
103 if ($post->post_type !== 'boxzilla-box') {
104 continue;
105 }
106
107 $new_post_id = wp_insert_post(
108 [
109 'post_type' => $post->post_type,
110 'post_title' => $post->post_title,
111 'post_content' => $post->post_content,
112 'post_status' => 'draft',
113 ]
114 );
115
116 $options = get_post_meta($post_id, 'boxzilla_options', true);
117 add_post_meta($new_post_id, 'boxzilla_options', $options);
118
119 $this->flush_rules($new_post_id);
120 }
121
122 return $redirect_to;
123 }
124
125 /**
126 * All logic for admin notices lives here.
127 */
128 public function notices()
129 {
130 global $pagenow,
131 $current_screen;
132
133 if (
134 ( $pagenow === 'plugins.php' || ( $current_screen && $current_screen->post_type === 'boxzilla-box' ) )
135 && current_user_can('install_plugins')
136 && is_plugin_active('scroll-triggered-boxes/index.php')
137 ) {
138 ?>
139 <div class="notice notice-info">
140 <p><?php esc_html_e('Awesome, you are using Boxzilla! You can now safely deactivate the Scroll Triggered Boxes plugin.', 'boxzilla'); ?></p>
141 </div>
142 <?php
143 }
144 }
145
146 /**
147 * Checks current version against stored version & runs necessary update routines.
148 */
149 protected function run_migrations()
150 {
151
152 // Only run if db option is at older version than code constant
153 $previous_version = get_option('boxzilla_version', '0');
154 $current_version = $this->plugin->version();
155
156 if (version_compare($current_version, $previous_version, '<=')) {
157 return;
158 }
159
160 $upgrade_routines = new Migrations($previous_version, $current_version, __DIR__ . '/migrations');
161 $upgrade_routines->run();
162 update_option('boxzilla_version', $current_version);
163 }
164
165 public function lazy_add_hooks()
166 {
167 global $pagenow;
168
169 add_action('admin_enqueue_scripts', [ $this, 'load_assets' ]);
170 add_action('add_meta_boxes', [ $this, 'add_meta_boxes' ]);
171 add_filter('tiny_mce_before_init', [ $this, 'tinymce_init' ]);
172 add_filter('manage_edit-boxzilla-box_columns', [ $this, 'post_type_column_titles' ]);
173 add_action('manage_boxzilla-box_posts_custom_column', [ $this, 'post_type_column_content' ], 10, 2);
174 add_filter('admin_footer_text', [ $this, 'admin_footer_text' ]);
175
176 if ($pagenow === 'plugins.php') {
177 add_filter('plugin_action_links', [ $this, 'add_plugin_settings_link' ], 10, 2);
178 add_filter('plugin_row_meta', [ $this, 'add_plugin_meta_links' ], 10, 2);
179 }
180 }
181
182 /**
183 * @param $post_id
184 */
185 public function post_type_column_box_id_content($post_id)
186 {
187 echo $post_id;
188 }
189
190 /**
191 * @param $column
192 * @param $post_id
193 */
194 public function post_type_column_content($column, $post_id)
195 {
196 $method_name = 'post_type_column_' . $column . '_content';
197 if (method_exists($this, $method_name)) {
198 call_user_func([ $this, $method_name ], $post_id);
199 }
200 }
201
202 /**
203 * @param $columns
204 *
205 * @return mixed
206 */
207 public function post_type_column_titles($columns)
208 {
209 $columns = self::array_insert(
210 $columns,
211 [
212 'box_id' => esc_html__('Box ID', 'boxzilla'),
213 ],
214 1
215 );
216
217 $columns['title'] = esc_html__('Box Title', 'boxzilla');
218
219 return $columns;
220 }
221
222 /**
223 * Register stuffs
224 */
225 public function register()
226 {
227
228 register_setting('boxzilla_settings', 'boxzilla_settings', [ $this, 'sanitize_settings' ]);
229
230 wp_register_script(
231 'boxzilla-admin',
232 $this->plugin->url('/assets/js/admin-script.js'),
233 [
234 'jquery',
235 'wp-util',
236 'wp-color-picker',
237 'suggest',
238 ],
239 $this->plugin->version(),
240 true
241 );
242
243 wp_register_style('boxzilla-admin', $this->plugin->url('/assets/css/admin-styles.css'), [], $this->plugin->version());
244 }
245
246 /**
247 * Renders the Boxzilla admin menu items
248 */
249 public function menu()
250 {
251 $menu_items = [
252 [
253 esc_html__('Settings', 'boxzilla'),
254 esc_html__('Settings', 'boxzilla'),
255 'boxzilla-settings',
256 [ $this, 'show_settings_page' ],
257 ],
258 [
259 esc_html__('Extensions', 'boxzilla'),
260 '<span style="color: orange">' . esc_html__('Extensions', 'boxzilla') . '</span>',
261 'boxzilla-extensions',
262 [ $this, 'show_extensions_page' ],
263 ],
264 ];
265
266 $menu_items = apply_filters('boxzilla_admin_menu_items', $menu_items);
267
268 foreach ($menu_items as $item) {
269 add_submenu_page('edit.php?post_type=boxzilla-box', $item[0] . '- Boxzilla', $item[1], 'manage_options', $item[2], $item[3]);
270 }
271 }
272
273 /**
274 * Shows the settings page
275 */
276 public function show_settings_page()
277 {
278 $opts = $this->boxzilla->options;
279 require __DIR__ . '/views/settings.php';
280 }
281
282 /**
283 * Shows the extensions page
284 */
285 public function show_extensions_page()
286 {
287 $extensions = $this->fetch_extensions();
288 require __DIR__ . '/views/extensions.php';
289 }
290
291 /**
292 * Are we currently editing a box?
293 *
294 * @return bool
295 */
296 protected function on_edit_box_page()
297 {
298 global $pagenow;
299
300 if (! in_array($pagenow, [ 'post-new.php', 'post.php' ], true)) {
301 return false;
302 }
303
304 if (isset($_GET['post_type']) && $_GET['post_type'] === 'boxzilla-box') {
305 return true;
306 }
307
308 if (get_post_type() === 'boxzilla-box') {
309 return true;
310 }
311
312 return false;
313 }
314
315 /**
316 * @param $args
317 *
318 * @return mixed
319 */
320 public function tinymce_init($args)
321 {
322
323 // only act on our post type
324 if (get_post_type() !== 'boxzilla-box') {
325 return $args;
326 }
327
328 $args['setup'] = 'function( editor ) { if(typeof(window.Boxzilla_Admin) === \'undefined\') { return; } editor.on("PreInit", window.Boxzilla_Admin.Designer.init ); }';
329
330 return $args;
331 }
332
333 /**
334 * Load plugin assets
335 */
336 public function load_assets()
337 {
338 $screen = get_current_screen();
339
340 if (! $screen instanceof WP_Screen) {
341 return false;
342 }
343
344 if ($screen->base === 'edit' && $screen->post_type === 'boxzilla-box') {
345 wp_enqueue_style('boxzilla-admin');
346 }
347
348 if ($screen->base === 'post' && $screen->post_type === 'boxzilla-box') {
349 // color picker
350 wp_enqueue_style('wp-color-picker');
351
352 // load scripts
353 wp_enqueue_script('boxzilla-admin');
354
355 $data = [
356 'and' => esc_html__('and', 'boxzilla'),
357 'or' => esc_html__('or', 'boxzilla'),
358 'enterCommaSeparatedValues' => esc_html__('Enter a comma-separated list of values.', 'boxzilla'),
359 'enterCommaSeparatedPosts' => esc_html__("Enter a comma-separated list of post slugs or post ID's..", 'boxzilla'),
360 'enterCommaSeparatedPages' => esc_html__("Enter a comma-separated list of page slugs or page ID's..", 'boxzilla'),
361 'enterCommaSeparatedPostTypes' => esc_html__('Enter a comma-separated list of post types..', 'boxzilla'),
362 'enterCommaSeparatedRelativeUrls' => esc_html__("Enter a comma-separated list of relative URL's, eg /contact/", 'boxzilla'),
363 ];
364 wp_localize_script('boxzilla-admin', 'boxzilla_i18n', $data);
365
366 // load stylesheets
367 wp_enqueue_style('boxzilla-admin');
368
369 // allow add-ons to easily load their own scripts or stylesheets
370 do_action('boxzilla_load_admin_assets');
371 }
372
373 if (isset($_GET['page']) && $_GET['page'] === 'boxzilla-settings') {
374 wp_enqueue_style('boxzilla-admin');
375 }
376 }
377
378 /**
379 * Register meta boxes
380 *
381 * @param string $post_type
382 */
383 public function add_meta_boxes($post_type)
384 {
385 if ($post_type !== 'boxzilla-box') {
386 return;
387 }
388
389 add_meta_box(
390 'boxzilla-box-appearance-controls',
391 esc_html__('Box Appearance', 'boxzilla'),
392 [ $this, 'metabox_box_appearance_controls' ],
393 'boxzilla-box',
394 'normal',
395 'core'
396 );
397
398 add_meta_box(
399 'boxzilla-box-options-controls',
400 esc_html__('Box Options', 'boxzilla'),
401 [ $this, 'metabox_box_option_controls' ],
402 'boxzilla-box',
403 'normal',
404 'core'
405 );
406
407 add_meta_box(
408 'boxzilla-support',
409 esc_html__('Looking for help?', 'boxzilla'),
410 [ $this, 'metabox_support' ],
411 'boxzilla-box',
412 'side'
413 );
414
415 add_meta_box(
416 'boxzilla-our-other-plugins',
417 esc_html__('Our other plugins', 'boxzilla'),
418 [ $this, 'metabox_our_other_plugins' ],
419 'boxzilla-box',
420 'side'
421 );
422 }
423
424 /**
425 * @param \WP_Post $post
426 * @param $metabox
427 */
428 public function metabox_box_appearance_controls(\WP_Post $post, $metabox)
429 {
430
431 // get box options
432 $box = new Box($post);
433 $opts = $box->get_options();
434
435 // include view
436 include __DIR__ . '/views/metaboxes/box-appearance-controls.php';
437 }
438
439 /**
440 * @param \WP_Post $post
441 * @param $metabox
442 */
443 public function metabox_box_option_controls(\WP_Post $post, $metabox)
444 {
445
446 // get box options
447 $box = new Box($post);
448 $opts = $box->get_options();
449 $global_opts = $this->boxzilla->options;
450
451 if (empty($opts['rules'])) {
452 $opts['rules'][] = [
453 'condition' => '',
454 'qualifier' => 1,
455 'value' => '',
456 ];
457 }
458
459 // include view
460 include __DIR__ . '/views/metaboxes/box-option-controls.php';
461 }
462
463 /**
464 * @param \WP_Post $post
465 * @param $metabox
466 */
467 public function metabox_our_other_plugins(\WP_Post $post, $metabox)
468 {
469 include __DIR__ . '/views/metaboxes/our-other-plugins.php';
470 }
471
472 /**
473 * @param \WP_Post $post
474 * @param $metabox
475 */
476 public function metabox_support(\WP_Post $post, $metabox)
477 {
478 include __DIR__ . '/views/metaboxes/need-help.php';
479 }
480
481
482 /**
483 * Saves box options and rules
484 *
485 * @param int $box_id
486 * @param \WP_Post $post
487 */
488 public function save_box_options($box_id, $post)
489 {
490 // Only act on our own post type
491 if ($post->post_type !== 'boxzilla-box') {
492 return;
493 }
494
495 // is this a revision save?
496 if (wp_is_post_revision($box_id) || ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE )) {
497 return;
498 }
499
500 // can user edit this post?
501 if (! current_user_can('edit_post', $box_id)) {
502 return;
503 }
504
505 // make sure options array is set
506 if (! isset($_POST['boxzilla_box']) || ! is_array($_POST['boxzilla_box'])) {
507 return;
508 }
509
510 // get new options from $_POST
511 $opts = $this->sanitize_box_options($_POST['boxzilla_box']);
512
513 // allow extensions to filter the saved options
514 $opts = apply_filters('boxzilla_saved_options', $opts, $box_id);
515
516 // save individual box settings
517 update_post_meta($box_id, 'boxzilla_options', $opts);
518
519 // update global settings if given
520 if (! empty($_POST['boxzilla_global_settings'])) {
521 $raw_global_settings = $_POST['boxzilla_global_settings'];
522 $global_settings = get_option('boxzilla_settings', []);
523 if (! is_array($global_settings)) {
524 $global_settings = [];
525 }
526 if (isset($raw_global_settings['test_mode'])) {
527 $global_settings['test_mode'] = absint($raw_global_settings['test_mode']);
528 }
529 update_option('boxzilla_settings', $global_settings);
530 }
531
532 $this->flush_rules($box_id);
533 }
534
535 /**
536 * @param array $opts
537 *
538 * @return array
539 */
540 public function sanitize_settings($opts)
541 {
542 $opts['test_mode'] = (int) $opts['test_mode'] ? 1 : 0;
543 return $opts;
544 }
545
546 /**
547 * @param string $url_string
548 *
549 * @return string
550 */
551 public function sanitize_url($url_string)
552 {
553
554 // if empty, just return a slash
555 if (empty($url_string)) {
556 return '/';
557 }
558
559 // if string looks like an absolute URL, extract just the path
560 if (preg_match('/^((https|http)?\:\/\/)?(\w+\.)?\w+\.\w+\.*/i', $url_string)) {
561 // make sure URL has scheme prepended, to make parse_url() understand..
562 $url_string = 'https://' . str_replace([ 'http://', 'https://' ], '', $url_string);
563
564 // get just the path
565 $url_string = parse_url($url_string, PHP_URL_PATH);
566 }
567
568 // leading slash it
569 return $url_string;
570 }
571
572 /**
573 * @param array $rule
574 * @return array The sanitized rule array
575 */
576 public function sanitize_box_rule($rule)
577 {
578 $rule['value'] = trim($rule['value']);
579
580 // convert to array
581 $rule['value'] = explode(',', trim($rule['value'], ','));
582
583 // trim all whitespace in value field
584 $rule['value'] = array_map('trim', $rule['value']);
585
586 // Make sure "is_url" values have a leading slash
587 if ($rule['condition'] === 'is_url') {
588 $rule['value'] = array_map([ $this, 'sanitize_url' ], $rule['value']);
589 }
590
591 // (re)set value to 0 when condition is everywhere
592 if ($rule['condition'] === 'everywhere') {
593 $rule['value'] = '';
594 }
595
596 // convert back to string before saving
597 if (is_array($rule['value'])) {
598 $rule['value'] = join(',', $rule['value']);
599 }
600
601 return $rule;
602 }
603
604 /**
605 * @param array $css
606 * @return array
607 */
608 public function sanitize_box_css($css)
609 {
610
611 // sanitize settings
612 if ('' !== $css['width']) {
613 $css['width'] = absint($css['width']);
614 }
615
616 if ('' !== $css['border_width']) {
617 $css['border_width'] = absint($css['border_width']);
618 }
619
620 // make sure colors start with `#`
621 $color_keys = [ 'color', 'background_color', 'border_color' ];
622 foreach ($color_keys as $key) {
623 $value = $css[ $key ];
624 $color = sanitize_text_field($value);
625
626 // make sure color starts with `#`
627 if ('' !== $color && $color[0] !== '#') {
628 $color = '#' . $color;
629 }
630 $css[ $key ] = $color;
631 }
632
633 return $css;
634 }
635
636 /**
637 * Sanitize the options for this box.
638 *
639 * @param array $opts
640 *
641 * @return array
642 */
643 protected function sanitize_box_options($opts)
644 {
645 $defaults = [
646 'rules' => [],
647 'css' => [],
648 ];
649
650 $opts = array_replace_recursive($defaults, $opts);
651
652 $opts['rules'] = array_map([ $this, 'sanitize_box_rule' ], $opts['rules']);
653 $opts['css'] = $this->sanitize_box_css($opts['css']);
654 $opts['cookie']['triggered'] = absint($opts['cookie']['triggered']);
655 $opts['cookie']['dismissed'] = absint($opts['cookie']['dismissed']);
656 $opts['trigger'] = sanitize_text_field($opts['trigger']);
657 $opts['trigger_percentage'] = absint($opts['trigger_percentage']);
658 $opts['trigger_element'] = sanitize_text_field($opts['trigger_element']);
659 $opts['screen_size_condition']['value'] = intval($opts['screen_size_condition']['value']);
660
661 return $opts;
662 }
663
664 /**
665 * Add the settings link to the Plugins overview
666 *
667 * @param array $links
668 * @param string $slug
669 *
670 * @return array
671 */
672 public function add_plugin_settings_link($links, $slug)
673 {
674 if ($slug !== $this->plugin->slug() || ! is_array($links)) {
675 return $links;
676 }
677
678 $href = admin_url('edit.php?post_type=boxzilla-box');
679 $label = esc_html__('Boxes', 'boxzilla');
680 $settings_link = "<a href=\"{$href}\">{$label}</a>";
681 array_unshift($links, $settings_link);
682 return $links;
683 }
684
685 /**
686 * Adds meta links to the plugin in the WP Admin > Plugins screen
687 *
688 * @param array $links
689 * @param string $slug
690 *
691 * @return array
692 */
693 public function add_plugin_meta_links($links, $slug)
694 {
695 if ($slug !== $this->plugin->slug()) {
696 return $links;
697 }
698
699 $links[] = '<a href="https://boxzillaplugin.com/kb/">Documentation</a>';
700 $links[] = '<a href="https://boxzillaplugin.com/add-ons/">Add-ons</a>';
701
702 return $links;
703 }
704
705 /**
706 * Flush all box rules
707 *
708 * Loops through all published boxes and fills the rules option
709 *
710 * @param int $post_id
711 */
712 public function flush_rules($post_id)
713 {
714
715 // only act on our own post type
716 $post = get_post($post_id);
717 if (! $post instanceof WP_Post || $post->post_type !== 'boxzilla-box') {
718 return;
719 }
720
721 // get all published boxes
722 $query = new \WP_Query();
723 $boxes = $query->query(
724 [
725 'post_type' => 'boxzilla-box',
726 'post_status' => 'publish',
727 'posts_per_page' => - 1,
728 'ignore_sticky_posts' => true,
729 'no_found_rows' => true,
730 ]
731 );
732
733 // setup empty array of rules
734 $rules = [];
735
736 // fill rules array
737 if (is_array($boxes)) {
738 foreach ($boxes as $box) {
739 // get box meta data
740 $box_meta = get_post_meta($box->ID, 'boxzilla_options', true);
741 if (! $box_meta || ! is_array($box_meta) || empty($box_meta['rules'])) {
742 continue;
743 }
744
745 // add box rules to all rules
746 $rules[ $box->ID ] = (array) $box_meta['rules'];
747 $rules[ $box->ID ]['comparision'] = isset($box_meta['rules_comparision']) ? $box_meta['rules_comparision'] : 'any';
748 }
749 }
750
751 update_option('boxzilla_rules', $rules);
752 }
753
754 /**
755 * Fetches a list of available add-on plugins
756 *
757 * @return array
758 */
759 protected function fetch_extensions()
760 {
761 $extensions = get_transient('boxzilla_remote_extensions');
762 if ($extensions) {
763 return $extensions;
764 }
765
766 $response = wp_remote_get('https://my.boxzillaplugin.com/api/v2/plugins');
767 if (is_wp_error($response) || wp_remote_retrieve_response_code($response) >= 400) {
768 return [];
769 }
770
771 $body = wp_remote_retrieve_body($response);
772 $data = json_decode($body);
773 if (is_array($data)) {
774 set_transient('boxzilla_remote_extensions', $data, 24 * HOUR_IN_SECONDS);
775 return $data;
776 }
777
778 return [];
779 }
780
781 /**
782 * @param $arr
783 * @param $insert
784 * @param $position
785 *
786 * @return array
787 */
788 public static function array_insert($arr, $insert, $position)
789 {
790 $i = 0;
791 $ret = [];
792 foreach ($arr as $key => $value) {
793 if ($i == $position) {
794 foreach ($insert as $ikey => $ivalue) {
795 $ret[ $ikey ] = $ivalue;
796 }
797 }
798 $ret[ $key ] = $value;
799 $i++;
800 }
801
802 return $ret;
803 }
804
805 /**
806 * @param string $text
807 *
808 * @return string
809 */
810 public function admin_footer_text($text)
811 {
812 $screen = get_current_screen();
813
814 if (! $screen instanceof WP_Screen) {
815 return $text;
816 }
817
818 $on_edit_page = $screen->parent_base === 'edit' && $screen->post_type === 'boxzilla-box';
819 if ($on_edit_page) {
820 return sprintf('If you enjoy using <strong>Boxzilla</strong>, please <a href="%s" target="_blank">leave us a �
821
822
823
824
825 rating</a>. A <strong style="text-decoration: underline;">huge</strong> thank you in advance!', 'https://wordpress.org/support/view/plugin-reviews/boxzilla?rate=5#postform');
826 }
827
828 return $text;
829 }
830 }
831