diff --git a/extensions/premium/analytics/trunk/analytics.php b/extensions/premium/analytics/trunk/analytics.php index 989a86dd..09976f9d 100644 --- a/extensions/premium/analytics/trunk/analytics.php +++ b/extensions/premium/analytics/trunk/analytics.php @@ -95,7 +95,7 @@ function analytics_init() { if ( isset( $loaded ) ) return $loaded; - tsf_extension_manager()->register_premium_extension_autoload_path( TSFEM_E_ANALYTICS_PATH_CLASS, 'Analytics' ); + tsf_extension_manager()->_register_premium_extension_autoload_path( TSFEM_E_ANALYTICS_PATH_CLASS, 'Analytics' ); if ( is_admin() ) { new Analytics_Admin(); diff --git a/extensions/premium/monitor/trunk/inc/classes/monitor-admin.class.php b/extensions/premium/monitor/trunk/inc/classes/monitor-admin.class.php index c5f791bd..d4762d4d 100644 --- a/extensions/premium/monitor/trunk/inc/classes/monitor-admin.class.php +++ b/extensions/premium/monitor/trunk/inc/classes/monitor-admin.class.php @@ -243,7 +243,7 @@ public function init_monitor_page() { ?>
output_monitor_overview_wrapper(); + $this->output_monitor_overview_wrapper(); ?>
get_view( 'layout/pages/meta' ); } - /** - * Wraps pane data into HTML objects. Iterates over data. - * - * @since 1.0.0 - * @uses TSF_Extension_Manager_Extension\Monitor_Data->interpret_data_input() - * @generator - */ - protected function output_pane_data( $data, $type = '' ) { - echo '
'; - foreach ( $data as $key => $val ) { - yield $this->interpret_data_input( $key, $val, $type ); - } - echo '
'; - } - /** * Creates issues overview for the issues pane. * @@ -352,16 +337,18 @@ protected function output_pane_data( $data, $type = '' ) { */ protected function get_issues_overview() { + $output = ''; $issues = $this->get_data( 'issues', array() ); if ( empty( $issues ) ) { - return esc_html__( 'No data has been found as of yet.', 'the-seo-framework-extension-manager' ); + $output .= esc_html__( 'No data has been found as of yet.', 'the-seo-framework-extension-manager' ); } else { - /* foreach ( $this->output_pane_data( $issues, 'issues' ) as $output ) : - //* Already escaped. - echo $output; - endforeach;*/ + foreach ( $this->render_pane_slab_data( $issues, 'issues' ) as $slab ) + $output .= $slab; + } + + return sprintf( '
%s
', $output ); } /** @@ -396,6 +383,92 @@ protected function get_statistics_overview() { } } + /** + * Iterates over pane slab data. + * + * @since 1.0.0 + * @uses TSF_Extension_Manager_Extension\Monitor_Admin->setup_slab_title_prefix() + * @uses TSF_Extension_Manager_Extension\Monitor_Admin->make_slab_nav_entry() + * @uses TSF_Extension_Manager_Extension\Monitor_Admin->make_slab_info_entry() + * @generator + * + * @param array $data The fetched data. + * @param string $type The pane-date type. + * @yields Interpreted data from array in two slabs (js) or beneath eachother (no-js). + */ + protected function render_pane_slab_data( $data = array(), $type = '' ) { + + foreach ( $data as $key => $value ) : + //* @TODO var_dump() + //$this->setup_slab_title_prefix( $key, $value, $type ); + + yield $this->make_slab_nav_entry( $key, $type, -1 ); + yield $this->make_slab_info_entry( $key, $value, $type ); + endforeach; + + } + + /** + * Makes slab entry title from input $key and $type for when no JS is present. + * + * @since 1.0.0 + * + * @param string $key The array key. + * @param string $type The pane-data type. + * @return string The HTML formed data. + */ + protected function make_slab_nav_entry( $key, $type ) { + + $title = $this->get_slab_entry_title( $key, $type ); + $prefix = $this->get_slab_nav_entry_state_sign( $key, $type ); + + return sprintf( '

%s%s

', esc_attr( $key ), $prefix, esc_html( $title ) ); + } + + /** + * Interprets data input and finds an appropriate content function for it. + * + * @since 1.0.0 + * + * @param string $key The array key. + * @param mixed $value The array value attached to $key. + * @param string $type The pane-data type. + * @return string The HTML formed data. + */ + protected function make_slab_info_entry( $key, $value, $type ) { + + $output = Monitor_Output::parse_content( $key, $value, $type ); + + return sprintf( '
%s
', esc_attr( $key ), $output ); + } + + protected function get_slab_nav_entry_state_sign( $key, $type ) { + // TODO: set "good/okay/warning/bad/unknown" signs. + return '_X_'; + } + + /** + * Returns slab entry title based on $key and $type. + * + * @since 1.0.0 + * @staticvar array $cache Maintains the titles cache. + * + * @param string $key The array key. + * @param string $type The pane-data type. + * @return string The escaped $type $key title. + */ + protected function get_slab_entry_title( $key, $type ) { + + static $cache = array(); + + if ( isset( $cache[ $type ][ $key ] ) ) + return $cache[ $type ][ $key ]; + + $title = Monitor_Output::parse_title( $key, $type ); + + return $cache[ $type ][ $key ] = esc_html( $title ); + } + /** * Fetches files based on input to reduce memory overhead. * Passes on input vars. diff --git a/extensions/premium/monitor/trunk/inc/classes/monitor-data.class.php b/extensions/premium/monitor/trunk/inc/classes/monitor-data.class.php index db901d36..ca0f7bbe 100644 --- a/extensions/premium/monitor/trunk/inc/classes/monitor-data.class.php +++ b/extensions/premium/monitor/trunk/inc/classes/monitor-data.class.php @@ -111,85 +111,157 @@ protected function fetch_new_data( $ajax = false ) { return $fetched; } + /** + * Fetches remote monitor data to later be evaluated. + * + * @TODO make notes of: + * The data set is as follows : { + * 'type' => array : { // issues, poi, statistics + * 'key' => array : { // The issue instance key, like title, description, etc. + * 'test' => array : { // The test that has followed. + * 'type' : string // The type of test, i.e. "manual, equals, etc." + * // A manual test would require local implementation to parse the data. + * } + * } + * } + * } + * + * @return array The remote monitor data. + */ protected function api_get_remote_data() { //* This dummy data does NOT represent the final outcome. It's still in very much dev-environment. //* Please, don't expect anything from what you see here. It's a mind-map. $planned_dummy_data = array( 'issues' => array( - // Are titles outputted as it should? Take 3 samples and test them. + /* @NOTE might not be secure... and is overkill of data. + // Are titles outputted as it should? Take 2 samples and test them. 'title' => array( - 'home' => array( - array( - 'id' => 0, - 'value' => 'My WordPress Site — Just another WordPress site', - ), - ), - 'category' => array( - array( - 'id' => 1, - 'value' => 'Category: Uncategorized — My WordPress Site', - ), - ), - 'post' => array( - array( - 'id' => 1, - 'value' => 'Hello world! — My WordPress Site', + 'test' => array( + 'type' => 'equals', + 'requires_tsf' => '2.7.0', + 'func' => array( 'the_seo_framework', 'title' ), // VAR_DUMP() warning: is this secure??? + 'iterations' => array( + array( + 'value' => 'My WordPress Site — Just another WordPress site', + 'args' => array( + 'title' => '', + 'sep' => '', + 'seplocation' => '', + 'args' => array( + 'term_id' => 0, + 'taxonomy' => '', + 'meta' => array( 'boolean', true ), + 'page_on_front' => array( 'boolean', true ), + ), + ), + ), + array( + 'value' => 'Hello world! — My WordPress Site', + 'args' => array( + 'title' => '', + 'sep' => '', + 'seplocation' => '', + 'args' => array( + 'term_id' => 1, + 'taxonomy' => '', + 'meta' => array( 'boolean', true ), + 'page_on_front' => array( 'boolean', false ), + ), + ), + ), ), ), ), - // Are descriptions outputted as it should? Take 3 samples and test them. + // Are descriptions outputted as it should? Take 2 samples and test them. 'description' => array( - 'home' => array( - array( - 'id' => 0, - 'value' => 'Just another WordPress site on My WordPress Site', - ), - ), - 'category' => array( - array( - 'id' => 1, - 'value' => 'Uncategorized on My WordPress Site', - ), - ), - 'post' => array( - array( - 'id' => 1, - 'value' => 'Hello world! on My WordPress Site | Welcome to WordPress. This is your first post. Edit or delete it, then start writing!', + 'test' => array( + 'type' => 'equals', + 'requires_tsf' => '2.7.0', + 'func' => array( 'the_seo_framework', 'generate_description' ), + 'iterations' => array( + array( + 'value' => 'Just another WordPress site on My WordPress Site', + 'args' => array( + 'description' => '', + 'args' => array( + 'id' => 0, + 'taxonomy' => '', + 'get_custom_field' => array( 'boolean', true ), + 'is_home' => array( 'boolean', true ), + 'social' => array( 'boolean', true ), + ), + ), + ), + array( + 'value' => 'Hello world! on My WordPress Site | Welcome to WordPress. This is your first post. Edit or delete it, then start writing!', + 'args' => array( + 'description' => '', + 'args' => array( + 'id' => 1, + 'taxonomy' => '', + 'get_custom_field' => array( 'boolean', true ), + 'is_home' => array( 'boolean', false ), + 'social' => array( 'boolean', false ), + ), + ), + ), ), ), ), // Is canonical URL equal to page, and if not - are settings applied? 'canonical' => array( - 'home' => array( - array( - 'id' => 0, - 'value' => 'Just another WordPress site on My WordPress Site', - ), - ), - 'category' => array( - array( - 'id' => 1, - 'value' => 'Uncategorized on My WordPress Site', - ), - ), - 'post' => array( - array( - 'id' => 1, - 'value' => 'Hello world! on My WordPress Site | Welcome to WordPress. This is your first post. Edit or delete it, then start writing!', + 'test' => array( + 'type' => 'equals', + 'requires_tsf' => '2.7.0', + 'func' => array( 'the_seo_framework', 'the_url' ), + 'iterations' => array( + array( + 'value' => 'http://testmijnphp7.nl/', + 'args' => array( + 'url' => '', + 'args' => array( + 'id' => 0, + 'taxonomy' => '', + 'get_custom_field' => array( 'boolean', true ), + 'paged' => array( 'boolean', false ), + 'paged_plural' => array( 'boolean', false ), + 'home' => array( 'boolean', true ), + 'external' => array( 'boolean', true ), + ), + ), + ), + array( + 'value' => 'http://testmijnphp7.nl/hello-world/', + 'args' => array( + 'url' => '', + 'args' => array( + 'id' => 1, + 'taxonomy' => '', + 'get_custom_field' => array( 'boolean', true ), + 'paged' => array( 'boolean', false ), + 'paged_plural' => array( 'boolean', false ), + 'home' => array( 'boolean', false ), + 'external' => array( 'boolean', true ), + ), + ), + ), ), ), ), - // Is favicon set up? If not, mark if not static in public_html/www folder. + */ + // Is favicon set up? If not, mark if not static in public_html or www folder. 'favicon' => array( - // Test for on the homepage. - 'meta' => true, - // Test for http://example.com/favicon.ico - 'static' => false, + 'requires' => '1.0.0', + 'data' => array( + // Test for on the homepage. + 'meta' => true, + // Test for http://example.com/favicon.ico + 'static' => false, + ), ), - // Are there any duplicated pages? If so -> open submenu?? TODO. - 'duplicated' => array(), // Is theme mobile? If so, does it overflow? : 2 settings: ipad & iphone. + /* @TODO 'mobile' => array( 'home' => array( array( @@ -220,33 +292,44 @@ protected function api_get_remote_data() { 'html' => array( // Is this even feasible? ), + */ // Is there a html closing tag, at all? 'php' => array( - 'home' => array( + 'requires' => '1.0.0', + 'data' => array( array( - 'id' => 0, - 'closed' => 1, // Good. + 'home' => true, + 'post_id' => 0, + 'value' => true, ), - ), - 'category' => array( array( - 'id' => 1, - 'closed' => 0, // Bad. - ), - ), - 'post' => array( - array( - 'id' => 1, - 'closed' => 1, // Good. + 'home' => false, + 'post_id' => 1, + 'value' => false, ), ), ), + /* @TODO // Are images valid, and do they support mobile? Are they also optimized for performance? TODO 'img' => array( // Is this even feasible? ), // Is robots static or dynamic? If static, tell them. If dynamic, tell if it works. 'robots' => array( + 'test' => array( + 'type' => 'exists', + 'notify_key' => array( 'link', 'post_id' ), + 'values' => array( + array( + 'post_id' => 1, + 'value' => true, + ), + array( + 'post_id' => 1, + 'value' => true, + ), + ), + ), 'located' => true, 'value' => "User-agent: * \nDisallow: /", // This is expected with indexing disabled. Test with internal robots.txt, or from TSF? ), @@ -291,6 +374,7 @@ protected function api_get_remote_data() { 'external' => array( '', // Is this even feasible? ), + */ ), 'poi' => array( // Is the website too big in size? If so, is the issue HTMl, JS, CSS, img, etc. diff --git a/extensions/premium/monitor/trunk/inc/classes/monitor-output.class.php b/extensions/premium/monitor/trunk/inc/classes/monitor-output.class.php new file mode 100644 index 00000000..c050487e --- /dev/null +++ b/extensions/premium/monitor/trunk/inc/classes/monitor-output.class.php @@ -0,0 +1,152 @@ +_has_died() or false === ( tsf_extension_manager()->_verify_instance( $_instance, $bits[1] ) or tsf_extension_manager()->_maybe_die() ) ) + return; + +/** + * @package TSF_Extension_Manager\Traits + */ +use TSF_Extension_Manager\Enclose_Core_Final as Enclose_Core_Final; +use TSF_Extension_Manager\Construct_Core_Static_Final as Construct_Core_Static_Final; + +/** + * Monitor extension for The SEO Framework + * Copyright (C) 2016 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\Monitor_Output + * + * Parses and evaluates input data. + * + * @since 1.0.0 + * @access private + */ +final class Monitor_Output { + use Enclose_Core_Final, Construct_Core_Static_Final; + + /** + * Returns slab entry title based on $key and $type. + * + * @since 1.0.0 + * + * @param string $key The array key. + * @param string $type The pane-data type. + * @return string The $type $key title. + */ + public static function parse_title( $key, $type ) { + + switch ( $type ) : + case 'issues' : + switch ( $key ) : + case 'title' : + $title = __( 'Titles', 'the-seo-framework-extension-manager' ); + break 2; + + case 'description' : + $title = __( 'Descriptions', 'the-seo-framework-extension-manager' ); + break 2; + + case 'canonical' : + $title = __( 'Canonical URLs', 'the-seo-framework-extension-manager' ); + break 2; + + case 'favicon' : + $title = __( 'Favicon output', 'the-seo-framework-extension-manager' ); + break 2; + + case 'duplicated' : + $title = __( 'Duplicated content', 'the-seo-framework-extension-manager' ); + break 2; + + case 'mobile' : + $title = __( 'Mobile friendliness', 'the-seo-framework-extension-manager' ); + break 2; + + case 'html' : + $title = __( 'HTML output', 'the-seo-framework-extension-manager' ); + break 2; + + case 'php' : + $title = __( 'PHP errors', 'the-seo-framework-extension-manager' ); + break 2; + + case 'img' : + $title = __( 'Image sizes', 'the-seo-framework-extension-manager' ); + break 2; + + case 'robots' : + $title = __( 'Robots.txt output', 'the-seo-framework-extension-manager' ); + break 2; + + case 'sitemap' : + $title = __( 'Sitemap output', 'the-seo-framework-extension-manager' ); + break 2; + + case 'external' : + $title = __( 'External links', 'the-seo-framework-extension-manager' ); + break 2; + + default : + break 1; + endswitch; + + default : + $title = ucwords( str_replace( array( '-', '_' ), ' ', $key ) ); + break 1; + endswitch; + + return $title; + } + + /** + * Returns slab data content. + * + * @since 1.0.0 + * @staticvar object $tests The Monitor_Tests class isntance. + * + * @param string $key The array key. + * @param mixed $value The array value attached to $key. + * @param string $type The pane-data type. + * @return string The HTML formed data. + */ + public static function parse_content( $key, $value, $type ) { + + $content = ''; + + switch ( $type ) : + case 'issues' : + static $tests = null; + + if ( is_null( $tests ) ) + $tests = Monitor_Tests::get_instance(); + + if ( isset( $value['requires'] ) && version_compare( TSFEM_E_MONITOR_VERSION, $value['requires'], '>=' ) ) + $content = isset( $value['data'] ) ? $tests->$key( $value['data'] ) : ''; + break; + + default : + break; + endswitch; + + return $content; + } +} diff --git a/extensions/premium/monitor/trunk/inc/classes/monitor-tests.class.php b/extensions/premium/monitor/trunk/inc/classes/monitor-tests.class.php new file mode 100644 index 00000000..14a5b499 --- /dev/null +++ b/extensions/premium/monitor/trunk/inc/classes/monitor-tests.class.php @@ -0,0 +1,196 @@ +_has_died() or false === ( tsf_extension_manager()->_verify_instance( $_instance, $bits[1] ) or tsf_extension_manager()->_maybe_die() ) ) + return; + +/** + * @package TSF_Extension_Manager\Traits + */ +use TSF_Extension_Manager\Enclose_Core_Final as Enclose_Core_Final; +use TSF_Extension_Manager\Construct_Core_Static_Final as Construct_Core_Static_Final; + +/** + * Monitor extension for The SEO Framework + * Copyright (C) 2016 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\Monitor_Tests + * + * Tests Monitor Data input. + * + * @since 1.0.0 + * @access private + */ +final class Monitor_Tests { + use Enclose_Core_Final, Construct_Core_Static_Final; + + /** + * The object instance. + * + * @since 1.0.0 + * + * @var object|null This object instance. + */ + private static $instance = null; + + /** + * The constructor. Does nothing. + */ + private function construct() { } + + /** + * Handles unapproachable invoked methods. + * Silently ignores errors on this call. + * + * @param string $name + * @param array $arguments + * @return void. + */ + public function __call( $name, $arguments ) { + return; + } + + /** + * Sets the class instance. + * + * @since 1.0.0 + * @access private + */ + public static function set_instance() { + + if ( is_null( static::$instance ) ) { + static::$instance = new static(); + } + } + + /** + * Gets the class instance. It's set when it's null. + * + * @since 1.0.0 + * @access private + * + * @return object The current instance. + */ + public static function get_instance() { + + if ( is_null( static::$instance ) ) { + static::set_instance(); + } + + return static::$instance; + } + + public function favicon( $data ) { + + $content = ''; + + if ( isset( $data['meta'] ) || isset( $data['static'] ) ) { + if ( empty( $data['meta'] ) ) { + if ( empty( $data['static'] ) ) { + $content .= $this->wrap_info( esc_html__( 'No favicon has been found.', 'the-seo-framework-extension-manager' ) ); + } + $content .= $this->wrap_info( esc_html__( 'You should add a site icon through the customizer.', 'the-seo-framework-extension-manager' ) ); + } else { + $content .= $this->wrap_info( esc_html__( 'A dynamic favicon has been found, this increases support for mobile devices.', 'the-seo-framework-extension-manager' ) ); + } + } else { + $content = $this->no_data_found(); + } + + return $content; + } + + public function php( $data ) { + + $content = ''; + $links = array(); + + if ( is_array( $data ) ) { + foreach ( $data as $value ) : + if ( isset( $value['value'] ) && false === $value['value'] ) { + + $id = isset( $value['post_id'] ) ? $value['post_id'] : false; + + if ( false !== $id ) { + $home = isset( $value['home'] ) && $value['home']; + $url = the_seo_framework()->the_url( '', array( 'home' => $home, 'external' => true, 'id' => $id ) ); + $title = the_seo_framework()->title( '', '', '', array( 'notagline' => true, 'term_id' => $id, 'is_front_page' => $home, 'escape' => true ) ); + + $links[] = sprintf( '%s', $url, $title ); + } + } + endforeach; + } else { + return $this->no_data_found(); + } + + if ( empty( $links ) ) { + $content = $this->no_issue_found(); + } else { + $content = $this->wrap_info( esc_html__( 'Something is causing a PHP error on your website. This prevents correctly closing of HTML tags.', 'the-seo-framework-extension-manager' ) ); + $content .= sprintf( '

%s

', esc_html( _n( 'Affected page:', 'Affected pages:', count( $links ), 'the-seo-framework-extension-manager' ) ) ); + $content .= ''; + } + + $content .= $this->small_sample_disclaimer(); + + return $content; + } + + protected function wrap_info( $text ) { + return sprintf( '
%s
', $text ); + } + + protected function no_issue_found() { + + static $cache = null; + + if ( isset( $cache ) ) + return $cache; + + return $cache = sprintf( '

%s

', esc_html__( 'No issues have been found.', 'the-seo-framework-extension-manager' ) ); + } + + protected function no_data_found() { + + static $cache = null; + + if ( isset( $cache ) ) + return $cache; + + return $cache = sprintf( '

%s

', esc_html__( 'No data has been found on this issue.', 'the-seo-framework-extension-manager' ) ); + } + + protected function small_sample_disclaimer() { + + static $cache = null; + + if ( isset( $cache ) ) + return $cache; + + return $cache = sprintf( '

%s

', esc_html__( 'This has been evaluated with a small sample size.', 'the-seo-framework-extension-manager' ) ); + } +} diff --git a/extensions/premium/monitor/trunk/monitor.php b/extensions/premium/monitor/trunk/monitor.php index a0db3220..de39a9a8 100644 --- a/extensions/premium/monitor/trunk/monitor.php +++ b/extensions/premium/monitor/trunk/monitor.php @@ -6,7 +6,7 @@ /** * Extension Name: Monitor * Extension URI: https://premium.theseoframework.com/extensions/monitor/ - * Description: The Monitor extension keeps track of your website's SEO, social optimization, and SERP positioning. + * Description: The Monitor extension keeps track of your website's SEO, optimization, uptime and statistics. * Version: 1.0.0 * Author: Sybre Waaijer * Author URI: https://cyberwire.nl/ @@ -46,6 +46,12 @@ */ namespace TSF_Extension_Manager { + /** + * The extension version. + * @since 1.0.0 + */ + define( 'TSFEM_E_MONITOR_VERSION', '1.0.0' ); + /** * The extension file, absolute unix path. * @since 1.0.0 @@ -94,7 +100,7 @@ function monitor_init() { if ( isset( $loaded ) ) return $loaded; - tsf_extension_manager()->register_premium_extension_autoload_path( TSFEM_E_MONITOR_PATH_CLASS, 'Monitor' ); + tsf_extension_manager()->_register_premium_extension_autoload_path( TSFEM_E_MONITOR_PATH_CLASS, 'Monitor' ); if ( is_admin() ) { new Monitor_Admin(); diff --git a/extensions/premium/monitor/trunk/views/layout/pages/monitor.php b/extensions/premium/monitor/trunk/views/layout/pages/monitor.php index 0e282e69..3713a21a 100644 --- a/extensions/premium/monitor/trunk/views/layout/pages/monitor.php +++ b/extensions/premium/monitor/trunk/views/layout/pages/monitor.php @@ -7,28 +7,24 @@ defined( 'ABSPATH' ) and $_class = monitor_class() and $this instanceof $_class or die; ?> -
+
do_pane_wrap( __( 'Issues', 'the-seo-framework-extension-manager' ), $this->get_issues_overview(), array( - 'full' => true, + 'full' => false, 'collapse' => true, 'move' => false, 'ajax' => true, 'ajax_id' => 'tsfem-feed-ajax', ) ); -?> -
-
-do_pane_wrap( __( 'Points of Interest', 'the-seo-framework-extension-manager' ), $this->get_poi_overview(), array( - 'full' => true, + 'full' => false, 'collapse' => true, 'move' => false, 'ajax' => true, @@ -37,7 +33,7 @@ ); ?>
-
+
do_pane_wrap( __( 'Statistics', 'the-seo-framework-extension-manager' ), diff --git a/inc/classes/core.class.php b/inc/classes/core.class.php index b413e4c4..e641078e 100644 --- a/inc/classes/core.class.php +++ b/inc/classes/core.class.php @@ -199,11 +199,18 @@ public function init_extensions() { * Verifies integrity of the options. * * @since 1.0.0 + * @staticvar bool $cache * * @return bool True if options are valid, false if not. */ protected function are_options_valid() { - return $this->verify_options_hash( serialize( $this->get_all_options() ) ); + + static $cache = null; + + if ( isset( $cache ) ) + return $cache; + + return $cache = $this->verify_options_hash( serialize( $this->get_all_options() ) ); } /** @@ -841,7 +848,8 @@ final protected function get_bits() { /** * Count to create an irregular bit verification. * This can jump multiple sequences while maintaining the previous. - * It traverses in three dimensions: up (positive), down (negative) and right (new sequence). + * It traverses in three (actually two, but get_verification_instance makes it + * three) dimensions: up (positive), down (negative) and right (new sequence). */ $bit = $_bit <= 0 ? ~$bit-- | ~$_bit-- : ~$bit-- | ~$_bit++ and $bit = $bit++ & $_bit-- @@ -1141,15 +1149,16 @@ public function get_support_link( $type = 'free', $love = true ) { * If the account isn't premium, it will not be loaded. * * @since 1.0.0 + * @access private * @staticvar bool $autoload_inactive Whether the autoloader is active. * * @param string $path The extension path to look for. * @param string $class_base Class base words. * @return bool True on success, false on failure. */ - public function register_premium_extension_autoload_path( $path, $class_base ) { + public function _register_premium_extension_autoload_path( $path, $class_base ) { - if ( false === $this->is_premium_user() ) + if ( false === $this->is_premium_user() || false === $this->are_options_valid() ) return false; static $autoload_inactive = true; @@ -1576,22 +1585,36 @@ public function is_tsf_extension_manager_page( $secure = true ) { * Determines whether the plugin's activated. Either free or premium. * * @since 1.0.0 + * @staticvar bool $cache * * @return bool True if the plugin is activated. */ protected function is_plugin_activated() { - return 'Activated' === $this->get_option( '_activated' ); + + static $cache = null; + + if ( isset( $cache ) ) + return $cache; + + return $cache = 'Activated' === $this->get_option( '_activated' ); } /** * Determines whether the plugin's use is premium. * * @since 1.0.0 + * @staticvar bool $cache * * @return bool True if the plugin is connected to the API handler. */ protected function is_premium_user() { - return 'Premium' === $this->get_option( '_activation_level' ); + + static $cache = null; + + if ( isset( $cache ) ) + return $cache; + + return $cache = 'Premium' === $this->get_option( '_activation_level' ); } /** diff --git a/inc/classes/layout.class.php b/inc/classes/layout.class.php index d033f6b6..00ec8d03 100644 --- a/inc/classes/layout.class.php +++ b/inc/classes/layout.class.php @@ -232,8 +232,17 @@ private static function get_account_info() { if ( isset( $data['timestamp'] ) && isset( $data['divider'] ) ) { $class .= ' tsfem-has-hover-balloon'; + /** + * @TODO bugfix/make consistent? + * It only refreshes when a premium extension is being activated. + * Otherwise, it will continue to count into negatives. + * + * This might prevent rechecking "decoupled" websites... which in that case is a bug. + */ $next_check_min = round( ( floor( $data['timestamp'] * $data['divider'] ) - time() ) / 60 ); - $level_desc = sprintf( __( 'Next check is in %s minutes.', 'the-seo-framework-extension-manager' ), $next_check_min ); + + if ( $next_check_min > 0 ) + $level_desc = sprintf( __( 'Next check is scheduled in %s minutes.', 'the-seo-framework-extension-manager' ), $next_check_min ); } $level_desc = isset( $level_desc ) ? sprintf( ' data-desc="%s"', esc_html( $level_desc ) ) : ''; diff --git a/inc/classes/panes.class.php b/inc/classes/panes.class.php index f12f362b..2d79ee0b 100644 --- a/inc/classes/panes.class.php +++ b/inc/classes/panes.class.php @@ -65,7 +65,7 @@ protected function get_seo_trends_and_updates_overview() { $output = $this->get_trends_activation_output(); } - return sprintf( '', $output ); + return sprintf( '', $output ); } /** @@ -79,7 +79,7 @@ protected function get_extensions_actions_overview() { $output = $this->get_actions_output(); - return sprintf( '
%s
', $output ); + return sprintf( '
%s
', $output ); } /** @@ -93,7 +93,7 @@ protected function get_extension_overview() { $output = $this->get_extensions_output(); - return sprintf( '
%s
', $output ); + return sprintf( '
%s
', $output ); } /** @@ -116,10 +116,10 @@ protected function get_trends_output() { $feed_error = esc_html__( 'There are no trends and updates to report yet.', 'the-seo-framework-extension-manager' ); $output .= sprintf( '

%s

', $feed_error ); } else { - $output .= sprintf( '
%s
', $feed ); + $output .= sprintf( '
%s
', $feed ); } - return sprintf( '', $output ); + return sprintf( '', $output ); } /** @@ -305,7 +305,7 @@ protected function get_actions_left_output() { $output .= $this->get_support_buttons(); - return sprintf( '
%s
', $output ); + return sprintf( '
%s
', $output ); } /** @@ -319,7 +319,7 @@ protected function get_actions_right_output() { $output = ''; - if ( $this->is_premium_user() ) { + if ( $this->is_premium_user() && $this->are_options_valid() ) { $output .= $this->get_account_information(); $output .= $this->get_account_extend_form(); } else { @@ -328,7 +328,7 @@ protected function get_actions_right_output() { $output .= $this->get_deactivation_button(); - return sprintf( '
%s
', $output ); + return sprintf( '
%s
', $output ); } /** @@ -357,7 +357,7 @@ protected function get_account_information() { $title = sprintf( '

%s

', esc_html__( 'Account information', 'the-seo-framework-extension-manager' ) ); - return sprintf( '', $title, $output ); + return sprintf( '', $title, $output ); } /** @@ -391,7 +391,7 @@ protected function get_account_upgrade_form() { $title = sprintf( '

%s

', esc_html__( 'Upgrade your account', 'the-seo-framework-extension-manager' ) ); - return sprintf( '', $title, $form ); + return sprintf( '', $title, $form ); } /** @@ -436,7 +436,7 @@ protected function get_deactivation_button() { $extra = sprintf( '
%s
', $_extra ); - return sprintf( '', $title, $button, $extra ); + return sprintf( '', $title, $button, $extra ); } /** diff --git a/inc/classes/trends.class.php b/inc/classes/trends.class.php index bf62205a..d54d1622 100644 --- a/inc/classes/trends.class.php +++ b/inc/classes/trends.class.php @@ -90,7 +90,7 @@ private static function prototype_trends() { $xml = simplexml_load_string( $xml, 'SimpleXMLElement', $options ); if ( ! isset( $xml->entry ) || empty( $xml->entry ) ) { - set_transient( $transient_name, '', DAY_IN_SECONDS ); + set_transient( $transient_name, '', HOUR_IN_SECONDS * 2 ); return ''; } diff --git a/inc/traits/ui.trait.php b/inc/traits/ui.trait.php index 1fd39ec8..48a12ae2 100644 --- a/inc/traits/ui.trait.php +++ b/inc/traits/ui.trait.php @@ -88,7 +88,13 @@ public function enqueue_admin_scripts( $hook ) { if ( $this->ui_hook === $hook ) { - //* Set names. + /** + * Set JS and CSS names. + * + * Currently, it works best with Default and Midnight. And a little + * with Blue, Ectoplasm, Ocean. + * @TODO consider visually appealing versions for other Admin Color Schemes. + */ $this->css_name = 'tsf-extension-manager'; $this->js_name = 'tsf-extension-manager'; diff --git a/index.php b/index.php new file mode 100644 index 00000000..5e95d8de --- /dev/null +++ b/index.php @@ -0,0 +1,6 @@ + * { @@ -683,7 +696,7 @@ body.tsfem .wrap { .tsfem-actions-wrap, .tsfem-extensions-wrap, .tsfem-pane-inner-wrap { - padding: 20px 20px 0; + padding: 20px; height: auto; max-height: 100%; min-height: 150px; @@ -754,7 +767,7 @@ h4.tsfem-support-title { .tsfem-description { font-size: 0.93em; - font-style: italic; + font-style: oblique; text-indent: .5em; } @@ -767,6 +780,10 @@ h4.tsfem-support-title { margin: 0; } +.tsfem-extensions-overview-content { + flex: 1 1 100%; +} + .tsfem-extension-entry-wrap { padding: 0; margin: 0 0 20px; @@ -790,9 +807,9 @@ h4.tsfem-support-title { margin: 0; padding: 0; min-height: 122px; - box-shadow: 0 2px 1px 1px rgba(0,0,0,.04); - border: 1px solid #ddd; - background: #fdfdfd; + -webkit-box-shadow: 0 1px 3px 1px #ccc; + box-shadow: 0 1px 3px 1px #ccc; + background: #fefefe; align-self: auto; } @@ -905,9 +922,9 @@ h5.tsfem-extension-type { /* ### Feed pane. /*--------------------------------------*/ -.tsfem-feed-wrap { +/*.tsfem-feed-wrap { overflow: hidden; -} +}*/ .tsfem-enable-feed-button { margin-bottom: 14px; @@ -958,7 +975,7 @@ h5.tsfem-extension-type { .tsfem-feed-entry { margin-bottom: 14px; - box-shadow: 0px 2px 2px 0px rgba(204, 204, 204, 0.4); + border-bottom: 2px solid #dedede; display: inline-block; } @@ -975,7 +992,7 @@ h5.tsfem-extension-type { .tsfem #input-activation .tsfem-button { margin-left: 0; margin-right: 0; - border-radius: 0; + border-radius: 3px; width: 100%; font-size: 13px; } @@ -1123,12 +1140,12 @@ h5.tsfem-extension-type { .tsfem-trends-wrap, .tsfem-actions-wrap, .tsfem-extensions-wrap, - .tsfem-extensions-panes-row { + .tsfem-panes-row { min-height: initial; height: initial; } - .tsfem-extensions-panes-row .tsfem-pane-half { + .tsfem-panes-row .tsfem-pane-half { margin-bottom: 20px; } @@ -1137,7 +1154,7 @@ h5.tsfem-extension-type { flex: 1 1 auto; } - .tsfem-extensions-panes-row .tsfem-pane-half:last-of-type { + .tsfem-panes-row .tsfem-pane-half:last-of-type { margin-bottom: 0; } @@ -1182,7 +1199,7 @@ h5.tsfem-extension-type { position: absolute; padding: 0; margin: 0; - border-radius: 0px; + border-radius: 3px; bottom: 0; left: 0; opacity: 1; @@ -1210,6 +1227,7 @@ h5.tsfem-extension-type { margin: 0; max-width: 200px; padding: 10px 12px; + -webkit-box-shadow: 0px 0px 2px rgba(0,0,0,0.6); box-shadow: 0px 0px 2px rgba(0,0,0,0.6); background: #05809e; text-shadow: 0 -1px 1px #006a85,1px 0 1px #006a85,0 1px 1px #006a85,-1px 0 1px #006a85; @@ -1218,7 +1236,7 @@ h5.tsfem-extension-type { font-weight: 500; line-height: 1.625em; color: #fff; - border-radius: 0px; + border-radius: 3px; word-wrap: break-word; overflow-wrap: break-word; white-space: pre-line; @@ -1271,4 +1289,5 @@ h5.tsfem-extension-type { .tsfem-has-hover-balloon { position: relative; + cursor: help; } diff --git a/lib/css/tsf-extension-manager.min.css b/lib/css/tsf-extension-manager.min.css index 9f1aa3eb..962a7704 100644 --- a/lib/css/tsf-extension-manager.min.css +++ b/lib/css/tsf-extension-manager.min.css @@ -1 +1 @@ -.wrap.tsfem .notice{margin:0 0 20px;box-shadow:0 2px 1px 1px rgba(0,0,0,.04)}.tsfem-top-wrap .notice:first-of-type{margin-top:18px}.tsfem-disable-cursor{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.tsfem-question-cursor{cursor:help}.tsfem-flex{box-sizing:border-box;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex:1 1 auto;flex:1 1 auto;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-flex-direction:column;flex-direction:column;-webkit-justify-content:flex-start;justify-content:flex-start}.tsfem-flex-row{-webkit-flex-direction:row;flex-direction:row}.tsfem-flex-wrap{-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap}.tsfem-flex-nowrap{-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap}.tsfem-flex-grow{-webkit-flex-grow:1;flex-grow:1}.tsfem-flex-nogrow{-webkit-flex:0 1 auto;flex:0 1 auto}.tsfem-flex-noshrink{-webkit-flex:1 0 auto;flex:1 0 auto}.tsfem-flex-nogrowshrink{-webkit-flex:0 0 auto;flex:0 0 auto}.tsfem-flex-space{-webkit-justify-content:space-between;justify-content:space-between}body.tsfem .wrap{height:calc(100vh - 140px);margin-top:20px;overflow:visible;padding:0}.tsfem-top-wrap{background-color:#333;box-shadow:0 2px 1px 1px rgba(0,0,0,.2);width:100%;margin:0 0 20px;padding:20px}.tsfem-footer-wrap{width:100%;padding:1.33em 40px;margin:0 auto;overflow:hidden;-webkit-align-items:center;align-items:center;white-space:nowrap}.tsfem.connect-wrap{max-width:690px;min-width:175px}.tsfem-top-about,.tsfem-top-actions,.tsfem-top-wrap .tsfem-title{text-align:left;-webkit-align-items:center;align-items:center}.tsfem-top-about,.tsfem-top-actions{-webkit-justify-content:flex-end;justify-content:flex-end}.tsfem-top-wrap .tsfem-title{-webkit-flex:1 999 auto;flex:1 999 auto}.tsfem-top-about{-webkit-flex:1 1 160px;flex:1 1 160px}.tsfem-top-actions>div{display:inline-block;white-space:pre}.tsfem-top-about>div{display:inline-block;text-align:justify;max-width:calc(100% - 1.2em - 14px);color:#fff;letter-spacing:.2px;text-shadow:0 -1px 1px #222,1px 0 1px #222,0 1px 1px #222,-1px 0 1px #222}.tsfem-top-wrap .tsfem-title header{width:calc(100% - 7px)}.tsfem-top-wrap .tsfem-title h1{font-family:Verdana,sans-serif;word-break:break-word;font-size:1.9em;font-weight:400;color:#0ebfe9;margin:0;padding:0;display:inline}.tsfem-top-wrap .tsfem-title .tsfem-logo>svg{display:inline-block;white-space:pre-wrap;padding:0;margin-right:7px;margin-left:.1em;line-height:29px;vertical-align:top}.tsfem input[type=checkbox]:focus,.tsfem input[type=color]:focus,.tsfem input[type=date]:focus,.tsfem input[type=datetime-local]:focus,.tsfem input[type=datetime]:focus,.tsfem input[type=email]:focus,.tsfem input[type=month]:focus,.tsfem input[type=number]:focus,.tsfem input[type=password]:focus,.tsfem input[type=radio]:focus,.tsfem input[type=search]:focus,.tsfem input[type=tel]:focus,.tsfem input[type=text]:focus,.tsfem input[type=time]:focus,.tsfem input[type=url]:focus,.tsfem input[type=week]:focus,.tsfem select:focus,.tsfem textarea:focus{border-color:#057f9c;-webkit-box-shadow:0 0 2px rgba(66,144,183,.8);box-shadow:0 0 2px rgba(66,144,183,.8)}.tsfem-button,.tsfem-button-primary,.tsfem-button-primary:disabled,.tsfem-button:disabled{display:inline-block;padding:7px 14px;background:#eee;border:0;border-radius:3px;-webkit-box-shadow:0 3px 0 #cacaca;box-shadow:0 3px 0 #cacaca;color:#777;text-shadow:0 -1px 1px #efefef,1px 0 1px #efefef,0 1px 1px #efefef,-1px 0 1px #efefef;font-size:1em;line-height:1em;text-decoration:none;text-align:center;margin-top:-3px;margin-bottom:3px;cursor:pointer;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-appearance:none;-moz-appearance:none;appearance:none}.tsfem-button-primary,.tsfem-button-primary:disabled{background:#05809e;-webkit-box-shadow:0 3px 0 #006a85;box-shadow:0 3px 0 #006a85;color:#fff;text-shadow:0 -1px 1px #006a85,1px 0 1px #006a85,0 1px 1px #006a85,-1px 0 1px #006a85}.tsfem-about-activation:before{display:inline-block;color:#fff;content:"\f112";font-size:1.2em;line-height:1.2em;font-family:dashicons;text-decoration:inherit;font-weight:400;font-style:normal;margin:0 7px;vertical-align:baseline;white-space:pre-wrap;text-shadow:0 -1px 1px #222,1px 0 1px #222,0 1px 1px #222,-1px 0 1px #222}.tsfem-account-active,.tsfem-button-primary-bright,.tsfem-button-star{color:#fff;background:#0ebfe9;-webkit-box-shadow:0 3px 0 #057f9c;box-shadow:0 3px 0 #057f9c;text-shadow:0 -1px 1px #057f9c,1px 0 1px #057f9c,0 1px 1px #057f9c,-1px 0 1px #057f9c}.tsfem-account-inactive,.tsfem-button-extension-activate,.tsfem-button-love{color:#fff;background:#00cd98;-webkit-box-shadow:0 3px 0 #008964;box-shadow:0 3px 0 #008964;text-shadow:0 -1px 1px #008964,1px 0 1px #008964,0 1px 1px #008964,-1px 0 1px #008964}.tsfem-account-about-to-expire,.tsfem-button-extension-deactivate,.tsfem-button-warning{color:#fff;background:#ff746a;-webkit-box-shadow:0 3px 0 #d14b44;box-shadow:0 3px 0 #d14b44;text-shadow:0 -1px 1px #d14b44,1px 0 1px #d14b44,0 1px 1px #d14b44,-1px 0 1px #d14b44}.tsfem-account-about-to-expire:after,.tsfem-account-active:after,.tsfem-account-inactive:after,.tsfem-button-flag:after,.tsfem-button-love:after,.tsfem-button-star:after,.tsfem-button-warning:after{display:inline-block;width:1em;content:"\f155";font-size:1em;line-height:1em;font-family:dashicons;text-decoration:inherit;font-weight:400;font-style:normal;vertical-align:top;margin-left:7px}.tsfem-button-flag:after{content:"\f227"}.tsfem-account-about-to-expire:after,.tsfem-button-warning:after{content:"\f534"}.tsfem-account-inactive:after,.tsfem-button-love:after{content:"\f487"}.tsfem-button-primary:active,.tsfem-button-primary:focus,.tsfem-button:active,.tsfem-button:focus{margin-top:0;margin-bottom:0;box-shadow:none;outline:0}.tsfem-button:hover{color:#777;-webkit-filter:hue-rotate(5deg) saturate(1.2) brightness(.975);filter:hue-rotate(5deg) saturate(1.2) brightness(.975)}.tsfem-button-primary:active,.tsfem-button-primary:focus,.tsfem-button-primary:hover{color:#fff;-webkit-filter:hue-rotate(5deg) saturate(1.2);filter:hue-rotate(5deg) saturate(1.2)}.tsfem-switch-button-container-wrap{display:inline-block;perspective:800px;perspective-origin:50% 120px}.tsfem-pane-header>*,.tsfem-switch-button-container{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox}.tsfem-switch-button-container{position:relative;margin:0 auto;width:120px;height:27px;padding:0;box-sizing:border-box;display:flex;-webkit-flex:1 1 auto;flex:1 1 auto;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-flex-direction:column;flex-direction:column;-webkit-justify-content:center;justify-content:center}.tsfem-switch-button-container input[type=checkbox],.tsfem-switch-button-container input[type=submit]{width:0;height:0;position:absolute;left:-9001px;overflow:hidden}.tsfem-switch-button-container label{position:absolute;top:0;left:0;text-align:center;-webkit-transition:transform 1s,opacity .5s;transition:transform 1s,opacity .5s;z-index:1}.tsfem-switch-button-container input[type=checkbox]+label{z-index:2;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.tsfem-switch-button-container input[type=submit]+label{-webkit-transform:rotateX(-100deg);transform:rotateX(-100deg);transform-origin:0 100% 0;opacity:0;-webkit-transition:-webkit-transform 1.2s,opacity .6s;transition:transform 1.2s,opacity .6s}.tsfem-switch-button-container input[type=checkbox]:checked:before{content:none}.tsfem-switch-button-container input[type=checkbox]:checked+label{-webkit-transform:rotateX(90deg) translateY(-100%);transform:rotateX(90deg) translateY(-100%);opacity:0;z-index:0;-webkit-transition:-webkit-transform 1.2s,opacity .6s;transition:transform 1.2s,opacity .6s}.tsfem-switch-button-container input[type=checkbox]:checked~input[type=submit]+label{-webkit-transform:rotateX(0);transform:rotateX(0);opacity:1;z-index:2;-webkit-transition:transform 1s,opacity .5s;transition:transform 1s,opacity .5s}.tsfem-button-disabled,.tsfem-button-disabled:active,.tsfem-button-disabled:focus,.tsfem-button-disabled:hover{-webkit-filter:grayscale(1);filter:grayscale(1);cursor:not-allowed;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.tsfem-connect-action,.tsfem-connect-description{width:100%}.tsfem-connect-action{text-align:right}.tsfem-connect-action form{margin:0;padding:0}.tsfem-connect-description h3{font-size:1.66em;color:#057f9c;font-weight:400;margin:14px 0}.tsfem-connect-fields-row{-webkit-justify-content:flex-end;justify-content:flex-end}.tsfem-connect-fields-row>:nth-child(1n+2) .tsfem-button{margin-left:7px}.tsfem-connect-option{width:100%;max-width:690px;padding:1.33em 40px;margin-bottom:20px;border:1px solid #e2e2e2;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.04);box-shadow:0 1px 1px rgba(0,0,0,.04);background:#fff;overflow:hidden;border-radius:3px;-webkit-align-items:center;align-items:center}.tsfem-connect-option.tsfem-connect-highlighted{border:1px solid #057f9c}.tsfem-panes-wrap{width:100%;margin:0;padding:0;-webkit-align-items:center;align-items:center;min-height:33vh}.tsfem-extensions-panes-row{width:100%;margin:0 0 20px;padding:0;min-height:150px}.tsfem-extensions-panes-row:last-of-type{margin:0}.tsfem-pane-full,.tsfem-pane-half{max-width:calc(50% - 10px);background:#fff;border-radius:3px;-webkit-box-shadow:0 2px 1px 1px rgba(0,0,0,.04);box-shadow:0 2px 1px 1px rgba(0,0,0,.04);margin:0;padding:0;max-height:100%;-webkit-flex:1 1 100%;flex:1 1 100%}.tsfem-pane-full ::-webkit-scrollbar,.tsfem-pane-half ::-webkit-scrollbar{width:12px;height:6px;background:#fafafa}.tsfem-pane-full ::-webkit-scrollbar-thumb,.tsfem-pane-half ::-webkit-scrollbar-thumb{background-color:#0ebfdd;-webkit-box-shadow:0 0 10px rgba(0,0,0,.3) inset;box-shadow:0 0 10px rgba(0,0,0,.3) inset}.tsfem-pane-full ::-webkit-scrollbar-thumb:active,.tsfem-pane-half ::-webkit-scrollbar-thumb:active{background-color:#0cabc6}.tsfem-pane-half:first-of-type{margin-right:20px}.tsfem-pane-full{-webkit-flex-direction:row;flex-direction:row;max-width:100%}.tsfem-pane-wrap{padding:0;margin:0;overflow:visible;max-height:100%}.tsfem-pane-content,.tsfem-pane-header{margin:0;padding:14px 20px;border:1px solid #ddd}.tsfem-pane-content{padding:0;border-top:0;-webkit-flex:1 1 33vh;flex:1 1 33vh;max-height:100%}.tsfem-pane-header>*{font-size:1em;line-height:1em;margin:0;padding:0;box-sizing:border-box;display:flex;-webkit-flex:1 1 auto;flex:1 1 auto;-webkit-flex-direction:row;flex-direction:row;-webkit-align-items:center;align-items:center;-webkit-justify-content:flex-end;justify-content:flex-end}.tsfem-desc-balloon,.tsfem-extension-entry>*{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox}.tsfem-pane-header .tsfem-ajax{max-width:initial;font-size:1em;word-break:break-word;text-align:right;-webkit-flex:0 1 auto;flex:0 1 auto;margin-left:7px}.tsfem-pane-header h3{font-size:1.33em;color:#057f9c;font-weight:400;-webkit-justify-content:flex-start;justify-content:flex-start}.tsfem-pane-content h4.tsfem-status-title{margin:0}.tsfem-actions-wrap,.tsfem-extensions-wrap,.tsfem-pane-inner-wrap,.tsfem-trends-wrap{padding:20px 20px 0;height:auto;max-height:100%;min-height:150px;width:100%;overflow:auto}.tsfem-actions,.tsfem-trends,.tsfem-trends-activation{max-width:1600px;min-height:150px}.tsfem-actions>div{margin-right:20px;margin-bottom:20px;-webkit-flex-basis:200px;flex-basis:200px;min-width:200px}.tsfem-actions>div:last-of-type{margin-right:0;margin-bottom:0}.tsfem-actions>div>div{margin-bottom:20px}.tsfem-flex-account-info-rows{line-height:1.625em}.tsfem-actions-account-info-title{font-weight:600}h4.tsfem-form-title,h4.tsfem-info-title,h4.tsfem-status-title,h4.tsfem-support-title{padding:0;margin:0 0 14px;font-size:1.33em;font-weight:400;color:#057f9c}.tsfem-account-upgrade,.tsfem-support-buttons{max-width:300px}.tsfem-support-buttons{margin-bottom:14px;-webkit-flex-basis:50%;flex-basis:50%}.tsfem-support-buttons:last-of-type{margin-bottom:0}.tsfem-description{font-size:.93em;font-style:italic;text-indent:.5em}.tsfem-extensions-wrap{height:auto;max-height:750px;padding-right:0;margin:0}.tsfem-extension-entry-wrap{padding:0;margin:0 20px 20px 0;-webkit-flex-basis:270px;flex-basis:270px;min-width:calc(50% - 40px)}.tsfem-extension-entry-wrap:first-of-type:last-of-type,.tsfem-extension-entry-wrap:nth-child(n+2):last-of-type:nth-child(odd){-webkit-flex-grow:0;flex-grow:0;-webkit-flex-basis:100%;flex-basis:100%}.tsfem-extension-entry{margin:0;padding:0;min-height:122px;box-shadow:0 2px 1px 1px rgba(0,0,0,.04);border:1px solid #ddd;background:#fdfdfd;align-self:auto}.tsfem-extension-entry>*{min-height:100px;margin:0;padding:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:flex;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-flex-direction:column;flex-direction:column;width:auto;-webkit-justify-content:flex-start;justify-content:flex-start}.tsfem-extension-icon-wrap{min-width:120px;-webkit-flex-grow:0;flex-grow:0}.tsfem-extension-about-wrap{min-width:220px;-webkit-flex-direction:column;flex-direction:column;-webkit-justify-content:space-around;justify-content:space-around;padding:10px 15px;-webkit-flex-grow:1;flex-grow:1;-webkit-flex-basis:25%;flex-basis:25%}.tsfem-extension-description-wrap{min-width:280px;-webkit-flex-grow:3;flex-grow:3;-webkit-flex-basis:280px;flex-basis:280px}h4.tsfem-extension-title{min-width:120px;font-size:1.33em;word-break:break-word;font-weight:600;color:#0ebfe9;margin:0;padding:0}h5.tsfem-extension-type{margin:0;padding:0;font-size:.83em}.tsfem-extension-subheader{padding:.667em 0}.tsfem-extension-action input,.tsfem-extension-action>*{width:100%;text-align:center}.tsfem-extension-party:before{display:inline-block;width:1em;line-height:1;font-family:dashicons;font-style:normal;font-weight:400;font-size:1.225em;vertical-align:middle;content:"";margin-right:4px}.tsfem-extension-first-party-icon:before{content:"\f338"}.tsfem-extension-third-party-icon:before{content:"\f307"}.tsfem-extension-description{padding:1.33em}.tsfem-extension-description-header{padding-bottom:1em}.tsfem-extension-description-footer>span{white-space:pre}.tsfem-feed-wrap{overflow:hidden}.tsfem-enable-feed-button{margin-bottom:14px;-webkit-flex-basis:50%;flex-basis:50%;max-width:300px}.tsfem-feed-entry,.tsfem-feed-top{width:100%;margin:0;padding:0;overflow:hidden}.tsfem-feed-content,.tsfem-feed-top{margin-bottom:14px}.tsfem-feed-top{word-wrap:break-word;white-space:pre-wrap;overflow-wrap:normal;-webkit-align-items:baseline;align-items:baseline}.tsfem-desc-balloon>span,.tsfem-feed-top time{overflow-wrap:break-word;word-wrap:break-word}.tsfem-feed-top h4{white-space:pre-wrap;margin:0;padding:0}.tsfem-feed-top time{white-space:pre;white-space:nowrap;word-spacing:-1px;font-size:.9em;margin:0 7px;padding:0;font-style:oblique}.tsfem-feed-entry{margin-bottom:14px;box-shadow:0 2px 2px 0 rgba(204,204,204,.4);display:inline-block}.tsfem #input-activation{width:100%;margin:0;padding:0}.tsfem #input-activation .regular-text,.tsfem #input-activation .tsfem-button{margin-left:0;margin-right:0;border-radius:0;width:100%;font-size:13px}.tsfem #input-activation .regular-text:nth-child(1n+2){margin-top:4px;margin-bottom:8px}#wpfooter .tsfem-footer-wrap .tsfem-footer-motto,#wpfooter .tsfem-footer-wrap .tsfem-footer-title{font-size:13px;margin:0;color:#646d78;font-weight:300;cursor:default}#wpfooter .tsfem-footer-wrap .tsfem-footer-title{font-weight:400;color:#555d66}.tsfem-ajax:after,.tsfem-dashicon:after{display:inline-block;line-height:1;font-family:dashicons;font-style:normal;font-weight:400;font-size:1.225em;width:1em;vertical-align:baseline;content:"";margin-left:4px}.tsfem-dashicon:after{font-size:1.2em;vertical-align:middle;vertical-align:text-bottom;margin-left:2px}.tsfem-ajax.tsfem-loading:after{content:"\f463";color:#007bd2;-webkit-animation:tsfem-spin 1.5s linear infinite;-moz-animation:tsfem-spin 1.5s linear infinite;-o-animation:tsfem-spin 1.5s linear infinite;animation:tsfem-spin 1.5s linear infinite}.tsfem-ajax.tsfem-error:after,.tsfem-dashicon.tsfem-error:after{content:"\f158";color:#dd3811}.tsfem-ajax.tsfem-success:after,.tsfem-dashicon.tsfem-success:after{content:"\f147";color:#0cc34b}.tsfem-ajax.tsfem-unknown:after,.tsfem-dashicon.tsfem-unknown:after{content:"\f223";color:#007bd2}.tsfem-dashicon.tsfem-warning:after{content:"\f227";color:#ffa01b}@-webkit-keyframes tsfem-spin{0%{-webkit-transform:rotate(0)}100%{-webkit-transform:rotate(360deg)}}@keyframes tsfem-spin{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}@media screen and (min-height:800px){.tsfem-extensions-wrap{max-height:initial}}@media only screen and (min-height:600px) and (max-width:782px){body.tsfem .wrap{margin-bottom:0}body.tsfem #wpfooter{margin:0}}@media screen and (max-height:800px),(max-width:782px){body.tsfem .wrap{height:auto;margin-bottom:30px}}@media screen and (max-width:782px){body.tsfem .wrap{margin-bottom:0}}@media only screen and (max-height:600px),(max-width:782px){.tsfem-pane-full,.tsfem-pane-half{min-width:100%;max-width:100%}.tsfem-actions-wrap,.tsfem-extensions-panes-row,.tsfem-extensions-wrap,.tsfem-trends-wrap{min-height:initial;height:initial}.tsfem-extensions-panes-row .tsfem-pane-half{margin-bottom:20px}.tsfem-pane-content{-webkit-flex:1 1 auto;flex:1 1 auto}.tsfem-extensions-panes-row .tsfem-pane-half:last-of-type{margin-bottom:0}.tsfem-pane-wrap{max-height:75vh}.tsfem-pane-half:first-of-type{margin-right:0}body.tsfem #wpfooter{display:block}}.tsfem-desc-balloon,.tsfem-desc-balloon>span{margin:0;border-radius:0;box-sizing:border-box;left:0}@media only screen and (max-width:600px){.tsfem-connect-option{-webkit-flex-direction:column;flex-direction:column;-webkit-align-items:flex-start;align-items:flex-start;padding:1em 20px 1.66em}.tsfem-connect-action{text-align:left}.tsfem-connect-fields-row{-webkit-justify-content:flex-start;justify-content:flex-start}}.tsfem-desc-balloon{position:absolute;padding:0;bottom:0;opacity:1;z-index:9999999;display:flex;-webkit-flex:1 1 auto;flex:1 1 auto;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-flex-direction:row;flex-direction:row;-webkit-justify-content:flex-start;justify-content:flex-start}.tsfem-desc-balloon>span{position:relative;top:0;max-width:200px;padding:10px 12px;box-shadow:0 0 2px rgba(0,0,0,.6);background:#05809e;text-shadow:0 -1px 1px #006a85,1px 0 1px #006a85,0 1px 1px #006a85,-1px 0 1px #006a85;text-align:left;font-size:1em;font-weight:500;line-height:1.625em;color:#fff;white-space:pre-line;word-break:break-word;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex:1 0 auto;flex:1 0 auto;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-flex-direction:row;flex-direction:row;-webkit-justify-content:flex-start;justify-content:flex-start}.tsfem-desc-balloon>span *{white-space:pre;overflow:auto;word-wrap:break-word;overflow-wrap:break-word}.tsfem-desc-balloon>span span{text-decoration:underline}.tsfem-desc-balloon>div{width:0;height:0;border-left:12px solid transparent;border-right:12px solid transparent;border-top:12px solid #05809e;position:absolute;bottom:-8px;z-index:9999998;left:0}.tsfem-hidden{display:none}.tsfem-has-hover-balloon{position:relative} +.wrap.tsfem .notice{margin:0 0 20px;-webkit-box-shadow:0 1px 2px 1px #ccc;box-shadow:0 1px 2px 1px #ccc;border-radius:3px}.tsfem-top-wrap .notice:first-of-type{margin-top:18px}.tsfem-disable-cursor{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.tsfem-question-cursor{cursor:help}.tsfem-flex{box-sizing:border-box;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex:1 1 auto;flex:1 1 auto;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-flex-direction:column;flex-direction:column;-webkit-justify-content:flex-start;justify-content:flex-start}.tsfem-flex-row{-webkit-flex-direction:row;flex-direction:row}.tsfem-flex-wrap{-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap}.tsfem-flex-nowrap{-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap}.tsfem-flex-grow{-webkit-flex-grow:1;flex-grow:1}.tsfem-flex-nogrow{-webkit-flex:0 1 auto;flex:0 1 auto}.tsfem-flex-noshrink{-webkit-flex:1 0 auto;flex:1 0 auto}.tsfem-flex-nogrowshrink{-webkit-flex:0 0 auto;flex:0 0 auto}.tsfem-flex-space{-webkit-justify-content:space-between;justify-content:space-between}body.tsfem .wrap{height:calc(100vh - 140px);margin-top:20px;overflow:visible;padding:0}.tsfem-top-wrap{background-color:#333;-webkit-box-shadow:0 1px 2px 1px #aaa;box-shadow:0 1px 2px 1px #aaa;width:100%;margin:0 0 20px;padding:20px;border-radius:3px}.tsfem-footer-wrap{width:100%;padding:1.33em 40px;margin:0 auto;overflow:hidden;-webkit-align-items:center;align-items:center;white-space:nowrap}.tsfem.connect-wrap{max-width:690px;min-width:175px}.tsfem-top-about,.tsfem-top-actions,.tsfem-top-wrap .tsfem-title{text-align:left;-webkit-align-items:center;align-items:center}.tsfem-top-about,.tsfem-top-actions{-webkit-justify-content:flex-end;justify-content:flex-end}.tsfem-top-wrap .tsfem-title{-webkit-flex:1 999 auto;flex:1 999 auto}.tsfem-top-about{-webkit-flex:1 1 160px;flex:1 1 160px}.tsfem-top-actions>div{display:inline-block;white-space:pre}.tsfem-top-about>div{display:inline-block;text-align:justify;max-width:calc(100% - 1.2em - 14px);color:#fff;letter-spacing:.2px;text-shadow:0 -1px 1px #222,1px 0 1px #222,0 1px 1px #222,-1px 0 1px #222}.tsfem-top-wrap .tsfem-title header{width:calc(100% - 7px)}.tsfem-top-wrap .tsfem-title h1{font-family:Verdana,sans-serif;word-break:break-word;font-size:1.9em;font-weight:400;color:#0ebfe9;margin:0;padding:0;display:inline}.tsfem-top-wrap .tsfem-title .tsfem-logo>svg{display:inline-block;white-space:pre-wrap;padding:0;margin-right:7px;margin-left:.1em;line-height:29px;vertical-align:top}.tsfem input[type=checkbox]:focus,.tsfem input[type=color]:focus,.tsfem input[type=date]:focus,.tsfem input[type=datetime-local]:focus,.tsfem input[type=datetime]:focus,.tsfem input[type=email]:focus,.tsfem input[type=month]:focus,.tsfem input[type=number]:focus,.tsfem input[type=password]:focus,.tsfem input[type=radio]:focus,.tsfem input[type=search]:focus,.tsfem input[type=tel]:focus,.tsfem input[type=text]:focus,.tsfem input[type=time]:focus,.tsfem input[type=url]:focus,.tsfem input[type=week]:focus,.tsfem select:focus,.tsfem textarea:focus{border-color:#057f9c;-webkit-box-shadow:0 0 2px rgba(66,144,183,.8);box-shadow:0 0 2px rgba(66,144,183,.8)}.tsfem-button,.tsfem-button-primary,.tsfem-button-primary:disabled,.tsfem-button:disabled{display:inline-block;padding:7px 14px;background:#eee;border:0;border-radius:3px;-webkit-box-shadow:0 3px 0 #cacaca;box-shadow:0 3px 0 #cacaca;color:#777;text-shadow:0 -1px 1px #efefef,1px 0 1px #efefef,0 1px 1px #efefef,-1px 0 1px #efefef;font-size:1em;line-height:1em;text-decoration:none;text-align:center;margin-top:-3px;margin-bottom:3px;cursor:pointer;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-appearance:none;-moz-appearance:none;appearance:none}.tsfem-button-primary,.tsfem-button-primary:disabled{background:#05809e;-webkit-box-shadow:0 3px 0 #006a85;box-shadow:0 3px 0 #006a85;color:#fff;text-shadow:0 -1px 1px #006a85,1px 0 1px #006a85,0 1px 1px #006a85,-1px 0 1px #006a85}.tsfem-about-activation:before{display:inline-block;color:#fff;content:"\f112";font-size:1.2em;line-height:1.2em;font-family:dashicons;text-decoration:inherit;font-weight:400;font-style:normal;margin:0 7px;vertical-align:baseline;white-space:pre-wrap;text-shadow:0 -1px 1px #222,1px 0 1px #222,0 1px 1px #222,-1px 0 1px #222}.tsfem-account-active,.tsfem-button-primary-bright,.tsfem-button-star{color:#fff;background:#0ebfe9;-webkit-box-shadow:0 3px 0 #057f9c;box-shadow:0 3px 0 #057f9c;text-shadow:0 -1px 1px #057f9c,1px 0 1px #057f9c,0 1px 1px #057f9c,-1px 0 1px #057f9c}.tsfem-account-inactive,.tsfem-button-extension-activate,.tsfem-button-love{color:#fff;background:#00cd98;-webkit-box-shadow:0 3px 0 #008964;box-shadow:0 3px 0 #008964;text-shadow:0 -1px 1px #008964,1px 0 1px #008964,0 1px 1px #008964,-1px 0 1px #008964}.tsfem-account-about-to-expire,.tsfem-button-extension-deactivate,.tsfem-button-warning{color:#fff;background:#ff746a;-webkit-box-shadow:0 3px 0 #d14b44;box-shadow:0 3px 0 #d14b44;text-shadow:0 -1px 1px #d14b44,1px 0 1px #d14b44,0 1px 1px #d14b44,-1px 0 1px #d14b44}.tsfem-account-about-to-expire:after,.tsfem-account-active:after,.tsfem-account-inactive:after,.tsfem-button-flag:after,.tsfem-button-love:after,.tsfem-button-star:after,.tsfem-button-warning:after{display:inline-block;width:1em;content:"\f155";font-size:1em;line-height:1em;font-family:dashicons;text-decoration:inherit;font-weight:400;font-style:normal;vertical-align:top;margin-left:7px}.tsfem-button-flag:after{content:"\f227"}.tsfem-account-about-to-expire:after,.tsfem-button-warning:after{content:"\f534"}.tsfem-account-inactive:after,.tsfem-button-love:after{content:"\f487"}.tsfem-button-primary:active,.tsfem-button-primary:focus,.tsfem-button:active,.tsfem-button:focus{margin-top:0;margin-bottom:0;-webkit-box-shadow:none;box-shadow:none;outline:0}.tsfem-button:hover{color:#777;-webkit-filter:hue-rotate(5deg) saturate(1.2) brightness(.975);filter:hue-rotate(5deg) saturate(1.2) brightness(.975)}.tsfem-button-primary:active,.tsfem-button-primary:focus,.tsfem-button-primary:hover{color:#fff;-webkit-filter:hue-rotate(5deg) saturate(1.2);filter:hue-rotate(5deg) saturate(1.2)}.tsfem-switch-button-container-wrap{display:inline-block;perspective:800px;perspective-origin:50% 120px}.tsfem-pane-header>*,.tsfem-switch-button-container{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox}.tsfem-switch-button-container{position:relative;margin:0 auto;width:120px;height:27px;padding:0;box-sizing:border-box;display:flex;-webkit-flex:1 1 auto;flex:1 1 auto;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-flex-direction:column;flex-direction:column;-webkit-justify-content:center;justify-content:center}.tsfem-switch-button-container input[type=checkbox],.tsfem-switch-button-container input[type=submit]{width:0;height:0;position:absolute;left:-9001px;overflow:hidden}.tsfem-switch-button-container label{position:absolute;top:0;left:0;text-align:center;-webkit-transition:transform 1s,opacity .5s;transition:transform 1s,opacity .5s;z-index:1}.tsfem-switch-button-container input[type=checkbox]+label{z-index:2;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.tsfem-switch-button-container input[type=submit]+label{-webkit-transform:rotateX(-100deg);transform:rotateX(-100deg);transform-origin:0 100% 0;opacity:0;-webkit-transition:-webkit-transform 1.2s,opacity .6s;transition:transform 1.2s,opacity .6s}.tsfem-switch-button-container input[type=checkbox]:checked:before{content:none}.tsfem-switch-button-container input[type=checkbox]:checked+label{-webkit-transform:rotateX(90deg) translateY(-100%);transform:rotateX(90deg) translateY(-100%);opacity:0;z-index:0;-webkit-transition:-webkit-transform 1.2s,opacity .6s;transition:transform 1.2s,opacity .6s}.tsfem-switch-button-container input[type=checkbox]:checked~input[type=submit]+label{-webkit-transform:rotateX(0);transform:rotateX(0);opacity:1;z-index:2;-webkit-transition:transform 1s,opacity .5s;transition:transform 1s,opacity .5s}.tsfem-button-disabled,.tsfem-button-disabled:active,.tsfem-button-disabled:focus,.tsfem-button-disabled:hover{-webkit-filter:grayscale(1);filter:grayscale(1);cursor:not-allowed;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.tsfem-connect-action,.tsfem-connect-description{width:100%}.tsfem-connect-action{text-align:right}.tsfem-connect-action form{margin:0;padding:0}.tsfem-connect-description h3{font-size:1.66em;color:#057f9c;font-weight:400;margin:14px 0}.tsfem-connect-fields-row{-webkit-justify-content:flex-end;justify-content:flex-end}.tsfem-connect-fields-row>:nth-child(1n+2) .tsfem-button{margin-left:7px}.tsfem-connect-option{width:100%;max-width:690px;padding:1.33em 40px;margin-bottom:20px;border:1px solid #e2e2e2;-webkit-box-shadow:0 1px 3px 1px #dedede;box-shadow:0 1px 3px 1px #dedede;background:#fff;overflow:hidden;border-radius:3px;-webkit-align-items:center;align-items:center}.tsfem-connect-option.tsfem-connect-highlighted{border:1px solid #057f9c}.tsfem-panes-wrap{width:100%;margin:0;padding:0;-webkit-align-items:center;align-items:center;min-height:33vh}.tsfem-panes-row{width:100%;margin:0 0 20px;padding:0;min-height:150px}.tsfem-panes-row:last-of-type{margin:0}.tsfem-pane-full,.tsfem-pane-half{max-width:calc(50% - 10px);background:#fff;border-radius:3px;-webkit-box-shadow:0 1px 3px 1px #ccc;box-shadow:0 1px 3px 1px #ccc;margin:0;padding:0;max-height:100%;-webkit-flex:1 1 100%;flex:1 1 100%;overflow:hidden}.tsfem-pane-full ::-webkit-scrollbar,.tsfem-pane-half ::-webkit-scrollbar{width:12px;height:6px;background:#fafafa}.tsfem-pane-full ::-webkit-scrollbar-thumb,.tsfem-pane-half ::-webkit-scrollbar-thumb{background-color:#0ebfdd;-webkit-box-shadow:0 0 10px rgba(0,0,0,.3) inset;box-shadow:0 0 10px rgba(0,0,0,.3) inset}.tsfem-pane-full ::-webkit-scrollbar-thumb:active,.tsfem-pane-half ::-webkit-scrollbar-thumb:active{background-color:#0cabc6}.tsfem-pane-half:first-of-type{margin-right:20px}.tsfem-pane-full{-webkit-flex-direction:row;flex-direction:row;max-width:100%}.tsfem-pane-wrap{padding:0;margin:0;overflow:visible;max-height:100%}.tsfem-pane-header{-webkit-box-shadow:0 1px 3px 0 #ccc;box-shadow:0 1px 3px 0 #ccc;z-index:5}.tsfem-pane-content,.tsfem-pane-header{margin:0;padding:14px 20px}.tsfem-pane-content{padding:0;border-top:0;-webkit-flex:1 1 33vh;flex:1 1 33vh;max-height:100%;overflow:hidden}.tsfem-pane-header>*{font-size:1em;line-height:1em;margin:0;padding:0;box-sizing:border-box;display:flex;-webkit-flex:1 1 auto;flex:1 1 auto;-webkit-flex-direction:row;flex-direction:row;-webkit-align-items:center;align-items:center;-webkit-justify-content:flex-end;justify-content:flex-end}.tsfem-desc-balloon,.tsfem-extension-entry>*{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox}.tsfem-pane-header .tsfem-ajax{max-width:initial;font-size:1em;word-break:break-word;text-align:right;-webkit-flex:0 1 auto;flex:0 1 auto;margin-left:7px}.tsfem-pane-header h3{font-size:1.33em;color:#057f9c;font-weight:400;-webkit-justify-content:flex-start;justify-content:flex-start}.tsfem-pane-content h4.tsfem-status-title{margin:0}.tsfem-actions-wrap,.tsfem-extensions-wrap,.tsfem-pane-inner-wrap,.tsfem-trends-wrap{padding:20px;height:auto;max-height:100%;min-height:150px;width:100%;overflow:auto}.tsfem-actions,.tsfem-trends,.tsfem-trends-activation{max-width:1600px;min-height:150px}.tsfem-actions>div{margin-right:20px;margin-bottom:20px;-webkit-flex-basis:200px;flex-basis:200px;min-width:200px}.tsfem-actions>div:last-of-type{margin-right:0;margin-bottom:0}.tsfem-actions>div>div{margin-bottom:20px}.tsfem-flex-account-info-rows{line-height:1.625em}.tsfem-actions-account-info-title{font-weight:600}h4.tsfem-form-title,h4.tsfem-info-title,h4.tsfem-status-title,h4.tsfem-support-title{padding:0;margin:0 0 14px;font-size:1.33em;font-weight:400;color:#057f9c}.tsfem-account-upgrade,.tsfem-support-buttons{max-width:300px}.tsfem-support-buttons{margin-bottom:14px;-webkit-flex-basis:50%;flex-basis:50%}.tsfem-support-buttons:last-of-type{margin-bottom:0}.tsfem-description{font-size:.93em;font-style:oblique;text-indent:.5em}.tsfem-extensions-wrap{height:auto;max-height:750px;padding-right:0;margin:0}.tsfem-extensions-overview-content{flex:1 1 100%}.tsfem-extension-entry-wrap{padding:0;margin:0 20px 20px 0;-webkit-flex-basis:270px;flex-basis:270px;min-width:calc(50% - 40px)}.tsfem-extension-entry-wrap:first-of-type:last-of-type,.tsfem-extension-entry-wrap:nth-child(n+2):last-of-type:nth-child(odd){-webkit-flex-grow:0;flex-grow:0;-webkit-flex-basis:100%;flex-basis:100%}.tsfem-extension-entry{margin:0;padding:0;min-height:122px;-webkit-box-shadow:0 1px 3px 1px #ccc;box-shadow:0 1px 3px 1px #ccc;background:#fefefe;align-self:auto}.tsfem-extension-entry>*{min-height:100px;margin:0;padding:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:flex;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-flex-direction:column;flex-direction:column;width:auto;-webkit-justify-content:flex-start;justify-content:flex-start}.tsfem-desc-balloon,.tsfem-desc-balloon>span{border-radius:3px;box-sizing:border-box;left:0}.tsfem-extension-icon-wrap{min-width:120px;-webkit-flex-grow:0;flex-grow:0}.tsfem-extension-about-wrap{min-width:220px;-webkit-flex-direction:column;flex-direction:column;-webkit-justify-content:space-around;justify-content:space-around;padding:10px 15px;-webkit-flex-grow:1;flex-grow:1;-webkit-flex-basis:25%;flex-basis:25%}.tsfem-extension-description-wrap{min-width:280px;-webkit-flex-grow:3;flex-grow:3;-webkit-flex-basis:280px;flex-basis:280px}h4.tsfem-extension-title{min-width:120px;font-size:1.33em;word-break:break-word;font-weight:600;color:#0ebfe9;margin:0;padding:0}h5.tsfem-extension-type{margin:0;padding:0;font-size:.83em}.tsfem-extension-subheader{padding:.667em 0}.tsfem-extension-action input,.tsfem-extension-action>*{width:100%;text-align:center}.tsfem-extension-party:before{display:inline-block;width:1em;line-height:1;font-family:dashicons;font-style:normal;font-weight:400;font-size:1.225em;vertical-align:middle;content:"";margin-right:4px}.tsfem-extension-first-party-icon:before{content:"\f338"}.tsfem-extension-third-party-icon:before{content:"\f307"}.tsfem-extension-description{padding:1.33em}.tsfem-extension-description-header{padding-bottom:1em}.tsfem-extension-description-footer>span{white-space:pre}.tsfem-enable-feed-button{margin-bottom:14px;-webkit-flex-basis:50%;flex-basis:50%;max-width:300px}.tsfem-feed-entry,.tsfem-feed-top{width:100%;margin:0;padding:0;overflow:hidden}.tsfem-feed-content,.tsfem-feed-top{margin-bottom:14px}.tsfem-feed-top{word-wrap:break-word;white-space:pre-wrap;overflow-wrap:normal;-webkit-align-items:baseline;align-items:baseline}.tsfem-desc-balloon>span,.tsfem-feed-top time{overflow-wrap:break-word;word-wrap:break-word}.tsfem-feed-top h4{white-space:pre-wrap;margin:0;padding:0}.tsfem-feed-top time{white-space:pre;white-space:nowrap;word-spacing:-1px;font-size:.9em;margin:0 7px;padding:0;font-style:oblique}.tsfem-feed-entry{margin-bottom:14px;border-bottom:2px solid #dedede;display:inline-block}.tsfem #input-activation{width:100%;margin:0;padding:0}.tsfem #input-activation .regular-text,.tsfem #input-activation .tsfem-button{margin-left:0;margin-right:0;border-radius:3px;width:100%;font-size:13px}.tsfem #input-activation .regular-text:nth-child(1n+2){margin-top:4px;margin-bottom:8px}#wpfooter .tsfem-footer-wrap .tsfem-footer-motto,#wpfooter .tsfem-footer-wrap .tsfem-footer-title{font-size:13px;margin:0;color:#646d78;font-weight:300;cursor:default}#wpfooter .tsfem-footer-wrap .tsfem-footer-title{font-weight:400;color:#555d66}.tsfem-ajax:after,.tsfem-dashicon:after{display:inline-block;line-height:1;font-family:dashicons;font-style:normal;font-weight:400;font-size:1.225em;width:1em;vertical-align:baseline;content:"";margin-left:4px}.tsfem-dashicon:after{font-size:1.2em;vertical-align:middle;vertical-align:text-bottom;margin-left:2px}.tsfem-ajax.tsfem-loading:after{content:"\f463";color:#007bd2;-webkit-animation:tsfem-spin 1.5s linear infinite;-moz-animation:tsfem-spin 1.5s linear infinite;-o-animation:tsfem-spin 1.5s linear infinite;animation:tsfem-spin 1.5s linear infinite}.tsfem-ajax.tsfem-error:after,.tsfem-dashicon.tsfem-error:after{content:"\f158";color:#dd3811}.tsfem-ajax.tsfem-success:after,.tsfem-dashicon.tsfem-success:after{content:"\f147";color:#0cc34b}.tsfem-ajax.tsfem-unknown:after,.tsfem-dashicon.tsfem-unknown:after{content:"\f223";color:#007bd2}.tsfem-dashicon.tsfem-warning:after{content:"\f227";color:#ffa01b}@-webkit-keyframes tsfem-spin{0%{-webkit-transform:rotate(0)}100%{-webkit-transform:rotate(360deg)}}@keyframes tsfem-spin{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}@media screen and (min-height:800px){.tsfem-extensions-wrap{max-height:initial}}@media only screen and (min-height:600px) and (max-width:782px){body.tsfem .wrap{margin-bottom:0}body.tsfem #wpfooter{margin:0}}@media screen and (max-height:800px),(max-width:782px){body.tsfem .wrap{height:auto;margin-bottom:30px}}@media screen and (max-width:782px){body.tsfem .wrap{margin-bottom:0}}@media only screen and (max-height:600px),(max-width:782px){.tsfem-pane-full,.tsfem-pane-half{min-width:100%;max-width:100%}.tsfem-actions-wrap,.tsfem-extensions-wrap,.tsfem-panes-row,.tsfem-trends-wrap{min-height:initial;height:initial}.tsfem-panes-row .tsfem-pane-half{margin-bottom:20px}.tsfem-pane-content{-webkit-flex:1 1 auto;flex:1 1 auto}.tsfem-panes-row .tsfem-pane-half:last-of-type{margin-bottom:0}.tsfem-pane-wrap{max-height:75vh}.tsfem-pane-half:first-of-type{margin-right:0}body.tsfem #wpfooter{display:block}}@media only screen and (max-width:600px){.tsfem-connect-option{-webkit-flex-direction:column;flex-direction:column;-webkit-align-items:flex-start;align-items:flex-start;padding:1em 20px 1.66em}.tsfem-connect-action{text-align:left}.tsfem-connect-fields-row{-webkit-justify-content:flex-start;justify-content:flex-start}}.tsfem-desc-balloon{position:absolute;padding:0;margin:0;bottom:0;opacity:1;z-index:9999999;display:flex;-webkit-flex:1 1 auto;flex:1 1 auto;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-flex-direction:row;flex-direction:row;-webkit-justify-content:flex-start;justify-content:flex-start}.tsfem-desc-balloon>span{position:relative;top:0;margin:0;max-width:200px;padding:10px 12px;-webkit-box-shadow:0 0 2px rgba(0,0,0,.6);box-shadow:0 0 2px rgba(0,0,0,.6);background:#05809e;text-shadow:0 -1px 1px #006a85,1px 0 1px #006a85,0 1px 1px #006a85,-1px 0 1px #006a85;text-align:left;font-size:1em;font-weight:500;line-height:1.625em;color:#fff;white-space:pre-line;word-break:break-word;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex:1 0 auto;flex:1 0 auto;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-flex-direction:row;flex-direction:row;-webkit-justify-content:flex-start;justify-content:flex-start}.tsfem-desc-balloon>span *{white-space:pre;overflow:auto;word-wrap:break-word;overflow-wrap:break-word}.tsfem-desc-balloon>span span{text-decoration:underline}.tsfem-desc-balloon>div{width:0;height:0;border-left:12px solid transparent;border-right:12px solid transparent;border-top:12px solid #05809e;position:absolute;bottom:-8px;z-index:9999998;left:0}.tsfem-nomargin{margin:0;padding:0}.tsfem-hidden{display:none}.tsfem-has-hover-balloon{position:relative;cursor:help} diff --git a/lib/fonts/eula.txt b/lib/fonts/eula.txt index a5710bc1..95beec4e 100644 --- a/lib/fonts/eula.txt +++ b/lib/fonts/eula.txt @@ -1,7 +1,6 @@ TrueType core fonts for the Web EULA - END-USER LICENSE AGREEMENT FOR - MICROSOFT SOFTWARE + END-USER LICENSE AGREEMENT FOR MICROSOFT SOFTWARE IMPORTANT-READ CAREFULLY: This Microsoft End-User License Agreement ("EULA") is a legal agreement between you (either an individual or a single entity) and @@ -70,7 +69,7 @@ NO WARRANTIES. Microsoft expressly disclaims any warranty for the SOFTWARE PRODUCT. The SOFTWARE PRODUCT and any related documentation is provided "as is" without warranty of any kind, either express or implied, including, without limitation, the implied warranties or merchantability, fitness for a particular -purpose, or noninfringement. The entire risk arising out of use or performance +purpose, or noninfringement. The entire risk arising out of use or performance of the SOFTWARE PRODUCT remains with you. NO LIABILITY FOR CONSEQUENTIAL DAMAGES. In no event shall Microsoft or its diff --git a/lib/images/index.php b/lib/images/index.php index 0cd6d24a..0ff7dbb7 100644 --- a/lib/images/index.php +++ b/lib/images/index.php @@ -1,6 +1,6 @@ ' . esc_html( $option ) . ' is a protected option.' ); + /** + * Load class overloading traits. + */ + _tsf_extension_manager_load_trait( 'overload' ); + return SecureOption::verify_option_instance( $new_value, $old_value, $option ); } @@ -243,7 +248,7 @@ function _autoload_tsf_extension_manager_classes( $class ) { * @staticvar array $loaded * * @param string $file Where the trait is for. - * @return void. + * @return void; Early if file is already loaded. */ function _tsf_extension_manager_load_trait( $file ) { diff --git a/the-seo-framework-extension-manager.php b/the-seo-framework-extension-manager.php index 1135d5ab..3ead4f13 100644 --- a/the-seo-framework-extension-manager.php +++ b/the-seo-framework-extension-manager.php @@ -21,7 +21,7 @@ * * Not all states are the same for each site at all times, making it a vibrant plugin. * Use isset/function_exists/method_exists as much as possible if you wish to apply alterations. - * Alternatively, look at The SEO Framework's can_i_use() function. + * Alternatively, look at The SEO Framework's can_i_use() method. */ /** @@ -41,35 +41,6 @@ * along with this program. If not, see . */ -register_activation_hook( __FILE__, 'tsf_extension_manager_check_php' ); -/** - * Checks whether the server can run this plugin on activation. - * If not, it will deactivate this plugin. - * - * @since 1.0.0 - */ -function tsf_extension_manager_check_php() { - - //* Let's have some fun with teapots. - $error = floor( time() / DAY_IN_SECONDS ) === floor( strtotime( 'first day of April ' . date( 'Y', time() ) ) / DAY_IN_SECONDS ) ? 418 : 500; - - if ( ! defined( 'PHP_VERSION_ID' ) || PHP_VERSION_ID < 50500 ) { - deactivate_plugins( plugin_basename( __FILE__ ) ); - wp_die( 'The SEO Framework - Extension Manager requires PHP 5.4 or later. Sorry about that!
- Do you want to go back?', - 'The SEO Framework - Extension Manager « Server Requirements', - array( 'response' => intval( $error ) ) - ); - } elseif ( $GLOBALS['wp_db_version'] < 35700 ) { - deactivate_plugins( plugin_basename( __FILE__ ) ); - wp_die( 'The SEO Framework - Extension Manager requires WordPress 4.4 or later. Sorry about that!
- Do you want to go back?', - 'The SEO Framework - Extension Manager « WordPress Requirements', - array( 'response' => intval( $error ) ) - ); - } -} - /** * CDN Cache buster. 3 point. * @since 1.0.0 @@ -136,10 +107,84 @@ function tsf_extension_manager_check_php() { */ define( 'TSF_EXTENSION_MANAGER_EXTENSION_OPTIONS', 'tsf-extension-manager-extension-settings' ); +add_action( 'activate_' . TSF_EXTENSION_MANAGER_PLUGIN_BASENAME, '_tsf_extension_manager_test_server' ); +/** + * Checks whether the server can run this plugin on activation. + * If not, it will deactivate this plugin. + * + * This function will create a parse error on PHP < 5.3 (use of goto wrappers). + * Which makes a knowledge database entry easier to make as it won't change anytime soon. + * Otherwise, it will crash in the first called file because of the "use" keyword. + * + * @since 1.0.0 + * @see register_activation_hook() + * @link https://developer.wordpress.org/reference/functions/register_activation_hook/ + * + * @param bool $network_wide Whether the plugin is activated on a multisite network. + * @return void Early if tests pass. + */ +function _tsf_extension_manager_test_server( $network_wide = false ) { + + evaluate : { + PHP_VERSION_ID < 50521 and $test = 1 + or PHP_VERSION_ID >= 50600 && PHP_VERSION_ID < 50605 and $test = 2 + or $GLOBALS['wp_db_version'] < 35700 and $test = 3 + or $test = true; + } + + //* All good. + if ( true === $test ) + return; + + deactivate : { + //* Not good. Deactivate plugin. + deactivate_plugins( plugin_basename( __FILE__ ) ); + } + + //* Let's have some fun with teapots. + $response = floor( time() / DAY_IN_SECONDS ) === floor( strtotime( 'first day of April ' . date( 'Y' ) ) / DAY_IN_SECONDS ) ? 418 : 500; + + switch ( $test ) : + case 1 : + case 2 : + //* PHP requirements not met, always count up to encourage best standards. + $requirement = 1 === $test ? 'PHP 5.5.21 or later' : 'PHP 5.6.5 or later'; + $issue = 'PHP version'; + $version = phpversion(); + $subtitle = 'Server Requirements'; + break; + + case 3 : + //* WordPress requirements not met. + $requirement = 'WordPress 4.4 or later'; + $issue = 'WordPress version'; + $version = $GLOBALS['wp_version']; + $subtitle = 'WordPress Requirements'; + break; + + default : + return; + endswitch; + + $network = $network_wide ? 'network/' : ''; + $pluginspage = admin_url( $network . 'plugins.php' ); + + wp_die( + sprintf( + '

The SEO Framework - Extension Manager requires %s. Sorry about that!
Your %s is: %s

+

Do you want to go back?

', + esc_html( $requirement ), esc_html( $issue ), esc_html( $version ), esc_url( $pluginspage ) + ), + sprintf( 'The SEO Framework - Extension Manager « %s', esc_attr( $subtitle ) ), + array( 'response' => intval( $response ) ) + ); +} + add_action( 'plugins_loaded', 'init_tsf_extension_manager_locale', 4 ); /** - * Plugin locale 'the-seo-framework-extension-manager' - * Locale folder the-seo-framework-extension-manager/language/ + * Loads plugin locale: 'the-seo-framework-extension-manager' + * Locale folder: the-seo-framework-extension-manager/language/ + * * @since 1.0.0 * @staticvar $loaded Determines if the textdomain has already been loaded. * diff --git a/views/layout/general/top.php b/views/layout/general/top.php index 32d39fb8..f9ff8cfd 100644 --- a/views/layout/general/top.php +++ b/views/layout/general/top.php @@ -47,10 +47,12 @@ /** * Test for GD library functionality upon logo. * + * Only runs on activation page. + * * This is the first step towards "true" Google pixel guidelines character count testing. * Let's see how this works out :). */ -if ( extension_loaded( 'gd' ) && function_exists( 'imageftbbox' ) ) : +if ( false === $this->is_plugin_activated() && extension_loaded( 'gd' ) && function_exists( 'imageftbbox' ) ) : $font = $this->get_font_file_location( 'verdana.ttf' ); if ( file_exists( $font ) ) : //* Calculate text-width. 1.9em @ 13px body. diff --git a/views/layout/pages/extensions.php b/views/layout/pages/extensions.php index c29d0516..5fcefdc9 100644 --- a/views/layout/pages/extensions.php +++ b/views/layout/pages/extensions.php @@ -2,7 +2,7 @@ defined( 'ABSPATH' ) and tsf_extension_manager()->_verify_instance( $_instance, $bits[1] ) or die; ?> -
+
do_pane_wrap( __( 'SEO Trends and Updates', 'the-seo-framework-extension-manager' ), @@ -29,7 +29,7 @@ ); ?>
-
+
do_pane_wrap( __( 'Extensions', 'the-seo-framework-extension-manager' ),