diff --git a/.gitattributes b/.gitattributes index 06ffb2f4..3cfe6e64 100644 --- a/.gitattributes +++ b/.gitattributes @@ -18,9 +18,12 @@ *.woff binary *.woff2 binary +# GitHub baggage for development. .github export-ignore .gitattributes export-ignore +.gitignore export-ignore .editorconfig export-ignore README.md export-ignore +composer.json export-ignore phpcs.xml export-ignore *.po export-ignore diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 00000000..cf812e9d --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,3 @@ +# Contributing to the Extension Manager + +Please see [The SEO Framework's CONTRIBUTING.md](https://github.com/sybrew/the-seo-framework/blob/master/.github/CONTRIBUTING.md). diff --git a/.github/ISSUE_TEMPLATE/---security-issue.md b/.github/ISSUE_TEMPLATE/---security-issue.md new file mode 100644 index 00000000..74b3480f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/---security-issue.md @@ -0,0 +1,12 @@ +--- +name: "\U0001F46E Security issue" +about: "Please report security issues at https://theseoframework.com/contact/." +title: '' +labels: '' +assignees: sybrew + +--- + +Please disclose security issues responsibly and not publicly via GitHub. + +So, for security reasons, please report all security issues via https://theseoframework.com/contact/. diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 329e42e3..1413ac40 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,6 +1,9 @@ --- -name: Bug report -about: Create a report to help us improve +name: "\U0001F98B Bug report" +about: "Report a bug if something's not working as expected for you in the Extension Manager." +title: '' +labels: '' +assignees: '' --- @@ -18,7 +21,10 @@ Steps to reproduce the behavior: **Screenshots** - + **Additional context** - + diff --git a/.github/ISSUE_TEMPLATE/enhancement.md b/.github/ISSUE_TEMPLATE/enhancement.md new file mode 100644 index 00000000..16c6299e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/enhancement.md @@ -0,0 +1,10 @@ +--- +name: "\U0001F4A1 Enhancement idea" +about: "We'd love to know what could've been done better in the Extension Manager." +title: '' +labels: '' +assignees: '' + +--- + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 9137197c..14afb619 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,10 +1,10 @@ --- -name: Feature request -about: Suggest an idea for this project +name: "\U0001F680 Feature request" +about: "Suggest a new feature for the Extension Manager." +title: '' +labels: '' +assignees: '' --- -For increased exposure, features requests should be sent to the main project: -https://github.com/sybrew/the-seo-framework/issues/new - -Feature requests here will be closed. + diff --git a/.github/ISSUE_TEMPLATE/support.md b/.github/ISSUE_TEMPLATE/support.md index 52991722..2e6c027e 100644 --- a/.github/ISSUE_TEMPLATE/support.md +++ b/.github/ISSUE_TEMPLATE/support.md @@ -1,7 +1,10 @@ --- -name: Support -about: Describe your issue clear and concise +name: "\U0001F4AC Support" +about: "Are you running into issues with the Extension Manager? Reach out to us here." +title: '' +labels: '' +assignees: '' --- - + diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..4a36087c --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +## Packages +*.zip +*.7z +*.gz +*.tar +*.rar + +## Local storage +*.log +*.ser +*.sql +*.sqlite + +## Common work-file extensions +*.tmp +*.bak + +## OS generated files +.DS_Store +.DS_Store? +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db diff --git a/bootstrap/upgrader.class.php b/bootstrap/upgrader.class.php index 6a5e6094..055842e1 100644 --- a/bootstrap/upgrader.class.php +++ b/bootstrap/upgrader.class.php @@ -52,14 +52,14 @@ * @access private * @uses trait \TSF_Extension_Manager\Enclose_Stray_Private * @uses trait \TSF_Extension_Manager\Construct_Core_Once_Interface - * @see package \TSF_Extension_Manager\Overload + * @see @package TSF_Extension_Manager\Overload * @uses trait \TSF_Extension_Manager\Memory - * @see package \TSF_Extension_Manager\Factory + * @see @package TSF_Extension_Manager\Factory */ final class Upgrader { use Enclose_Stray_Private, - Construct_Core_Once_Interface, - Memory; + Construct_Core_Once_Interface, + Memory; /** * The db revision option key. @@ -114,10 +114,10 @@ private function construct() { * @since 1.5.0 */ private function set_defaults() { - $this->upgrades = new \stdClass; + $this->upgrades = new \stdClass; $this->previous_db_versions = \get_option( static::O, [] ); - $this->current_db_versions = $this->previous_db_versions; - $this->active_callbacks = []; + $this->current_db_versions = $this->previous_db_versions; + $this->active_callbacks = []; } /** diff --git a/extensions/essentials/focus/trunk/lib/js/tsfem-focus-inpost.js b/extensions/essentials/focus/trunk/lib/js/tsfem-focus-inpost.js index 47f7fedd..1169260d 100644 --- a/extensions/essentials/focus/trunk/lib/js/tsfem-focus-inpost.js +++ b/extensions/essentials/focus/trunk/lib/js/tsfem-focus-inpost.js @@ -299,7 +299,7 @@ window.tsfem_e_focus_inpost = function( $ ) { /** * @param {(boolean|object|array)} inflections - * @param {boolean|object|array)} synonyms + * @param {(boolean|object|array)} synonyms */ let inflections = activeWords( idPrefix ).get( 'inflections' ), synonyms = activeWords( idPrefix ).get( 'synonyms' ); diff --git a/extensions/free/cord/trunk/cord.php b/extensions/free/cord/trunk/cord.php new file mode 100644 index 00000000..30286c8d --- /dev/null +++ b/extensions/free/cord/trunk/cord.php @@ -0,0 +1,164 @@ +. + */ + +/** + * The extension version. + * @since 1.0.0 + * NOTE: The presence does NOT guarantee the extension is loaded!!! + */ +define( 'TSFEM_E_CORD_VERSION', '1.0.0' ); + +/** + * The extension database version. + * @since 1.0.0 + */ +define( 'TSFEM_E_CORD_DB_VERSION', '1000' ); + +/** + * The extension file, absolute unix path. + * @since 1.0.0 + */ +define( 'TSFEM_E_CORD_BASE_FILE', __FILE__ ); + +/** + * The extension map URL. Used for calling browser files. + * @since 1.0.0 + */ +define( 'TSFEM_E_CORD_DIR_URL', \TSF_Extension_Manager\extension_dir_url( TSFEM_E_CORD_BASE_FILE ) ); + +/** + * The extension file relative to the plugins dir. + * @since 1.0.0 + */ +define( 'TSFEM_E_CORD_DIR_PATH', \TSF_Extension_Manager\extension_dir_path( TSFEM_E_CORD_BASE_FILE ) ); + +/** + * The plugin class map absolute path. + * @since 1.0.0 + */ +define( 'TSFEM_E_CORD_PATH_CLASS', TSFEM_E_CORD_DIR_PATH . 'inc' . DIRECTORY_SEPARATOR . 'classes' . DIRECTORY_SEPARATOR ); + +/** + * The plugin trait map absolute path. + * @since 1.0.0 + */ +define( 'TSFEM_E_CORD_PATH_TRAIT', TSFEM_E_CORD_DIR_PATH . 'inc' . DIRECTORY_SEPARATOR . 'traits' . DIRECTORY_SEPARATOR ); + +/** + * Verify integrity and sets up autoloader. + * @since 1.0.0 + */ +if ( false === \tsf_extension_manager()->_init_early_extension_autoloader( TSFEM_E_CORD_PATH_CLASS, 'Cord', $_instance, $bits ) ) + return; + +// if ( TSFEM_E_CORD_DB_VERSION > \tsf_extension_manager_db_version( 'cord' ) ) { +// require TSFEM_E_CORD_DIR_PATH . 'upgrade.php'; +// } + +\add_action( 'plugins_loaded', __NAMESPACE__ . '\\_cord_init', 11 ); +/** + * Initializes the extension. + * + * @since 1.0.0 + * @staticvar bool $loaded True when loaded. + * @action 'plugins_loaded' + * @priority 11 + * @access private + * + * @return bool True if class is loaded. + */ +function _cord_init() { + + static $loaded; + + if ( isset( $loaded ) ) + return $loaded; + + if ( \is_admin() ) { + new Admin; + } else { + // new Front; // TODO CRON? + } + + return $loaded = true; +} + +/** + * Returns the active base class. + * + * @since 1.0.0 + * + * @return string The active class name. + */ +function get_active_class() { + if ( \is_admin() ) { + return __NAMESPACE__ . '\\Admin'; + } else { + return __NAMESPACE__ . '\\Front'; // TODO + } +} + +/** + * Returns the settings class. + * + * @since 1.0.0 + * + * @return string The settings class name. + */ +function get_layout_class() { + return __NAMESPACE__ . '\\Settings'; +} + +/** + * Requires trait files once. + * + * @since 1.0.0 + * @uses TSFEM_E_CORD_PATH_TRAIT + * @access private + * @staticvar array $loaded + * + * @param string $file Trait file name. + * @return bool True if loaded, false otherwise. + */ +function _load_trait( $file ) { + + static $loaded = []; + + if ( isset( $loaded[ $file ] ) ) + return $loaded[ $file ]; + + return $loaded[ $file ] = (bool) require TSFEM_E_CORD_PATH_TRAIT . $file . '.trait.php'; +} diff --git a/extensions/free/cord/trunk/inc/classes/admin.class.php b/extensions/free/cord/trunk/inc/classes/admin.class.php new file mode 100644 index 00000000..5c8ad071 --- /dev/null +++ b/extensions/free/cord/trunk/inc/classes/admin.class.php @@ -0,0 +1,225 @@ +_has_died() or false === ( \tsf_extension_manager()->_verify_instance( $_instance, $bits[1] ) or \tsf_extension_manager()->_maybe_die() ) ) + return; + +/** + * Cord extension for The SEO Framework + * Copyright (C) 2019 Sybre Waaijer, CyberWire (https://cyberwire.nl/) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * Class TSF_Extension_Manager\Extension\Cord\Admin + * + * Holds extension admin page methods. + * + * @since 1.0.0 + * @access private + * @errorval 109xxxx + * @uses TSF_Extension_Manager\Traits + * @final + */ +final class Admin extends Core { + use \TSF_Extension_Manager\Enclose_Stray_Private, + \TSF_Extension_Manager\Construct_Master_Once_Interface; + + /** + * Name of the page hook when the menu is registered. + * + * @since 1.0.0 + * + * @var string Page hook. + */ + protected $cord_menu_page_hook; + + /** + * The extension page ID/slug. + * + * @since 1.0.0 + * + * @var string Page ID/Slug + */ + protected $cord_page_slug; + + /** + * Constructor, initializes WordPress actions. + * + * @since 1.0.0 + */ + private function construct() { + + //* Sets Cord's page slug. + $this->cord_page_slug = 'theseoframework-cord'; + + //* Load admin actions. + $this->load_admin_actions(); + } + + /** + * Loads admin actions. + * + * @since 1.0.0 + */ + private function load_admin_actions() { + + //* Initialize menu links + \add_action( 'admin_menu', [ $this, '_init_menu' ] ); + + //* Initialize Cord page actions. Requires $this->cord_menu_page_hook to be set. + \add_action( 'admin_init', [ $this, '_load_cord_admin_actions' ], 10 ); + } + + /** + * Initializes extension menu. + * + * @since 1.0.0 + * @uses \the_seo_framework()->load_options variable. Applies filters 'the_seo_framework_load_options' + * @uses \tsf_extension_manager()->can_do_settings() + * @access private + */ + public function _init_menu() { + + if ( \tsf_extension_manager()->can_do_settings() && \the_seo_framework()->load_options ) + \add_action( 'admin_menu', [ $this, '_add_menu_link' ], 11 ); + } + + /** + * Adds menu link for Cord, when possible, underneath The SEO Framework + * SEO settings. + * + * @since 1.0.0 + * @uses \the_seo_framework()->seo_settings_page_slug. + * @access private + */ + public function _add_menu_link() { + + $menu = [ + 'parent_slug' => \the_seo_framework()->seo_settings_page_slug, + 'page_title' => 'Cord', + 'menu_title' => 'Cord', + 'capability' => 'manage_options', + 'menu_slug' => $this->cord_page_slug, + 'callback' => [ $this, '_output_cord_settings_page' ], + ]; + + $this->cord_menu_page_hook = \add_submenu_page( + $menu['parent_slug'], + $menu['page_title'], + $menu['menu_title'], + $menu['capability'], + $menu['menu_slug'], + $menu['callback'] + ); + } + + /** + * Outputs Cord's settings page. + * + * @since 1.0.0 + * @access private + */ + public function _output_cord_settings_page() { + $this->get_cord_settings_instance()->_output_settings_page( $this ); + } + + /** + * Hooks admin actions into the TSF Extension Manager pagehook. + * + * @since 1.0.0 + * @uses $this->cord_menu_page_hook variable. + * @access private + */ + public function _load_cord_admin_actions() { + + if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { + $this->do_settings_page_ajax_actions(); + } else { + \add_action( 'load-' . $this->cord_menu_page_hook, [ $this, '_do_settings_page_actions' ] ); + } + } + + /** + * Hooks admin actions into the Cord pagehook. + * Early enough for admin_notices and admin_head :). + * + * @since 1.0.0 + * @access private + * + * @return bool True on actions loaded, false on second load or incorrect page. + */ + public function _do_settings_page_actions() { + + if ( false === $this->is_cord_page() ) + return false; + + if ( \TSF_Extension_Manager\has_run( __METHOD__ ) ) + return false; + + $this->get_cord_settings_instance()->_init( $this, $this->cord_page_slug, $this->cord_menu_page_hook, $this->o_index ); + + return true; + } + + /** + * Hooks admin AJAX actions into the Cord pagehook. + * + * @since 1.0.0 + * + * @return bool True on actions loaded, false on second load or incorrect page. + */ + protected function do_settings_page_ajax_actions() { + + if ( \TSF_Extension_Manager\has_run( __METHOD__ ) ) + return false; + + $this->get_cord_settings_instance()->_init_ajax( $this, $this->o_index ); + + return true; + } + + /** + * Determines whether we're on the Cord overview page. + * + * @since 1.0.0 + * @staticvar bool $cache + * @access private + * + * @return bool + */ + public function is_cord_page() { + + static $cache; + + //* Don't load from $_GET request. + return isset( $cache ) ? $cache : $cache = \the_seo_framework()->is_menu_page( $this->cord_menu_page_hook ); + } + + /** + * Sets up and returns \TSF_Extension_Manager\Extension\Cord\Settings. + * + * @since 1.0.0 + * + * @return object \TSF_Extension_Manager\Extension\Cord\Settings + */ + protected function get_cord_settings_instance() { + return \TSF_Extension_Manager\Extension\Cord\Settings::get_instance(); + } +} diff --git a/extensions/free/cord/trunk/inc/classes/core.class.php b/extensions/free/cord/trunk/inc/classes/core.class.php new file mode 100644 index 00000000..1259d846 --- /dev/null +++ b/extensions/free/cord/trunk/inc/classes/core.class.php @@ -0,0 +1,66 @@ +_has_died() or false === ( \tsf_extension_manager()->_verify_instance( $_instance, $bits[1] ) or \tsf_extension_manager()->_maybe_die() ) ) + return; + +/** + * Cord extension for The SEO Framework + * Copyright (C) 2019 Sybre Waaijer, CyberWire (https://cyberwire.nl/) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * Require extension options trait. + * @since 1.0.0 + */ +\TSF_Extension_Manager\_load_trait( 'extension/options' ); + +/** + * Class TSF_Extension_Manager\Extension\Cord\Core + * + * Holds extension core methods. + * + * @since 1.0.0 + * @access private + * @uses TSF_Extension_Manager\Traits + */ +class Core { + use \TSF_Extension_Manager\Enclose_Stray_Private, + \TSF_Extension_Manager\Construct_Core_Interface, + \TSF_Extension_Manager\Extension_Options; + + /** + * Child constructor. + * + * @since 1.0.0 + */ + private function construct() { + + $that = __NAMESPACE__ . ( \is_admin() ? '\\Admin' : '\\Front' ); + $this instanceof $that or \wp_die( -1 ); + + /** + * Set options index. + * @see trait TSF_Extension_Manager\Extension_Options + */ + $this->o_index = 'cord'; + } +} diff --git a/extensions/free/cord/trunk/inc/classes/settings.class.php b/extensions/free/cord/trunk/inc/classes/settings.class.php new file mode 100644 index 00000000..0a8a4312 --- /dev/null +++ b/extensions/free/cord/trunk/inc/classes/settings.class.php @@ -0,0 +1,388 @@ +_has_died() or false === ( \tsf_extension_manager()->_verify_instance( $_instance, $bits[1] ) or \tsf_extension_manager()->_maybe_die() ) ) + return; + +/** + * Cord extension for The SEO Framework + * Copyright (C) 2019 Sybre Waaijer, CyberWire (https://cyberwire.nl/) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** +* Require user interface trait. +* @since 1.0.0 +*/ +\TSF_Extension_Manager\_load_trait( 'core/ui' ); + +/** + * Require error trait. + * @since 1.0.0 + */ +\TSF_Extension_Manager\_load_trait( 'core/error' ); + +/** + * Require Cord POST handling trait. + * @since 1.0.0 + */ +\TSF_Extension_Manager\Extension\Cord\_load_trait( 'secure-post' ); + +/** + * Class TSF_Extension_Manager\Extension\Cord\Settings + * + * Holds extension settings methods. + * + * @since 1.0.0 + * @access private + * @errorval 109xxxx + */ +final class Settings { + use \TSF_Extension_Manager\Enclose_Core_Final, + \TSF_Extension_Manager\Construct_Core_Static_Final_Instance, + \TSF_Extension_Manager\UI, + \TSF_Extension_Manager\Extension_Options, + \TSF_Extension_Manager\Error, + Secure_Post; + + /** + * The settings page slug. + * + * @since 1.0.0 + * + * @var string $slug + */ + protected $slug = ''; + + /** + * Initializes and outputs Settings page. + * + * @since 1.0.0 + * @access private + * + * @param object \TSF_Extension_Manager\Extension\Cord\Core $_core + * Used for integrity. + * @param string $slug The menu slug. + * @param string $hook The menu hook. + * @param string $o_index The options index. + */ + public function _init( Core $_core, $slug, $hook, $o_index ) { + + /** + * Set options index. + * @see trait TSF_Extension_Manager\Extension_Options + */ + $this->o_index = $o_index; + + $this->_init_main(); + + /** + * Set page slug. + */ + $this->slug = $slug; + + /** + * Set UI hook. + * @see trait TSF_Extension_Manager\UI + */ + $this->ui_hook = $hook; + + /** + * Initialize user interface. + * @see trait TSF_Extension_Manager\UI + */ + $this->init_tsfem_ui(); + } + + /** + * Initializes AJAX for Settings page. + * + * @since 1.0.0 + * @access private + * + * @param object \TSF_Extension_Manager\Extension\Cord\Core $_core + * Used for integrity. + * @param string $o_index The options index. + */ + public function _init_ajax( Core $_core, $o_index ) { + /** + * Set options index. + * @see trait TSF_Extension_Manager\Extension_Options + */ + $this->o_index = $o_index; + + $this->_init_main(); + } + + /** + * Initializes main Settings page properties and methods. + * + * Both for AJAX and HTML output. + * + * @since 1.0.0 + * @access private + * @return void Early on second call. + */ + private function _init_main() { + + if ( \TSF_Extension_Manager\has_run( __METHOD__ ) ) return; + + /** + * Set form arguments. + * @see class TSF_Extension_Manager\FormGenerator + */ + $this->form_args = [ + 'caller' => __CLASS__, + 'o_index' => $this->o_index, + 'o_key' => '', + 'use_stale' => true, + 'levels' => 5, + 'architecture' => null, + ]; + + /** + * Set error notice option. + * @see trait TSF_Extension_Manager\Error + */ + $this->error_notice_option = 'tsfem_e_cord_error_notice_option'; + + /** + * Initialize error interface. + * @see trait TSF_Extension_Manager\Error + */ + $this->init_errors(); + + /** + * Sets nonces. + * @see trait TSF_Extension_Manager\Extension\Cord\Secure_Post + */ + $this->set_nonces(); + + /** + * Initialize POST data checks. + * @see trait TSF_Extension_Manager\Extension\Cord\Secure_Post + */ + $this->init_post_checks(); + } + + /** + * Initializes and outputs Settings page. + * + * @since 1.0.0 + * @access private + * + * @param object \TSF_Extension_Manager\Extension\Cord\Core $_core Used for integrity. + */ + public function _output_settings_page( Core $_core ) { + \add_action( 'tsfem_header', [ $this, '_output_cord_header' ] ); + \add_action( 'tsfem_content', [ $this, '_output_cord_content' ] ); + \add_action( 'tsfem_footer', [ $this, '_output_cord_footer' ] ); + + $this->wrap_type = 'row'; + $this->ui_wrap( 'panes' ); + } + + /** + * Outputs monitor header. + * + * @since 1.0.0 + * @access private + */ + final public function _output_cord_header() { + $this->get_view( 'layout/general/top' ); + } + + /** + * Outputs monitor content. + * + * @since 1.0.0 + * @access private + */ + final public function _output_cord_content() { + $this->get_view( 'layout/pages/cord' ); + } + + /** + * Outputs monitor footer. + * + * @since 1.0.0 + * @access private + */ + final public function _output_cord_footer() { + $this->get_view( 'layout/general/footer' ); + } + + /** + * Initializes user interface styles, scripts and footer. + * + * @since 1.0.0 + * @see trait TSF_Extension_Manager\UI + */ + private function init_tsfem_ui() { + + \add_action( 'tsfem_before_enqueue_scripts', [ $this, '_register_cord_scripts' ] ); + + //* Add something special for Vivaldi + \add_action( 'admin_head', [ $this, '_output_theme_color_meta' ], 0 ); + + /** + * Initialize UI calls. + * @see trait TSF_Extension_Manager\UI + */ + $this->init_ui(); + } + + /** + * Registers default TSFEM Cord admin scripts. + * Also registers TSF scripts, for TT (tooltip) support. + * + * @since 1.1.3 + * @access private + * @internal + * @staticvar bool $registered : Prevents Re-registering of the script. + * + * @param \The_SEO_Framework\Builders\Scripts $scripts + */ + public function _register_cord_scripts( $scripts ) { + static $registered = false; + if ( $registered ) return; + + /** + * Registers media scripts. + * @see trait TSF_Extension_Manager\UI + */ + $this->register_media_scripts(); + + $scripts::register( [ + [ + 'id' => 'tsfem-cord', + 'type' => 'js', + 'deps' => [ 'wp-util', 'tsf-tt', 'tsfem-form', 'tsfem-media' ], + 'autoload' => true, + 'name' => 'tsfem-cord', + 'base' => TSFEM_E_CORD_DIR_URL . 'lib/js/', + 'ver' => TSFEM_E_CORD_VERSION, + 'l10n' => [ + 'name' => 'tsfem_e_cordL10n', + 'data' => [ + 'nonce' => \wp_create_nonce( 'tsfem-e-cord-ajax-nonce' ), + 'i18n' => [], + ], + ], + ], + ] ); + $registered = true; + } + + /** + * Outputs Settings Panel overview for Cord settings. + * + * @since 1.0.0 + * + * @param object \TSF_Extension_Manager\Extension\Cord\Settings $_s Used for integrity. + */ + public function _get_cord_settings_overview( self $_s ) { + $this->get_view( 'layout/pages/settings' ); + } + + /** + * Outputs Statistics Panel overview for Cord stats. + * + * @since 1.0.0 + * + * @param object \TSF_Extension_Manager\Extension\Cord\Settings $_s Used for integrity. + */ + public function _get_cord_stats_overview( self $_s ) { + $this->get_view( 'layout/pages/stats' ); + } + + /** + * Outputs Logs Panel overview for Cord logs. + * + * @since 1.0.0 + * + * @param object \TSF_Extension_Manager\Extension\Cord\Settings $_s Used for integrity. + */ + public function _get_cord_logs_overview( self $_s ) { + $this->get_view( 'layout/pages/logs' ); + } + + /** + * Outputs bottom wrap for Cord Settings. + * + * @since 1.0.0 + * + * @param object \TSF_Extension_Manager\Extension\Cord\Settings $_s Used for integrity. + */ + public function _get_cord_settings_bottom_wrap( self $_s ) { + echo ''; + } + + /** + * Outputs bottom wrap for Cord Stats. + * + * @since 1.0.0 + * + * @param object \TSF_Extension_Manager\Extension\Cord\Settings $_s Used for integrity. + */ + public function _get_cord_stats_bottom_wrap( self $_s ) { + echo ''; + } + + /** + * Outputs bottom wrap for Cord Logs. + * + * @since 1.0.0 + * + * @param object \TSF_Extension_Manager\Extension\Cord\Settings $_s Used for integrity. + */ + public function _get_cord_logs_bottom_wrap( self $_s ) { + echo ''; + } + + /** + * Outputs theme color meta tag for Vivaldi and mobile browsers. + * Does not always work. So many browser bugs... It's just fancy. + * + * @since 1.0.0 + * @access private + */ + public function _output_theme_color_meta() { + $this->get_view( 'layout/general/meta' ); + } + + /** + * Fetches files based on input to reduce memory overhead. + * Passes on input vars. + * + * @since 1.0.0 + * + * @param string $view The file name. + * @param array $args The arguments to be supplied within the file name. + * Each array key is converted to a variable with its value attached. + */ + private function get_view( $view, array $args = [] ) { + + foreach ( $args as $key => $val ) { + $$key = $val; + } + + include TSFEM_E_CORD_DIR_PATH . 'views' . DIRECTORY_SEPARATOR . $view . '.php'; + } +} diff --git a/extensions/free/cord/trunk/inc/traits/secure-post.trait.php b/extensions/free/cord/trunk/inc/traits/secure-post.trait.php new file mode 100644 index 00000000..40bf77c4 --- /dev/null +++ b/extensions/free/cord/trunk/inc/traits/secure-post.trait.php @@ -0,0 +1,155 @@ +. + */ + +/** + * Holds secure POST functions for @package TSF_Extension_Manager\Extension\Cord\Settings. + * + * Note: This trait has dependencies! + * + * @since 1.0.0 + * @uses trait \TSF_Extension_Manager\Extension_Options + * @uses trait \TSF_Extension_Manager\Error + * @access private + * @errorval 109xxxx + */ +trait Secure_Post { + + /** + * The POST nonce validation name, action and name. + * + * @since 1.0.0 + * + * @var string The validation nonce name. + * @var string The validation request name. + * @var string The validation nonce action. + */ + protected $nonce_name; + protected $request_name = []; + protected $nonce_action = []; + + /** + * Sets extension nonces. + * + * @since 1.0.0 + */ + protected function set_nonces() { + + $this->nonce_name = 'tsfem_e_cord_nonce_name'; + + $this->request_name = [ + //* Reference convenience. + 'default' => 'default', + + //* Update options. + 'update' => 'update', + ]; + + $this->nonce_action = [ + //* Reference convenience. + 'default' => 'tsfem_e_cord_nonce_action', + + //* Update options. + 'update' => 'tsfem_e_cord_nonce_action_cord_update', + ]; + } + + /** + * Checks POST for data through admin actions. + * + * @since 1.0.0 + */ + protected function init_post_checks() { + + // AJAX only, not registered. Also, this method AFTER admin_init, so it went by unnoticed. + // \add_action( 'admin_init', [ $this, '_handle_update_post' ] ); + + if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { + $this->init_ajax_post_checks(); + } else { + $this->init_default_post_checks(); + } + } + + /** + * Checks AJAX POST for data through admin actions. + * + * @since 1.0.0 + */ + protected function init_ajax_post_checks() {} + + /** + * Checks POST for data through admin actions. + * + * @since 1.0.0 + */ + protected function init_default_post_checks() {} + + /** + * Checks AJAX form POST for data through admin actions. + * + * @NOTE: Nonce and user capabilities MUST be validated before calling this. + * + * @since 1.0.0 + * @uses trait \TSF_Extension_Manager\Extension_Options + * @uses trait \TSF_Extension_Manager\Error + * @uses class \TSF_Extension_Manager\Extension\Cord\Options + */ + public function _do_ajax_form_save() { + + $post_data = isset( $_POST['data'] ) ? $_POST['data'] : ''; // CSRF, sanitization, input var ok. + + parse_str( $post_data, $data ); + + $send = []; + + /** + * If this page doesn't parse the site options, + * there's no need to check them on each request. + */ + if ( empty( $data ) + || ( ! isset( $data[ TSF_EXTENSION_MANAGER_EXTENSION_OPTIONS ][ $this->o_index ] ) ) + || ( ! is_array( $data[ TSF_EXTENSION_MANAGER_EXTENSION_OPTIONS ][ $this->o_index ] ) ) + ) { + $type = 'failure'; + $send['results'] = $this->get_ajax_notice( false, 1090100 ); + } else { + + $options = $data[ TSF_EXTENSION_MANAGER_EXTENSION_OPTIONS ][ $this->o_index ]; + $success = $this->update_stale_options_array_by_key( $options ); + $this->process_all_stored_data(); + + if ( ! $success ) { + $type = 'failure'; + $send['results'] = $this->get_ajax_notice( false, 1090101 ); + } else { + $type = 'success'; + $send['results'] = $this->get_ajax_notice( true, 1090102 ); + $send['sdata'] = $this->get_stale_option( key( $options ) ); + } + } + + \tsf_extension_manager()->send_json( $send, \tsf_extension_manager()->coalesce_var( $type, 'failure' ) ); + } +} diff --git a/extensions/free/cord/trunk/index.php b/extensions/free/cord/trunk/index.php new file mode 100644 index 00000000..d8e50ed2 --- /dev/null +++ b/extensions/free/cord/trunk/index.php @@ -0,0 +1,2 @@ + + + + + + + + +
+

