diff --git a/autodescription.php b/autodescription.php index b89f351c..5c0e9399 100644 --- a/autodescription.php +++ b/autodescription.php @@ -3,7 +3,7 @@ * Plugin Name: The SEO Framework * Plugin URI: https://theseoframework.com/ * Description: An automated, advanced, accessible, unbranded and extremely fast SEO solution for your WordPress website. - * Version: 5.1.3-dev-6 + * Version: 5.1.3-dev-7 * Author: Sybre Waaijer * Author URI: https://theseoframework.com/ * License: GPLv3 @@ -45,7 +45,7 @@ * * @since 2.3.5 */ -define( 'THE_SEO_FRAMEWORK_VERSION', '5.1.2' ); +define( 'THE_SEO_FRAMEWORK_VERSION', '5.1.3' ); /** * The plugin Database version. @@ -54,7 +54,7 @@ * * @since 2.7.0 */ -define( 'THE_SEO_FRAMEWORK_DB_VERSION', '5100' ); +define( 'THE_SEO_FRAMEWORK_DB_VERSION', '5130' ); /** * The plugin file, absolute unix path. diff --git a/bootstrap/upgrade.php b/bootstrap/upgrade.php index 616328a3..2f45eaaf 100644 --- a/bootstrap/upgrade.php +++ b/bootstrap/upgrade.php @@ -214,7 +214,7 @@ function _upgrade( $previous_version ) { '2701', '2802', '2900', '3001', '3103', '3300', '4051', '4103', '4110', '4200', '4270', - '5001', '5050', '5100', + '5001', '5050', '5100', '5130', ]; // phpcs:enable, WordPress.Arrays.ArrayDeclarationSpacing.ArrayItemNoNewLine @@ -967,3 +967,15 @@ function _do_upgrade_5100() { Data\Plugin::update_option( 'homepage_redirect', '' ); } } + +/** + * Registers new option 'author_tags'. + * + * @since 5.1.3 + */ +function _do_upgrade_5130() { + + if ( \get_option( 'the_seo_framework_initial_db_version' ) < '5130' ) { + Data\Plugin::update_option( 'author_tags', 1 ); + } +} diff --git a/inc/classes/data/filter/plugin.class.php b/inc/classes/data/filter/plugin.class.php index fa72ef6a..2dc88c26 100644 --- a/inc/classes/data/filter/plugin.class.php +++ b/inc/classes/data/filter/plugin.class.php @@ -154,6 +154,7 @@ public static function register_sanitizers_jit() { 'alter_archive_query_type' => 'alter_query_type', 'alter_search_query_type' => 'alter_query_type', 'alter_search_query' => 'checkbox', + 'author_tags' => 'checkbox', 'author_noarchive' => 'checkbox', 'author_nofollow' => 'checkbox', 'author_noindex' => 'checkbox', @@ -224,6 +225,7 @@ public static function register_sanitizers_jit() { 'og_tags' => 'checkbox', 'paged_noindex' => 'checkbox', 'pint_verification' => 'verification_code', + 'post_author' => 'metadata_text', 'post_modify_time' => 'checkbox', 'post_publish_time' => 'checkbox', 'prev_next_archives' => 'checkbox', diff --git a/inc/classes/data/plugin/setup.class.php b/inc/classes/data/plugin/setup.class.php index 3a12264c..f6a81324 100644 --- a/inc/classes/data/plugin/setup.class.php +++ b/inc/classes/data/plugin/setup.class.php @@ -267,6 +267,9 @@ public static function get_default_options() { 'post_publish_time' => 1, // Article Published Time. 'post_modify_time' => 1, // Article Modified Time. + // Author. + 'post_author' => '', // Author fallback name. + // Twitter. 'twitter_card' => 'summary_large_image', // Twitter Card layout. If no twitter:image image is found, it'll change to 'summary', radio 'twitter_site' => '', // Twitter business @username. @@ -281,6 +284,7 @@ public static function get_default_options() { 'og_tags' => 1, // Output of Open Graph meta tags. 'facebook_tags' => 1, // Output the Facebook meta tags. 'twitter_tags' => 1, // Output the Twitter meta tags. + 'author_tags' => 1, // Output author meta tags. 'oembed_scripts' => 1, // Enable WordPress's oEmbed scripts. // Social title settings. diff --git a/inc/classes/front/meta/generator/author.class.php b/inc/classes/front/meta/generator/author.class.php new file mode 100644 index 00000000..2ad4e189 --- /dev/null +++ b/inc/classes/front/meta/generator/author.class.php @@ -0,0 +1,71 @@ +. + */ + +/** + * Holds Author generators for meta tag output. + * + * @since 5.1.3 + * @access private + */ +final class Author { + + /** + * @since 5.1.3 + * @var callable[] GENERATORS A list of autoloaded meta callbacks. + */ + public const GENERATORS = [ + [ __CLASS__, 'generate_author' ], + ]; + + /** + * Generates the author meta tag. + * + * @since 5.0.0 + * @generator + */ + public static function generate_author() { + + // Only output on singular posts/pages where an author is available + if ( ! Query::is_singular() ) return; + + $author_name = Meta\Author::get_author_name(); + + if ( \strlen( $author_name ) ) + yield 'author' => [ + 'attributes' => [ + 'name' => 'author', + 'content' => $author_name, + ], + ]; + } +} diff --git a/inc/classes/front/meta/head.class.php b/inc/classes/front/meta/head.class.php index 00ba511c..bb19ac03 100644 --- a/inc/classes/front/meta/head.class.php +++ b/inc/classes/front/meta/head.class.php @@ -118,6 +118,7 @@ public static function print_tags() { 'Robots', 'URI', 'Description', + 'Author', 'Theme_Color', 'Open_Graph', 'Facebook', @@ -170,6 +171,10 @@ public static function print_tags() { $remove_pools[] = 'Twitter'; } + // Remove Author generator if author meta is disabled + if ( ! Data\Plugin::get_option( 'author_tags' ) ) + $remove_pools[] = 'Author'; + /** * @since 5.0.0 * @param string[] $generator_pools A list of tag pools requested for the current query. diff --git a/inc/classes/meta/author.class.php b/inc/classes/meta/author.class.php new file mode 100644 index 00000000..17330ef1 --- /dev/null +++ b/inc/classes/meta/author.class.php @@ -0,0 +1,52 @@ +. + */ + +/** + * Holds getters for meta tag output. + * + * @since 5.1.3 + * @access protected + * Use tsf()->author() instead. + */ +class Author { + + /** + * @since 5.1.3 + * + * @return string The author name. + */ + public static function get_author_name() { + + // Only output on single posts where an author is available + if ( ! Query::is_single() ) return; + + return Title::get_user_title( Query::get_post_author_id() ) + ?: Data\Plugin::get_option( 'post_author' ); + } +} diff --git a/inc/classes/meta/facebook.class.php b/inc/classes/meta/facebook.class.php index 15fd07db..9ff0c320 100644 --- a/inc/classes/meta/facebook.class.php +++ b/inc/classes/meta/facebook.class.php @@ -45,6 +45,11 @@ public static function get_author() { if ( 'article' !== Open_Graph::get_type() ) return; + // Check if author tags are enabled + if ( ! Data\Plugin::get_option( 'author_tags' ) ) { + return Data\Plugin::get_option( 'facebook_author' ); + } + return Data\Plugin\User::get_current_post_author_meta_item( 'facebook_page' ) ?: Data\Plugin::get_option( 'facebook_author' ); } diff --git a/inc/classes/meta/schema/entities/author.class.php b/inc/classes/meta/schema/entities/author.class.php index 26d3eb89..8109df3c 100644 --- a/inc/classes/meta/schema/entities/author.class.php +++ b/inc/classes/meta/schema/entities/author.class.php @@ -110,7 +110,7 @@ public static function build( $args = null ) { $entity = [ '@type' => static::$type, '@id' => static::get_id( [ 'uid' => $author_id ] ), - 'name' => $user_data->display_name ?? '', // Yes, this could lead to an empty Author entity in a corner case. + 'name' => Meta\Title::get_user_title( $author_id ), // Yes, this could lead to an empty Author entity in a corner case. // Let's not; may invoke bad bots. Let's do this via sameas. // 'url' => Meta\URI::get_bare_author_url( $author_id ), ]; diff --git a/inc/classes/meta/title.class.php b/inc/classes/meta/title.class.php index 711140ed..487a3822 100644 --- a/inc/classes/meta/title.class.php +++ b/inc/classes/meta/title.class.php @@ -701,7 +701,7 @@ public static function get_term_title( $term = null ) { */ public static function get_user_title( $user_id = 0 ) { return Sanitize::metadata_content( - \get_userdata( $user_id ?: Query::get_the_real_id() )->display_name ?? '' + Data\User::get_userdata( $user_id ?: Query::get_the_real_id(), 'display_name' ) ?? '' ); } diff --git a/inc/classes/meta/twitter.class.php b/inc/classes/meta/twitter.class.php index d2f6b515..4c8fdfd8 100644 --- a/inc/classes/meta/twitter.class.php +++ b/inc/classes/meta/twitter.class.php @@ -207,6 +207,12 @@ public static function get_site() { * @return string */ public static function get_creator() { + + // Check if author tags are enabled + if ( ! Data\Plugin::get_option( 'author_tags' ) ) { + return Data\Plugin::get_option( 'twitter_creator' ); + } + return Data\Plugin\User::get_current_post_author_meta_item( 'twitter_page' ) ?: Data\Plugin::get_option( 'twitter_creator' ); } diff --git a/inc/classes/pool.class.php b/inc/classes/pool.class.php index 83ef58b4..8216089b 100644 --- a/inc/classes/pool.class.php +++ b/inc/classes/pool.class.php @@ -174,6 +174,25 @@ public static function utils() { }; } + /** + * Returns the Author API class as instantiated object with deprecation capabilities. + * This allows for easy API access, and it allows us to silence fatal errors. + * + * @since 5.1.3 + * @api Not used internally. + * + * @return \The_SEO_Framework\Meta\Author + */ + public static function author() { + return static::$pool['author'] ??= new class extends Meta\Author { + use Static_Deprecator; + + private $colloquial_handle = 'tsf()->author()'; + private $deprecated_methods = []; + private $deprecated_properties = []; + }; + } + /** * Returns the Breadcrumbs API class as instantiated object with deprecation capabilities. * This allows for easy API access, and it allows us to silence fatal errors. diff --git a/inc/views/profile/settings.php b/inc/views/profile/settings.php index 7fb9af98..2ce5131b 100644 --- a/inc/views/profile/settings.php +++ b/inc/views/profile/settings.php @@ -49,6 +49,8 @@ ], ]; +// Only show authorial info if author_tags is enabled +if ( Data\Plugin::get_option( 'author_tags' ) ) : ?>
+ +
+ ++ +
+@@ -373,7 +424,7 @@ ?> - +
diff --git a/readme.txt b/readme.txt index 17256c48..589ecdcf 100644 --- a/readme.txt +++ b/readme.txt @@ -275,19 +275,43 @@ TODO in `The_SEO_Framework\Data\User::get_userdata()`, we may want implement thi **For everyone:** -* **Fixed:** - * Abbreviations at the start of sentences are now properly considered by the description generator. -* **Removed:** - * TODO Compatibility with the Headway theme has been removed. The theme is no longer maintained since 2017 and the developer's website is down. +* **Added:** Comprehensive author meta tag support with global fallback option for improved LinkedIn and social media post sharing. +* **Added:** Author Name Fallback setting in Social Meta Tags > Author Settings for specifying a default author name. +* **Added:** Authorial tags toggle now controls visibility of author profile settings and fallback behavior. +* **Enhanced:** Facebook and Twitter author meta generation now respects the authorial tags setting with proper fallback to global settings. +* **Enhanced:** Profile settings for authorial information are now conditionally displayed based on authorial tags setting. +* **Fixed:** Author meta tags implementation follows WordPress coding standards and TSF architecture patterns. **For developers:** +* **Added:** `tsf()->author()` method for accessing author meta functionality via the pool pattern. +* **Added:** `post_author` option with proper sanitization and validation. +* **Enhanced:** JavaScript toggle functionality for hiding author override messages when authorial tags are disabled. + +This release augments issues #515, #595, #654, and #690 by providing comprehensive author meta tag functionality with global fallback support. + +TODO we should probably make this v5.2 (major), and add all the author stuff. +TODO remove all deprecated filters, keep all deprecated methods/functions. + +**For everyone:** + +* **Added:** + * We added a new toggle for author meta data in the site settings. It's enabled by default for everyone, for it extends existing plugin behavior. + * When enabled: + * A new "author" metatag is added for posts, it shows the "display name" for the author. + * When disabled the primary author can still be assigned. + * TODO IMPLEMENT: Authors have "Facebook profile page" and "X profile handle" links added to their profile pages. + * This is already implemented, but we should toggle the listeners for this. + * TODO IMPLEMENT "Authors can override this option on their profile page." must be hidden when toggled off. + * TODO Authors now have all SEO fields on their profile pages, like we have for terms. + * TODO Authors get the SEO Bar displayed on the author overview list pages. + * Bonus: This allows you to quickly see who is an Author and who isn't. + * Non-authors get a medium dash symbol instead of the SEO Bar. + * TODO You can now sort by SEO Bar status. This revolutionary (wow, I'm marketing!) feature allows you to quickly find posts and terms that need your attention. * **Fixed:** - * Resolved an issue where pools `tsf()->escape()` and `tsf()->sanitize()` were incorrectly marked to be from pool `tsf()->filter()->escape()` and `tsf()->filter()->sanitize()` respectively. -* **Other:** - * We now properly capitalize the proper noun Boolean. - * `tsfCanonicalL10n.allowCanonicalURLNotationTracker` is renamed to `tsfCanonicalL10n.allowCanonicalURLNotationTracker`, which is more consistent with the rest of the codebase. - * This change is not backward compatible; however, the property was marked with the comment "TEMP: [...]", as it was a quick workaround for a compatibility issue with multilingual plugins. + * Abbreviations at the start of sentences are now properly considered by the description generator. +* **Removed:** + * TODO Compatibility with the Headway theme has been removed. The theme is no longer maintained since 2017 and the developer's website is down. **For translators:** @@ -297,8 +321,17 @@ TODO in `The_SEO_Framework\Data\User::get_userdata()`, we may want implement thi **For developers:** +* **Option notes:** + * Resolved an issue where pools `tsf()->escape()` and `tsf()->sanitize()` were incorrectly marked to be from pool `tsf()->filter()->escape()` and `tsf()->filter()->sanitize()` respectively. + * Of option `autodescription-site-settings` (constant `THE_SEO_FRAMEWORK_SITE_OPTIONS`, pool `tsf()->data()->plugin()`, or legacy API `tsf()->get_options()`): + * Added index `author_meta`. Default `1`, also for existing installations. +* **Fixed:** + * Resolved an issue where pools `tsf()->escape()` and `tsf()->sanitize()` were incorrectly marked to be from pool `tsf()->filter()->escape()` and `tsf()->filter()->sanitize()` respectively. * **Other:** + * We now properly capitalize the proper noun Boolean. * We updated our coding standards, so the code is slightly altered. + * `tsfCanonicalL10n.allowCanonicalURLNotationTracker` is renamed to `tsfCanonicalL10n.allowCanonicalURLNotationTracker`, which is more consistent with the rest of the codebase. + * This change is not backward compatible; however, the property was marked with the comment "TEMP: [...]", as it was a quick workaround for a compatibility issue with multilingual plugins. ### 5.1.2