set_objects() when is_admin() is true. */ public function __construct( &$plugin ) { $this->p =& $plugin; if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } $doing_ajax = SucomUtilWP::doing_ajax(); require_once WPSSO_PLUGINDIR . 'lib/admin-head.php'; new WpssoAdminHead( $plugin ); require_once WPSSO_PLUGINDIR . 'lib/admin-dashboard.php'; new WpssoAdminDashboard( $plugin ); /* * The WpssoScript add_iframe_inline_script() method includes jQuery in the thickbox iframe to add the * iframe_parent arguments when the Install or Update button is clicked. * * These class properties are used by both the WpssoAdmin plugin_complete_actions() and * plugin_complete_redirect() methods to direct the user back to the thickbox iframe parent after plugin * installation / activation / update. */ if ( ! empty( $_GET[ 'wpsso_pageref_title' ] ) ) { $this->pageref_title = esc_html( urldecode( $_GET[ 'wpsso_pageref_title' ] ) ); } if ( ! empty( $_GET[ 'wpsso_pageref_url' ] ) ) { $this->pageref_url = esc_url_raw( urldecode( $_GET[ 'wpsso_pageref_url' ] ) ); } add_action( 'activated_plugin', array( $this, 'activated_plugin' ), 10, 2 ); add_action( 'after_switch_theme', array( $this, 'after_switch_theme' ), 10, 2 ); add_action( 'upgrader_process_complete', array( $this, 'upgrader_process_complete' ), 10, 2 ); /* * Refresh the cache when the WordPress "home" or "blog_public" options are changed. * * See WpssAdmin->wp_site_option_changed(). * See SucomUpdate->wp_site_option_changed(). * See SucomUtilWP->raw_do_option(). * See SucomUpdateUtilWP->raw_do_option(). */ add_action( 'update_option_home', array( $this, 'wp_site_option_changed' ), PHP_INT_MAX, 3 ); add_action( 'update_option_blog_public', array( $this, 'wp_site_option_changed' ), PHP_INT_MAX, 3 ); add_action( 'sucom_update_option_home', array( $this, 'wp_site_option_changed' ), PHP_INT_MAX, 3 ); /* * This filter re-sorts (if necessary) the active plugins array to load WPSSO Core before its add-ons. */ add_filter( 'pre_update_option_active_plugins', array( $this, 'pre_update_active_plugins' ), PHP_INT_MAX, 3 ); add_filter( 'pre_update_option_active_sitewide_plugins', array( $this, 'pre_update_active_plugins' ), PHP_INT_MAX, 3 ); /* * Define and disable the "Expect: 100-continue" header. */ add_filter( 'http_request_args', array( $this, 'add_expect_header' ), 1000, 2 ); add_filter( 'http_request_host_is_external', array( $this, 'allow_safe_hosts' ), 1000, 3 ); /* * Provides plugin data / information from the readme.txt for additional add-ons. Don't hook the * 'plugins_api_result' filter if the update manager is active as it provides more complete plugin data * than what's available from the readme.txt. */ if ( empty( $this->p->avail[ 'p_ext' ][ 'um' ] ) ) { add_filter( 'plugins_api_result', array( $this, 'external_plugin_data' ), 1000, 3 ); } /* * Add plugin / add-on settings links to the WordPress Plugins page. Hook this filter even when doing ajax * since the WordPress plugin search results are created using an ajax call. * * The 5th argument is $menu_lib = 'submenu', so always limit the method arguments to 4. */ add_filter( 'plugin_action_links', array( $this, 'add_plugin_action_links' ), 1000, 4 ); if ( is_multisite() ) { /* * The 5th argument is $menu_lib = 'sitesubmenu', so always limit the method arguments to 4. */ add_filter( 'network_admin_plugin_action_links', array( $this, 'add_site_plugin_action_links' ), 1000, 4 ); } if ( ! $doing_ajax ) { add_action( 'wp_dashboard_setup', array( $this, 'dashboard_setup' ) ); /* * The admin_menu action is run before admin_init. */ add_action( 'admin_menu', array( $this, 'load_menu_objects' ), -1000 ); add_action( 'admin_menu', array( $this, 'add_admin_menus' ), WPSSO_ADD_MENU_PRIORITY, 0 ); add_action( 'admin_menu', array( $this, 'add_admin_submenus' ), WPSSO_ADD_SUBMENU_PRIORITY, 0 ); add_action( 'admin_init', array( $this, 'init_check_options' ), -1000 ); add_action( 'admin_init', array( $this, 'add_plugins_page_upgrade_notice' ) ); add_action( 'admin_init', array( $this, 'register_setting' ) ); if ( is_multisite() ) { add_action( 'network_admin_menu', array( $this, 'load_network_menu_objects' ), -1000 ); add_action( 'network_admin_menu', array( $this, 'add_network_admin_menus' ), WPSSO_ADD_MENU_PRIORITY, 0 ); add_action( 'network_admin_edit_' . WPSSO_SITE_OPTIONS_NAME, array( $this, 'save_site_settings' ) ); } add_filter( 'install_plugin_complete_actions', array( $this, 'plugin_complete_actions' ), 1000, 1 ); add_filter( 'update_plugin_complete_actions', array( $this, 'plugin_complete_actions' ), 1000, 1 ); add_filter( 'wp_redirect', array( $this, 'plugin_complete_redirect' ), 1000, 1 ); add_filter( 'wp_redirect', array( $this, 'profile_updated_redirect' ), -100, 2 ); add_action( 'admin_bar_menu', array( $this, 'add_admin_bar_menu_notices' ), WPSSO_TB_NOTICE_MENU_ORDER ); add_action( 'admin_bar_menu', array( $this, 'check_admin_bar_menu_notices' ), PHP_INT_MAX ); add_filter( 'sucom_toolbar_notice_types', array( $this, 'filter_toolbar_notice_types' ) ); } } /* * Since WPSSO Core v9.3.0. * * This action is run by WordPress after any plugin is activated. * * If a plugin is silently activated (such as during an update), this action does not run. */ public function activated_plugin( $plugin_base, $network_activation ) { $this->p->reg->reset_admin_checks(); /* * Since WPSSO Core v15.11.0. * * If the array of supported plugins has changed, then refresh the cache. */ $old_avail = $this->p->avail; $new_avail = $this->p->check->get_avail(); foreach ( array( 'p', 'p_ext' ) as $key ) { // Remove plugin and add-on information. unset( $old_avail[ $key ], $new_avail[ $key ] ); } if ( $old_avail !== $new_avail ) { $user_id = get_current_user_id(); WpssoRegister::do_multisite( $network_activation, array( $this->p->util->cache, 'schedule_refresh' ), $args = array( $user_id ) ); } } /* * Since WPSSO Core v9.3.0. * * This action is run by WordPress multiple times and the parameters differ according to the context (ie. if the * old theme exists or not). If the old theme is missing, the parameter will be the slug of the old theme. */ public function after_switch_theme( $old_name, $old_theme ) { $this->p->reg->reset_admin_checks(); } /* * Since WPSSO Core v9.3.0. * * This action is run by WordPress when the upgrader process is complete. */ public function upgrader_process_complete( $wp_upgrader_obj, $hook_extra ) { $this->p->reg->reset_admin_checks(); } /* * Refresh the cache when the WordPress "home" or "blog_public" options are changed. * * See WpssAdmin->wp_site_option_changed(). * See SucomUpdate->wp_site_option_changed(). * See SucomUtilWP->raw_do_option(). * See SucomUpdateUtilWP->raw_do_option(). */ public function wp_site_option_changed( $old_value, $new_value, $option_name ) { if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } static $do_once = null; if ( $do_once ) return; // Stop here. $do_once = true; /* * Standardize old and new values for string comparison. */ $old_value = strtolower( untrailingslashit( maybe_serialize( $old_value ) ) ); $new_value = strtolower( untrailingslashit( maybe_serialize( $new_value ) ) ); if ( $old_value !== $new_value ) { if ( $user_id = get_current_user_id() ) { if ( 'home' === $option_name ) { $notice_msg = sprintf( __( 'The Site Address URL value has been changed from %1$s to %2$s.', 'wpsso' ), $old_value, $new_value ); $notice_key = __FUNCTION__ . '_' . $option_name . '_' . $old_value . '_' . $new_value; $this->p->notice->upd( $notice_msg, $user_id, $notice_key ); } } $this->p->util->cache->schedule_refresh(); } } public function dashboard_setup() { $classnames = $this->p->get_lib_classnames( 'dashboard' ); // Always returns an array. foreach ( $classnames as $id => $classname ) { new $classname( $this->p ); } } public function load_network_menu_objects() { if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } $this->load_menu_objects( array( 'submenu', 'sitesubmenu' ) ); } public function load_menu_objects( $menu_libs = array() ) { if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } if ( empty( $menu_libs ) ) { $menu_libs = array( 'dashboard', 'plugins', 'profile', 'settings', 'submenu', 'tools', 'users' ); } foreach ( $menu_libs as $menu_lib ) { foreach ( $this->p->cf[ 'plugin' ] as $ext => $info ) { if ( ! isset( $info[ 'lib' ][ $menu_lib ] ) ) { // Not all add-ons have submenus. continue; } foreach ( $info[ 'lib' ][ $menu_lib ] as $menu_id => $menu_name ) { $add_menu_item = true; if ( empty( $this->p->cf[ 'menu' ][ 'must_load' ][ $menu_id ] ) ) { // Settings page can be disabled. $opt_key = SucomUtil::sanitize_key( 'plugin_add_' . $menu_lib . '_' . $menu_id ); $filter_name = SucomUtil::sanitize_hookname( 'wpsso_add_menu_' . $menu_lib . '_item_' . $menu_id ); $add_menu_item = isset( $this->p->options[ $opt_key ] ) ? (bool) $this->p->options[ $opt_key ] : true; $add_menu_item = apply_filters( $filter_name, $add_menu_item ); } if ( $add_menu_item ) { $classname = apply_filters( $ext . '_load_lib', false, $menu_lib . '/' . $menu_id ); if ( is_string( $classname ) && class_exists( $classname ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'loading classname ' . $classname . ' for menu id ' . $menu_id ); } $menu_title = $this->get_submenu_title( $info, $menu_lib, $menu_id ); $this->submenu[ $menu_id ] = new $classname( $this->p, $menu_id, $menu_title, $menu_lib, $ext ); } elseif ( $this->p->debug->enabled ) { $this->p->debug->log( 'classname not found for menu lib ' . $menu_lib . '/' . $menu_id ); } } } } } } public function add_network_admin_menus() { if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } $this->add_admin_menus( 'sitesubmenu' ); } /* * Add a new main menu and its sub-menu items. */ public function add_admin_menus( $menu_lib = 'submenu' ) { foreach ( $this->p->cf[ '*' ][ 'lib' ][ $menu_lib ] as $this->menu_id => $this->menu_name ) { if ( isset( $this->submenu[ $this->menu_id ] ) ) { $menu_slug = 'wpsso-' . $this->menu_id; $this->submenu[ $this->menu_id ]->add_menu_page( $menu_slug ); break; } } $menu_args = $this->get_submenu_args( $menu_lib ); foreach ( $menu_args as $menu_id => $args ) { if ( isset( $this->submenu[ $menu_id ] ) ) { // Just in case. call_user_func_array( array( $this->submenu[ $menu_id ], 'add_submenu_page' ), $args ); } } } /* * Add sub-menu items to existing WordPress menus. */ public function add_admin_submenus() { foreach ( array( 'dashboard', 'plugins', 'profile', 'settings', 'tools', 'users' ) as $menu_lib ) { $menu_args = $this->get_submenu_args( $menu_lib ); foreach ( $menu_args as $menu_id => $args ) { if ( isset( $this->submenu[ $menu_id ] ) ) { // Just in case. call_user_func_array( array( $this->submenu[ $menu_id ], 'add_submenu_page' ), $args ); } } } } /* * Return and maybe set/reset the WpssoAdmin->form value. */ public function &get_form_object( $menu_ext ) { if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } if ( ! isset( $this->form ) ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'form object not defined' ); } $this->set_form_object( $menu_ext ); } elseif ( $this->form->get_ext_id() !== $menu_ext ) { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'form object does not match' ); } $this->set_form_object( $menu_ext ); } return $this->form; } /* * Set the WpssoAdmin->form value. */ protected function set_form_object( $menu_ext ) { // $menu_ext required for text_domain. if ( $this->p->debug->enabled ) { $this->p->debug->log( 'setting form object for ' . $menu_ext ); } $defs = $this->p->opt->get_defaults(); $this->form = new SucomForm( $this->p, WPSSO_OPTIONS_NAME, $this->p->options, $defs, $menu_ext ); } public function register_setting() { register_setting( 'wpsso_settings', WPSSO_OPTIONS_NAME, $args = array( 'sanitize_callback' => array( $this, 'settings_sanitation' ), ) ); } /* * Runs at admin_init priority -1000. */ public function init_check_options() { if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } /* * set_options() may have loaded the static defaults for new or missing options. * * After all objects have been loaded, and all filter / action hooks registered, check to see if the * options array needs to be reloaded from the filtered defaults. */ if ( ! empty( $this->p->options[ '__reload_defaults' ] ) ) { $this->p->options = $this->p->opt->get_defaults(); } if ( is_multisite() ) { // Load the site options array. if ( ! empty( $this->p->site_options[ '__reload_defaults' ] ) ) { $this->p->site_options = $this->p->opt->get_site_defaults(); } } if ( $this->p->debug->enabled ) { $this->p->debug->log( 'checking ' . WPSSO_OPTIONS_NAME . ' options' ); } $this->p->options = $this->p->opt->check_options( WPSSO_OPTIONS_NAME, $this->p->options, $network = false ); if ( is_multisite() ) { // Load the site options array. if ( $this->p->debug->enabled ) { $this->p->debug->log( 'checking ' . WPSSO_SITE_OPTIONS_NAME . ' options' ); } $this->p->site_options = $this->p->opt->check_options( WPSSO_SITE_OPTIONS_NAME, $this->p->site_options, $network = true ); } /* * Init option checks. * * See WpssoWcmd->init_check_options(). */ do_action( 'wpsso_init_check_options' ); } public function add_plugins_page_upgrade_notice() { if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } foreach ( $this->p->cf[ 'plugin' ] as $ext => $info ) { if ( ! empty( $info[ 'base' ] ) ) { /* * Fires at the end of the update message container in each row of the plugins list table. * * See wordpress/wp-admin/includes/update.php:587. */ add_action( 'in_plugin_update_message-' . $info[ 'base' ], array( $this, 'show_upgrade_notice' ), 10, 2 ); } } } public function show_upgrade_notice( $data, $response ) { if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } if ( isset( $data[ 'upgrade_notice' ] ) ) { // Just in case. echo ''; echo '' . strip_tags( $data[ 'upgrade_notice' ] ) . ''; echo ''; } } protected function add_menu_page( $menu_slug ) { $pkg_info = $this->p->util->get_pkg_info(); // Uses a local cache. $page_title = $pkg_info[ 'wpsso' ][ 'short_pkg' ] . ' - ' . $this->menu_name; $menu_title = $this->get_menu_title(); $cf_wp_admin = $this->p->cf[ 'wp' ][ 'admin' ]; $capability = isset( $cf_wp_admin[ $this->menu_lib ][ 'cap' ] ) ? $cf_wp_admin[ $this->menu_lib ][ 'cap' ] : 'manage_options'; $icon_url = 'none'; // Icon provided by WpssoStyle::admin_register_page_styles(). $function = array( $this, 'show_settings_page' ); $position = WPSSO_MENU_ORDER; $this->pagehook = add_menu_page( $page_title, $menu_title, $capability, $menu_slug, $function, $icon_url, $position ); add_action( 'load-' . $this->pagehook, array( $this, 'load_settings_page' ) ); } protected function add_submenu_page( $parent_slug, $menu_id, $menu_title, $menu_lib, $menu_ext, $css_class = '' ) { $css_class = trim( 'wpsso-menu-item wpsso-' . $menu_id . ' ' . $css_class ); $menu_title = '
'; // Wrap the title string. $pkg_info = $this->p->util->get_pkg_info(); // Uses a local cache. $page_title = $pkg_info[ $menu_ext ][ 'short_pkg' ] . ' - ' . $menu_title; $cf_wp_admin = $this->p->cf[ 'wp' ][ 'admin' ]; $capability = isset( $cf_wp_admin[ $menu_lib ][ 'cap' ] ) ? $cf_wp_admin[ $menu_lib ][ 'cap' ] : 'manage_options'; $menu_slug = 'wpsso-' . $menu_id; $function = array( $this, 'show_settings_page' ); $position = null; if ( isset( $cf_wp_admin[ $menu_lib ][ 'sub' ][ $menu_id ] ) ) { $cf_menu_id = $cf_wp_admin[ $menu_lib ][ 'sub' ][ $menu_id ]; $capability = isset( $cf_menu_id[ 'cap' ] ) ? $cf_menu_id[ 'cap' ] : $capability; $position = isset( $cf_menu_id[ 'pos' ] ) ? $cf_menu_id[ 'pos' ] : $position; } $this->pagehook = add_submenu_page( $parent_slug, $page_title, $menu_title, $capability, $menu_slug, $function, $position ); add_action( 'load-' . $this->pagehook, array( $this, 'load_settings_page' ) ); } /* * Add plugin links for the WordPress network plugins page. */ public function add_site_plugin_action_links( $action_links, $plugin_base, $plugin_data, $context, $menu_lib = 'sitesubmenu' ) { return $this->add_plugin_action_links( $action_links, $plugin_base, $plugin_data, $context, $menu_lib ); } /* * Add plugin links for the WordPress plugins page. * * $plugin_data is an array of plugin data. See get_plugin_data(). * * $context can be 'all', 'active', 'inactive', 'recently_activated', 'upgrade', 'mustuse', 'dropins', or 'search'. */ public function add_plugin_action_links( $action_links, $plugin_base, $plugin_data, $context, $menu_lib = 'submenu' ) { if ( ! isset( $this->p->cf[ '*' ][ 'base' ][ $plugin_base ] ) ) { return $action_links; } $ext = $this->p->cf[ '*' ][ 'base' ][ $plugin_base ]; $info = $this->p->cf[ 'plugin' ][ $ext ]; $settings_page = empty( $info[ 'lib' ][ $menu_lib ] ) ? '' : key( $info[ 'lib' ][ $menu_lib ] ); $settings_page_url = $this->p->util->get_admin_url( $settings_page ); switch ( $ext ) { case 'wpsso': $addons_page = 'sitesubmenu' === $menu_lib ? 'site-addons' : 'addons'; $addons_page_url = $this->p->util->get_admin_url( $addons_page ); if ( ! empty( $settings_page ) ) { $action_links[] = '' . _x( 'Plugin Settings', 'plugin action link', 'wpsso' ) . ''; } $action_links[] = '' . _x( 'Plugin Add-ons', 'plugin action link', 'wpsso' ) . ''; break; default: if ( ! empty( $settings_page ) ) { $action_links[] = '' . _x( 'Add-on Settings', 'plugin action link', 'wpsso' ) . ''; } break; } return $action_links; } /* * Define and disable the "Expect: 100-continue" header. * * $parsed_args should be an array, so make sure other filters aren't giving us a string or boolean. */ public function add_expect_header( $parsed_args, $url ) { if ( ! is_array( $parsed_args ) ) { // Just in case. $parsed_args = array(); } if ( empty( $parsed_args[ 'headers' ] ) ) { // Just in case. $parsed_args[ 'headers' ] = array(); /* * WordPress allows passing headers as a string -- fix that issue here so we can update the 'headers' array * properly. * * See https://core.trac.wordpress.org/browser/tags/5.7.1/src/wp-includes/class-http.php#L310. */ } elseif ( ! is_array( $parsed_args[ 'headers' ] ) ) { $processedHeaders = WP_Http::processHeaders( $parsed_args[ 'headers' ] ); $parsed_args[ 'headers' ] = $processedHeaders[ 'headers' ]; } $parsed_args[ 'headers' ][ 'Expect' ] = ''; return $parsed_args; } public function allow_safe_hosts( $is_allowed, $ip, $url ) { if ( $is_allowed ) { // Already allowed. return $is_allowed; } if ( isset( $this->p->cf[ 'extend' ] ) ) { foreach ( $this->p->cf[ 'extend' ] as $host ) { if ( 0 === strpos( $url, $host ) ) { return true; } } } return $is_allowed; } /* * Provides plugin data / information from the readme.txt for additional add-ons. */ public function external_plugin_data( $result, $action = null, $args = null ) { if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } if ( 'plugin_information' !== $action ) { // This filter only provides plugin data. return $result; } elseif ( empty( $args->slug ) ) { // Make sure we have a slug in the request. return $result; } elseif ( empty( $this->p->cf[ '*' ][ 'slug' ][ $args->slug ] ) ) { // Make sure the plugin slug is one of ours. return $result; } elseif ( isset( $result->slug ) && $result->slug === $args->slug ) { // If the object from WordPress looks complete, return it as-is. return $result; } $ext = $this->p->cf[ '*' ][ 'slug' ][ $args->slug ]; // Get the add-on acronym to read its config. if ( empty( $this->p->cf[ 'plugin' ][ $ext ] ) ) { // Make sure we have a config for that acronym. return $result; } $plugin_data = $this->get_plugin_data( $ext, $read_cache = true ); // Get plugin data from the plugin readme. if ( empty( $plugin_data ) ) { // Make sure we have some data to return. return $result; } $plugin_data->external = true; // Let WordPress known that this is not a wordpress.org plugin. return $plugin_data; } /* * This method receives only a partial options array, so re-create a full one. * * WordPress handles the actual saving of the options to the database table. */ public function settings_sanitation( $opts ) { $current_user_id = get_current_user_id(); // Always returns an integer. /* * Clear any old notices for the current user before sanitation checks. */ $this->p->notice->clear(); /* * Make sure the input is an array. */ if ( ! is_array( $opts ) ) { $opts = array(); } $defs = $this->p->opt->get_defaults(); $opts = SucomUtil::restore_checkboxes( $opts ); $opts = SucomUtil::array_merge_recursive_distinct( $this->p->options, $opts ); $opts = $this->p->opt->sanitize( $opts, $defs, $network = false ); $opts = apply_filters( 'wpsso_save_settings_options', $opts, $network = false, $upgrading = false ); /* * Maybe clear dismissed notices for enabled options that were previously disabled. * * See WpssoAdminHeadSuggestOptions->suggest_options_integration(). */ foreach ( array( 'add_link_rel_canonical' => 'suggest-options-seo-add_link_rel_canonical', 'add_link_rel_shortlink' => 'suggest-options-seo-add_link_rel_shortlink', 'add_meta_name_description' => 'suggest-options-seo-add_meta_name_description', 'add_meta_name_robots' => 'suggest-options-seo-add_meta_name_robots', 'plugin_filter_content' => 'notice-content-filters-disabled', 'plugin_check_img_dims' => 'notice-check-img-dims-disabled', 'plugin_inherit_featured' => 'notice-wc-inherit-featured-disabled', ) as $opt_key => $notice_key ) { if ( ! empty( $opts[ $opt_key ] ) ) { // Option is enabled. $this->p->notice->clear_dismissed( $notice_key, $current_user_id ); } } /* * Update the current options with any changes. */ $this->p->options = $opts; $this->settings_saved_notice(); return $opts; } public function settings_saved_notice() { $cache_md5_pre = 'wpsso_h_'; // Use 'wpsso_cache_expire_head_markup' filter. $cache_exp_secs = $this->p->util->get_cache_exp_secs( $cache_md5_pre, $cache_type = 'transient' ); $user_id = get_current_user_id(); $notice_msg = '' . __( 'Plugin settings have been saved.', 'wpsso' ) . '' . ' '; $notice_key = 'settings-saved'; if ( $cache_exp_secs > 0 ) { $refresh_cache_url = $this->p->util->get_admin_url( $menu_id = 'tools' ); $refresh_cache_url = add_query_arg( 'wpsso-action', 'refresh_cache', $refresh_cache_url ); $refresh_cache_url = wp_nonce_url( $refresh_cache_url, WpssoAdmin::get_nonce_action(), WPSSO_NONCE_NAME ); $refresh_cache_link = '' . __( 'refresh the cache now', 'wpsso' ) . ''; $human_cache_exp = human_time_diff( 0, $cache_exp_secs ); $notice_msg .= sprintf( __( 'You can %1$s or let the cache refresh over the next %2$s.', 'wpsso' ), $refresh_cache_link, $human_cache_exp ); } $this->p->notice->upd( $notice_msg, $user_id, $notice_key ); } public function save_site_settings() { $default_page = key( $this->p->cf[ '*' ][ 'lib' ][ 'sitesubmenu' ] ); $default_page_url = $this->p->util->get_admin_url( $default_page ); $setting_page = SucomUtil::get_request_value( 'page', 'POST', $default_page ); $setting_page_url = $this->p->util->get_admin_url( $setting_page ); if ( empty( $_POST[ WPSSO_NONCE_NAME ] ) ) { // WPSSO_NONCE_NAME is an md5() string. if ( $this->p->debug->enabled ) { $this->p->debug->log( 'nonce token validation post field missing' ); } wp_redirect( $default_page_url ); // Do not trust the 'page' request value. exit; } elseif ( ! wp_verify_nonce( $_POST[ WPSSO_NONCE_NAME ], WpssoAdmin::get_nonce_action() ) ) { $this->p->notice->err( __( 'Nonce token validation failed for network options (update ignored).', 'wpsso' ) ); wp_redirect( $default_page_url ); // Do not trust the 'page' request value. exit; } elseif ( ! current_user_can( 'manage_network_options' ) ) { $this->p->notice->err( __( 'Insufficient privileges to manage network options.', 'wpsso' ) ); wp_redirect( $setting_page_url ); exit; } /* * Clear any old notices for the current user before sanitation checks. */ $this->p->notice->clear(); /* * Make sure the input is an array. */ $opts = array(); if ( isset( $_POST[ WPSSO_SITE_OPTIONS_NAME ] ) && is_array( $_POST[ WPSSO_SITE_OPTIONS_NAME ] ) ) { $opts = $_POST[ WPSSO_SITE_OPTIONS_NAME ]; } $defs = $this->p->opt->get_site_defaults(); $opts = SucomUtil::restore_checkboxes( $opts ); $opts = SucomUtil::array_merge_recursive_distinct( $this->p->site_options, $opts ); // Complete the array with previous options. $opts = $this->p->opt->sanitize( $opts, $defs, $network = true ); $opts = apply_filters( 'wpsso_save_settings_options', $opts, $network = true, $upgrading = false ); /* * Update the current options with any changes. */ $this->p->site_options = $opts; update_site_option( WPSSO_SITE_OPTIONS_NAME, $opts ); $this->p->notice->upd( '' . __( 'Network plugin settings have been saved.', 'wpsso' ) . '' ); $setting_page_url = add_query_arg( 'settings-updated', 'true', $setting_page_url ); wp_redirect( $setting_page_url ); exit; // Stop after redirect. } public function load_settings_page() { if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } $user_id = get_current_user_id(); $action_query = 'wpsso-action'; $action_value = SucomUtil::get_request_value( $action_query ) ; // POST or GET with sanitize_text_field(). $action_value = SucomUtil::sanitize_hookname( $action_value ); $nonce_value = SucomUtil::get_request_value( WPSSO_NONCE_NAME ) ; // POST or GET with sanitize_text_field(). $_SERVER[ 'REQUEST_URI' ] = remove_query_arg( array( $action_query, WPSSO_NONCE_NAME ) ); wp_enqueue_script( 'postbox' ); if ( ! empty( $action_value ) ) { if ( empty( $nonce_value ) ) { // WPSSO_NONCE_NAME is an md5() string. if ( $this->p->debug->enabled ) { $this->p->debug->log( 'nonce token validation field missing' ); } } elseif ( ! wp_verify_nonce( $nonce_value, WpssoAdmin::get_nonce_action() ) ) { $notice_msg = sprintf( __( 'Nonce token validation failed for %1$s action "%2$s".', 'wpsso' ), 'admin', $action_value ); $this->p->notice->err( $notice_msg, $user_id ); } else { if ( $this->p->debug->enabled ) { $this->p->debug->log( 'action_value = ' . $action_value ); } switch ( $action_value ) { case 'refresh_cache': $this->p->util->cache->schedule_refresh( $user_id ); break; case 'clear_cache_files': $cleared_count = $this->p->util->cache->clear_cache_files(); $notice_msg = sprintf( __( '%s cache files have been cleared.', 'wpsso' ), $cleared_count ); $this->p->notice->upd( $notice_msg, $user_id ); break; case 'clear_ignored_urls': $cleared_count = $this->p->util->cache->clear_ignored_urls(); $notice_msg = sprintf( __( '%s failed URL connections have been cleared.', 'wpsso' ), $cleared_count ); $this->p->notice->upd( $notice_msg, $user_id ); break; case 'clear_db_transients': $cleared_count = $this->p->util->cache->clear_db_transients( $key_prefix = '', $incl_shortened = true ); $notice_msg = sprintf( __( '%s database transients have been cleared.', 'wpsso' ), $cleared_count ); $this->p->notice->upd( $notice_msg, $user_id ); break; case 'clear_db_transients_expired': $cleared_count = $this->p->util->cache->clear_db_transients_expired(); $notice_msg = sprintf( __( '%s expired transients have been cleared.', 'wpsso' ), $cleared_count ); $this->p->notice->upd( $notice_msg, $user_id ); break; case 'clear_db_transients_no_short': $cleared_count = $this->p->util->cache->clear_db_transients( $key_prefix = '', $incl_shortened = false ); $notice_msg = sprintf( __( '%s database transients have been cleared (shortened URLs preserved).', 'wpsso' ), $cleared_count ); $this->p->notice->upd( $notice_msg, $user_id ); break; case 'clear_db_transients_shortened': $cleared_count = $this->p->util->cache->clear_db_transients( $key_prefix = 'wpsso_s_', $incl_shortened = true ); $notice_msg = sprintf( __( '%s shortened URL database transients have been cleared.', 'wpsso' ), $cleared_count ); $this->p->notice->upd( $notice_msg, $user_id ); break; case 'clear_cron_jobs': $count_cron_jobs = number_format_i18n( $this->p->util->count_cron_jobs() ); update_option( 'cron', '' ); $notice_msg = sprintf( __( '%s WordPress cron jobs have been cleared.', 'wpsso' ), $count_cron_jobs ); $this->p->notice->upd( $notice_msg, $user_id ); break; case 'flush_rewrite_rules': /* * Update .htaccess and the 'rewrite_rules' option. * * This is an expensive operation so it should only be used when necessary. */ flush_rewrite_rules( $hard = true ); $notice_msg = __( 'The WordPress rewrite rules have been flushed.', 'wpsso' ); $this->p->notice->upd( $notice_msg, $user_id ); break; case 'add_persons': $this->p->user->schedule_add_person_role( $user_id ); break; case 'remove_persons': $this->p->user->schedule_remove_person_role( $user_id ); break; case 'reset_user_metabox_layout': WpssoUser::delete_metabox_prefs( $user_id ); $user_obj = get_userdata( $user_id ); $user_name = $user_obj->display_name; $notice_msg = sprintf( __( 'Metabox layout preferences for user ID #%d "%s" have been reset.', 'wpsso' ), $user_id, $user_name ); $this->p->notice->upd( $notice_msg, $user_id ); break; case 'reset_user_dismissed_notices': $this->p->notice->reset_dismissed( $user_id ); $user_obj = get_userdata( $user_id ); $user_name = $user_obj->display_name; $notice_msg = sprintf( __( 'Dismissed notices for user ID #%d "%s" have been reset.', 'wpsso' ), $user_id, $user_name ); $this->p->notice->upd( $notice_msg, $user_id ); break; case 'change_show_options': $_SERVER[ 'REQUEST_URI' ] = remove_query_arg( array( 'show-opts' ) ); $show_opts_key = isset( $_GET[ 'show-opts' ] ) ? SucomUtil::sanitize_key( $_GET[ 'show-opts' ] ) : null; if ( isset( $this->p->cf[ 'form' ][ 'show_options' ][ $show_opts_key ] ) ) { $show_name_transl = _x( $this->p->cf[ 'form' ][ 'show_options' ][ $show_opts_key ], 'option value', 'wpsso' ); WpssoUser::save_pref( array( 'show_opts' => $show_opts_key ) ); $notice_msg = '' . __( 'Option preference has been saved.', 'wpsso' ) . ''; $notice_msg .= sprintf( __( 'Now viewing "%s" by default.', 'wpsso' ), $show_name_transl ); $this->p->notice->upd( $notice_msg, $user_id ); } break; case 'reload_default_image_sizes': $defs = $this->p->opt->get_defaults(); $img_defs = SucomUtil::preg_grep_keys( '/_img_(width|height|crop|crop_x|crop_y)$/', $defs ); $this->p->options = SucomUtil::array_merge_recursive_distinct( $this->p->options, $img_defs ); $this->p->opt->save_options( WPSSO_OPTIONS_NAME, $this->p->options, $network = false ); $this->p->notice->upd( __( 'Image size settings have been reloaded with their default values and saved.', 'wpsso' ) ); break; case 'export_plugin_settings_json': $this->export_plugin_settings_json(); break; case 'import_plugin_settings_json': $this->import_plugin_settings_json(); break; default: if ( $this->p->debug->enabled ) { $this->p->debug->log( 'doing action "wpsso_load_settings_page_' . $action_value . '"' ); } do_action( 'wpsso_load_settings_page_' . $action_value, $this->pagehook, $this->menu_id, $this->menu_name, $this->menu_lib ); break; } } } $menu_ext = empty( $this->menu_ext ) ? $this->p->id : $this->menu_ext; $this->get_form_object( $menu_ext ); // Return and maybe set/reset the WpssoAdmin->form value. $this->add_settings_page_callbacks(); $this->add_settings_page_metaboxes(); $this->add_settings_page_footer(); } /* * Add actions and filters for this settings page. * * See WpssoAdmin->load_settings_page(). */ protected function add_settings_page_callbacks() { } /* * Add metaboxes for this settings page. * * See WpssoAdmin->load_settings_page(). */ protected function add_settings_page_metaboxes( $callback_args = array() ) { if ( $this->p->debug->enabled ) { $this->p->debug->mark(); } /* * Defined in the child construct method of this settings page. */ if ( empty( $this->menu_metaboxes ) ) { return; } foreach ( $this->menu_metaboxes as $metabox_id => $metabox_title ) { $metabox_screen = $this->pagehook; $metabox_context = 'normal'; $metabox_prio = 'default'; $callback_args[ 'page_id' ] = $this->menu_id; $callback_args[ 'metabox_id' ] = $metabox_id; $callback_args[ 'metabox_title' ] = $metabox_title; $callback_args[ 'network' ] = 'sitesubmenu' === $this->menu_lib ? true : false; $method_name = method_exists( $this, 'show_metabox_' . $metabox_id ) ? 'show_metabox_' . $metabox_id : 'show_metabox_table'; add_meta_box( $this->pagehook . '_' . $metabox_id, $metabox_title, array( $this, $method_name ), $metabox_screen, $metabox_context, $metabox_prio, $callback_args ); /* * Add a class to set a minimum width for the network postboxes. */ if ( 'sitesubmenu' === $this->menu_lib ) { add_filter( 'postbox_classes_' . $this->pagehook . '_' . $this->pagehook . '_' . $metabox_id, array( $this, 'add_class_postbox_network' ) ); } } } /* * Include add-on name and version in settings page footer. */ protected function add_settings_page_footer() { add_filter( 'admin_footer_text', array( $this, 'admin_footer_ext' ) ); add_filter( 'update_footer', array( $this, 'admin_footer_host' ) ); } protected function maybe_show_language_notice() { $current_locale = SucomUtilWP::get_locale(); $default_locale = SucomUtilWP::get_locale( 'default' ); if ( $current_locale && $default_locale && $current_locale !== $default_locale ) { $notice_msg = sprintf( __( 'Your current language selection [%1$s] is different from the default site language [%2$s].', 'wpsso' ), $current_locale, $default_locale ) . ' '; $notice_msg .= sprintf( __( 'Localized option values [%1$s] will be used for webpages and content in that language.', 'wpsso' ), $current_locale ); $notice_key = $this->menu_id . '-language-notice-' . $current_locale . '-' . $default_locale; $this->p->notice->inf( $notice_msg, null, $notice_key, $dismiss_time = true ); } } public function show_settings_page() { if ( ! $this->is_settings() ) { // Default check is for $this->menu_id. settings_errors( WPSSO_OPTIONS_NAME ); } if ( ! $this->is_plugin_found() ) { // Just in case. return; } $side_col_boxes = $this->get_side_col_boxes(); $dashicon_html = $this->get_menu_dashicon_html( $this->menu_id ); /* * Settings page wrapper. */ echo '| ' . "\n"; echo $box; echo ' |