+ TSFEM_E_CORD_DIR_URL . 'lib/images/cordlogo.svg', + //'2x' => TSFEM_E_CORD_DIR_URL . 'lib/images/cordlogo-58x58.png', + '1x' => TSFEM_E_CORD_DIR_URL . 'lib/images/cordlogo-29x29px.png', + ]; + $size = '1em'; + + printf( + '', + sprintf( + '%2$s', + \esc_attr( $size ), + sprintf( + 'extension-icon', + \esc_url( $image['svg'], [ 'http', 'https' ] ), + \esc_url( $image['1x'], [ 'http', 'https' ] ), + \esc_attr( $size ) + ) + ), + 'Cord' + ); + ?> +

+
+ +
+ _do_pane_wrap_callable( + \__( 'Settings', 'the-seo-framework-extension-manager' ), + [ $this, '_get_cord_settings_overview' ], + [ + 'full' => false, + 'collapse' => true, + 'move' => false, + 'pane_id' => 'tsfem-e-cord-settings-pane', + 'ajax' => true, + 'ajax_id' => 'tsfem-e-cord-settings-ajax', + 'secure_obj' => true, + 'footer' => [ $this, '_get_cord_settings_bottom_wrap' ], + ] + ); + \tsf_extension_manager()->_do_pane_wrap_callable( + \__( 'Statistics', 'the-seo-framework-extension-manager' ), + [ $this, '_get_cord_stats_overview' ], + [ + 'full' => false, + 'collapse' => true, + 'move' => false, + 'push' => true, + 'pane_id' => 'tsfem-e-cord-stats-pane', + 'ajax' => true, + 'ajax_id' => 'tsfem-e-cord-stats-ajax', + 'secure_obj' => true, + 'footer' => [ $this, '_get_cord_stats_bottom_wrap' ], + ] + ); + ?> +
+
+ _do_pane_wrap_callable( + \__( 'Logs', 'the-seo-framework-extension-manager' ), + [ $this, '_get_cord_logs_overview' ], + [ + 'full' => true, + 'collapse' => true, + 'move' => false, + 'pane_id' => 'tsfem-e-cord-logs-pane', + 'ajax' => true, + 'ajax_id' => 'tsfem-e-cord-logs-ajax', + 'secure_obj' => true, + 'secure_obj' => true, + 'footer' => [ $this, '_get_cord_logs_bottom_wrap' ], + ] + ); + ?> +
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+`. -2. The [tsfep-extension name="title-fix"] annotation for that the Title is fixed: ``. +2. The [tsfep-extension name="title-fix"] annotation for that the title is fixed: ``. 3. At the bottom of the styled sitemap: `Generated by The SEO Framework`. ## Usage diff --git a/extensions/free/origin/trunk/readme.md b/extensions/free/origin/trunk/readme.md index 2d385565..812336ab 100644 --- a/extensions/free/origin/trunk/readme.md +++ b/extensions/free/origin/trunk/readme.md @@ -28,7 +28,7 @@ There is no setup required, and no options are available. [tsfep-image id="1"] -### Not all attachments are affected the same way +### Not all attachments are affected in the same way For an attachment page to redirect a visitor to its respective page, the attachment must be uploaded via the post edit screen. diff --git a/extensions/premium/local/trunk/inc/classes/admin.class.php b/extensions/premium/local/trunk/inc/classes/admin.class.php index e24eb20f..99bc0a91 100644 --- a/extensions/premium/local/trunk/inc/classes/admin.class.php +++ b/extensions/premium/local/trunk/inc/classes/admin.class.php @@ -114,8 +114,8 @@ public function _add_menu_link() { $menu = [ 'parent_slug' => \the_seo_framework()->seo_settings_page_slug, - 'page_title' => \esc_html__( 'Local SEO', 'the-seo-framework-extension-manager' ), - 'menu_title' => \esc_html__( 'Local', 'the-seo-framework-extension-manager' ), + 'page_title' => 'Local SEO', + 'menu_title' => 'Local', 'capability' => 'manage_options', 'menu_slug' => $this->local_page_slug, 'callback' => [ $this, '_output_local_settings_page' ], @@ -174,7 +174,7 @@ public function _do_settings_page_actions() { if ( \TSF_Extension_Manager\has_run( __METHOD__ ) ) return false; - $this->get_local_settings_instance()->_init( $this, $this->local_page_slug, $this->local_menu_page_hook ); + $this->get_local_settings_instance()->_init( $this, $this->local_page_slug, $this->local_menu_page_hook, $this->o_index ); return true; } @@ -191,7 +191,7 @@ protected function do_settings_page_ajax_actions() { if ( \TSF_Extension_Manager\has_run( __METHOD__ ) ) return false; - $this->get_local_settings_instance()->_init_ajax( $this ); + $this->get_local_settings_instance()->_init_ajax( $this, $this->o_index ); return true; } @@ -214,7 +214,7 @@ public function is_local_page() { } /** - * Sets up and returns Transporter_Steps. + * Sets up and returns \TSF_Extension_Manager\Extension\Local\Settings. * * @since 1.0.0 * diff --git a/extensions/premium/local/trunk/inc/classes/settings.class.php b/extensions/premium/local/trunk/inc/classes/settings.class.php index 00d9e313..02c981a0 100644 --- a/extensions/premium/local/trunk/inc/classes/settings.class.php +++ b/extensions/premium/local/trunk/inc/classes/settings.class.php @@ -83,11 +83,19 @@ final class Settings { * @since 1.0.0 * @access private * - * @param object \TSF_Extension_Manager\Extension\Local\Core $_core Used for integrity. - * @param string $slug The menu slug. - * @param string $hook The menu hook. + * @param object \TSF_Extension_Manager\Extension\Local\Core $_core + * Used for integrity. + * @param string $slug The menu slug. + * @param string $hook The menu hook. + * @param string $o_index The options index. */ - public function _init( Core $_core, $slug, $hook ) { + public function _init( Core $_core, $slug, $hook, $o_index ) { + + /** + * Set options index. + * @see trait TSF_Extension_Manager\Extension_Options + */ + $this->o_index = $o_index; $this->_init_main(); @@ -115,9 +123,17 @@ public function _init( Core $_core, $slug, $hook ) { * @since 1.0.0 * @access private * - * @param object \TSF_Extension_Manager\Extension\Local\Core $_core Used for integrity. + * @param object \TSF_Extension_Manager\Extension\Local\Core $_core + * Used for integrity. + * @param string $o_index The options index. */ - public function _init_ajax( Core $_core ) { + public function _init_ajax( Core $_core, $o_index ) { + /** + * Set options index. + * @see trait TSF_Extension_Manager\Extension_Options + */ + $this->o_index = $o_index; + $this->_init_main(); } @@ -134,12 +150,6 @@ private function _init_main() { if ( \TSF_Extension_Manager\has_run( __METHOD__ ) ) return; - /** - * Set options index. - * @see trait TSF_Extension_Manager\Extension_Options - */ - $this->o_index = 'local'; - /** * Set form arguments. * @see class TSF_Extension_Manager\FormGenerator diff --git a/extensions/premium/local/trunk/inc/traits/schema-packer.trait.php b/extensions/premium/local/trunk/inc/traits/schema-packer.trait.php index 14c037a2..29dbd2fb 100644 --- a/extensions/premium/local/trunk/inc/traits/schema-packer.trait.php +++ b/extensions/premium/local/trunk/inc/traits/schema-packer.trait.php @@ -104,7 +104,7 @@ private function correct_precision( $reset = false ) { if ( $this->should_change_precision() && $this->can_change_precision() ) { if ( $reset ) { - $prev && ini_set( 'serialize_precision', $prev ); + isset( $prev ) and ini_set( 'serialize_precision', $prev ); } else { $prev = ini_get( 'serialize_precision' ); ini_set( 'serialize_precision', '-1' ); @@ -143,7 +143,7 @@ protected function remove_scheme( $url ) { protected function get_schema() { $schema_file = TSFEM_E_LOCAL_DIR_PATH . 'lib' . DIRECTORY_SEPARATOR . 'schema' . DIRECTORY_SEPARATOR . 'schema.json'; - $timeout = stream_context_create( [ 'http' => [ 'timeout' => 3 ] ] ); + $timeout = stream_context_create( [ 'http' => [ 'timeout' => 3 ] ] ); return json_decode( file_get_contents( $schema_file, false, $timeout ) ); } @@ -203,7 +203,7 @@ protected function pack_data( array $data, $pretty = false ) { goto reset; } - $options = JSON_UNESCAPED_SLASHES; + $options = JSON_UNESCAPED_SLASHES; $options |= $pretty ? JSON_PRETTY_PRINT : 0; $output = json_encode( $_data, $options ); @@ -230,14 +230,14 @@ protected function pack_data( array $data, $pretty = false ) { */ protected function process_all_stored_data() { - $data = $this->get_stale_extension_options(); + $data = $this->get_stale_extension_options(); $schema = $this->get_schema(); $this->correct_precision(); $packer = new \TSF_Extension_Manager\SchemaPacker( $data, $schema ); - $count = isset( $data['department']['count'] ) ? $data['department']['count'] : 0; + $count = isset( $data['department']['count'] ) ? $data['department']['count'] : 0; $main_url = isset( $data['department'][1]['url'] ) ? $this->remove_scheme( $data['department'][1]['url'] ) : 1; $json_options = JSON_UNESCAPED_SLASHES | JSON_PRESERVE_ZERO_FRACTION; diff --git a/extensions/premium/local/trunk/inc/traits/secure-post.trait.php b/extensions/premium/local/trunk/inc/traits/secure-post.trait.php index 2ca4471c..e8ef8cc3 100644 --- a/extensions/premium/local/trunk/inc/traits/secure-post.trait.php +++ b/extensions/premium/local/trunk/inc/traits/secure-post.trait.php @@ -274,7 +274,7 @@ private function send_ajax_form_json_validation() { } else { $options = $data[ TSF_EXTENSION_MANAGER_EXTENSION_OPTIONS ][ $this->o_index ]; - $data = $this->pack_data( $options, true ); + $data = $this->pack_data( $options, true ); if ( ! $data ) { $type = 'failure'; diff --git a/extensions/premium/local/trunk/views/layout/general/footer.php b/extensions/premium/local/trunk/views/layout/general/footer.php index 14c75c4a..01e3d592 100644 --- a/extensions/premium/local/trunk/views/layout/general/footer.php +++ b/extensions/premium/local/trunk/views/layout/general/footer.php @@ -16,7 +16,7 @@ 'diligent', ]; $motto_key = mt_rand( 0, count( $mottos ) - 1 ); -$motto = 'A ' . $mottos[ $motto_key ] . ' Solution'; +$motto = 'A ' . $mottos[ $motto_key ] . ' Solution'; ?>
-_do_pane_wrap( \__( 'Control Panel', 'the-seo-framework-extension-manager' ), $this->get_cp_overview(), @@ -33,10 +33,10 @@ 'ajax_id' => 'tsfem-e-monitor-issues-ajax', ] ); -?> + ?>
-_do_pane_wrap( \__( 'Statistics', 'the-seo-framework-extension-manager' ), $this->get_stats_overview(), @@ -49,6 +49,6 @@ 'ajax_id' => 'tsfem-e-monitor-stats-ajax', ] ); -?> + ?>
. + */ + +/** + * Handles oAuth for various abitrary and undefined applications. + * + * @since 2.1.0 + * @access protected + * @abstract + */ +abstract class OAuth { + use Enclose_Core_Final, + Ignore_Properties_Core_Public_Final; + + public $response; + public $last_response; + public $auth_type; + public $nonce; + public $timestamp; + public $token; + public $version; + + public function __construct( $consumer_key, $consumer_secret, $signature_method = 'hmacsha1', $auth_type = 1 ) { + $this->consumer_key = $consumer_key; + $this->consumer_secret = $consumer_secret; + $this->signature_method = $signature_method; + $this->set_auth_type( $auth_type ); + } + + abstract public function generate_signature( $http_method, $url, $extra_parameters = null ); + + abstract public function get_access_token( $access_token_url, $auth_session_handle = '', $verifier_token = '', $http_method = 'GET' ); + + abstract public function get_request_token( $request_token_url, $url, $extra_parameters = null ); + + /** + * @param string $url The request URL. + * @param array $query_args The GET query arguments. + * @param string $http_method Accepts 'GET', 'POST', 'HEAD', 'PUT', 'DELETE', 'TRACE', 'OPTIONS', or 'PATCH'. + * Some transports technically allow others, but should not be assumed. Default 'GET'. + * @param array $http_args The HTTP request arguments, excluding the method. See $http_method. + */ + public function fetch( $url, $query_args = [], $http_method = 'GET', $http_args = [] ) { + + $this->last_response = $this->response; + + $url = \add_query_arg( $query_args, $url ); + + $http_args = array_merge( $http_args, [ + 'timeout' => 7, + 'headers' => [], + 'cookies' => [], + 'body' => null, + 'headers' => [], //! $http_method === 'POST' only. + ] ); + + $args['method'] = strtoupper( $http_method ); + + return $this->response = \wp_remote_request( $url, $http_args ); + } + + final public function get_response_body() { + return \wp_remote_retrieve_body( $this->response ); + } + + final public function get_response_header() { + return \wp_remote_retrieve_headers( $this->response ); + } + + final public function get_last_response() { + return $this->last_response; + } + + final public function get_last_response_headers() { + return \wp_remote_retrieve_headers( $this->last_response ); + } + + final public function get_last_response_body() { + return \wp_remote_retrieve_body( $this->last_response ); + } + + final public function set_auth_type( $auth_type = 0 ) { + $this->auth_type = $auth_type; + } + + final public function set_nonce( $nonce = '' ) { + $this->nonce = $nonce; + } + + final public function set_timestamp() { + $this->timestamp = microtime( true ); + } + + final public function set_token( $token, $token_secret ) { + $this->token = compact( 'token', 'token_secret' ); + } + + final public function set_version( $version ) { + $this->version = $version; + } +} diff --git a/inc/classes/adminpages.class.php b/inc/classes/adminpages.class.php index da8357d9..4a86ab18 100644 --- a/inc/classes/adminpages.class.php +++ b/inc/classes/adminpages.class.php @@ -123,7 +123,7 @@ final public function _add_menu_link() { $menu = [ 'parent_slug' => \the_seo_framework()->seo_settings_page_slug, - 'page_title' => \esc_html__( 'SEO Extensions', 'the-seo-framework-extension-manager' ), + 'page_title' => 'Extension Manager', 'menu_title' => \esc_html__( 'Extensions', 'the-seo-framework-extension-manager' ), 'capability' => TSF_EXTENSION_MANAGER_MAIN_ADMIN_ROLE, 'menu_slug' => $this->seo_extensions_page_slug, diff --git a/inc/classes/api.class.php b/inc/classes/api.class.php index e05bcbf8..4f2a8e80 100644 --- a/inc/classes/api.class.php +++ b/inc/classes/api.class.php @@ -420,6 +420,7 @@ final private function _verify_api_access( $object, $key ) { * Generates API access keys. * * @since 1.5.0 + * @since 2.1.0 Enabled entropy to prevent system sleep. * * @param string|bool $class The class name. If false, no key is generated. * @return array $keys, the storage keys. Passed by reference. @@ -429,7 +430,7 @@ final private function &generate_api_access_key( $class = false ) { static $keys = []; if ( false !== $class ) - $keys[ $class ] = mt_rand() . uniqid(); + $keys[ $class ] = mt_rand() . uniqid( '', true ); return $keys; } diff --git a/inc/classes/core.class.php b/inc/classes/core.class.php index 11d175fb..eedfd1ae 100644 --- a/inc/classes/core.class.php +++ b/inc/classes/core.class.php @@ -695,7 +695,7 @@ final protected function stop_class_filters( $current_filter, $key ) { * @access private * * @param string $instance The verification instance key. Passed by reference. - * @param int $bit The verification instance bit. Passed by reference. + * @param int $bit The verification instance bit. Passed by reference. * @return bool True if verified. */ final public function _verify_instance( &$instance, &$bit ) { @@ -711,9 +711,9 @@ final public function _verify_instance( &$instance, &$bit ) { * @since 1.0.0 * @access private * - * @param int $count The amount of instances to loop for. + * @param int $count The amount of instances to loop for. * @param string $instance The verification instance key. Passed by reference. - * @param array $bits The verification instance bits. Passed by reference. + * @param array $bits The verification instance bits. Passed by reference. * @yield array Generator : { * $instance string The verification instance key * $bits array The verification instance bits @@ -740,7 +740,7 @@ final public function _yield_verification_instance( $count, &$instance, &$bits ) * @param array $bits The verification bits. Passed by reference. */ final protected function get_verification_codes( &$instance = null, &$bits = null ) { - $bits = $this->get_bits(); + $bits = $this->get_bits(); $instance = $this->get_verification_instance( $bits[1] ); } @@ -781,7 +781,7 @@ final protected function get_verification_instance( $bit = null ) { } //* Set retval and empty to prevent recursive timing attacks. - $_retval = $instance[ $bit ]; + $_retval = $instance[ $bit ]; $instance = []; return $_retval; @@ -1089,9 +1089,9 @@ final public function is_plugin_in_network_mode() { * @see $this->_yield_verification_instance() for faster looping instances. * @access private * - * @param object $object The class object. Passed by reference. + * @param object $object The class object. Passed by reference. * @param string $_instance The verification instance. Passed by reference. - * @param array $bits The verification bits. Passed by reference. + * @param array $bits The verification instance bits. Passed by reference. * @return bool True on success, false on failure. */ final public function _request_premium_extension_verification_instance( &$object, &$_instance, &$bits ) { @@ -1253,7 +1253,7 @@ final protected function autoload_extension_class( $class ) { return $loaded[ $class ]; $_class = str_replace( '\\TSF_Extension_Manager\\Extension\\', '', $class ); - $_ns = substr( $_class, 0, strpos( $_class, '\\' ) ); + $_ns = substr( $_class, 0, strpos( $_class, '\\' ) ); $_path = $this->get_extension_autload_path( $_ns ); @@ -1269,9 +1269,11 @@ final protected function autoload_extension_class( $class ) { //= Needs to be "_once", because `Extensions_Actions::include_extension` also loads it. return $loaded[ $class ] = require_once $_path . $_file . '.class.php'; } else { - \the_seo_framework()->_doing_it_wrong( __METHOD__, 'Class ' . \esc_html( $class ) . ' has not been registered.' ); + \the_seo_framework()->_doing_it_wrong( __METHOD__, 'Class ' . \esc_html( $class ) . ' has not been registered. Check the capitalization!' ); + + //* Prevent fatal errors. + $this->create_class_alias( $class ); - //* Most likely, a fatal error will now occur. return $loaded[ $class ] = false; } } diff --git a/inc/classes/inpostgui.class.php b/inc/classes/inpostgui.class.php index 1504e9bf..6dd520f7 100644 --- a/inc/classes/inpostgui.class.php +++ b/inc/classes/inpostgui.class.php @@ -613,6 +613,7 @@ public function _output_tab_content( $tab ) { * @see \TSF_Extension_Manager\InpostGUI::verify( $secret ) * * @since 1.5.0 + * @since 2.1.0 Enabled entropy to prevent system sleep. * @uses static::$include_secret * * @param string $file The file location. @@ -625,7 +626,7 @@ private function output_view( $file, array $args ) { unset( $_key, $_val, $args ); //= Prevent private includes hijacking. - static::$include_secret = $_secret = mt_rand() . uniqid(); + static::$include_secret = $_secret = mt_rand() . uniqid( '', true ); include $file; static::$include_secret = null; } diff --git a/inc/classes/layout.class.php b/inc/classes/layout.class.php index 08d8c91e..8ff39ac9 100644 --- a/inc/classes/layout.class.php +++ b/inc/classes/layout.class.php @@ -40,11 +40,11 @@ final class Layout extends Secure_Abstract { * * @since 1.0.0 * - * @param string $type Required. The instance type. - * @param string $instance Required. The instance key. Passed by reference. - * @param int $bit Required. The instance bit. Passed by reference. + * @param string $type Required. The instance type. + * @param string $instance Required. The verification instance key. Passed by reference. + * @param array $bits Required. The verification instance bits. Passed by reference. */ - public static function initialize( $type = '', &$instance = '', &$bits = null ) { + public static function initialize( $type = '', &$instance = '', &$bits = [] ) { self::reset(); @@ -129,14 +129,16 @@ private static function get_disconnect_button() { if ( 'form' === self::get_property( '_type' ) ) { $nonce_action = \tsf_extension_manager()->_get_nonce_action_field( self::$request_name['deactivate'] ); - $nonce = \wp_nonce_field( self::$nonce_action['deactivate'], self::$nonce_name, true, false ); + $nonce = \wp_nonce_field( self::$nonce_action['deactivate'], self::$nonce_name, true, false ); $field_id = 'disconnect-switcher'; + $deactivate_i18n = \__( 'Disconnect', 'the-seo-framework-extension-manager' ); - $ays_i18n = \__( 'Are you sure?', 'the-seo-framework-extension-manager' ); - $da_i18n = \__( 'Disconnect account?', 'the-seo-framework-extension-manager' ); + $ays_i18n = \__( 'Are you sure?', 'the-seo-framework-extension-manager' ); + $da_i18n = \__( 'Disconnect account?', 'the-seo-framework-extension-manager' ); $button_class = 'tsfem-switcher-button tsfem-button-primary tsfem-button-red tsfem-button-warning'; + $button = vsprintf( '', [ @@ -146,8 +148,9 @@ private static function get_disconnect_button() { ] ); - $switcher_class = 'tsfem-button-flag tsfem-button'; + $switcher_class = 'tsfem-button-flag tsfem-button'; $switcher_class .= \tsf_extension_manager()->are_options_valid() ? '' : ' tsfem-button-pulse'; + $switcher = '
' . '' . '' @@ -246,27 +249,27 @@ private static function get_account_info() { switch ( $level ) : case 'Enterprise': - $_level = \__( 'Enterprise', 'the-seo-framework-extension-manager' ); + $_level = \__( 'Enterprise', 'the-seo-framework-extension-manager' ); $_class[] = $valid_options ? 'tsfem-success' : 'tsfem-error'; break; case 'Premium': - $_level = \__( 'Premium', 'the-seo-framework-extension-manager' ); + $_level = \__( 'Premium', 'the-seo-framework-extension-manager' ); $_class[] = $valid_options ? 'tsfem-success' : 'tsfem-error'; break; case 'Essentials': - $_level = \__( 'Essentials', 'the-seo-framework-extension-manager' ); + $_level = \__( 'Essentials', 'the-seo-framework-extension-manager' ); $_class[] = $valid_options ? 'tsfem-success' : 'tsfem-error'; break; case 'Free': - $_level = \__( 'Free', 'the-seo-framework-extension-manager' ); + $_level = \__( 'Free', 'the-seo-framework-extension-manager' ); $_class[] = $valid_options ? 'tsfem-success' : 'tsfem-error'; break; default: - $_level = $level; + $_level = $level; $_class[] = 'tsfem-error'; break; endswitch; diff --git a/inc/traits/core/error.trait.php b/inc/traits/core/error.trait.php index 271c170e..984e1b82 100644 --- a/inc/traits/core/error.trait.php +++ b/inc/traits/core/error.trait.php @@ -584,17 +584,19 @@ protected function get_error_notice_by_key( $key, $get_type = true ) { break; case 1070100: - case 1070701: + case 1090100: $message = \esc_html__( 'Invalid data was sent to the server.', 'the-seo-framework-extension-manager' ); $type = 'error'; break; case 1070101: - $message = \esc_url__( "Settings aren't saved", 'the-seo-framework-extension-manager' ); + case 1090101: + $message = \esc_url__( "A database error occurred. Settings aren't saved.", 'the-seo-framework-extension-manager' ); $type = 'error'; break; case 1070102: + case 1090102: $message = \esc_html__( 'Settings are saved.', 'the-seo-framework-extension-manager' ); $type = 'success'; break; @@ -602,7 +604,7 @@ protected function get_error_notice_by_key( $key, $get_type = true ) { case 1011700: case 1071100: case 1071101: - $message = \esc_html__( 'Unable to verify if settings are saved.', 'the-seo-framework-extension-manager' ); + $message = \esc_html__( 'Unable to verify if settings are saved. Refresh this page to manually verify.', 'the-seo-framework-extension-manager' ); $type = 'error'; break; diff --git a/inc/traits/manager/extensions.trait.php b/inc/traits/manager/extensions.trait.php index 4e2287f3..50b11cf8 100644 --- a/inc/traits/manager/extensions.trait.php +++ b/inc/traits/manager/extensions.trait.php @@ -78,134 +78,147 @@ private static function get_extensions() { */ return [ 'local' => [ - 'slug' => 'local', - 'network' => '0', - 'type' => 'premium', - 'area' => 'business', - 'author' => 'Sybre Waaijer', - 'party' => 'first', + 'slug' => 'local', + 'network' => '0', + 'type' => 'premium', + 'area' => 'business', + 'author' => 'Sybre Waaijer', + 'party' => 'first', 'last_updated' => '1546685158', - 'requires' => '4.7.0', - 'tested' => '5.0.3', + 'requires' => '4.7.0', + 'tested' => '5.0.3', 'requires_tsf' => '3.1.0', - 'tested_tsf' => '3.2.2', + 'tested_tsf' => '3.2.3', ], 'focus' => [ - 'slug' => 'focus', - 'network' => '0', - 'type' => 'essentials+', - 'area' => 'audit, content, keywords', - 'author' => 'Sybre Waaijer', - 'party' => 'first', + 'slug' => 'focus', + 'network' => '0', + 'type' => 'essentials+', + 'area' => 'audit, content, keywords', + 'author' => 'Sybre Waaijer', + 'party' => 'first', 'last_updated' => '1547763663', - 'requires' => '4.8.0', - 'tested' => '5.0.3', + 'requires' => '4.8.0', + 'tested' => '5.0.3', 'requires_tsf' => '3.1.0', - 'tested_tsf' => '3.2.2', + 'tested_tsf' => '3.2.3', ], 'articles' => [ - 'slug' => 'articles', - 'network' => '0', - 'type' => 'essentials', - 'area' => 'blogging, news', - 'author' => 'Sybre Waaijer', - 'party' => 'first', + 'slug' => 'articles', + 'network' => '0', + 'type' => 'essentials', + 'area' => 'blogging, news', + 'author' => 'Sybre Waaijer', + 'party' => 'first', 'last_updated' => '1541599634', - 'requires' => '4.6.0', - 'tested' => '5.0.3', + 'requires' => '4.6.0', + 'tested' => '5.0.3', 'requires_tsf' => '2.8.2', - 'tested_tsf' => '3.2.2', + 'tested_tsf' => '3.2.3', ], 'honeypot' => [ - 'slug' => 'honeypot', - 'network' => '0', - 'type' => 'essentials', - 'area' => 'anti-spam', - 'author' => 'Sybre Waaijer', - 'party' => 'first', + 'slug' => 'honeypot', + 'network' => '0', + 'type' => 'essentials', + 'area' => 'anti-spam', + 'author' => 'Sybre Waaijer', + 'party' => 'first', 'last_updated' => '1542470700', - 'requires' => '4.6.0', - 'tested' => '5.0.3', + 'requires' => '4.6.0', + 'tested' => '5.1.0', 'requires_tsf' => '2.7.0', - 'tested_tsf' => '3.2.2', + 'tested_tsf' => '3.2.3', ], + // 'cord' => [ + // 'slug' => 'cord', + // 'network' => '0', + // 'type' => 'free', + // 'area' => 'indexing', + // 'author' => 'Sybre Waaijer', + // 'party' => 'first', + // 'last_updated' => '1551428730', + // 'requires' => '4.9.0', + // 'tested' => '5.1.0', + // 'requires_tsf' => '3.2.0', + // 'tested_tsf' => '3.2.3', + // ], 'amp' => [ - 'slug' => 'amp', - 'network' => '0', - 'type' => 'free', - 'area' => 'general', - 'author' => 'Sybre Waaijer', - 'party' => 'first', + 'slug' => 'amp', + 'network' => '0', + 'type' => 'free', + 'area' => 'general', + 'author' => 'Sybre Waaijer', + 'party' => 'first', 'last_updated' => '1534366523', - 'requires' => '4.6.0', - 'tested' => '5.0.3', + 'requires' => '4.6.0', + 'tested' => '5.0.3', 'requires_tsf' => '2.8.2', - 'tested_tsf' => '3.2.2', + 'tested_tsf' => '3.2.3', ], 'monitor' => [ - 'slug' => 'monitor', - 'network' => '0', - 'type' => 'premium', - 'area' => 'uptime, syntax', - 'author' => 'Sybre Waaijer', - 'party' => 'first', + 'slug' => 'monitor', + 'network' => '0', + 'type' => 'premium', + 'area' => 'uptime, syntax', + 'author' => 'Sybre Waaijer', + 'party' => 'first', 'last_updated' => '1546666851', - 'requires' => '4.6.0', - 'tested' => '5.0.3', + 'requires' => '4.6.0', + 'tested' => '5.1.0', 'requires_tsf' => '3.1.0', - 'tested_tsf' => '3.2.2', + 'tested_tsf' => '3.2.3', ], 'incognito' => [ - 'slug' => 'incognito', - 'network' => '0', - 'type' => 'free', - 'area' => 'general', - 'author' => 'Sybre Waaijer', - 'party' => 'first', + 'slug' => 'incognito', + 'network' => '0', + 'type' => 'free', + 'area' => 'general', + 'author' => 'Sybre Waaijer', + 'party' => 'first', 'last_updated' => '1515109560', - 'requires' => '3.6.0', - 'tested' => '5.0.3', + 'requires' => '3.6.0', + 'tested' => '5.1.0', 'requires_tsf' => '2.2.0', - 'tested_tsf' => '3.2.2', + 'tested_tsf' => '3.2.3', ], 'origin' => [ - 'slug' => 'origin', - 'network' => '0', - 'type' => 'free', - 'area' => 'media', - 'author' => 'Sybre Waaijer', - 'party' => 'first', + 'slug' => 'origin', + 'network' => '0', + 'type' => 'free', + 'area' => 'media', + 'author' => 'Sybre Waaijer', + 'party' => 'first', 'last_updated' => '1541601833', - 'requires' => '4.6.0', - 'tested' => '5.0.3', + 'requires' => '4.6.0', + 'tested' => '5.1.0', 'requires_tsf' => '2.7.0', - 'tested_tsf' => '3.2.2', + 'tested_tsf' => '3.2.3', ], 'title-fix' => [ - 'slug' => 'title-fix', - 'network' => '0', - 'type' => 'free', - 'area' => 'theme', - 'author' => 'Sybre Waaijer', - 'party' => 'first', + 'slug' => 'title-fix', + 'network' => '0', + 'type' => 'free', + 'area' => 'theme', + 'author' => 'Sybre Waaijer', + 'party' => 'first', 'last_updated' => '1534366523', - 'requires' => '4.6.0', - 'tested' => '5.0.3', + 'requires' => '4.6.0', + 'tested' => '5.1.0', 'requires_tsf' => '2.7.0', - 'tested_tsf' => '3.2.2', + 'tested_tsf' => '3.2.3', ], // 'transporter' => [ - // 'slug' => 'transporter', - // 'network' => '0', - // 'type' => 'free', - // 'area' => 'media', - // 'author' => 'Sybre Waaijer', - // 'party' => 'first', + // 'slug' => 'transporter', + // 'network' => '0', + // 'type' => 'free', + // 'area' => 'media', + // 'author' => 'Sybre Waaijer', + // 'party' => 'first', // 'last_updated' => '1510175308', - // 'requires' => '4.4.0', - // 'tested' => '4.9.0', + // 'requires' => '4.4.0', + // 'tested' => '4.9.0', // 'requires_tsf' => '2.7.0', - // 'tested_tsf' => '3.0.0', + // 'tested_tsf' => '3.0.0', // ], ]; } @@ -223,9 +236,9 @@ private static function get_extensions() { */ private static function get_external_extensions_checksum() { return [ - 'sha256' => '52d25dc09b464caa0776227eff1fe9ea67465e2df20c47b06a468bb59e152281', - 'sha1' => '00a56ea8588dd6eea4ed2da399cbc0bb1731ed29', - 'md5' => 'b0ad3c6f630ba67179a44bc545014539', + 'sha256' => 'db2a53ca76e1c367324fddfe663388145206e8c7247350d3f4b62a1fca89eeaa', + 'sha1' => '578225b718c66b65a944b398f6dbb9d59005f6ce', + 'md5' => '63762091e66237ffc12feac67d491cb6', ]; } @@ -249,7 +262,14 @@ private static function get_extension( $slug ) { if ( isset( $extensions[ $slug ] ) ) { return $extensions[ $slug ]; } else { - \the_seo_framework()->_doing_it_wrong( __CLASS__ . '::' . __FUNCTION__, 'You must specify an existing extension slug.' ); + //! Extension that doesn't exist is registered as activated. + //! The user can't remove this notice without disconnecting the account. TODO Purge it? --> Not here! + //! TODO run $this->disable_extension( $slug ) and register a notice: "${slug} no longer exists" + //! TODO Forward this to the upgrader for whenever an extension's removed. + \the_seo_framework()->_doing_it_wrong( + __CLASS__ . '::' . __FUNCTION__, + sprintf( 'You must specify an existing extension slug. %s does not exist.', \esc_html( $slug ) ) + ); return []; } } else { @@ -650,6 +670,8 @@ private static function is_extension_compatible( $extension ) { if ( is_string( $extension ) ) $extension = static::get_extension( $extension ); + if ( ! $extension ) return -1; + static $cache = []; if ( isset( $cache[ $extension['slug'] ] ) ) @@ -749,6 +771,8 @@ private static function get_extension_compatibility( $extension ) { 'wp' => 0, ]; + if ( ! $extension ) return $compatibility; + $_tsf_version = THE_SEO_FRAMEWORK_VERSION; $_wp_version = $wp_version; diff --git a/readme.txt b/readme.txt index f4fe75e2..d5d8cb52 100644 --- a/readme.txt +++ b/readme.txt @@ -5,7 +5,7 @@ Tags: seo, extensions, local, keyword, articles, monitor, modules, schema, honey Requires at least: 4.6.0 Tested up to: 5.0.3 Requires PHP: 5.5.21 -Stable tag: 2.0.2 +Stable tag: 2.0.4 License: GPLv3 License URI: http://www.gnu.org/licenses/gpl-3.0.html @@ -168,6 +168,20 @@ If you were to get a plugin activation error, either open a support ticket [here == Changelog == += 2.1.0 = + +**Release date:** + +* Month nth, 2019 + +**Plugin improvements:** + +* **Changed:** TODO The plugin now communicates with `tsfcloud.net` (or .com?), instead of `dl.theseoframework.com` and `premium.theseoframework.com`. +* **Improved:** When an extension object is incorrectly registered, the plugin will now prevent a crash. +* **Improved:** The plugin's now lag-free on virtual machines running Windows Server, as it no longer has to wait for precision timers when creating communication keys. + +* TODO reinstate tsfem-button-pulse class. -> hue rotate? + = 2.0.4 = **Release date:** diff --git a/the-seo-framework-extension-manager.php b/the-seo-framework-extension-manager.php index 617c35b8..84587184 100644 --- a/the-seo-framework-extension-manager.php +++ b/the-seo-framework-extension-manager.php @@ -3,7 +3,7 @@ * Plugin Name: The SEO Framework - Extension Manager * Plugin URI: https://theseoframework.com/extension-manager/ * Description: Add more powerful SEO features to The SEO Framework right from your WordPress Dashboard. - * Version: 2.0.4 + * Version: 2.1.0-dev * Author: Sybre Waaijer * Author URI: https://theseoframework.com/ * License: GPLv3 @@ -39,7 +39,7 @@ * The plugin version. Always 3 point. * @since 1.0.0 */ -define( 'TSF_EXTENSION_MANAGER_VERSION', '2.0.4' ); +define( 'TSF_EXTENSION_MANAGER_VERSION', '2.1.0' ); /** * The plugin's database version. diff --git a/views/layout/general/top.php b/views/layout/general/top.php index 61f2c1e5..4db8e87f 100644 --- a/views/layout/general/top.php +++ b/views/layout/general/top.php @@ -47,8 +47,6 @@ $about = '
' . \esc_html( $info ) . '
'; } -$extensions_i18n = \__( 'Extensions', 'the-seo-framework-extension-manager' ); - /** * Test for GD library functionality upon logo. * @@ -89,10 +87,9 @@ $size = '1em'; printf( - /* translators: %1$s = SEO, %2$s = Extensions */ - \esc_html__( '%1$s %2$s', 'the-seo-framework-extension-manager' ), + '%s %s', sprintf( - '', + '', sprintf( '%2$s', \esc_attr( $size ), @@ -104,7 +101,7 @@ ) ) ), - \esc_html( $extensions_i18n ) + 'Manager' ); ?>