PluginProbe
WPSSO Core – Complete Schema Markup and Meta Tags / 22.7.0
WPSSO Core – Complete Schema Markup and Meta Tags v22.7.0
22.7.0 22.6.1 22.6.0 22.5.3 22.5.2 22.5.1 22.4.0 22.3.0 22.2.1 22.2.0 22.1.3 22.1.2 22.1.1 22.1.0 22.0.0 21.13.3 21.13.2 trunk 21.13.1
wpsso / lib / admin.php

admin.php in WPSSO Core – Complete Schema Markup and Meta Tags 22.7.0, at lib/admin.php

2,682 lines 79.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 * License: GPLv3
4 * License URI: https://www.gnu.org/licenses/gpl.txt
5 * Copyright 2012-2026 Jean-Sebastien Morisset (https://wpsso.com/)
6 */
7
8 if ( ! defined( 'ABSPATH' ) ) {
9
10 die( 'These aren\'t the droids you\'re looking for.' );
11 }
12
13 if ( ! defined( 'WPSSO_PLUGINDIR' ) ) {
14
15 die( 'Do. Or do not. There is no try.' );
16 }
17
18 if ( ! class_exists( 'SucomPlugin' ) ) {
19
20 require_once WPSSO_PLUGINDIR . 'lib/com/plugin.php';
21 }
22
23 if ( ! class_exists( 'WpssoAdmin' ) ) {
24
25 class WpssoAdmin {
26
27 protected $p; // Wpsso class object.
28 protected $menu_id;
29 protected $menu_name;
30 protected $menu_lib;
31 protected $menu_ext; // Lowercase acronyn for plugin or add-on.
32 protected $menu_metaboxes;
33 protected $pagehook;
34 protected $pageref_url;
35 protected $pageref_title;
36 protected $toolbar_notice_types = array( 'err', 'warn', 'inf', 'upd' );
37
38 public static $readme = array();
39
40 public $form = null;
41 public $lang = array();
42 public $submenu = array();
43
44 /*
45 * Instantiated by Wpsso->set_objects() when is_admin() is true.
46 */
47 public function __construct( &$plugin ) {
48
49 $this->p =& $plugin;
50
51 if ( $this->p->debug->enabled ) {
52
53 $this->p->debug->mark();
54 }
55
56 $doing_ajax = SucomUtilWP::doing_ajax();
57
58 require_once WPSSO_PLUGINDIR . 'lib/admin-head.php';
59
60 new WpssoAdminHead( $plugin );
61
62 require_once WPSSO_PLUGINDIR . 'lib/admin-dashboard.php';
63
64 new WpssoAdminDashboard( $plugin );
65
66 /*
67 * The WpssoScript add_iframe_inline_script() method includes jQuery in the thickbox iframe to add the
68 * iframe_parent arguments when the Install or Update button is clicked.
69 *
70 * These class properties are used by both the WpssoAdmin plugin_complete_actions() and
71 * plugin_complete_redirect() methods to direct the user back to the thickbox iframe parent after plugin
72 * installation / activation / update.
73 */
74 if ( ! empty( $_GET[ 'wpsso_pageref_title' ] ) ) {
75
76 $this->pageref_title = esc_html( urldecode( $_GET[ 'wpsso_pageref_title' ] ) );
77 }
78
79 if ( ! empty( $_GET[ 'wpsso_pageref_url' ] ) ) {
80
81 $this->pageref_url = esc_url_raw( urldecode( $_GET[ 'wpsso_pageref_url' ] ) );
82 }
83
84 add_action( 'activated_plugin', array( $this, 'activated_plugin' ), 10, 2 );
85 add_action( 'after_switch_theme', array( $this, 'after_switch_theme' ), 10, 2 );
86 add_action( 'upgrader_process_complete', array( $this, 'upgrader_process_complete' ), 10, 2 );
87
88 /*
89 * Refresh the cache when the WordPress "home" or "blog_public" options are changed.
90 *
91 * See WpssAdmin->wp_site_option_changed().
92 * See SucomUpdate->wp_site_option_changed().
93 * See SucomUtilWP->raw_do_option().
94 * See SucomUpdateUtilWP->raw_do_option().
95 */
96 add_action( 'update_option_home', array( $this, 'wp_site_option_changed' ), PHP_INT_MAX, 3 );
97 add_action( 'update_option_blog_public', array( $this, 'wp_site_option_changed' ), PHP_INT_MAX, 3 );
98 add_action( 'sucom_update_option_home', array( $this, 'wp_site_option_changed' ), PHP_INT_MAX, 3 );
99
100 /*
101 * This filter re-sorts (if necessary) the active plugins array to load WPSSO Core before its add-ons.
102 */
103 add_filter( 'pre_update_option_active_plugins', array( $this, 'pre_update_active_plugins' ), PHP_INT_MAX, 3 );
104 add_filter( 'pre_update_option_active_sitewide_plugins', array( $this, 'pre_update_active_plugins' ), PHP_INT_MAX, 3 );
105
106 /*
107 * Define and disable the "Expect: 100-continue" header.
108 */
109 add_filter( 'http_request_args', array( $this, 'add_expect_header' ), 1000, 2 );
110
111 add_filter( 'http_request_host_is_external', array( $this, 'allow_safe_hosts' ), 1000, 3 );
112
113 /*
114 * Provides plugin data / information from the readme.txt for additional add-ons. Don't hook the
115 * 'plugins_api_result' filter if the update manager is active as it provides more complete plugin data
116 * than what's available from the readme.txt.
117 */
118 if ( empty( $this->p->avail[ 'p_ext' ][ 'um' ] ) ) {
119
120 add_filter( 'plugins_api_result', array( $this, 'external_plugin_data' ), 1000, 3 );
121 }
122
123 /*
124 * Add plugin / add-on settings links to the WordPress Plugins page. Hook this filter even when doing ajax
125 * since the WordPress plugin search results are created using an ajax call.
126 *
127 * The 5th argument is $menu_lib = 'submenu', so always limit the method arguments to 4.
128 */
129 add_filter( 'plugin_action_links', array( $this, 'add_plugin_action_links' ), 1000, 4 );
130
131 if ( is_multisite() ) {
132
133 /*
134 * The 5th argument is $menu_lib = 'sitesubmenu', so always limit the method arguments to 4.
135 */
136 add_filter( 'network_admin_plugin_action_links', array( $this, 'add_site_plugin_action_links' ), 1000, 4 );
137 }
138
139 if ( ! $doing_ajax ) {
140
141 add_action( 'wp_dashboard_setup', array( $this, 'dashboard_setup' ) );
142
143 /*
144 * The admin_menu action is run before admin_init.
145 */
146 add_action( 'admin_menu', array( $this, 'load_menu_objects' ), -1000 );
147 add_action( 'admin_menu', array( $this, 'add_admin_menus' ), WPSSO_ADD_MENU_PRIORITY, 0 );
148 add_action( 'admin_menu', array( $this, 'add_admin_submenus' ), WPSSO_ADD_SUBMENU_PRIORITY, 0 );
149
150 add_action( 'admin_init', array( $this, 'init_check_options' ), -1000 );
151 add_action( 'admin_init', array( $this, 'add_plugins_page_upgrade_notice' ) );
152 add_action( 'admin_init', array( $this, 'register_setting' ) );
153
154 if ( is_multisite() ) {
155
156 add_action( 'network_admin_menu', array( $this, 'load_network_menu_objects' ), -1000 );
157 add_action( 'network_admin_menu', array( $this, 'add_network_admin_menus' ), WPSSO_ADD_MENU_PRIORITY, 0 );
158 add_action( 'network_admin_edit_' . WPSSO_SITE_OPTIONS_NAME, array( $this, 'save_site_settings' ) );
159 }
160
161 add_filter( 'install_plugin_complete_actions', array( $this, 'plugin_complete_actions' ), 1000, 1 );
162 add_filter( 'update_plugin_complete_actions', array( $this, 'plugin_complete_actions' ), 1000, 1 );
163
164 add_filter( 'wp_redirect', array( $this, 'plugin_complete_redirect' ), 1000, 1 );
165 add_filter( 'wp_redirect', array( $this, 'profile_updated_redirect' ), -100, 2 );
166
167 add_action( 'admin_bar_menu', array( $this, 'add_admin_bar_menu_notices' ), WPSSO_TB_NOTICE_MENU_ORDER );
168 add_action( 'admin_bar_menu', array( $this, 'check_admin_bar_menu_notices' ), PHP_INT_MAX );
169
170 add_filter( 'sucom_toolbar_notice_types', array( $this, 'filter_toolbar_notice_types' ) );
171 }
172 }
173
174 /*
175 * Since WPSSO Core v9.3.0.
176 *
177 * This action is run by WordPress after any plugin is activated.
178 *
179 * If a plugin is silently activated (such as during an update), this action does not run.
180 */
181 public function activated_plugin( $plugin_base, $network_activation ) {
182
183 $this->p->reg->reset_admin_checks();
184
185 /*
186 * Since WPSSO Core v15.11.0.
187 *
188 * If the array of supported plugins has changed, then refresh the cache.
189 */
190 $old_avail = $this->p->avail;
191 $new_avail = $this->p->check->get_avail();
192
193 foreach ( array( 'p', 'p_ext' ) as $key ) { // Remove plugin and add-on information.
194
195 unset( $old_avail[ $key ], $new_avail[ $key ] );
196 }
197
198 if ( $old_avail !== $new_avail ) {
199
200 $user_id = get_current_user_id();
201
202 WpssoRegister::do_multisite( $network_activation, array( $this->p->util->cache, 'schedule_refresh' ), $args = array( $user_id ) );
203 }
204 }
205
206 /*
207 * Since WPSSO Core v9.3.0.
208 *
209 * This action is run by WordPress multiple times and the parameters differ according to the context (ie. if the
210 * old theme exists or not). If the old theme is missing, the parameter will be the slug of the old theme.
211 */
212 public function after_switch_theme( $old_name, $old_theme ) {
213
214 $this->p->reg->reset_admin_checks();
215 }
216
217 /*
218 * Since WPSSO Core v9.3.0.
219 *
220 * This action is run by WordPress when the upgrader process is complete.
221 */
222 public function upgrader_process_complete( $wp_upgrader_obj, $hook_extra ) {
223
224 $this->p->reg->reset_admin_checks();
225 }
226
227 /*
228 * Refresh the cache when the WordPress "home" or "blog_public" options are changed.
229 *
230 * See WpssAdmin->wp_site_option_changed().
231 * See SucomUpdate->wp_site_option_changed().
232 * See SucomUtilWP->raw_do_option().
233 * See SucomUpdateUtilWP->raw_do_option().
234 */
235 public function wp_site_option_changed( $old_value, $new_value, $option_name ) {
236
237 if ( $this->p->debug->enabled ) {
238
239 $this->p->debug->mark();
240 }
241
242 static $do_once = null;
243
244 if ( $do_once ) return; // Stop here.
245
246 $do_once = true;
247
248 /*
249 * Standardize old and new values for string comparison.
250 */
251 $old_value = strtolower( untrailingslashit( maybe_serialize( $old_value ) ) );
252 $new_value = strtolower( untrailingslashit( maybe_serialize( $new_value ) ) );
253
254 if ( $old_value !== $new_value ) {
255
256 if ( $user_id = get_current_user_id() ) {
257
258 if ( 'home' === $option_name ) {
259
260 $notice_msg = sprintf( __( 'The Site Address URL value has been changed from %1$s to %2$s.', 'wpsso' ), $old_value, $new_value );
261 $notice_key = __FUNCTION__ . '_' . $option_name . '_' . $old_value . '_' . $new_value;
262
263 $this->p->notice->upd( $notice_msg, $user_id, $notice_key );
264 }
265 }
266
267 $this->p->util->cache->schedule_refresh();
268 }
269 }
270
271 public function dashboard_setup() {
272
273 $classnames = $this->p->get_lib_classnames( 'dashboard' ); // Always returns an array.
274
275 foreach ( $classnames as $id => $classname ) {
276
277 new $classname( $this->p );
278 }
279 }
280
281 public function load_network_menu_objects() {
282
283 if ( $this->p->debug->enabled ) {
284
285 $this->p->debug->mark();
286 }
287
288 $this->load_menu_objects( array( 'submenu', 'sitesubmenu' ) );
289 }
290
291 public function load_menu_objects( $menu_libs = array() ) {
292
293 if ( $this->p->debug->enabled ) {
294
295 $this->p->debug->mark();
296 }
297
298 if ( empty( $menu_libs ) ) {
299
300 $menu_libs = array( 'dashboard', 'plugins', 'profile', 'settings', 'submenu', 'tools', 'users' );
301 }
302
303 foreach ( $menu_libs as $menu_lib ) {
304
305 foreach ( $this->p->cf[ 'plugin' ] as $ext => $info ) {
306
307 if ( ! isset( $info[ 'lib' ][ $menu_lib ] ) ) { // Not all add-ons have submenus.
308
309 continue;
310 }
311
312 foreach ( $info[ 'lib' ][ $menu_lib ] as $menu_id => $menu_name ) {
313
314 $add_menu_item = true;
315
316 if ( empty( $this->p->cf[ 'menu' ][ 'must_load' ][ $menu_id ] ) ) { // Settings page can be disabled.
317
318 $opt_key = SucomUtil::sanitize_key( 'plugin_add_' . $menu_lib . '_' . $menu_id );
319 $filter_name = SucomUtil::sanitize_hookname( 'wpsso_add_menu_' . $menu_lib . '_item_' . $menu_id );
320 $add_menu_item = isset( $this->p->options[ $opt_key ] ) ? (bool) $this->p->options[ $opt_key ] : true;
321 $add_menu_item = apply_filters( $filter_name, $add_menu_item );
322 }
323
324 if ( $add_menu_item ) {
325
326 $classname = apply_filters( $ext . '_load_lib', false, $menu_lib . '/' . $menu_id );
327
328 if ( is_string( $classname ) && class_exists( $classname ) ) {
329
330 if ( $this->p->debug->enabled ) {
331
332 $this->p->debug->log( 'loading classname ' . $classname . ' for menu id ' . $menu_id );
333 }
334
335 $menu_title = $this->get_submenu_title( $info, $menu_lib, $menu_id );
336
337 $this->submenu[ $menu_id ] = new $classname( $this->p, $menu_id, $menu_title, $menu_lib, $ext );
338
339 } elseif ( $this->p->debug->enabled ) {
340
341 $this->p->debug->log( 'classname not found for menu lib ' . $menu_lib . '/' . $menu_id );
342 }
343 }
344 }
345 }
346 }
347 }
348
349 public function add_network_admin_menus() {
350
351 if ( $this->p->debug->enabled ) {
352
353 $this->p->debug->mark();
354 }
355
356 $this->add_admin_menus( 'sitesubmenu' );
357 }
358
359 /*
360 * Add a new main menu and its sub-menu items.
361 */
362 public function add_admin_menus( $menu_lib = 'submenu' ) {
363
364 foreach ( $this->p->cf[ '*' ][ 'lib' ][ $menu_lib ] as $this->menu_id => $this->menu_name ) {
365
366 if ( isset( $this->submenu[ $this->menu_id ] ) ) {
367
368 $menu_slug = 'wpsso-' . $this->menu_id;
369
370 $this->submenu[ $this->menu_id ]->add_menu_page( $menu_slug );
371
372 break;
373 }
374 }
375
376 $menu_args = $this->get_submenu_args( $menu_lib );
377
378 foreach ( $menu_args as $menu_id => $args ) {
379
380 if ( isset( $this->submenu[ $menu_id ] ) ) { // Just in case.
381
382 call_user_func_array( array( $this->submenu[ $menu_id ], 'add_submenu_page' ), $args );
383 }
384 }
385 }
386
387 /*
388 * Add sub-menu items to existing WordPress menus.
389 */
390 public function add_admin_submenus() {
391
392 foreach ( array( 'dashboard', 'plugins', 'profile', 'settings', 'tools', 'users' ) as $menu_lib ) {
393
394 $menu_args = $this->get_submenu_args( $menu_lib );
395
396 foreach ( $menu_args as $menu_id => $args ) {
397
398 if ( isset( $this->submenu[ $menu_id ] ) ) { // Just in case.
399
400 call_user_func_array( array( $this->submenu[ $menu_id ], 'add_submenu_page' ), $args );
401 }
402 }
403 }
404 }
405
406 /*
407 * Return and maybe set/reset the WpssoAdmin->form value.
408 */
409 public function &get_form_object( $menu_ext ) {
410
411 if ( $this->p->debug->enabled ) {
412
413 $this->p->debug->mark();
414 }
415
416 if ( ! isset( $this->form ) ) {
417
418 if ( $this->p->debug->enabled ) {
419
420 $this->p->debug->log( 'form object not defined' );
421 }
422
423 $this->set_form_object( $menu_ext );
424
425 } elseif ( $this->form->get_ext_id() !== $menu_ext ) {
426
427 if ( $this->p->debug->enabled ) {
428
429 $this->p->debug->log( 'form object does not match' );
430 }
431
432 $this->set_form_object( $menu_ext );
433 }
434
435 return $this->form;
436 }
437
438 /*
439 * Set the WpssoAdmin->form value.
440 */
441 protected function set_form_object( $menu_ext ) { // $menu_ext required for text_domain.
442
443 if ( $this->p->debug->enabled ) {
444
445 $this->p->debug->log( 'setting form object for ' . $menu_ext );
446 }
447
448 $defs = $this->p->opt->get_defaults();
449
450 $this->form = new SucomForm( $this->p, WPSSO_OPTIONS_NAME, $this->p->options, $defs, $menu_ext );
451 }
452
453 public function register_setting() {
454
455 register_setting( 'wpsso_settings', WPSSO_OPTIONS_NAME, $args = array(
456 'sanitize_callback' => array( $this, 'settings_sanitation' ),
457 ) );
458 }
459
460 /*
461 * Runs at admin_init priority -1000.
462 */
463 public function init_check_options() {
464
465 if ( $this->p->debug->enabled ) {
466
467 $this->p->debug->mark();
468 }
469
470 /*
471 * set_options() may have loaded the static defaults for new or missing options.
472 *
473 * After all objects have been loaded, and all filter / action hooks registered, check to see if the
474 * options array needs to be reloaded from the filtered defaults.
475 */
476 if ( ! empty( $this->p->options[ '__reload_defaults' ] ) ) {
477
478 $this->p->options = $this->p->opt->get_defaults();
479 }
480
481 if ( is_multisite() ) { // Load the site options array.
482
483 if ( ! empty( $this->p->site_options[ '__reload_defaults' ] ) ) {
484
485 $this->p->site_options = $this->p->opt->get_site_defaults();
486 }
487 }
488
489 if ( $this->p->debug->enabled ) {
490
491 $this->p->debug->log( 'checking ' . WPSSO_OPTIONS_NAME . ' options' );
492 }
493
494 $this->p->options = $this->p->opt->check_options( WPSSO_OPTIONS_NAME, $this->p->options, $network = false );
495
496 if ( is_multisite() ) { // Load the site options array.
497
498 if ( $this->p->debug->enabled ) {
499
500 $this->p->debug->log( 'checking ' . WPSSO_SITE_OPTIONS_NAME . ' options' );
501 }
502
503 $this->p->site_options = $this->p->opt->check_options( WPSSO_SITE_OPTIONS_NAME, $this->p->site_options, $network = true );
504 }
505
506 /*
507 * Init option checks.
508 *
509 * See WpssoWcmd->init_check_options().
510 */
511 do_action( 'wpsso_init_check_options' );
512 }
513
514 public function add_plugins_page_upgrade_notice() {
515
516 if ( $this->p->debug->enabled ) {
517
518 $this->p->debug->mark();
519 }
520
521 foreach ( $this->p->cf[ 'plugin' ] as $ext => $info ) {
522
523 if ( ! empty( $info[ 'base' ] ) ) {
524
525 /*
526 * Fires at the end of the update message container in each row of the plugins list table.
527 *
528 * See wordpress/wp-admin/includes/update.php:587.
529 */
530 add_action( 'in_plugin_update_message-' . $info[ 'base' ], array( $this, 'show_upgrade_notice' ), 10, 2 );
531 }
532 }
533 }
534
535 public function show_upgrade_notice( $data, $response ) {
536
537 if ( $this->p->debug->enabled ) {
538
539 $this->p->debug->mark();
540 }
541
542 if ( isset( $data[ 'upgrade_notice' ] ) ) { // Just in case.
543
544 echo '<span style="display:table;border-collapse:collapse;margin-left:26px;">';
545 echo '<span style="display:table-cell;">' . strip_tags( $data[ 'upgrade_notice' ] ) . '</span>';
546 echo '</span>';
547 }
548 }
549
550 protected function add_menu_page( $menu_slug ) {
551
552 $pkg_info = $this->p->util->get_pkg_info(); // Uses a local cache.
553 $page_title = $pkg_info[ 'wpsso' ][ 'short_pkg' ] . ' - ' . $this->menu_name;
554 $menu_title = $this->get_menu_title();
555 $cf_wp_admin = $this->p->cf[ 'wp' ][ 'admin' ];
556 $capability = isset( $cf_wp_admin[ $this->menu_lib ][ 'cap' ] ) ? $cf_wp_admin[ $this->menu_lib ][ 'cap' ] : 'manage_options';
557 $icon_url = 'none'; // Icon provided by WpssoStyle::admin_register_page_styles().
558 $function = array( $this, 'show_settings_page' );
559 $position = WPSSO_MENU_ORDER;
560
561 $this->pagehook = add_menu_page( $page_title, $menu_title, $capability, $menu_slug, $function, $icon_url, $position );
562
563 add_action( 'load-' . $this->pagehook, array( $this, 'load_settings_page' ) );
564 }
565
566 protected function add_submenu_page( $parent_slug, $menu_id, $menu_title, $menu_lib, $menu_ext, $css_class = '' ) {
567
568 $css_class = trim( 'wpsso-menu-item wpsso-' . $menu_id . ' ' . $css_class );
569 $menu_title = '<div class="' . $css_class . ' menu-item-label">' . $menu_title . '</div>'; // Wrap the title string.
570 $pkg_info = $this->p->util->get_pkg_info(); // Uses a local cache.
571 $page_title = $pkg_info[ $menu_ext ][ 'short_pkg' ] . ' - ' . $menu_title;
572 $cf_wp_admin = $this->p->cf[ 'wp' ][ 'admin' ];
573 $capability = isset( $cf_wp_admin[ $menu_lib ][ 'cap' ] ) ? $cf_wp_admin[ $menu_lib ][ 'cap' ] : 'manage_options';
574 $menu_slug = 'wpsso-' . $menu_id;
575 $function = array( $this, 'show_settings_page' );
576 $position = null;
577
578 if ( isset( $cf_wp_admin[ $menu_lib ][ 'sub' ][ $menu_id ] ) ) {
579
580 $cf_menu_id = $cf_wp_admin[ $menu_lib ][ 'sub' ][ $menu_id ];
581 $capability = isset( $cf_menu_id[ 'cap' ] ) ? $cf_menu_id[ 'cap' ] : $capability;
582 $position = isset( $cf_menu_id[ 'pos' ] ) ? $cf_menu_id[ 'pos' ] : $position;
583 }
584
585 $this->pagehook = add_submenu_page( $parent_slug, $page_title, $menu_title, $capability, $menu_slug, $function, $position );
586
587 add_action( 'load-' . $this->pagehook, array( $this, 'load_settings_page' ) );
588 }
589
590 /*
591 * Add plugin links for the WordPress network plugins page.
592 */
593 public function add_site_plugin_action_links( $action_links, $plugin_base, $plugin_data, $context, $menu_lib = 'sitesubmenu' ) {
594
595 return $this->add_plugin_action_links( $action_links, $plugin_base, $plugin_data, $context, $menu_lib );
596 }
597
598 /*
599 * Add plugin links for the WordPress plugins page.
600 *
601 * $plugin_data is an array of plugin data. See get_plugin_data().
602 *
603 * $context can be 'all', 'active', 'inactive', 'recently_activated', 'upgrade', 'mustuse', 'dropins', or 'search'.
604 */
605 public function add_plugin_action_links( $action_links, $plugin_base, $plugin_data, $context, $menu_lib = 'submenu' ) {
606
607 if ( ! isset( $this->p->cf[ '*' ][ 'base' ][ $plugin_base ] ) ) {
608
609 return $action_links;
610 }
611
612 $ext = $this->p->cf[ '*' ][ 'base' ][ $plugin_base ];
613 $info = $this->p->cf[ 'plugin' ][ $ext ];
614 $settings_page = empty( $info[ 'lib' ][ $menu_lib ] ) ? '' : key( $info[ 'lib' ][ $menu_lib ] );
615 $settings_page_url = $this->p->util->get_admin_url( $settings_page );
616
617 switch ( $ext ) {
618
619 case 'wpsso':
620
621 $addons_page = 'sitesubmenu' === $menu_lib ? 'site-addons' : 'addons';
622 $addons_page_url = $this->p->util->get_admin_url( $addons_page );
623
624 if ( ! empty( $settings_page ) ) {
625
626 $action_links[] = '<a href="' . $settings_page_url . '">' . _x( 'Plugin Settings', 'plugin action link', 'wpsso' ) . '</a>';
627 }
628
629 $action_links[] = '<a href="' . $addons_page_url . '">' . _x( 'Plugin Add-ons', 'plugin action link', 'wpsso' ) . '</a>';
630
631 break;
632
633 default:
634
635 if ( ! empty( $settings_page ) ) {
636
637 $action_links[] = '<a href="' . $settings_page_url . '">' . _x( 'Add-on Settings', 'plugin action link', 'wpsso' ) . '</a>';
638 }
639
640 break;
641 }
642
643 return $action_links;
644 }
645
646 /*
647 * Define and disable the "Expect: 100-continue" header.
648 *
649 * $parsed_args should be an array, so make sure other filters aren't giving us a string or boolean.
650 */
651 public function add_expect_header( $parsed_args, $url ) {
652
653 if ( ! is_array( $parsed_args ) ) { // Just in case.
654
655 $parsed_args = array();
656 }
657
658 if ( empty( $parsed_args[ 'headers' ] ) ) { // Just in case.
659
660 $parsed_args[ 'headers' ] = array();
661
662 /*
663 * WordPress allows passing headers as a string -- fix that issue here so we can update the 'headers' array
664 * properly.
665 *
666 * See https://core.trac.wordpress.org/browser/tags/5.7.1/src/wp-includes/class-http.php#L310.
667 */
668 } elseif ( ! is_array( $parsed_args[ 'headers' ] ) ) {
669
670 $processedHeaders = WP_Http::processHeaders( $parsed_args[ 'headers' ] );
671
672 $parsed_args[ 'headers' ] = $processedHeaders[ 'headers' ];
673 }
674
675 $parsed_args[ 'headers' ][ 'Expect' ] = '';
676
677 return $parsed_args;
678 }
679
680 public function allow_safe_hosts( $is_allowed, $ip, $url ) {
681
682 if ( $is_allowed ) { // Already allowed.
683
684 return $is_allowed;
685 }
686
687 if ( isset( $this->p->cf[ 'extend' ] ) ) {
688
689 foreach ( $this->p->cf[ 'extend' ] as $host ) {
690
691 if ( 0 === strpos( $url, $host ) ) {
692
693 return true;
694 }
695 }
696 }
697
698 return $is_allowed;
699 }
700
701 /*
702 * Provides plugin data / information from the readme.txt for additional add-ons.
703 */
704 public function external_plugin_data( $result, $action = null, $args = null ) {
705
706 if ( $this->p->debug->enabled ) {
707
708 $this->p->debug->mark();
709 }
710
711 if ( 'plugin_information' !== $action ) { // This filter only provides plugin data.
712
713 return $result;
714
715 } elseif ( empty( $args->slug ) ) { // Make sure we have a slug in the request.
716
717 return $result;
718
719 } elseif ( empty( $this->p->cf[ '*' ][ 'slug' ][ $args->slug ] ) ) { // Make sure the plugin slug is one of ours.
720
721 return $result;
722
723 } elseif ( isset( $result->slug ) && $result->slug === $args->slug ) { // If the object from WordPress looks complete, return it as-is.
724
725 return $result;
726 }
727
728 $ext = $this->p->cf[ '*' ][ 'slug' ][ $args->slug ]; // Get the add-on acronym to read its config.
729
730 if ( empty( $this->p->cf[ 'plugin' ][ $ext ] ) ) { // Make sure we have a config for that acronym.
731
732 return $result;
733 }
734
735 $plugin_data = $this->get_plugin_data( $ext, $read_cache = true ); // Get plugin data from the plugin readme.
736
737 if ( empty( $plugin_data ) ) { // Make sure we have some data to return.
738
739 return $result;
740 }
741
742 $plugin_data->external = true; // Let WordPress known that this is not a wordpress.org plugin.
743
744 return $plugin_data;
745 }
746
747 /*
748 * This method receives only a partial options array, so re-create a full one.
749 *
750 * WordPress handles the actual saving of the options to the database table.
751 */
752 public function settings_sanitation( $opts ) {
753
754 $current_user_id = get_current_user_id(); // Always returns an integer.
755
756 /*
757 * Clear any old notices for the current user before sanitation checks.
758 */
759 $this->p->notice->clear();
760
761 /*
762 * Make sure the input is an array.
763 */
764 if ( ! is_array( $opts ) ) {
765
766 $opts = array();
767 }
768
769 $defs = $this->p->opt->get_defaults();
770 $opts = SucomUtil::restore_checkboxes( $opts );
771 $opts = SucomUtil::array_merge_recursive_distinct( $this->p->options, $opts );
772 $opts = $this->p->opt->sanitize( $opts, $defs, $network = false );
773 $opts = apply_filters( 'wpsso_save_settings_options', $opts, $network = false, $upgrading = false );
774
775 /*
776 * Maybe clear dismissed notices for enabled options that were previously disabled.
777 *
778 * See WpssoAdminHeadSuggestOptions->suggest_options_integration().
779 */
780 foreach ( array(
781 'add_link_rel_canonical' => 'suggest-options-seo-add_link_rel_canonical',
782 'add_link_rel_shortlink' => 'suggest-options-seo-add_link_rel_shortlink',
783 'add_meta_name_description' => 'suggest-options-seo-add_meta_name_description',
784 'add_meta_name_robots' => 'suggest-options-seo-add_meta_name_robots',
785 'plugin_filter_content' => 'notice-content-filters-disabled',
786 'plugin_check_img_dims' => 'notice-check-img-dims-disabled',
787 'plugin_inherit_featured' => 'notice-wc-inherit-featured-disabled',
788 ) as $opt_key => $notice_key ) {
789
790 if ( ! empty( $opts[ $opt_key ] ) ) { // Option is enabled.
791
792 $this->p->notice->clear_dismissed( $notice_key, $current_user_id );
793 }
794 }
795
796 /*
797 * Update the current options with any changes.
798 */
799 $this->p->options = $opts;
800
801 $this->settings_saved_notice();
802
803 return $opts;
804 }
805
806 public function settings_saved_notice() {
807
808 $cache_md5_pre = 'wpsso_h_'; // Use 'wpsso_cache_expire_head_markup' filter.
809 $cache_exp_secs = $this->p->util->get_cache_exp_secs( $cache_md5_pre, $cache_type = 'transient' );
810 $user_id = get_current_user_id();
811 $notice_msg = '<strong>' . __( 'Plugin settings have been saved.', 'wpsso' ) . '</strong>' . ' ';
812 $notice_key = 'settings-saved';
813
814 if ( $cache_exp_secs > 0 ) {
815
816 $refresh_cache_url = $this->p->util->get_admin_url( $menu_id = 'tools' );
817 $refresh_cache_url = add_query_arg( 'wpsso-action', 'refresh_cache', $refresh_cache_url );
818 $refresh_cache_url = wp_nonce_url( $refresh_cache_url, WpssoAdmin::get_nonce_action(), WPSSO_NONCE_NAME );
819 $refresh_cache_link = '<a href="' . $refresh_cache_url . '">' . __( 'refresh the cache now', 'wpsso' ) . '</a>';
820 $human_cache_exp = human_time_diff( 0, $cache_exp_secs );
821
822 $notice_msg .= sprintf( __( 'You can %1$s or let the cache refresh over the next %2$s.', 'wpsso' ), $refresh_cache_link, $human_cache_exp );
823 }
824
825 $this->p->notice->upd( $notice_msg, $user_id, $notice_key );
826 }
827
828 public function save_site_settings() {
829
830 $default_page = key( $this->p->cf[ '*' ][ 'lib' ][ 'sitesubmenu' ] );
831 $default_page_url = $this->p->util->get_admin_url( $default_page );
832 $setting_page = SucomUtil::get_request_value( 'page', 'POST', $default_page );
833 $setting_page_url = $this->p->util->get_admin_url( $setting_page );
834
835 if ( empty( $_POST[ WPSSO_NONCE_NAME ] ) ) { // WPSSO_NONCE_NAME is an md5() string.
836
837 if ( $this->p->debug->enabled ) {
838
839 $this->p->debug->log( 'nonce token validation post field missing' );
840 }
841
842 wp_redirect( $default_page_url ); // Do not trust the 'page' request value.
843
844 exit;
845
846 } elseif ( ! wp_verify_nonce( $_POST[ WPSSO_NONCE_NAME ], WpssoAdmin::get_nonce_action() ) ) {
847
848 $this->p->notice->err( __( 'Nonce token validation failed for network options (update ignored).', 'wpsso' ) );
849
850 wp_redirect( $default_page_url ); // Do not trust the 'page' request value.
851
852 exit;
853
854 } elseif ( ! current_user_can( 'manage_network_options' ) ) {
855
856 $this->p->notice->err( __( 'Insufficient privileges to manage network options.', 'wpsso' ) );
857
858 wp_redirect( $setting_page_url );
859
860 exit;
861 }
862
863 /*
864 * Clear any old notices for the current user before sanitation checks.
865 */
866 $this->p->notice->clear();
867
868 /*
869 * Make sure the input is an array.
870 */
871 $opts = array();
872
873 if ( isset( $_POST[ WPSSO_SITE_OPTIONS_NAME ] ) && is_array( $_POST[ WPSSO_SITE_OPTIONS_NAME ] ) ) {
874
875 $opts = $_POST[ WPSSO_SITE_OPTIONS_NAME ];
876 }
877
878 $defs = $this->p->opt->get_site_defaults();
879 $opts = SucomUtil::restore_checkboxes( $opts );
880 $opts = SucomUtil::array_merge_recursive_distinct( $this->p->site_options, $opts ); // Complete the array with previous options.
881 $opts = $this->p->opt->sanitize( $opts, $defs, $network = true );
882 $opts = apply_filters( 'wpsso_save_settings_options', $opts, $network = true, $upgrading = false );
883
884 /*
885 * Update the current options with any changes.
886 */
887 $this->p->site_options = $opts;
888
889 update_site_option( WPSSO_SITE_OPTIONS_NAME, $opts );
890
891 $this->p->notice->upd( '<strong>' . __( 'Network plugin settings have been saved.', 'wpsso' ) . '</strong>' );
892
893 $setting_page_url = add_query_arg( 'settings-updated', 'true', $setting_page_url );
894
895 wp_redirect( $setting_page_url );
896
897 exit; // Stop after redirect.
898 }
899
900 public function load_settings_page() {
901
902 if ( $this->p->debug->enabled ) {
903
904 $this->p->debug->mark();
905 }
906
907 $user_id = get_current_user_id();
908 $action_query = 'wpsso-action';
909 $action_value = SucomUtil::get_request_value( $action_query ) ; // POST or GET with sanitize_text_field().
910 $action_value = SucomUtil::sanitize_hookname( $action_value );
911 $nonce_value = SucomUtil::get_request_value( WPSSO_NONCE_NAME ) ; // POST or GET with sanitize_text_field().
912
913 $_SERVER[ 'REQUEST_URI' ] = remove_query_arg( array( $action_query, WPSSO_NONCE_NAME ) );
914
915 wp_enqueue_script( 'postbox' );
916
917 if ( ! empty( $action_value ) ) {
918
919 if ( empty( $nonce_value ) ) { // WPSSO_NONCE_NAME is an md5() string.
920
921 if ( $this->p->debug->enabled ) {
922
923 $this->p->debug->log( 'nonce token validation field missing' );
924 }
925
926 } elseif ( ! wp_verify_nonce( $nonce_value, WpssoAdmin::get_nonce_action() ) ) {
927
928 $notice_msg = sprintf( __( 'Nonce token validation failed for %1$s action "%2$s".', 'wpsso' ), 'admin', $action_value );
929
930 $this->p->notice->err( $notice_msg, $user_id );
931
932 } else {
933
934 if ( $this->p->debug->enabled ) {
935
936 $this->p->debug->log( 'action_value = ' . $action_value );
937 }
938
939 switch ( $action_value ) {
940
941 case 'refresh_cache':
942
943 $this->p->util->cache->schedule_refresh( $user_id );
944
945 break;
946
947 case 'clear_cache_files':
948
949 $cleared_count = $this->p->util->cache->clear_cache_files();
950
951 $notice_msg = sprintf( __( '%s cache files have been cleared.', 'wpsso' ), $cleared_count );
952
953 $this->p->notice->upd( $notice_msg, $user_id );
954
955 break;
956
957 case 'clear_ignored_urls':
958
959 $cleared_count = $this->p->util->cache->clear_ignored_urls();
960
961 $notice_msg = sprintf( __( '%s failed URL connections have been cleared.', 'wpsso' ), $cleared_count );
962
963 $this->p->notice->upd( $notice_msg, $user_id );
964
965 break;
966
967 case 'clear_db_transients':
968
969 $cleared_count = $this->p->util->cache->clear_db_transients( $key_prefix = '', $incl_shortened = true );
970
971 $notice_msg = sprintf( __( '%s database transients have been cleared.', 'wpsso' ), $cleared_count );
972
973 $this->p->notice->upd( $notice_msg, $user_id );
974
975 break;
976
977 case 'clear_db_transients_expired':
978
979 $cleared_count = $this->p->util->cache->clear_db_transients_expired();
980
981 $notice_msg = sprintf( __( '%s expired transients have been cleared.', 'wpsso' ), $cleared_count );
982
983 $this->p->notice->upd( $notice_msg, $user_id );
984
985 break;
986
987 case 'clear_db_transients_no_short':
988
989 $cleared_count = $this->p->util->cache->clear_db_transients( $key_prefix = '', $incl_shortened = false );
990
991 $notice_msg = sprintf( __( '%s database transients have been cleared (shortened URLs preserved).', 'wpsso' ),
992 $cleared_count );
993
994 $this->p->notice->upd( $notice_msg, $user_id );
995
996 break;
997
998 case 'clear_db_transients_shortened':
999
1000 $cleared_count = $this->p->util->cache->clear_db_transients( $key_prefix = 'wpsso_s_', $incl_shortened = true );
1001
1002 $notice_msg = sprintf( __( '%s shortened URL database transients have been cleared.', 'wpsso' ),
1003 $cleared_count );
1004
1005 $this->p->notice->upd( $notice_msg, $user_id );
1006
1007 break;
1008
1009 case 'clear_cron_jobs':
1010
1011 $count_cron_jobs = number_format_i18n( $this->p->util->count_cron_jobs() );
1012
1013 update_option( 'cron', '' );
1014
1015 $notice_msg = sprintf( __( '%s WordPress cron jobs have been cleared.', 'wpsso' ), $count_cron_jobs );
1016
1017 $this->p->notice->upd( $notice_msg, $user_id );
1018
1019 break;
1020
1021 case 'flush_rewrite_rules':
1022
1023 /*
1024 * Update .htaccess and the 'rewrite_rules' option.
1025 *
1026 * This is an expensive operation so it should only be used when necessary.
1027 */
1028 flush_rewrite_rules( $hard = true );
1029
1030 $notice_msg = __( 'The WordPress rewrite rules have been flushed.', 'wpsso' );
1031
1032 $this->p->notice->upd( $notice_msg, $user_id );
1033
1034 break;
1035
1036 case 'add_persons':
1037
1038 $this->p->user->schedule_add_person_role( $user_id );
1039
1040 break;
1041
1042 case 'remove_persons':
1043
1044 $this->p->user->schedule_remove_person_role( $user_id );
1045
1046 break;
1047
1048 case 'reset_user_metabox_layout':
1049
1050 WpssoUser::delete_metabox_prefs( $user_id );
1051
1052 $user_obj = get_userdata( $user_id );
1053
1054 $user_name = $user_obj->display_name;
1055
1056 $notice_msg = sprintf( __( 'Metabox layout preferences for user ID #%d "%s" have been reset.', 'wpsso' ),
1057 $user_id, $user_name );
1058
1059 $this->p->notice->upd( $notice_msg, $user_id );
1060
1061 break;
1062
1063 case 'reset_user_dismissed_notices':
1064
1065 $this->p->notice->reset_dismissed( $user_id );
1066
1067 $user_obj = get_userdata( $user_id );
1068
1069 $user_name = $user_obj->display_name;
1070
1071 $notice_msg = sprintf( __( 'Dismissed notices for user ID #%d "%s" have been reset.', 'wpsso' ),
1072 $user_id, $user_name );
1073
1074 $this->p->notice->upd( $notice_msg, $user_id );
1075
1076 break;
1077
1078 case 'change_show_options':
1079
1080 $_SERVER[ 'REQUEST_URI' ] = remove_query_arg( array( 'show-opts' ) );
1081
1082 $show_opts_key = isset( $_GET[ 'show-opts' ] ) ? SucomUtil::sanitize_key( $_GET[ 'show-opts' ] ) : null;
1083
1084 if ( isset( $this->p->cf[ 'form' ][ 'show_options' ][ $show_opts_key ] ) ) {
1085
1086 $show_name_transl = _x( $this->p->cf[ 'form' ][ 'show_options' ][ $show_opts_key ], 'option value', 'wpsso' );
1087
1088 WpssoUser::save_pref( array( 'show_opts' => $show_opts_key ) );
1089
1090 $notice_msg = '<strong>' . __( 'Option preference has been saved.', 'wpsso' ) . '</strong>';
1091
1092 $notice_msg .= sprintf( __( 'Now viewing "%s" by default.', 'wpsso' ), $show_name_transl );
1093
1094 $this->p->notice->upd( $notice_msg, $user_id );
1095 }
1096
1097 break;
1098
1099 case 'reload_default_image_sizes':
1100
1101 $defs = $this->p->opt->get_defaults();
1102
1103 $img_defs = SucomUtil::preg_grep_keys( '/_img_(width|height|crop|crop_x|crop_y)$/', $defs );
1104
1105 $this->p->options = SucomUtil::array_merge_recursive_distinct( $this->p->options, $img_defs );
1106
1107 $this->p->opt->save_options( WPSSO_OPTIONS_NAME, $this->p->options, $network = false );
1108
1109 $this->p->notice->upd( __( 'Image size settings have been reloaded with their default values and saved.', 'wpsso' ) );
1110
1111 break;
1112
1113 case 'export_plugin_settings_json':
1114
1115 $this->export_plugin_settings_json();
1116
1117 break;
1118
1119 case 'import_plugin_settings_json':
1120
1121 $this->import_plugin_settings_json();
1122
1123 break;
1124
1125 default:
1126
1127 if ( $this->p->debug->enabled ) {
1128
1129 $this->p->debug->log( 'doing action "wpsso_load_settings_page_' . $action_value . '"' );
1130 }
1131
1132 do_action( 'wpsso_load_settings_page_' . $action_value, $this->pagehook,
1133 $this->menu_id, $this->menu_name, $this->menu_lib );
1134
1135 break;
1136 }
1137 }
1138 }
1139
1140 $menu_ext = empty( $this->menu_ext ) ? $this->p->id : $this->menu_ext;
1141
1142 $this->get_form_object( $menu_ext ); // Return and maybe set/reset the WpssoAdmin->form value.
1143
1144 $this->add_settings_page_callbacks();
1145
1146 $this->add_settings_page_metaboxes();
1147
1148 $this->add_settings_page_footer();
1149 }
1150
1151 /*
1152 * Add actions and filters for this settings page.
1153 *
1154 * See WpssoAdmin->load_settings_page().
1155 */
1156 protected function add_settings_page_callbacks() {
1157 }
1158
1159 /*
1160 * Add metaboxes for this settings page.
1161 *
1162 * See WpssoAdmin->load_settings_page().
1163 */
1164 protected function add_settings_page_metaboxes( $callback_args = array() ) {
1165
1166 if ( $this->p->debug->enabled ) {
1167
1168 $this->p->debug->mark();
1169 }
1170
1171 /*
1172 * Defined in the child construct method of this settings page.
1173 */
1174 if ( empty( $this->menu_metaboxes ) ) {
1175
1176 return;
1177 }
1178
1179 foreach ( $this->menu_metaboxes as $metabox_id => $metabox_title ) {
1180
1181 $metabox_screen = $this->pagehook;
1182 $metabox_context = 'normal';
1183 $metabox_prio = 'default';
1184
1185 $callback_args[ 'page_id' ] = $this->menu_id;
1186 $callback_args[ 'metabox_id' ] = $metabox_id;
1187 $callback_args[ 'metabox_title' ] = $metabox_title;
1188 $callback_args[ 'network' ] = 'sitesubmenu' === $this->menu_lib ? true : false;
1189
1190 $method_name = method_exists( $this, 'show_metabox_' . $metabox_id ) ?
1191 'show_metabox_' . $metabox_id : 'show_metabox_table';
1192
1193 add_meta_box( $this->pagehook . '_' . $metabox_id, $metabox_title, array( $this, $method_name ),
1194 $metabox_screen, $metabox_context, $metabox_prio, $callback_args );
1195
1196 /*
1197 * Add a class to set a minimum width for the network postboxes.
1198 */
1199 if ( 'sitesubmenu' === $this->menu_lib ) {
1200
1201 add_filter( 'postbox_classes_' . $this->pagehook . '_' . $this->pagehook . '_' . $metabox_id,
1202 array( $this, 'add_class_postbox_network' ) );
1203 }
1204 }
1205 }
1206
1207 /*
1208 * Include add-on name and version in settings page footer.
1209 */
1210 protected function add_settings_page_footer() {
1211
1212 add_filter( 'admin_footer_text', array( $this, 'admin_footer_ext' ) );
1213
1214 add_filter( 'update_footer', array( $this, 'admin_footer_host' ) );
1215 }
1216
1217 protected function maybe_show_language_notice() {
1218
1219 $current_locale = SucomUtilWP::get_locale();
1220 $default_locale = SucomUtilWP::get_locale( 'default' );
1221
1222 if ( $current_locale && $default_locale && $current_locale !== $default_locale ) {
1223
1224 $notice_msg = sprintf( __( 'Your current language selection [%1$s] is different from the default site language [%2$s].', 'wpsso' ),
1225 $current_locale, $default_locale ) . ' ';
1226
1227 $notice_msg .= sprintf( __( 'Localized option values [%1$s] will be used for webpages and content in that language.', 'wpsso' ),
1228 $current_locale );
1229
1230 $notice_key = $this->menu_id . '-language-notice-' . $current_locale . '-' . $default_locale;
1231
1232 $this->p->notice->inf( $notice_msg, null, $notice_key, $dismiss_time = true );
1233 }
1234 }
1235
1236 public function show_settings_page() {
1237
1238 if ( ! $this->is_settings() ) { // Default check is for $this->menu_id.
1239
1240 settings_errors( WPSSO_OPTIONS_NAME );
1241 }
1242
1243 if ( ! $this->is_plugin_found() ) { // Just in case.
1244
1245 return;
1246 }
1247
1248 $side_col_boxes = $this->get_side_col_boxes();
1249 $dashicon_html = $this->get_menu_dashicon_html( $this->menu_id );
1250
1251 /*
1252 * Settings page wrapper.
1253 */
1254 echo '<div id="' . $this->pagehook . '" class="wrap">' . "\n";
1255
1256 /*
1257 * Settings page header.
1258 */
1259 echo '<div id="wpsso-setting-page-header">' . "\n";
1260 echo '<h1>' . $dashicon_html . ' '. $this->menu_name . '</h1>' . "\n";
1261 echo '</div><!-- #wpsso-setting-page-header -->' . "\n";
1262
1263 /*
1264 * Settings page content.
1265 */
1266 echo '<div id="wpsso-setting-page-content" class="' . ( empty( $side_col_boxes ) ? 'no' : 'has' ) . '-side-column">' . "\n";
1267
1268 /*
1269 * Metaboxes.
1270 */
1271 echo '<div id="poststuff" class="metabox-holder no-right-sidebar">' . "\n";
1272 echo '<div id="post-body" class="no-sidebar">' . "\n";
1273 echo '<div id="post-body-content" class="no-sidebar-content">' . "\n";
1274
1275 $this->show_post_body_settings_form();
1276
1277 echo '</div><!-- #post-body-content -->' . "\n";
1278 echo '</div><!-- #post-body -->' . "\n";
1279 echo '</div><!-- #poststuff -->' . "\n";
1280
1281 /*
1282 * Information boxes.
1283 */
1284 if ( ! empty( $side_col_boxes ) ) {
1285
1286 echo '<div id="side-column">' . "\n";
1287
1288 foreach ( $side_col_boxes as $box ) {
1289
1290 echo '<table class="sucom-settings wpsso side-box">' . "\n";
1291 echo '<tr><td>' . "\n";
1292 echo $box;
1293 echo '</td></tr>' . "\n";
1294 echo '</table><!-- .side-box -->' . "\n";
1295 }
1296
1297 echo '</div><!-- #side-column -->' . "\n";
1298 }
1299
1300 echo '</div><!-- #wpsso-setting-page-content -->' . "\n";
1301 echo '</div><!-- #' . $this->pagehook .' -->' . "\n";
1302
1303 /*
1304 * The type="text/javascript" attribute is unnecessary for JavaScript resources and creates warnings in the W3C validator.
1305 */
1306 ?><script>
1307
1308 jQuery( function(){
1309
1310 jQuery( '.if-js-closed' ).removeClass( 'if-js-closed' ).addClass( 'closed' ); // Postboxes that should be closed.
1311
1312 postboxes.add_postbox_toggles( '<?php echo $this->pagehook; ?>' ); // Postboxes setup.
1313 });
1314
1315 </script><?php
1316 }
1317
1318 public function profile_updated_redirect( $url, $status ) {
1319
1320 if ( false !== strpos( $url, 'updated=' ) && strpos( $url, 'wp_http_referer=' ) ) {
1321
1322 /*
1323 * Match WordPress behavior (users page for admins, profile page for everyone else).
1324 */
1325 $menu_lib = current_user_can( 'list_users' ) ? 'users' : 'profile';
1326 $parent_slug = $this->p->cf[ 'wp' ][ 'admin' ][ $menu_lib ][ 'page' ];
1327 $referer_match = '/' . $parent_slug . '?page=wpsso-';
1328
1329 /*
1330 * Parses string as if it were the query string passed via a URL and sets variables in the current
1331 * scope (or in the array if result is provided).
1332 *
1333 * See https://www.php.net/manual/en/function.parse-str.php.
1334 */
1335 parse_str( wp_parse_url( $url, PHP_URL_QUERY ), $parts );
1336
1337 if ( strpos( $parts[ 'wp_http_referer' ], $referer_match ) ) {
1338
1339 // translators: Please ignore - translation uses a different text domain.
1340 $this->p->notice->upd( __( 'Profile updated.' ) );
1341
1342 $url = add_query_arg( 'updated', true, $parts[ 'wp_http_referer' ] );
1343 }
1344 }
1345
1346 return $url;
1347 }
1348
1349 protected function show_post_body_settings_form() {
1350
1351 $menu_hookname = SucomUtil::sanitize_hookname( $this->menu_id );
1352 $form_css_id = 'wpsso_settings_form_' . $menu_hookname;
1353
1354 switch ( $this->menu_lib ) {
1355
1356 case 'profile':
1357
1358 $user_id = get_current_user_id();
1359 $user_obj = get_user_to_edit( $user_id );
1360 $admin_color = get_user_option( 'admin_color', $user_id ); // Note that $user_id is the second argument.
1361
1362 if ( empty( $admin_color ) ) {
1363
1364 $admin_color = 'fresh';
1365 }
1366
1367 /*
1368 * Match WordPress behavior (users page for admins, profile page for everyone else).
1369 */
1370 $referer_admin_url = current_user_can( 'list_users' ) ?
1371 $this->p->util->get_admin_url( $this->menu_id, null, 'users' ) :
1372 $this->p->util->get_admin_url( $this->menu_id, null, $this->menu_lib );
1373
1374 echo '<form name="wpsso" id="' . $form_css_id . '" action="user-edit.php" method="post">' . "\n";
1375 echo '<input type="hidden" name="wp_http_referer" value="' . $referer_admin_url . '" />' . "\n";
1376 echo '<input type="hidden" name="action" value="update" />' . "\n";
1377 echo '<input type="hidden" name="user_id" value="' . $user_id . '" />' . "\n";
1378 echo '<input type="hidden" name="nickname" value="' . $user_obj->nickname . '" />' . "\n";
1379 echo '<input type="hidden" name="email" value="' . $user_obj->user_email . '" />' . "\n";
1380 echo '<input type="hidden" name="admin_color" value="' . $admin_color . '" />' . "\n";
1381 echo '<input type="hidden" name="rich_editing" value="' . $user_obj->rich_editing . '" />' . "\n";
1382 echo '<input type="hidden" name="comment_shortcuts" value="' . $user_obj->comment_shortcuts . '" />' . "\n";
1383 echo '<input type="hidden" name="admin_bar_front" value="' . _get_admin_bar_pref( 'front', $user_id ) . '" />' . "\n";
1384
1385 wp_nonce_field( 'update-user_' . $user_id );
1386
1387 break;
1388
1389 case 'dashboard':
1390 case 'plugins':
1391 case 'settings':
1392 case 'submenu':
1393 case 'tools':
1394
1395 echo '<form name="wpsso" id="' . $form_css_id . '" action="options.php" method="post">' . "\n";
1396
1397 settings_fields( 'wpsso_settings' );
1398
1399 break;
1400
1401 case 'sitesubmenu':
1402
1403 echo '<form name="wpsso" id="' . $form_css_id . '" action="edit.php?action=' . WPSSO_SITE_OPTIONS_NAME . '" method="post">' . "\n";
1404 echo '<input type="hidden" name="page" value="' . $this->menu_id . '" />' . "\n";
1405
1406 break;
1407
1408 default:
1409
1410 return;
1411 }
1412
1413 echo "\n" . '<!-- wpsso nonce fields -->' . "\n";
1414
1415 wp_nonce_field( WpssoAdmin::get_nonce_action(), WPSSO_NONCE_NAME );
1416
1417 echo "\n";
1418
1419 wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false );
1420
1421 echo "\n";
1422
1423 wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false );
1424
1425 echo "\n";
1426
1427 do_meta_boxes( $this->pagehook, $context = 'normal', $object = null );
1428
1429 do_action( 'wpsso_form_content_metaboxes_' . $menu_hookname, $this->pagehook );
1430
1431 echo $this->get_form_buttons();
1432
1433 echo '</form>', "\n";
1434 }
1435
1436 protected function get_side_col_boxes() {
1437
1438 $col_boxes = array();
1439 $pkg_info = $this->p->util->get_pkg_info(); // Uses a local cache.
1440 $icon_px = 128;
1441
1442 foreach ( $this->p->cf[ 'plugin' ] as $ext => $info ) {
1443
1444 if ( empty( $info[ 'version' ] ) ) { // Exclude add-ons that are not active.
1445
1446 continue;
1447
1448 } elseif ( empty( $info[ 'url' ][ 'purchase' ] ) ) {
1449
1450 continue;
1451
1452 } elseif ( $pkg_info[ $ext ][ 'pp' ] ) {
1453
1454 continue;
1455 }
1456
1457 $box = '<div class="side-box-header">' . "\n";
1458 // translators: %s is the Premium add-on short name.
1459 $box .= '<h2>' . sprintf( __( 'Upgrade to %s', 'wpsso' ), $pkg_info[ $ext ][ 'short_pro' ] ) . '</h2>' . "\n";
1460 $box .= '</div><!-- .side-box-header -->' . "\n";
1461
1462 $box .= '<div class="side-box-icon">' . "\n";
1463 $box .= $this->get_ext_img_icon( $ext, $icon_px ) . "\n";
1464 $box .= '</div><!-- .side-box-icon -->' . "\n";
1465
1466 $box .= '<div class="side-box-content has-buttons">' . "\n";
1467 $box .= $this->p->msgs->get( 'column-purchase-' . $ext, $info ) . "\n";
1468 $box .= '</div><!-- .side-box-content -->' . "\n";
1469
1470 $box .= '<div class="side-box-buttons">' . "\n";
1471 $box .= $this->form->get_button( sprintf( _x( 'Get %s', 'submit button', 'wpsso' ), $pkg_info[ $ext ][ 'short_pro' ] ),
1472 'button-secondary', 'column-purchase', $info[ 'url' ][ 'purchase' ], true ) . "\n";
1473 $box .= '</div><!-- .side-box-buttons -->' . "\n";
1474
1475 $col_boxes[] = $box;
1476 }
1477
1478 return $col_boxes;
1479 }
1480
1481 /*
1482 * Called by WpssoAdmin->show_post_body_settings_form().
1483 * Called by WpssoSubmenuTools->show_post_body_settings_form().
1484 */
1485 protected function get_form_buttons() {
1486
1487 $this->add_form_buttons_submit( $form_button_rows );
1488 $this->add_form_buttons_change_show_options( $form_button_rows );
1489 $this->add_form_buttons( $form_button_rows );
1490
1491 $filter_name = 'wpsso_form_button_rows';
1492
1493 if ( $this->p->debug->enabled ) {
1494
1495 $this->p->debug->log( 'applying filters "' . $filter_name . '"' );
1496 }
1497
1498 $form_button_rows = apply_filters( $filter_name, $form_button_rows, $this->menu_id );
1499
1500 $buttons_html = '';
1501 $button_row_num = 1;
1502
1503 foreach ( $form_button_rows as $key => $buttons_row ) {
1504
1505 if ( $button_row_num >= 3 ) {
1506
1507 $css_class = 'button-secondary'; // Third+ row.
1508
1509 } elseif ( $button_row_num >= 2 ) {
1510
1511 $css_class = 'button-secondary button-alt'; // Second row.
1512
1513 } else $css_class = 'button-secondary button-highlight'; // First row.
1514
1515 $buttons_html .= '<div class="submit-buttons">';
1516
1517 foreach ( $buttons_row as $action_value => $mixed ) {
1518
1519 if ( empty( $action_value ) || empty( $mixed ) ) { // Just in case.
1520
1521 continue;
1522 }
1523
1524 if ( is_string( $mixed ) ) {
1525
1526 if ( 'submit' === $action_value ) {
1527
1528 $buttons_html .= $this->form->get_submit( $mixed, 'button-primary' );
1529
1530 } else {
1531
1532 $action_url = $this->p->util->get_admin_url( '?wpsso-action=' . $action_value );
1533 $button_url = wp_nonce_url( $action_url, WpssoAdmin::get_nonce_action(), WPSSO_NONCE_NAME );
1534 $buttons_html .= $this->form->get_button( $mixed, $css_class, '', $button_url );
1535 }
1536
1537 } elseif ( is_array( $mixed ) ) {
1538
1539 if ( ! empty( $mixed[ 'html' ] ) ) {
1540
1541 $buttons_html .= $mixed[ 'html' ];
1542 }
1543 }
1544 }
1545
1546 $buttons_html .= '</div>';
1547
1548 $button_row_num++;
1549 }
1550
1551 return $buttons_html;
1552 }
1553
1554 protected function add_form_buttons_submit( &$form_button_rows ) {
1555
1556 if ( $this->menu_lib === 'profile' ) {
1557
1558 $submit_label_transl = _x( 'Save Profile Settings', 'submit button', 'wpsso' );
1559
1560 } else $submit_label_transl = _x( 'Save Plugin Settings', 'submit button', 'wpsso' );
1561
1562 $form_button_rows = array(
1563 array(
1564 'submit' => $submit_label_transl,
1565 ),
1566 );
1567 }
1568
1569 protected function add_form_buttons_change_show_options( &$form_button_rows ) {
1570
1571 $change_show_next_key = SucomUtil::next_key( WpssoUser::show_opts(), $this->p->cf[ 'form' ][ 'show_options' ] );
1572 $change_show_name_transl = _x( $this->p->cf[ 'form' ][ 'show_options' ][ $change_show_next_key ], 'option value', 'wpsso' );
1573 $change_show_label_transl = sprintf( _x( 'Change to "%s" View', 'submit button', 'wpsso' ), $change_show_name_transl );
1574
1575 $form_button_rows[ 0 ][ 'change_show_options&show-opts=' . $change_show_next_key ] = $change_show_label_transl;
1576 }
1577
1578 protected function add_form_buttons( &$form_button_rows ) {
1579 }
1580
1581 /*
1582 * Callback method must be public for add_meta_box() hook.
1583 *
1584 * See WpssoAdmin->add_settings_page_metaboxes().
1585 */
1586 public function show_metabox_table( $obj, $mb ) {
1587
1588 $args = isset( $mb[ 'args' ] ) ? $mb[ 'args' ] : array();
1589 $page_id = isset( $args[ 'page_id' ] ) ? $args[ 'page_id' ] : '';
1590 $metabox_id = isset( $args[ 'metabox_id' ] ) ? $args[ 'metabox_id' ] : '';
1591 $filter_prefix = 'wpsso_mb_' . $page_id . '_' . $metabox_id;
1592 $filter_name = SucomUtil::sanitize_hookname( $filter_prefix . '_rows' );
1593 $table_rows = $this->get_table_rows( $page_id, $metabox_id, $tab_key = '', $args );
1594
1595 if ( $this->p->debug->enabled ) {
1596
1597 $this->p->debug->log( 'applying filters "' . $filter_name . '"' );
1598 }
1599
1600 $table_rows = apply_filters( $filter_name, $table_rows, $this->form, $args );
1601
1602 $this->p->util->metabox->do_table( $table_rows, 'metabox-' . $page_id . '-' . $metabox_id );
1603 }
1604
1605 /*
1606 * Since WPSSO Core v17.0.0.
1607 */
1608 protected function show_metabox_tabbed( $obj, $mb, array $tabs ) {
1609
1610 $args = isset( $mb[ 'args' ] ) ? $mb[ 'args' ] : array();
1611 $page_id = isset( $args[ 'page_id' ] ) ? $args[ 'page_id' ] : '';
1612 $metabox_id = isset( $args[ 'metabox_id' ] ) ? $args[ 'metabox_id' ] : '';
1613 $filter_prefix = 'wpsso_mb_' . $page_id . '_' . $metabox_id;
1614 $filter_name = SucomUtil::sanitize_hookname( $filter_prefix . '_tabs' );
1615
1616 if ( $this->p->debug->enabled ) {
1617
1618 $this->p->debug->log( 'applying filters "' . $filter_name . '"' );
1619 }
1620
1621 $tabs = apply_filters( $filter_name, $tabs );
1622
1623 if ( empty( $tabs ) ) {
1624
1625 return $this->show_metabox_table( $obj, $mb );
1626 }
1627
1628 $table_rows = array();
1629
1630 foreach ( $tabs as $tab_key => $tab_title ) {
1631
1632 $table_rows[ $tab_key ] = $this->get_table_rows( $page_id, $metabox_id, $tab_key, $args );
1633
1634 $filter_name = SucomUtil::sanitize_hookname( $filter_prefix . '_' . $tab_key . '_rows' );
1635
1636 if ( $this->p->debug->enabled ) {
1637
1638 $this->p->debug->log( 'applying filters "' . $filter_name . '"' );
1639 }
1640
1641 $table_rows[ $tab_key ] = apply_filters( $filter_name, $table_rows[ $tab_key ], $this->form, $args );
1642 }
1643
1644 $this->p->util->metabox->do_tabbed( $metabox_id, $tabs, $table_rows );
1645 }
1646
1647 /*
1648 * This method is extended by each submenu page with additional arguments:
1649 *
1650 * get_table_rows( $page_id, $metabox_id, $tab_key = '', $args = array() );
1651 */
1652 protected function get_table_rows( $page_id, $metabox_id ) {
1653
1654 return array();
1655 }
1656
1657 public function get_table_rows_cols( array $table_cells, $row_cols = 2 ) {
1658
1659 sort( $table_cells );
1660
1661 $per_col = ceil( count( $table_cells ) / $row_cols );
1662
1663 $table_rows = array();
1664
1665 foreach ( $table_cells as $num => $cell ) {
1666
1667 if ( empty( $table_rows[ $num % $per_col ] ) ) { // Initialize the array element.
1668
1669 $table_rows[ $num % $per_col ] = '';
1670 }
1671
1672 $table_rows[ $num % $per_col ] .= $cell; // Create the html for each row.
1673 }
1674
1675 return $table_rows;
1676 }
1677
1678 /*
1679 * Always call as WpssoAdmin::get_nonce_action() to have a reliable __METHOD__ value.
1680 */
1681 public static function get_nonce_action() {
1682
1683 $salt = __FILE__ . __LINE__ . __METHOD__;
1684
1685 return md5( $salt );
1686 }
1687
1688 private function is_plugin_found() {
1689
1690 if ( ! SucomPlugin::is_plugin_active( 'wpsso/wpsso.php' ) ) {
1691
1692 $pkg_info = $this->p->util->get_pkg_info(); // Uses a local cache.
1693 $notice_msg = sprintf( __( 'The %s plugin is not active and/or not found.', 'wpsso' ), $pkg_info[ 'wpsso' ][ 'name' ] );
1694 $notice_key = 'wpsso-not-found';
1695
1696 $this->p->notice->err( $notice_msg, $user_id = null, $notice_key );
1697
1698 return false;
1699 }
1700
1701 return true;
1702 }
1703
1704 private function is_settings( $menu_id = false ) {
1705
1706 return $this->is_lib( 'settings', $menu_id );
1707 }
1708
1709 private function is_lib( $lib_name, $menu_id = false ) {
1710
1711 if ( false === $menu_id ) {
1712
1713 $menu_id = $this->menu_id;
1714 }
1715
1716 return isset( $this->p->cf[ '*' ][ 'lib' ][ $lib_name ][ $menu_id ] ) ? true : false;
1717 }
1718
1719 public function add_admin_bar_menu_notices( $wp_admin_bar ) {
1720
1721 $menu_icon = '<span class="ab-icon" id="wpsso-toolbar-notices-icon"></span>';
1722 $menu_count = '<span class="ab-label" id="wpsso-toolbar-notices-count">0</span>';
1723 $menu_text = sprintf( __( 'Fetching %s notifications...', 'wpsso' ), $this->get_menu_title() );
1724
1725 $wp_admin_bar->add_node( array(
1726 'id' => 'wpsso-toolbar-notices',
1727 'title' => $menu_icon . $menu_count,
1728 'parent' => false,
1729 'href' => false,
1730 'group' => false,
1731 'meta' => array(),
1732 ) );
1733
1734 $wp_admin_bar->add_node( array(
1735 'id' => 'wpsso-toolbar-notices-container',
1736 'title' => $menu_text,
1737 'parent' => 'wpsso-toolbar-notices',
1738 'href' => false,
1739 'group' => false,
1740 'meta' => array(),
1741 ) );
1742 }
1743
1744 public function check_admin_bar_menu_notices( $wp_admin_bar ) {
1745
1746 if ( ! $wp_admin_bar->get_node( 'wpsso-toolbar-notices' ) ) {
1747
1748 $this->toolbar_notice_types = array();
1749 }
1750 }
1751
1752 /*
1753 * $toolbar_notice_types may be false if is_admin_bar_showing() is false.
1754 */
1755 public function filter_toolbar_notice_types( $toolbar_notice_types ) {
1756
1757 return is_array( $toolbar_notice_types ) ? $this->toolbar_notice_types : $toolbar_notice_types;
1758 }
1759
1760 public function admin_footer_ext( $footer_html ) {
1761
1762 if ( SucomUtil::get_const( 'WPSSO_ADMIN_FOOTER_DISABLE' ) ) {
1763
1764 return '';
1765 }
1766
1767 $pkg_info = $this->p->util->get_pkg_info(); // Uses a local cache.
1768 $footer_html = '<div class="admin-footer-ext">';
1769
1770 if ( isset( $pkg_info[ $this->menu_ext ][ 'name_pkg' ] ) ) {
1771
1772 $footer_html .= $pkg_info[ $this->menu_ext ][ 'name_pkg' ] . '<br/>';
1773 }
1774
1775 if ( isset( $pkg_info[ $this->menu_ext ][ 'gen' ] ) ) {
1776
1777 $footer_html .= $pkg_info[ $this->menu_ext ][ 'gen' ] . '<br/>';
1778 }
1779
1780 $footer_html .= '</div>';
1781
1782 return $footer_html;
1783 }
1784
1785 public function admin_footer_host( $footer_html ) {
1786
1787 if ( SucomUtil::get_const( 'WPSSO_ADMIN_FOOTER_DISABLE' ) ) {
1788
1789 return '';
1790 }
1791
1792 global $wp_version;
1793
1794 $home_url = SucomUtilWP::raw_get_home_url();
1795 $home_path = preg_replace( '/^[a-z]+:\/\//i', '', $home_url );
1796
1797 $footer_html = '<div class="admin-footer-host">';
1798 $footer_html .= $home_path . '<br/>';
1799 $footer_html .= 'WordPress ' . $wp_version . '<br/>';
1800 $footer_html .= 'PHP ' . phpversion() . '<br/>';
1801 $footer_html .= '</div>';
1802
1803 return $footer_html;
1804 }
1805
1806 /*
1807 * WordPress sorts the active plugins array before updating the 'active_plugins' option. The default PHP sort order
1808 * loads WPSSO add-ons before the WPSSO Core plugin. This filter re-sorts (if necessary) the active plugins array
1809 * to load WPSSO Core before its add-ons. This allows WPSSO Core to load the latest WpssoAbstractAddOn and
1810 * SucomAbstractAddon classes (for example) before any (possibly older) add-on does.
1811 *
1812 * When activating a plugin, the activate_plugin() function executes the following:
1813 *
1814 * $current = get_option( 'active_plugins', array() ); // Get current plugins array.
1815 * $current[] = $plugin; // Add the new plugin.
1816 * sort( $current ); // Sort the plugin array.
1817 * update_option( 'active_plugins', $current ); // Save the plugin array.
1818 *
1819 * See the activate_plugin() function in wordpress/wp-admin/includes/plugin.php for additional context.
1820 */
1821 public function pre_update_active_plugins( $current, $old_value, $option = 'active_plugins' ) {
1822
1823 usort( $current, array( __CLASS__, 'sort_active_plugins' ) );
1824
1825 return $current;
1826 }
1827
1828 /*
1829 * Since WPSSO Core v13.4.1.
1830 */
1831 public static function get_option_unit_comment( $opt_key ) {
1832
1833 if ( $unit_text = WpssoUtilUnits::get_mixed_label( $opt_key ) ) {
1834
1835 return ' ' . sprintf( _x( 'in %s', 'option comment', 'wpsso' ), $unit_text );
1836 }
1837
1838 return '';
1839 }
1840
1841 public static function get_option_site_use( $name, $form, $network = false, $is_enabled = false ) {
1842
1843 $html = '';
1844
1845 if ( $network ) {
1846
1847 $wpsso =& Wpsso::get_instance();
1848 $pkg_info = $wpsso->util->get_pkg_info(); // Uses a local cache.
1849 $is_enabled = $is_enabled || $pkg_info[ 'wpsso' ][ 'pp' ] ? true : false;
1850
1851 $html .= $form->get_th_html( _x( 'Site Use', 'option label (very short)', 'wpsso' ), 'site-use' );
1852 $html .= $is_enabled ? '<td class="site-use">' : '<td class="blank site-use">';
1853 $html .= $is_enabled ? $form->get_select( $name . ':use', WpssoConfig::$cf[ 'form' ][ 'site_option_use' ], $css_class = 'site-use' ) :
1854 $form->get_no_select( $name . ':use', WpssoConfig::$cf[ 'form' ][ 'site_option_use' ], $css_class = 'site-use' );
1855 $html .= '</td>';
1856 }
1857
1858 return $html;
1859 }
1860
1861 public function plugin_complete_actions( $actions ) {
1862
1863 if ( ! empty( $this->pageref_url ) && ! empty( $this->pageref_title ) ) {
1864
1865 foreach ( $actions as $action => &$html ) {
1866
1867 switch ( $action ) {
1868
1869 case 'plugins_page':
1870
1871 $html = '<a href="' . $this->pageref_url . '" target="_parent">' .
1872 sprintf( __( 'Return to %s', 'wpsso' ), $this->pageref_title ) . '</a>';
1873
1874 break;
1875
1876 default:
1877
1878 if ( preg_match( '/^(.*href=["\'])([^"\']+)(["\'].*)$/', $html, $matches ) ) {
1879
1880 $url = add_query_arg( array(
1881 'wpsso_pageref_url' => urlencode( $this->pageref_url ),
1882 'wpsso_pageref_title' => urlencode( $this->pageref_title ),
1883 ), $matches[ 2 ] );
1884
1885 $html = $matches[ 1 ] . $url . $matches[ 3 ];
1886 }
1887
1888 break;
1889 }
1890 }
1891 }
1892
1893 return $actions;
1894 }
1895
1896 public function plugin_complete_redirect( $url ) {
1897
1898 if ( strpos( $url, '?activate=true' ) ) {
1899
1900 if ( ! empty( $this->pageref_url ) ) {
1901
1902 // translators: Please ignore - translation uses a different text domain.
1903 $this->p->notice->upd( __( 'Plugin <strong>activated</strong>.' ) );
1904
1905 $url = $this->pageref_url;
1906 }
1907 }
1908
1909 return $url;
1910 }
1911
1912 public function get_check_for_updates_link( $get_notice = true ) {
1913
1914 $check_url = '';
1915 $notice_msg = '';
1916
1917 if ( class_exists( 'WpssoUm' ) ) {
1918
1919 $pkg_info = $this->p->util->get_pkg_info(); // Uses a local cache.
1920 $check_url = $this->p->util->get_admin_url( 'um-general?wpsso-action=check_for_updates' );
1921 $check_url = wp_nonce_url( $check_url, WpssoAdmin::get_nonce_action(), WPSSO_NONCE_NAME );
1922
1923 // translators: %1$s is the URL, %2$s is the short plugin name.
1924 $notice_transl = __( 'You may <a href="%1$s">refresh the update information for %2$s and its add-ons</a> to check if newer versions are available.', 'wpsso' );
1925
1926 $notice_msg = sprintf( $notice_transl, $check_url, $pkg_info[ 'wpsso' ][ 'short_pkg' ] );
1927
1928 } elseif ( empty( $_GET[ 'force-check' ] ) ) {
1929
1930 $check_url = self_admin_url( 'update-core.php?force-check=1' );
1931
1932 // translators: %1$s is the URL.
1933 $notice_transl = __( 'You may <a href="%1$s">refresh the update information for WordPress (plugins, themes and translations)</a> to check if newer versions are available.', 'wpsso' );
1934
1935 $notice_msg = sprintf( $notice_transl, $check_url );
1936 }
1937
1938 return $get_notice ? $notice_msg : $check_url;
1939 }
1940
1941 /*
1942 * Called from the network settings pages.
1943 *
1944 * Add a class to set a minimum width for the network postboxes.
1945 */
1946 public function add_class_postbox_network( $classes ) {
1947
1948 $classes[] = 'postbox-network';
1949
1950 return $classes;
1951 }
1952
1953 public function add_class_postbox_menu_id( $classes ) {
1954
1955 global $wp_current_filter;
1956
1957 $filter_name = end( $wp_current_filter );
1958 $postbox_name = preg_replace( '/^.*-(' . $this->menu_id . '_.*)$/u', '$1', $filter_name );
1959
1960 $classes[] = 'postbox-' . $this->menu_id;
1961 $classes[] = 'postbox-' . $postbox_name;
1962
1963 return $classes;
1964 }
1965
1966 public function export_plugin_settings_json() {
1967
1968 $date_slug = date( 'YmdHiT' );
1969 $home_slug = SucomUtil::sanitize_hookname( preg_replace( '/^.*\/\//', '', get_home_url() ) );
1970 $mime_type_gz = 'application/x-gzip';
1971 $file_name_gz = WpssoConfig::get_version( $add_slug = true ) . '-' . $home_slug . '-' . $date_slug . '.json.gz';
1972 $opts_encoded = wp_json_encode( $this->p->options );
1973 $gzdata = gzencode( $opts_encoded, 9, FORCE_GZIP );
1974 $filesize = strlen( $gzdata );
1975 $disposition = 'attachment';
1976 $chunksize = 1024 * 32; // 32kb per fread().
1977
1978 session_write_close();
1979
1980 ignore_user_abort();
1981
1982 set_time_limit( 300 ); // 5 minutes.
1983
1984 ini_set( 'zlib.output_compression', 0 );
1985
1986 if ( function_exists( 'apache_setenv' ) ) {
1987
1988 apache_setenv( 'no-gzip', 1 );
1989 }
1990
1991 ob_implicit_flush( true );
1992 ob_end_flush();
1993
1994 /*
1995 * Remove all dots, except last one, for MSIE clients.
1996 */
1997 if ( strstr( $_SERVER[ 'HTTP_USER_AGENT' ], 'MSIE' ) ) {
1998
1999 $file_name_gz = preg_replace( '/\./', '%2e', $file_name_gz, substr_count( $file_name_gz, '.' ) - 1 );
2000 }
2001
2002 if ( isset( $_SERVER[ 'HTTPS' ] ) ) {
2003
2004 $gmt_date = gmdate( 'D, d M Y H:i:s' ) . ' GMT';
2005
2006 header( 'Pragma: ' );
2007 header( 'Cache-Control: ' );
2008 header( 'Expires: ' . $gmt_date );
2009 header( 'Last-Modified: ' . $gmt_date );
2010 header( 'Cache-Control: no-store, no-cache, must-revalidate' );
2011 header( 'Cache-Control: post-check=0, pre-check=0', false );
2012
2013 } elseif ( $disposition == 'attachment' ) {
2014
2015 header( 'Cache-control: private' );
2016
2017 } else {
2018
2019 header( 'Cache-Control: no-cache, must-revalidate' );
2020 header( 'Pragma: no-cache' );
2021 }
2022
2023 header( 'Content-Type: application/' . $mime_type_gz );
2024 header( 'Content-Disposition: ' . $disposition . '; filename="' . $file_name_gz . '"' );
2025 header( 'Content-Transfer-Encoding: binary' );
2026 header( 'Content-Length: ' . $filesize );
2027
2028 echo $gzdata;
2029
2030 flush();
2031
2032 sleep( 1 );
2033
2034 exit();
2035 }
2036
2037 public function import_plugin_settings_json() {
2038
2039 $mime_type_gz = 'application/x-gzip';
2040 $dot_file_ext = '.json.gz';
2041 $max_file_size = 100000; // 100K.
2042
2043 if ( ! isset( $_FILES[ 'file' ][ 'error' ] ) ) {
2044
2045 if ( $this->p->debug->enabled ) {
2046
2047 $this->p->debug->log( 'incomplete post method upload' );
2048 }
2049
2050 return false;
2051
2052 } elseif ( $_FILES[ 'file' ][ 'error' ] === UPLOAD_ERR_NO_FILE ) {
2053
2054 $this->p->notice->err( sprintf( __( 'Please select a %1$s settings file to import.', 'wpsso' ), $dot_file_ext ) );
2055
2056 return false;
2057
2058 } elseif ( $_FILES[ 'file' ][ 'type' ] !== 'application/x-gzip' ) {
2059
2060 $this->p->notice->err( sprintf( __( 'The %1$s settings file to import must be an "%2$s" mime type.', 'wpsso' ),
2061 $dot_file_ext, $mime_type_gz ) );
2062
2063 return false;
2064
2065 } elseif ( $_FILES[ 'file' ][ 'size' ] > $max_file_size ) { // Just in case.
2066
2067 $this->p->notice->err( sprintf( __( 'The %1$s settings file is larger than the maximum of %2$d bytes allowed.', 'wpsso' ),
2068 $dot_file_ext, $max_file_size ) );
2069
2070 return false;
2071 }
2072
2073 $gzdata = file_get_contents( $_FILES[ 'file' ][ 'tmp_name' ] );
2074
2075 @unlink( $_FILES[ 'file' ][ 'tmp_name' ] );
2076
2077 $opts_encoded = gzdecode( $gzdata );
2078
2079 if ( empty( $opts_encoded ) ) { // false or empty array.
2080
2081 $this->p->notice->err( sprintf( __( 'The %1$s settings file is appears to be empty or corrupted.', 'wpsso' ), $dot_file_ext ) );
2082
2083 return false;
2084 }
2085
2086 $opts = json_decode( $opts_encoded, $assoc = true );
2087
2088 if ( empty( $opts ) || ! is_array( $opts ) ) { // false or empty array.
2089
2090 $this->p->notice->err( sprintf( __( 'The %1$s settings file could not be decoded into a settings array.', 'wpsso' ), $dot_file_ext ) );
2091
2092 return false;
2093 }
2094
2095 $this->p->options = $this->p->opt->check_options( WPSSO_OPTIONS_NAME, $opts );
2096
2097 $this->p->opt->save_options( WPSSO_OPTIONS_NAME, $opts, $network = false );
2098
2099 $this->p->notice->upd( __( 'Import of plugin and add-on settings is complete.', 'wpsso' ) );
2100
2101 return true;
2102 }
2103
2104 public function get_menu_title() {
2105
2106 $menu_title = _x( $this->p->cf[ 'menu' ][ 'title' ], 'menu title', 'wpsso' );
2107
2108 $filter_name = 'wpsso_menu_title';
2109
2110 if ( $this->p->debug->enabled ) {
2111
2112 $this->p->debug->log( 'applying filters "' . $filter_name . '"' );
2113 }
2114
2115 return apply_filters( 'wpsso_menu_title', $menu_title );
2116 }
2117
2118 public function get_menu_dashicon_html( $menu_id, $css_class = '' ) {
2119
2120 $dashicon = $this->get_menu_dashicon( $menu_id );
2121
2122 return '<div class="' . trim( $css_class . ' dashicons-before dashicons-' . $dashicon ) . '"></div>';
2123 }
2124
2125 public function get_menu_dashicon( $menu_id ) {
2126
2127 $dashicon = 'admin-generic';
2128
2129 if ( ! empty( $this->p->cf[ 'menu' ][ 'dashicons' ][ $menu_id ] ) ) {
2130
2131 $dashicon = $this->p->cf[ 'menu' ][ 'dashicons' ][ $menu_id ];
2132
2133 } elseif ( ! empty( $this->p->cf[ 'menu' ][ 'dashicons' ][ '*' ] ) ) {
2134
2135 $dashicon = $this->p->cf[ 'menu' ][ 'dashicons' ][ '*' ];
2136 }
2137
2138 return $dashicon;
2139 }
2140
2141 public function get_submenu_title( $info, $menu_lib, $menu_id ) {
2142
2143 $menu_title = '';
2144
2145 if ( ! empty( $info[ 'lib' ][ $menu_lib ][ $menu_id ] ) ) { // Just in case.
2146
2147 $menu_title = $info[ 'lib' ][ $menu_lib ][ $menu_id ];
2148 }
2149
2150 if ( ! empty( $info[ 'text_domain' ] ) ) {
2151
2152 $menu_title = _x( $menu_title, 'lib file description', $info[ 'text_domain' ] );
2153 }
2154
2155 /*
2156 * Example $filter_name = 'wpsso_menu_your_sso_title'
2157 */
2158 $filter_name = SucomUtil::sanitize_hookname( 'wpsso_menu_' . $menu_id . '_title' );
2159
2160 if ( $this->p->debug->enabled ) {
2161
2162 $this->p->debug->log( 'applying filters "' . $filter_name . '"' );
2163 }
2164
2165 return apply_filters( $filter_name, $menu_title );
2166 }
2167
2168 /*
2169 * $menu_lib = 'dashboard', 'plugins', 'profile', 'settings', 'submenu', 'sitesubmenu', 'tools', or 'users'
2170 */
2171 public function get_submenu_args( $menu_lib = 'submenu' ) {
2172
2173 if ( $this->p->debug->enabled ) {
2174
2175 $this->p->debug->mark();
2176 }
2177
2178 if ( is_admin() ) {
2179
2180 static $local_cache = array();
2181
2182 if ( isset( $local_cache[ $menu_lib ] ) ) {
2183
2184 return $local_cache[ $menu_lib ];
2185 }
2186
2187 } else $local_cache = array();
2188
2189 $local_cache[ $menu_lib ] = array();
2190
2191 $top_first_id = false;
2192 $top_last_id = false;
2193 $ext_first_id = false;
2194 $ext_last_id = false;
2195
2196 $sorted_menu = array();
2197 $unsorted_menu = array();
2198
2199 if ( 'profile' === $menu_lib && current_user_can( 'list_users' ) ) { // Match WordPress behavior.
2200
2201 $parent_slug = $this->p->cf[ 'wp' ][ 'admin' ][ 'users' ][ 'page' ];
2202
2203 } elseif ( 'submenu' === $menu_lib || 'sitesubmenu' === $menu_lib ) { // Plugin submenu.
2204
2205 $parent_slug = 'wpsso-' . $this->menu_id;
2206
2207 } else $parent_slug = $this->p->cf[ 'wp' ][ 'admin' ][ $menu_lib ][ 'page' ];
2208
2209 foreach ( $this->p->cf[ 'plugin' ] as $ext => $info ) {
2210
2211 if ( ! isset( $info[ 'lib' ][ $menu_lib ] ) ) { // Not all add-ons have submenus.
2212
2213 continue;
2214 }
2215
2216 foreach ( $info[ 'lib' ][ $menu_lib ] as $menu_id => $menu_name ) {
2217
2218 $menu_title = $this->get_submenu_title( $info, $menu_lib, $menu_id );
2219
2220 /*
2221 * Leave WPSSO Core submenu items in their original order.
2222 */
2223 if ( 'wpsso' === $ext ) {
2224
2225 $unsorted_menu[] = array( $parent_slug, $menu_id, $menu_title, $menu_lib, $ext );
2226
2227 if ( false === $top_first_id ) $top_first_id = $menu_id;
2228
2229 $top_last_id = $menu_id;
2230
2231 /*
2232 * Sort add-on submenu items.
2233 */
2234 } else {
2235
2236 $sorted_menu[ $menu_title . '-' . $menu_id ] = array( $parent_slug, $menu_id, $menu_title, $menu_lib, $ext );
2237
2238 if ( false === $ext_first_id ) $ext_first_id = $menu_id;
2239
2240 $ext_last_id = $menu_id;
2241 }
2242 }
2243 }
2244
2245 SucomUtil::natksort( $sorted_menu );
2246
2247 foreach ( array_merge( $unsorted_menu, $sorted_menu ) as $sort_key => $args ) {
2248
2249 $menu_id = $args[ 1 ];
2250
2251 if ( 'submenu' === $menu_lib || 'sitesubmenu' === $menu_lib ) {
2252
2253 if ( $menu_id === $top_first_id ) {
2254
2255 $args[ 5 ] = 'top-first-submenu-page';
2256
2257 } elseif ( $menu_id === $top_last_id ) {
2258
2259 $args[ 5 ] = 'top-last-submenu-page' . ( empty( $ext_first_id ) ? ' no-add-ons' : ' with-add-ons' );
2260
2261 } elseif ( $menu_id === $ext_first_id ) {
2262
2263 $args[ 5 ] = 'ext-first-submenu-page';
2264
2265 } elseif ( $menu_id === $ext_last_id ) {
2266
2267 $args[ 5 ] = 'ext-last-submenu-page';
2268 }
2269 }
2270
2271 $local_cache[ $menu_lib ][ $menu_id ] = $args;
2272 }
2273
2274 return $local_cache[ $menu_lib ];
2275 }
2276
2277 /*
2278 * Get the plugin readme and convert array elements to a plugin data object.
2279 */
2280 public function get_plugin_data( $ext, $read_cache = true ) {
2281
2282 $data = new StdClass;
2283
2284 $info = $this->p->cf[ 'plugin' ][ $ext ];
2285
2286 $readme = $this->get_readme_info( $ext, $read_cache );
2287
2288 if ( empty( $readme ) ) { // Make sure we got something back.
2289
2290 return array();
2291 }
2292
2293 foreach ( array(
2294
2295 /*
2296 * Readme array => Plugin object.
2297 */
2298 'plugin_name' => 'name',
2299 'plugin_slug' => 'slug',
2300 'base' => 'plugin',
2301 'stable_tag' => 'version',
2302 'tested_up_to' => 'tested',
2303 'requires_at_least' => 'requires',
2304 'home' => 'homepage',
2305 'download' => 'download_link',
2306 'author' => 'author',
2307 'upgrade_notice' => 'upgrade_notice',
2308 'last_updated' => 'last_updated',
2309 'sections' => 'sections',
2310 'remaining_content' => 'other_notes', // Added to sections.
2311 'banners' => 'banners',
2312 'icons' => 'icons',
2313 ) as $readme_key => $prop_name ) {
2314
2315 switch ( $readme_key ) {
2316
2317 case 'base': // From plugin config.
2318
2319 if ( ! empty( $info[ $readme_key ] ) ) {
2320
2321 $data->$prop_name = $info[ $readme_key ];
2322 }
2323
2324 break;
2325
2326 case 'home': // From plugin config.
2327
2328 if ( ! empty( $info[ 'url' ][ 'purchase' ] ) ) { // Check for purchase url first.
2329
2330 $data->$prop_name = $info[ 'url' ][ 'purchase' ];
2331
2332 } elseif ( ! empty( $info[ 'url' ][ $readme_key ] ) ) {
2333
2334 $data->$prop_name = $info[ 'url' ][ $readme_key ];
2335 }
2336
2337 break;
2338
2339 case 'download': // From plugin config.
2340
2341 if ( ! empty( $info[ 'url' ][ $readme_key ] ) ) {
2342
2343 $data->$prop_name = $info[ 'url' ][ $readme_key ];
2344 }
2345
2346 break;
2347
2348 case 'banners': // From plugin config.
2349 case 'icons': // From plugin config.
2350
2351 if ( ! empty( $info[ 'assets' ][ $readme_key ] ) ) {
2352
2353 $data->$prop_name = $info[ 'assets' ][ $readme_key ]; // Array with 1x / 2x images.
2354 }
2355
2356 break;
2357
2358 case 'remaining_content':
2359
2360 if ( ! empty( $readme[ $readme_key ] ) ) {
2361
2362 $data->sections[ $prop_name ] = $readme[ $readme_key ];
2363 }
2364
2365 break;
2366
2367 default:
2368
2369 if ( ! empty( $readme[ $readme_key ] ) ) {
2370
2371 $data->$prop_name = $readme[ $readme_key ];
2372 }
2373
2374 break;
2375 }
2376 }
2377
2378 return $data;
2379 }
2380
2381 /*
2382 * Return an associative array with plugin readme info.
2383 *
2384 * See WpssoAdmin->get_plugin_data().
2385 * See WpssoAdminDashboard->show_metabox_version_info().
2386 * See WpssoUmActions->action_load_settings_page_check_for_updates().
2387 */
2388 public function get_readme_info( $ext, $read_cache = true ) {
2389
2390 if ( $this->p->debug->enabled ) {
2391
2392 $this->p->debug->log_args( array(
2393 'ext' => $ext,
2394 'read_cache' => $read_cache,
2395 ) );
2396 }
2397
2398 $rel_file = 'readme.txt';
2399 $file_content = '';
2400 $cache_md5_pre = 'wpsso_r_'; // 'wpsso_cache_expire_readme_info' filter.
2401 $cache_salt = __METHOD__ . '(ext:' . $ext . ')';
2402 $cache_id = $cache_md5_pre . md5( $cache_salt );
2403 $cache_exp_secs = $this->p->util->get_cache_exp_secs( $cache_md5_pre, $cache_type = 'transient' );
2404 $readme_info = array();
2405
2406 /*
2407 * Maybe get parsed readme from transient cache.
2408 */
2409 if ( $cache_exp_secs > 0 && $read_cache ) {
2410
2411 $readme_info = get_transient( $cache_id );
2412
2413 if ( ! empty( $readme_info ) && is_array( $readme_info ) ) {
2414
2415 if ( $this->p->debug->enabled ) {
2416
2417 $this->p->debug->log( 'readme_info retrieved from transient' );
2418 }
2419
2420 return $readme_info; // Stop here.
2421 }
2422
2423 } else delete_transient( $cache_id ); // Just in case.
2424
2425 $file_content = $this->get_ext_file_content( $ext, $rel_file, $read_cache );
2426
2427 if ( ! empty( $file_content ) ) {
2428
2429 if ( ! class_exists( 'SuextParseReadme' ) ) {
2430
2431 require_once WPSSO_PLUGINDIR . 'lib/ext/parse-readme.php';
2432 }
2433
2434 $readme_parser =& SuextParseReadme::get_instance();
2435
2436 $readme_info = $readme_parser->parse_content( $file_content );
2437 }
2438
2439 /*
2440 * Save the parsed readme to transient cache.
2441 */
2442 if ( $cache_exp_secs > 0 ) {
2443
2444 set_transient( $cache_id, $readme_info, $cache_exp_secs );
2445
2446 if ( $this->p->debug->enabled ) {
2447
2448 $this->p->debug->log( 'readme_info saved to transient cache for ' . $cache_exp_secs . ' seconds' );
2449 }
2450 }
2451
2452 return is_array( $readme_info ) ? $readme_info : array(); // Just in case.
2453 }
2454
2455 /*
2456 * See WpssoSubmenuSetup->show_metabox_setup_guide().
2457 */
2458 public function get_ext_file_content( $ext, $rel_file, $read_cache = true ) {
2459
2460 if ( $this->p->debug->enabled ) {
2461
2462 $this->p->debug->log_args( array(
2463 'ext' => $ext,
2464 'rel_file' => $rel_file,
2465 ) );
2466 }
2467
2468 $file_path = WpssoConfig::get_ext_file_path( $ext, $rel_file );
2469 $file_url = WpssoConfig::get_ext_file_url( $ext, $rel_file ); // Returns sanitized URL or false.
2470 $file_content = '';
2471 $text_domain = WpssoConfig::get_ext_text_domain( $ext );
2472 $cache_md5_pre = 'wpsso_c_'; // 'wpsso_cache_expire_file_content' filter.
2473 $cache_exp_secs = $this->p->util->get_cache_exp_secs( $cache_md5_pre, $cache_type = 'file' );
2474
2475 if ( $file_url ) { // Sanitized URL or false.
2476
2477 if ( $cache_exp_secs > 0 ) {
2478
2479 if ( ! $read_cache ) $this->p->cache->clear( $file_url );
2480
2481 $file_content = $this->p->cache->get( $file_url, $format = 'raw', $cache_type = 'file', $cache_exp_secs );
2482
2483 } else $this->p->cache->clear( $file_url );
2484 }
2485
2486 if ( empty( $file_content ) ) { // No content from the file URL.
2487
2488 if ( $file_path && file_exists( $file_path ) && $fh = @fopen( $file_path, 'rb' ) ) {
2489
2490 $file_content = fread( $fh, filesize( $file_path ) );
2491
2492 fclose( $fh );
2493 }
2494 }
2495
2496 if ( $text_domain ) { // False or text domain string.
2497
2498 if ( ! class_exists( 'SucomGetText' ) ) {
2499
2500 require_once WPSSO_PLUGINDIR . 'lib/com/gettext.php';
2501 }
2502
2503 $file_content = SucomGetText::get_html_transl( $file_content, $text_domain );
2504 }
2505
2506 return $file_content;
2507 }
2508
2509 /*
2510 * Plugin links for the addons and licenses settings page.
2511 */
2512 public function get_ext_action_links( $ext, $info, &$tabindex = false ) {
2513
2514 if ( $this->p->debug->enabled ) {
2515
2516 $this->p->debug->mark();
2517 }
2518
2519 $pkg_info = $this->p->util->get_pkg_info(); // Uses a local cache.
2520 $action_links = array();
2521
2522 if ( ! empty( $info[ 'base' ] ) ) {
2523
2524 $install_url = is_multisite() ?
2525 network_admin_url( 'plugin-install.php', null ) :
2526 get_admin_url( $blog_id = null, 'plugin-install.php' );
2527
2528 $details_url = add_query_arg( array(
2529 'plugin' => $info[ 'slug' ],
2530 'tab' => 'plugin-information',
2531 'TB_iframe' => 'true',
2532 'width' => $this->p->cf[ 'wp' ][ 'tb_iframe' ][ 'width' ],
2533 'height' => $this->p->cf[ 'wp' ][ 'tb_iframe' ][ 'height' ],
2534 ), $install_url );
2535
2536 if ( SucomPlugin::is_plugin_installed( $info[ 'base' ] ) ) { // Uses a local cache.
2537
2538 if ( $this->p->debug->enabled ) {
2539
2540 $this->p->debug->log( $info[ 'base' ] . ' is installed' );
2541 }
2542
2543 if ( SucomPlugin::have_plugin_update( $info[ 'base' ] ) ) { // Uses a local cache.
2544
2545 if ( $this->p->debug->enabled ) {
2546
2547 $this->p->debug->log( $info[ 'base' ] . ' has an update' );
2548 }
2549
2550 $action_links[] = '<a href="' . $details_url . '" class="thickbox" tabindex="' . ++$tabindex . '">' .
2551 '<font color="red">' . ( 'wpsso' === $ext ? _x( 'Plugin Details and Update',
2552 'plugin action link', 'wpsso' ) : _x( 'Add-on Details and Update',
2553 'plugin action link', 'wpsso' ) ) . '</font></a>';
2554 } else {
2555
2556 $action_links[] = '<a href="' . $details_url . '" class="thickbox" tabindex="' . ++$tabindex . '">' .
2557 ( 'wpsso' === $ext ? _x( 'Plugin Details', 'plugin action link', 'wpsso' ) :
2558 _x( 'Add-on Details', 'plugin action link', 'wpsso' ) ) . '</a>';
2559 }
2560
2561 } else {
2562
2563 if ( $this->p->debug->enabled ) {
2564
2565 $this->p->debug->log( $info[ 'base' ] . ' is not installed' );
2566 }
2567
2568 $action_links[] = '<a href="' . $details_url . '" class="thickbox" tabindex="' . ++$tabindex . '">' .
2569 ( 'wpsso' === $ext ? _x( 'Plugin Details and Install', 'plugin action link', 'wpsso' ) :
2570 _x( 'Add-on Details and Install', 'plugin action link', 'wpsso' ) ) . '</a>';
2571 }
2572
2573 } elseif ( ! empty( $info[ 'url' ][ 'home' ] ) ) {
2574
2575 $action_links[] = '<a href="' . $info[ 'url' ][ 'home' ] . '" tabindex="' . ++$tabindex . '">' .
2576 _x( 'About Page', 'plugin action link', 'wpsso' ) . '</a>';
2577 }
2578
2579 if ( ! empty( $info[ 'url' ][ 'docs' ] ) ) {
2580
2581 $action_links[] = '<a href="' . $info[ 'url' ][ 'docs' ] . '"' .
2582 ( false !== $tabindex ? ' tabindex="' . ++$tabindex . '"' : '' ) . '>' .
2583 _x( 'Documentation', 'plugin action link', 'wpsso' ) . '</a>';
2584 }
2585
2586 if ( ! empty( $info[ 'url' ][ 'support' ] ) && ! empty( $pkg_info[ $ext ][ 'pp' ] ) ) {
2587
2588 $action_links[] = '<a href="' . $info[ 'url' ][ 'support' ] . '"' .
2589 ( false !== $tabindex ? ' tabindex="' . ++$tabindex . '"' : '' ) . '>' .
2590 sprintf( _x( '%s Support', 'plugin action link', 'wpsso' ),
2591 _x( $this->p->cf[ 'packages' ][ 'pro' ], 'package name', 'wpsso' ) ) . '</a>';
2592
2593 } elseif ( ! empty( $info[ 'url' ][ 'forum' ] ) ) {
2594
2595 $action_links[] = '<a href="' . $info[ 'url' ][ 'forum' ] . '"' .
2596 ( false !== $tabindex ? ' tabindex="' . ++$tabindex . '"' : '' ) . '>' .
2597 _x( 'Community Forum', 'plugin action link', 'wpsso' ) . '</a>';
2598 }
2599
2600 if ( ! empty( $info[ 'url' ][ 'purchase' ] ) ) {
2601
2602 $action_links[] = $this->p->msgs->get( 'pro-purchase-link', array(
2603 'ext' => $ext,
2604 'url' => $info[ 'url' ][ 'purchase' ],
2605 'tabindex' => false !== $tabindex ? ++$tabindex : false,
2606 ) );
2607 }
2608
2609 return $action_links;
2610 }
2611
2612 /*
2613 * Returns a 128x128px image by default.
2614 */
2615 public function get_ext_img_icon( $ext, $icon_px = 128 ) {
2616
2617 /*
2618 * The default image is a transparent 1px gif.
2619 */
2620 $img_src = 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==';
2621 $img_srcset = '';
2622
2623 if ( ! empty( $this->p->cf[ 'plugin' ][ $ext ][ 'assets' ][ 'icons' ] ) ) {
2624
2625 $icons = $this->p->cf[ 'plugin' ][ $ext ][ 'assets' ][ 'icons' ];
2626
2627 if ( ! empty( $icons[ '1x' ] ) ) {
2628
2629 $img_src = $icons[ '1x' ];
2630 $img_srcset .= $icons[ '1x' ] . ' 128w, ';
2631 }
2632
2633 if ( ! empty( $icons[ '2x' ] ) ) {
2634
2635 $img_srcset .= $icons[ '2x' ] . ' 256w, ';
2636 }
2637 }
2638
2639 return '<img src="' . $img_src . '" srcset="' . trim( $img_srcset, $chars = ', ' ) . '" '
2640 . 'width="' . $icon_px . '" height="' . $icon_px . '" style="width:' . $icon_px . 'px; height:' . $icon_px . 'px;"/>';
2641 }
2642
2643 /*
2644 * Sort the WPSSO Core plugin slug before the WPSSO add-on slugs.
2645 */
2646 private static function sort_active_plugins( $a, $b ) {
2647
2648 $wpsso_any = 'wpsso';
2649 $wpsso_core = 'wpsso/';
2650 $wpsso_addon = 'wpsso-';
2651 $jsm_plugin = 'jsm-';
2652
2653 if ( 0 === strpos( $a, $wpsso_any ) || 0 === strpos( $b, $wpsso_any ) ) {
2654
2655 /*
2656 * Sort WPSSO Core plugin before WPSSO add-on.
2657 */
2658 if ( 0 === strpos( $a, $wpsso_core ) && 0 === strpos( $b, $wpsso_addon ) ) return -1;
2659
2660 /*
2661 * Sort WPSSO add-on after the WPSSO Core plugin.
2662 */
2663 if ( 0 === strpos( $a, $wpsso_addon ) && 0 === strpos( $b, $wpsso_core ) ) return 1;
2664
2665 /*
2666 * Sort WPSSO plugins before JSM plugins.
2667 */
2668 if ( 0 === strpos( $a, $wpsso_any ) && 0 === strpos( $b, $jsm_plugin ) ) return -1;
2669
2670 /*
2671 * Sort JSM plugins after WPSSO plugins.
2672 */
2673 if ( 0 === strpos( $a, $jsm_plugin ) && 0 === strpos( $b, $wpsso_any ) ) return 1;
2674
2675 return strcmp( $a, $b );
2676 }
2677
2678 return 0; // No change.
2679 }
2680 }
2681 }
2682