PluginProbe
Social Media Auto Poster – Schedule & Publish to Buffer / trunk
Social Media Auto Poster – Schedule & Publish to Buffer vtrunk
6.2.3 6.2.2 6.2.1 6.2.0 6.1.2 6.1.1 6.1.0 6.0.9 6.0.8 6.0.7 6.0.6 6.0.5 6.0.4 6.0.3 6.0.2 6.0.1 6.0.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.8.7 3.8.8 All 124 releases
wp-to-buffer / lib / shared / class-admin-ui.php

class-admin-ui.php in Social Media Auto Poster – Schedule & Publish to Buffer trunk, at lib/shared/class-admin-ui.php

1,138 lines 33.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Provides common functionality, styling and views for WP Zinc Plugins.
4 *
5 * @package WPZinc\Shared
6 * @author WP Zinc
7 */
8
9 namespace WPZinc\Shared;
10
11 // Exit if accessed directly.
12 if ( ! defined( 'ABSPATH' ) ) {
13 exit;
14 }
15
16 /**
17 * Provides common functionality, styling and views for WP Zinc Plugins.
18 */
19 class Admin_UI {
20
21 /**
22 * Holds the plugin object
23 *
24 * @since 1.0.0
25 *
26 * @var object
27 */
28 public $plugin;
29
30 /**
31 * Holds the exact path to this file's folder
32 *
33 * @since 1.0.0
34 *
35 * @var string
36 */
37 public $dashboard_folder;
38
39 /**
40 * Holds the exact URL to this file's folder
41 *
42 * @since 1.0.0
43 *
44 * @var string
45 */
46 public $dashboard_url;
47
48 /**
49 * Flag to show the Import and Export Sub Menu
50 *
51 * @since 1.0.0
52 *
53 * @var bool
54 */
55 private $show_import_export_menu = true;
56
57 /**
58 * Flag to show the Support Sub Menu
59 *
60 * @since 1.0.0
61 *
62 * @var bool
63 */
64 private $show_support_menu = false;
65
66 /**
67 * Flag to show the Review Request
68 *
69 * @since 1.0.0
70 *
71 * @var bool
72 */
73 private $show_review_request = true;
74
75 /**
76 * Holds the message to display when importing or exporting a configuration file.
77 *
78 * @since 1.0.0
79 *
80 * @var string
81 */
82 private $message = ''; // @phpstan-ignore property.onlyWritten
83
84 /**
85 * Holds the error message to display when importing or exporting a configuration file.
86 *
87 * @since 1.0.0
88 *
89 * @var string
90 */
91 private $error_message = ''; // @phpstan-ignore property.onlyWritten
92
93 /**
94 * Constructor
95 *
96 * @since 1.0.0
97 *
98 * @param object $plugin WordPress Plugin.
99 */
100 public function __construct( $plugin ) {
101
102 // Plugin Details.
103 $this->plugin = $plugin;
104
105 // Set class vars.
106 $this->dashboard_folder = plugin_dir_path( __FILE__ );
107 $this->dashboard_url = plugin_dir_url( __FILE__ );
108
109 // Admin CSS, JS and Menu.
110 add_filter( 'admin_body_class', array( $this, 'admin_body_class' ) );
111 add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts_css' ) );
112 add_action( str_replace( '-', '_', $this->plugin->name ) . '_admin_menu_import_export', array( $this, 'register_import_export_menu' ), 99 );
113 add_action( str_replace( '-', '_', $this->plugin->name ) . '_admin_menu_support', array( $this, 'register_support_menu' ), 99 );
114
115 // Plugin Actions.
116 if ( ! isset( $this->plugin->hide_upgrade_menu ) || ! $this->plugin->hide_upgrade_menu ) {
117 add_filter( 'plugin_action_links_' . $this->plugin->name . '/' . $this->plugin->name . '.php', array( $this, 'add_action_link' ), 10, 1 );
118 }
119
120 // Reviews.
121 if ( $this->plugin->review_name !== false ) {
122 add_action( 'wp_ajax_' . str_replace( '-', '_', $this->plugin->name ) . '_dismiss_review', array( $this, 'dismiss_review' ) );
123 add_action( 'admin_notices', array( $this, 'maybe_display_review_request' ) );
124 add_filter( 'admin_footer_text', array( $this, 'maybe_display_footer_review_request' ) );
125 }
126
127 // About.
128 if ( isset( $this->plugin->about_hook ) && $this->plugin->about_hook !== false ) {
129 add_action( $this->plugin->about_hook, array( $this, 'about_section' ) );
130 }
131
132 // Export and Redirects.
133 add_action( 'init', array( $this, 'export' ), 9999 );
134 add_action( 'init', array( $this, 'maybe_redirect' ), 2 );
135
136 // Permit wpzinc.com to be redirected to when using wp_safe_redirect().
137 add_filter( 'allowed_redirect_hosts', array( $this, 'allowed_redirect_hosts' ) );
138
139 }
140
141 /**
142 * Helper function to determine whether to load minified Javascript
143 * files or not.
144 *
145 * @since 1.0.0
146 *
147 * @return bool Load Minified JS
148 */
149 public function should_load_minified_js() {
150
151 // If script debugging is enabled, don't load minified JS.
152 if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
153 return false;
154 }
155
156 // If we can't determine a Plugin's activation state, minify JS.
157 if ( ! function_exists( 'is_plugin_active' ) ) {
158 return true;
159 }
160
161 // If a known third party Plugin exists that minifies JS, don't load minified JS
162 // as double minification seems to break things.
163 $minification_plugins = array(
164 'wp-rocket/wp-rocket.php',
165 );
166
167 /**
168 * Defines an array of third party minification Plugins that, if activate, will result
169 * in this Plugin's JS non-minified files being loaded.
170 *
171 * This allows said third party minification Plugins to minify JS, avoiding double minification
172 * which can result in errors.
173 *
174 * If SCRIPT_DEBUG is enabled, non minified JS will always be loaded, regardless of whether
175 * a minification Plugin is active.
176 *
177 * @since 1.0.0
178 *
179 * @param array $minification_plugins Plugin Folder and Filename Paths e.g. wp-rocket/wp-rocket.php
180 */
181 $minification_plugins = apply_filters( 'wpzinc_dashboard_should_load_minified_js_plugins', $minification_plugins );
182
183 // If no minification Plugins, load minified JS.
184 if ( ! count( $minification_plugins ) ) {
185 return true;
186 }
187
188 // Check if any minification Plugin is active.
189 foreach ( $minification_plugins as $plugin_folder_filename ) {
190 if ( is_plugin_active( $plugin_folder_filename ) ) {
191 // A known minification Plugin is active.
192 // Don't minify JS, as the third party Plugin will do this.
193 return false;
194 }
195 }
196
197 // If here, OK to load minified JS.
198 return true;
199
200 }
201
202 /**
203 * Shows the Import & Export Submenu in the Plugin's Menu
204 *
205 * @since 1.0.0
206 */
207 public function show_import_export_menu() {
208
209 $this->show_import_export_menu = true;
210
211 }
212
213 /**
214 * Hides the Import & Export Submenu in the Plugin's Menu
215 *
216 * @since 1.0.0
217 */
218 public function hide_import_export_menu() {
219
220 $this->show_import_export_menu = false;
221
222 }
223
224 /**
225 * Shows the Support Submenu in the Plugin's Menu
226 *
227 * @since 1.0.0
228 */
229 public function show_support_menu() {
230
231 $this->show_support_menu = true;
232
233 }
234
235 /**
236 * Hides the Support Submenu in the Plugin's Menu
237 *
238 * @since 1.0.0
239 */
240 public function hide_support_menu() {
241
242 $this->show_support_menu = false;
243
244 }
245
246 /**
247 * Disables the Review Request Notification, regardless of whether
248 * it has been set by the Plugin calling request_review()
249 *
250 * @since 1.0.0
251 */
252 public function disable_review_request() {
253
254 $this->show_review_request = false;
255
256 }
257
258 /**
259 * Adds the WP Zinc CSS class to the <body> tag when we're in the WordPress Admin interface
260 * and viewing a Plugin Screen
261 *
262 * This allows us to then override some WordPress layout styling on e.g. #wpcontent, without
263 * affecting other screens, Plugins etc.
264 *
265 * @since 1.0.0
266 *
267 * @param string $classes CSS Classes.
268 * @return string CSS Classes
269 */
270 public function admin_body_class( $classes ) {
271
272 // Define a list of strings that determine whether we're viewing a Plugin Screen.
273 $screens = array(
274 $this->plugin->name,
275 );
276
277 /**
278 * Filter the body classes to output on the <body> tag.
279 *
280 * @since 1.0.0
281 *
282 * @param array $screens Screens.
283 * @param string $classes Classes.
284 */
285 $screens = apply_filters( 'wpzinc_admin_body_class', $screens, $classes );
286
287 // Determine whether we're on a Plugin Screen.
288 $is_plugin_screen = $this->is_plugin_screen( $screens );
289
290 // Bail if we're not a Plugin screen.
291 if ( ! $is_plugin_screen ) {
292 return $classes;
293 }
294
295 // Add the wpzinc class and plugin name.
296 $classes .= ' wpzinc ' . $this->plugin->name;
297
298 // Return.
299 return trim( $classes );
300
301 }
302
303 /**
304 * Determines whether we're viewing this Plugin's screen in the WordPress Administration
305 * interface.
306 *
307 * @since 1.0.0
308 *
309 * @param array $screens Screens.
310 * @return bool Is Plugin Screen
311 */
312 private function is_plugin_screen( $screens ) {
313
314 // Bail if the current screen can't be obtained.
315 if ( ! function_exists( 'get_current_screen' ) ) {
316 return false;
317 }
318
319 // Bail if no screen names were specified to search for.
320 if ( empty( $screens ) ) {
321 return false;
322 }
323
324 // Get screen.
325 $screen = get_current_screen();
326
327 foreach ( $screens as $screen_name ) {
328 if ( strpos( $screen->id, $screen_name ) === false ) {
329 continue;
330 }
331
332 // We're on a Plugin Screen.
333 return true;
334 }
335
336 // If here, we're not on a Plugin Screen.
337 return false;
338
339 }
340
341 /**
342 * Register JS scripts, which Plugins may optionally load via wp_enqueue_script()
343 * Enqueues CSS
344 *
345 * @since 1.0.0
346 */
347 public function admin_scripts_css() {
348
349 // Determine whether to load minified versions of JS.
350 $minified = $this->should_load_minified_js();
351
352 // JS.
353 wp_register_script( 'wpzinc-admin-autocomplete-gutenberg', $this->dashboard_url . 'js/' . ( $minified ? 'min/' : '' ) . 'autocomplete-gutenberg' . ( $minified ? '-min' : '' ) . '.js', array(), $this->plugin->version, true );
354 wp_register_script( 'wpzinc-admin-autocomplete', $this->dashboard_url . 'js/' . ( $minified ? 'min/' : '' ) . 'autocomplete' . ( $minified ? '-min' : '' ) . '.js', array( 'wpzinc-admin-tribute' ), $this->plugin->version, true );
355 wp_register_script( 'wpzinc-admin-autosize', $this->dashboard_url . 'js/' . ( $minified ? 'min/' : '' ) . 'autosize' . ( $minified ? '-min' : '' ) . '.js', array(), $this->plugin->version, true );
356 wp_register_script( 'wpzinc-admin-conditional', $this->dashboard_url . 'js/' . ( $minified ? 'min/' : '' ) . 'jquery.form-conditionals' . ( $minified ? '-min' : '' ) . '.js', array( 'jquery' ), $this->plugin->version, true );
357 wp_register_script( 'wpzinc-admin-inline-search', $this->dashboard_url . 'js/' . ( $minified ? 'min/' : '' ) . 'inline-search' . ( $minified ? '-min' : '' ) . '.js', array( 'jquery' ), $this->plugin->version, true );
358 wp_register_script( 'wpzinc-admin-media-library', $this->dashboard_url . 'js/' . ( $minified ? 'min/' : '' ) . 'media-library' . ( $minified ? '-min' : '' ) . '.js', array( 'jquery', 'jquery-ui-sortable' ), $this->plugin->version, true );
359 wp_register_script( 'wpzinc-admin-modal', $this->dashboard_url . 'js/' . ( $minified ? 'min/' : '' ) . 'modal' . ( $minified ? '-min' : '' ) . '.js', array( 'jquery' ), $this->plugin->version, true );
360 wp_register_script( 'wpzinc-admin-notification', $this->dashboard_url . 'js/' . ( $minified ? 'min/' : '' ) . 'notification' . ( $minified ? '-min' : '' ) . '.js', array( 'jquery' ), $this->plugin->version, true );
361 wp_register_script( 'wpzinc-admin-review-notice', $this->dashboard_url . 'js/' . ( $minified ? 'min/' : '' ) . 'review-notice' . ( $minified ? '-min' : '' ) . '.js', array( 'jquery' ), $this->plugin->version, true );
362 wp_register_script( 'wpzinc-admin-selectize', $this->dashboard_url . 'js/' . ( $minified ? 'min/' : '' ) . 'selectize' . ( $minified ? '-min' : '' ) . '.js', array( 'jquery' ), $this->plugin->version, true );
363 wp_register_script( 'wpzinc-admin-synchronous-ajax', $this->dashboard_url . 'js/' . ( $minified ? 'min/' : '' ) . 'synchronous-ajax' . ( $minified ? '-min' : '' ) . '.js', array( 'jquery' ), $this->plugin->version, true );
364 wp_register_script( 'wpzinc-admin-tables', $this->dashboard_url . 'js/' . ( $minified ? 'min/' : '' ) . 'tables' . ( $minified ? '-min' : '' ) . '.js', array( 'jquery' ), $this->plugin->version, true );
365 wp_register_script( 'wpzinc-admin-tabs', $this->dashboard_url . 'js/' . ( $minified ? 'min/' : '' ) . 'tabs' . ( $minified ? '-min' : '' ) . '.js', array( 'jquery' ), $this->plugin->version, true );
366 wp_register_script( 'wpzinc-admin-tags', $this->dashboard_url . 'js/' . ( $minified ? 'min/' : '' ) . 'tags' . ( $minified ? '-min' : '' ) . '.js', array( 'jquery' ), $this->plugin->version, true );
367 wp_register_script( 'wpzinc-admin-tinymce-modal', $this->dashboard_url . 'js/' . ( $minified ? 'min/' : '' ) . 'tinymce-modal' . ( $minified ? '-min' : '' ) . '.js', array( 'jquery' ), $this->plugin->version, true );
368 wp_register_script( 'wpzinc-admin-toggle', $this->dashboard_url . 'js/' . ( $minified ? 'min/' : '' ) . 'toggle' . ( $minified ? '-min' : '' ) . '.js', array( 'jquery' ), $this->plugin->version, true );
369 wp_register_script( 'wpzinc-admin-tribute', $this->dashboard_url . 'js/' . ( $minified ? 'min/' : '' ) . 'tribute' . ( $minified ? '-min' : '' ) . '.js', array(), $this->plugin->version, true );
370 wp_register_script( 'wpzinc-admin', $this->dashboard_url . 'js/' . ( $minified ? 'min/' : '' ) . 'admin' . ( $minified ? '-min' : '' ) . '.js', array( 'jquery' ), $this->plugin->version, true );
371
372 // CSS.
373 wp_register_style( 'wpzinc-admin-selectize', $this->dashboard_url . 'css/selectize.css', array(), $this->plugin->version );
374 wp_enqueue_style( 'wpzinc-admin', $this->dashboard_url . 'css/admin.css', array(), $this->plugin->version );
375
376 // Depending on the screen we're on, maybe enqueue specific scripts now.
377 if ( ! function_exists( 'get_current_screen' ) ) {
378 return;
379 }
380
381 // Bail if we couldn't get the current screen.
382 $screen = get_current_screen();
383 if ( is_null( $screen ) ) {
384 return;
385 }
386
387 switch ( $screen->id ) {
388
389 /**
390 * Import / Export
391 * - Use of displayName is deliberate.
392 */
393 case sanitize_title( $this->plugin->displayName ) . '_page_' . $this->plugin->name . '-import-export':
394 wp_enqueue_script( 'wpzinc-admin-tabs' );
395 wp_enqueue_script( 'wpzinc-admin-toggle' );
396 break;
397
398 }
399
400 }
401
402 /**
403 * Registers the Import / Export Menu Link in the WordPress Administration interface
404 *
405 * @since 1.0.0
406 *
407 * @param string $parent_slug Parent Slug.
408 */
409 public function register_import_export_menu( $parent_slug = '' ) {
410
411 // Bail if the Import & Export Menu is hidden.
412 if ( ! $this->show_import_export_menu ) {
413 return;
414 }
415
416 // If a parent slug is defined, attach the submenu items to that.
417 // Otherwise use the plugin's name.
418 $slug = ( ! empty( $parent_slug ) ? $parent_slug : $this->plugin->name );
419
420 /**
421 * Filter the minimum capability for accessing Import and Export Sub Menu.
422 *
423 * @since 1.0.0
424 *
425 * @param string $minimum_capability Minimum Capability.
426 */
427 $minimum_capability = apply_filters( str_replace( '-', '_', $this->plugin->name ) . '_admin_admin_menu_minimum_capability', 'manage_options' ); // phpcs:ignore WordPress.NamingConventions.ValidHookName
428
429 add_submenu_page( $slug, __( 'Import & Export', 'wp-to-buffer' ), __( 'Import & Export', 'wp-to-buffer' ), $minimum_capability, $this->plugin->name . '-import-export', array( $this, 'import_export_screen' ) );
430
431 }
432
433 /**
434 * Registers the Support Menu Link in the WordPress Administration interface
435 *
436 * @since 1.0.0
437 *
438 * @param string $parent_slug Parent Slug.
439 */
440 public function register_support_menu( $parent_slug = '' ) {
441
442 // Bail if the Support Menu is hidden.
443 if ( ! $this->show_support_menu ) {
444 return;
445 }
446
447 // If a parent slug is defined, attach the submenu items to that.
448 // Otherwise use the plugin's name.
449 $slug = ( ! empty( $parent_slug ) ? $parent_slug : $this->plugin->name );
450
451 /**
452 * Filter the minimum capability for accessing Support Sub Menu.
453 *
454 * @since 1.0.0
455 *
456 * @param string $minimum_capability Minimum Capability.
457 */
458 $minimum_capability = apply_filters( str_replace( '-', '_', $this->plugin->name ) . '_admin_admin_menu_minimum_capability', 'manage_options' ); // phpcs:ignore WordPress.NamingConventions.ValidHookName
459
460 add_submenu_page( $slug, __( 'Support', 'wp-to-buffer' ), __( 'Support', 'wp-to-buffer' ), $minimum_capability, $this->plugin->name . '-support', array( $this, 'support_screen' ) );
461
462 }
463
464 /**
465 * Adds Plugin Action Links to the Plugin when activated in the Plugins Screen.
466 *
467 * @since 1.0.0
468 *
469 * @param array $links Action Links.
470 * @return array Action Links
471 */
472 public function add_action_link( $links ) {
473
474 // Bail if the licensing class exists,as this means we're on a Pro version.
475 if ( class_exists( 'LicensingUpdateManager' ) ) {
476 return $links;
477 }
478
479 // Add Links.
480 if ( $this->get_upgrade_url( 'plugins' ) ) {
481 $links[] = '<a href="' . esc_attr( $this->get_upgrade_url( 'plugins' ) ) . '" rel="noopener" target="_blank">' . __( 'Upgrade', 'wp-to-buffer' ) . '</a>';
482 }
483
484 /**
485 * Filter the action links.
486 *
487 * @since 1.0.0
488 *
489 * @param array $links Action Links.
490 * @param string $plugin_name Plugin Name.
491 * @param object $plugin Plugin.
492 */
493 $links = apply_filters( 'wpzinc_dashboard_add_action_link', $links, $this->plugin->name, $this->plugin );
494
495 // Return.
496 return $links;
497
498 }
499
500 /**
501 * Displays a dismissible WordPress Administration notice requesting a review, if requested
502 * by the main Plugin and the Review Request hasn't been disabled.
503 *
504 * @since 1.0.0
505 */
506 public function maybe_display_review_request() {
507
508 // If the review request is disabled, bail.
509 if ( ! $this->show_review_request ) {
510 return;
511 }
512
513 // If we're not an Admin user, bail.
514 if ( ! function_exists( 'current_user_can' ) ) {
515 return;
516 }
517 if ( ! current_user_can( 'activate_plugins' ) ) {
518 return;
519 }
520
521 // If the review request was dismissed by the user, bail.
522 if ( $this->dismissed_review() ) {
523 return;
524 }
525
526 // If no review request has been set by the plugin, bail.
527 if ( ! $this->requested_review() ) {
528 return;
529 }
530
531 // Enqueue JS.
532 wp_enqueue_script( 'wpzinc-admin-review-notice' );
533 wp_localize_script(
534 'wpzinc-admin-review-notice',
535 'wpzinc_admin_review_notice',
536 array(
537 'plugin_name' => $this->plugin->name,
538 'action' => esc_attr( str_replace( '-', '_', $this->plugin->name ) ) . '_dismiss_review',
539 'nonce' => wp_create_nonce( 'wpzinc_admin_review_notice_dismiss_review' ),
540 )
541 );
542
543 // If here, display the request for a review.
544 include_once $this->dashboard_folder . '/views/review-notice.php';
545
546 }
547
548 /**
549 * Displays a message in the WordPress Administration footer requesting a review if
550 * the Review Request hasn't been disabled.
551 *
552 * @since 1.0.0
553 *
554 * @param string $text Message.
555 */
556 public function maybe_display_footer_review_request( $text ) {
557
558 // If the review request is disabled, bail.
559 if ( ! $this->show_review_request ) {
560 return $text;
561 }
562
563 // Bail if we're not on a Plugin screen.
564 if ( ! filter_has_var( INPUT_GET, 'page' ) ) {
565 return $text;
566 }
567 $page = filter_input( INPUT_GET, 'page', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
568 if ( strpos( $page, $this->plugin->name ) === false ) {
569 return $text;
570 }
571
572 // Return review request text.
573 return sprintf(
574 /* translators: %1$s: Plugin Name, %2$s: Five Star Link to Review URL, %3$s: Link to Review URL, %4$s: Plugin Name */
575 __( 'Please rate %1$s %2$s on %3$s to help us grow %4$s. Thanks!', 'wp-to-buffer' ),
576 '<strong>' . $this->plugin->displayName . '</strong>',
577 '<a href="' . $this->get_review_url() . '" target="_blank">&#9733;&#9733;&#9733;&#9733;&#9733;</a>',
578 '<a href="' . $this->get_review_url() . '" target="_blank">WordPress.org</a>',
579 $this->plugin->displayName
580 );
581
582 }
583
584 /**
585 * Flag to indicate whether a review has been requested.
586 *
587 * @since 1.0.0
588 *
589 * @return bool Review Requested
590 */
591 public function requested_review() {
592
593 // If the review request is disabled, bail.
594 if ( ! $this->show_review_request ) {
595 return false;
596 }
597
598 $time = get_option( $this->plugin->review_name . '-review-request' );
599 if ( empty( $time ) ) {
600 return false;
601 }
602
603 // Check the current date and time matches or is later than the above value.
604 $now = time();
605 if ( $now >= ( $time + ( 3 * DAY_IN_SECONDS ) ) ) {
606 return true;
607 }
608
609 // We're not yet ready to show this review.
610 return false;
611
612 }
613
614 /**
615 * Requests a review notification, which is displayed on subsequent page loads.
616 *
617 * @since 1.0.0
618 */
619 public function request_review() {
620
621 // If the review request is disabled, bail.
622 if ( ! $this->show_review_request ) {
623 return;
624 }
625
626 // If a review has already been requested, bail.
627 $time = get_option( $this->plugin->review_name . '-review-request' );
628 if ( ! empty( $time ) ) {
629 return;
630 }
631
632 // Request a review, setting the value to the date and time now.
633 update_option( $this->plugin->review_name . '-review-request', time() );
634
635 }
636
637 /**
638 * Flag to indicate whether a review request has been dismissed by the user.
639 *
640 * @since 1.0.0
641 *
642 * @return bool Review Dismissed.
643 */
644 public function dismissed_review() {
645
646 return get_option( $this->plugin->review_name . '-review-dismissed' );
647
648 }
649
650 /**
651 * Dismisses the review notification, so it isn't displayed again.
652 *
653 * @since 1.0.0
654 */
655 public function dismiss_review() {
656
657 // Check nonce.
658 check_ajax_referer( 'wpzinc_admin_review_notice_dismiss_review', 'nonce' );
659
660 // Mark review as dismissed.
661 update_option( $this->plugin->review_name . '-review-dismissed', 1 );
662
663 // Send success response.
664 wp_send_json_success( 1 );
665
666 }
667
668 /**
669 * Returns the Review URL for this Plugin.
670 *
671 * @since 1.0.0
672 *
673 * @return string Review URL
674 */
675 public function get_review_url() {
676
677 $url = 'https://wordpress.org/support/plugin/' . $this->plugin->review_name . '/reviews/?filter=5#new-post';
678
679 return $url;
680
681 }
682
683 /**
684 * Returns the Upgrade URL for this Plugin.
685 *
686 * Adds Google Analytics UTM tracking, and optional coupon flag.
687 *
688 * @since 1.0.0
689 *
690 * @param string $utm_content UTM Content Value.
691 * @return mixed false | Upgrade URL
692 */
693 public function get_upgrade_url( $utm_content = '' ) {
694
695 // Bail if no Upgrade URL specified by the Plugin.
696 if ( ! isset( $this->plugin->upgrade_url ) ) {
697 return false;
698 }
699
700 // Return upgrade URL with UTM parameters appended.
701 return $this->append_utm_params_to_url( $this->plugin->upgrade_url, $utm_content );
702
703 }
704
705 /**
706 * Appends UTM parameters to a given URL.
707 *
708 * @since 1.0.0
709 *
710 * @param string $url URL to append UTM parameters to.
711 * @param string $utm_content UTM Content Value.
712 * @return string
713 */
714 public function append_utm_params_to_url( $url, $utm_content = '' ) {
715
716 return add_query_arg(
717 array(
718 'utm_source' => $this->plugin->name,
719 'utm_medium' => 'link',
720 'utm_content' => $utm_content,
721 'utm_campaign' => 'general',
722 ),
723 $url
724 );
725
726 }
727
728 /**
729 * Import / Export Screen.
730 *
731 * @since 1.0.0
732 */
733 public function import_export_screen() {
734
735 // Check nonce.
736 if ( isset( $_POST[ $this->plugin->name . '_nonce' ] ) && wp_verify_nonce( sanitize_key( $_POST[ $this->plugin->name . '_nonce' ] ), $this->plugin->name ) ) {
737 // Import if requested.
738 if ( isset( $_POST['import'] ) ) {
739 // Import JSON.
740 $this->import();
741 }
742 }
743
744 /**
745 * Filter the import sources.
746 *
747 * @since 1.0.0
748 *
749 * @param array $import_sources Import Sources.
750 */
751 $import_sources = apply_filters( str_replace( '-', '_', $this->plugin->name ) . '_import_sources', array() ); // phpcs:ignore WordPress.NamingConventions.ValidHookName
752
753 // Output view.
754 include_once $this->dashboard_folder . '/views/import-export.php';
755
756 }
757
758 /**
759 * Import JSON file upload that confirms to our standards.
760 *
761 * @since 1.0.0
762 */
763 private function import() {
764
765 // Check nonce.
766 if ( ! isset( $_POST[ $this->plugin->name . '_nonce' ] ) ) {
767 // Missing nonce.
768 return;
769 }
770
771 if ( ! wp_verify_nonce( sanitize_key( $_POST[ $this->plugin->name . '_nonce' ] ), $this->plugin->name ) ) {
772 // Invalid nonce.
773 return;
774 }
775
776 if ( ! isset( $_FILES['import']['type'] ) || ! isset( $_FILES['import']['tmp_name'] ) || ! isset( $_FILES['import']['size'] ) ) {
777 $this->error_message = __( 'Could not determine file type', 'wp-to-buffer' );
778 return;
779 }
780
781 if ( isset( $_FILES['import']['error'] ) && $_FILES['import']['error'] !== 0 ) {
782 $this->error_message = __( 'Error when uploading file.', 'wp-to-buffer' );
783 return;
784 }
785
786 // Determine if the file is JSON or ZIP.
787 switch ( $_FILES['import']['type'] ) {
788 /**
789 * ZIP File
790 */
791 case 'application/zip':
792 // Open ZIP file.
793 $zip = new \ZipArchive();
794 if ( $zip->open( sanitize_text_field( wp_unslash( $_FILES['import']['tmp_name'] ) ) ) !== true ) {
795 $this->error_message = __( 'Could not extract the supplied ZIP file.', 'wp-to-buffer' );
796 return;
797 }
798
799 // Extract and close.
800 $zip->extractTo( sys_get_temp_dir() );
801 $zip->close();
802
803 // Read JSON file.
804 // phpcs:disable WordPress.WP.AlternativeFunctions
805 $handle = fopen( sys_get_temp_dir() . '/export.json', 'r' );
806 $json = fread( $handle, filesize( sys_get_temp_dir() . '/export.json' ) );
807 fclose( $handle );
808 // phpcs:enable
809 break;
810
811 default:
812 // Read file.
813 // phpcs:disable WordPress.WP.AlternativeFunctions
814 $handle = fopen( sanitize_text_field( wp_unslash( $_FILES['import']['tmp_name'] ) ), 'r' );
815 $json = fread( $handle, (int) $_FILES['import']['size'] );
816 fclose( $handle );
817 // phpcs:enable
818
819 }
820
821 // Remove UTF8 BOM chars.
822 $bom = pack( 'H*', 'EFBBBF' );
823 $json = preg_replace( "/^$bom/", '', $json );
824
825 // Decode.
826 $import = json_decode( $json, true );
827
828 // Check data is an array.
829 if ( ! is_array( $import ) ) {
830 $this->error_message = __( 'Supplied file is not a valid JSON settings file, or has become corrupt.', 'wp-to-buffer' );
831 return;
832 }
833
834 // Allow Plugin to run its Import Routine using the supplied data now.
835 $result = true;
836 $result = apply_filters( str_replace( '-', '_', $this->plugin->name ) . '_import', $result, $import ); // phpcs:ignore WordPress.NamingConventions.ValidHookName
837
838 // Bail if an error occured.
839 if ( is_wp_error( $result ) ) {
840 $this->error_message = $result->get_error_message();
841 return;
842 }
843
844 $this->message = __( 'Settings imported.', 'wp-to-buffer' );
845
846 }
847
848 /**
849 * Support Screen
850 *
851 * @since 1.0.0
852 */
853 public function support_screen() {
854 // We never reach here, as we redirect earlier in the process.
855 }
856
857 /**
858 * About Section
859 *
860 * @since 1.0.0
861 */
862 public function about_section() {
863
864 // Define products.
865 $products = array(
866 array(
867 'name' => 'Page Generator Pro',
868 'url' => $this->append_utm_params_to_url( 'https://www.wpzinc.com/plugins/page-generator-pro/' ),
869 'icon' => 'https://www.wpzinc.com/wp-content/uploads/2025/01/page-generator-pro-logo-256x256-1.png',
870 'description' => 'Automatically mass build local SEO, programmatic SEO, GEO sites, directories or content at scale with one-click deployment. Save 40 hours per site.',
871 'price' => 99,
872 ),
873 array(
874 'name' => 'Social Post Flow',
875 'url' => $this->append_utm_params_to_url( 'https://www.socialpostflow.com' ),
876 'install_url' => admin_url(
877 add_query_arg(
878 array(
879 'tab' => 'search',
880 'type' => 'term',
881 's' => 'socialpostflow',
882 ),
883 'plugin-install.php'
884 )
885 ),
886 'icon' => 'https://www.socialpostflow.com/wp-content/uploads/2025/05/logo.png',
887 'description' => 'Automatically send content to your Social Post Flow account for scheduled publishing to Facebook, X/Twitter, Threads, Instagram, LinkedIn, Pinterest, Mastodon and Bluesky.',
888 'price' => 49,
889 ),
890 array(
891 'name' => 'WordPress to Buffer Pro',
892 'url' => $this->append_utm_params_to_url( 'https://www.wpzinc.com/plugins/wordpress-to-buffer-pro/' ),
893 'icon' => 'https://www.wpzinc.com/wp-content/uploads/2025/11/buffer-square.png',
894 'description' => 'Automatically send content to your Buffer account for scheduled publishing to Facebook, X, Instagram, Threads, LinkedIn, Pinterest, Mastodon, Bluesky, TikTok and Google Business.',
895 'price' => 39,
896 ),
897 array(
898 'name' => 'WordPress to Postiz Pro',
899 'url' => $this->append_utm_params_to_url( 'https://www.wpzinc.com/plugins/wordpress-to-postiz-pro/' ),
900 'icon' => 'https://www.wpzinc.com/wp-content/uploads/2026/08/postiz.png',
901 'description' => 'Automatically send content to your Postiz account for scheduled publishing to Facebook, Twitter, Instagram, Pinterest, LinkedIn and more.',
902 'price' => 39,
903 ),
904 array(
905 'name' => 'WordPress to Hootsuite Pro',
906 'url' => $this->append_utm_params_to_url( 'https://www.wpzinc.com/plugins/wordpress-to-hootsuite-pro/' ),
907 'icon' => 'https://www.wpzinc.com/wp-content/uploads/2018/03/hootsuite-logo-1.png',
908 'description' => 'Automatically send content to your Hootsuite account for scheduled publishing to Facebook, X / Twitter, LinkedIn, Pinterest and Threads.',
909 'price' => 39,
910 ),
911 array(
912 'name' => 'WordPress to SocialPilot Pro',
913 'url' => $this->append_utm_params_to_url( 'https://www.wpzinc.com/plugins/wordpress-to-socialpilot-pro/' ),
914 'icon' => 'https://www.wpzinc.com/wp-content/uploads/2019/05/socialpilot-logo.png',
915 'description' => 'Automatically send content to your SocialPilot account for scheduled publishing to Facebook, Twitter, Instagram, Pinterest, LinkedIn and more.',
916 'price' => 39,
917 ),
918 );
919
920 // Load view.
921 include $this->dashboard_folder . '/views/about.php';
922
923 }
924
925 /**
926 * If we have requested the export JSON, force a file download
927 *
928 * @since 1.0.0
929 */
930 public function export() {
931
932 // Check nonce.
933 if ( ! isset( $_POST[ $this->plugin->name . '_nonce' ] ) ) {
934 // Missing nonce.
935 return;
936 }
937
938 if ( ! wp_verify_nonce( sanitize_key( $_POST[ $this->plugin->name . '_nonce' ] ), $this->plugin->name ) ) {
939 // Invalid nonce.
940 return;
941 }
942
943 // Bail if no POST data.
944 if ( empty( $_POST ) ) {
945 return;
946 }
947
948 // Bail if not exporting.
949 if ( ! isset( $_POST['export'] ) ) {
950 return;
951 }
952
953 // Bail if no format specified.
954 if ( ! isset( $_POST['format'] ) ) {
955 return;
956 }
957
958 // Define the array to hold the export data.
959 $data = array();
960
961 /**
962 * Defines the data to include in the export file. Plugins hook into this
963 * to define the data.
964 *
965 * @since 1.0.0
966 *
967 * @param array $data Data.
968 * @param array $_POST POST Data.
969 */
970 $data = apply_filters( str_replace( '-', '_', $this->plugin->name ) . '_export', $data, $_POST );
971
972 // Force a file download, depending on the export format.
973 switch ( sanitize_text_field( wp_unslash( $_POST['format'] ) ) ) {
974 /**
975 * JSON, Zipped.
976 */
977 case 'zip':
978 $this->force_zip_file_download(
979 wp_json_encode(
980 array(
981 'data' => $data,
982 )
983 )
984 );
985 break;
986
987 /**
988 * JSON.
989 */
990 case 'json':
991 default:
992 $this->force_json_file_download(
993 wp_json_encode(
994 array(
995 'data' => $data,
996 )
997 )
998 );
999 break;
1000 }
1001
1002 }
1003
1004 /**
1005 * Force a browser download comprising of the given file, zipped.
1006 *
1007 * @since 1.0.0
1008 *
1009 * @param string $json JSON string.
1010 * @param string $filename Filename to Export Data to, excluding extension (default: export).
1011 */
1012 public function force_zip_file_download( $json, $filename = 'export' ) {
1013
1014 // Create new ZIP file.
1015 $zip = new \ZipArchive();
1016 $filename = $filename . '.zip';
1017
1018 // Bail if ZIP file couldn't be created.
1019 if ( $zip->open( $filename, \ZipArchive::CREATE ) !== true ) {
1020 return;
1021 }
1022
1023 // Add JSON data to export.json and close.
1024 $zip->addFromString( $filename . '.json', $json );
1025 $zip->close();
1026
1027 // Initialize WP_Filesystem.
1028 global $wp_filesystem;
1029 if ( empty( $wp_filesystem ) ) {
1030 require_once ABSPATH . '/wp-admin/includes/file.php';
1031 WP_Filesystem();
1032 }
1033
1034 // Read file contents.
1035 $zip_contents = $wp_filesystem->get_contents( $filename );
1036
1037 // Output ZIP data, prompting the browser to auto download as a ZIP file now.
1038 header( 'Content-type: application/zip' );
1039 header( 'Content-Disposition: attachment; filename=' . $filename . '.zip' );
1040 header( 'Pragma: no-cache' );
1041 header( 'Expires: 0' );
1042 echo $zip_contents; // phpcs:ignore WordPress.Security.EscapeOutput
1043
1044 // Delete the temporary file.
1045 $wp_filesystem->delete( $filename );
1046 exit();
1047
1048 }
1049
1050 /**
1051 * Force a browser download comprising of the given JSON data
1052 *
1053 * @since 1.0.0
1054 *
1055 * @param string $json JSON Data for file.
1056 * @param string $filename Filename to Export Data to, excluding extension (default: export).
1057 */
1058 public function force_json_file_download( $json, $filename = 'export' ) {
1059
1060 // Output JSON data, prompting the browser to auto download as a JSON file now.
1061 header( 'Content-type: application/x-msdownload' );
1062 header( 'Content-Disposition: attachment; filename=' . $filename . '.json' );
1063 header( 'Pragma: no-cache' );
1064 header( 'Expires: 0' );
1065 echo $json; // phpcs:ignore WordPress.Security.EscapeOutput
1066 exit();
1067
1068 }
1069
1070 /**
1071 * Force a browser download comprising of the given CSV data
1072 *
1073 * @since 1.0.0
1074 *
1075 * @param string $csv CSV Data for file.
1076 * @param string $filename Filename to Export Data to, excluding extension (default: export).
1077 */
1078 public function force_csv_file_download( $csv, $filename = 'export' ) {
1079
1080 // Output JSON data, prompting the browser to auto download as a JSON file now.
1081 header( 'Content-Type: text/csv; charset=UTF-8' );
1082 header( 'Content-Disposition: attachment; filename=' . $filename . '.csv' );
1083 header( 'Pragma: no-cache' );
1084 header( 'Expires: 0' );
1085 echo $csv; // phpcs:ignore WordPress.Security.EscapeOutput
1086 exit();
1087
1088 }
1089
1090 /**
1091 * If the Support or Upgrade menu item was clicked, redirect.
1092 *
1093 * @since 3.0
1094 */
1095 public function maybe_redirect() {
1096
1097 // Check we requested the support page.
1098 if ( ! filter_has_var( INPUT_GET, 'page' ) ) {
1099 return;
1100 }
1101
1102 // Sanitize page.
1103 $page = filter_input( INPUT_GET, 'page', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
1104
1105 // Redirect to Support.
1106 if ( $page === $this->plugin->name . '-support' ) {
1107 wp_safe_redirect( $this->plugin->support_url );
1108 die();
1109 }
1110
1111 // Redirect to Upgrade.
1112 if ( $page === $this->plugin->name . '-upgrade' ) {
1113 wp_safe_redirect( $this->get_upgrade_url( 'menu' ) );
1114 die();
1115 }
1116
1117 }
1118
1119 /**
1120 * Permit wpzinc.com to be redirected to when using wp_safe_redirect().
1121 *
1122 * @since 1.0.0
1123 *
1124 * @param array $hosts Hosts.
1125 * @return array Hosts.
1126 */
1127 public function allowed_redirect_hosts( $hosts ) {
1128
1129 $hosts[] = 'www.wpzinc.com';
1130 $hosts[] = 'www.socialpostflow.com';
1131 $hosts[] = 'app.socialpostflow.com';
1132
1133 return $hosts;
1134
1135 }
1136
1137 }
1138