This minor update packs a major punch. TSF now supports headless mode, cementing itself as a turnkey solution. We defenestrated the pernicious object caching mechanism, and we updated some options’ defaults effective only on new sites. We improved performance iterably, fixed about 12 bugs, and enjoyed the weather. Lastly, we introduced a new API for user meta handling, among other things — developers that wrote software interfacing with TSF are employed well reading the detailed changelog.
Pro tip: If you can no longer crop images via TSF, save your changes, and then try hitting CMD ⌘+OPTION ⌥+R (Safari Mac), CMD ⌘+SHIFT ⇧+R (Chrome/Firefox Mac), or CTRL ^+SHIFT ⇧+R (Windows); these keyboard shortcuts will force-fetch the latest scripts from your server.
Detailed log
> View code changes.
> View closed issues.
For everyone
- Added:
- Don’t care about SEO? Now you can use TSF headlessly. Simply add the following snippet to your
wp-config.php
file or a (mu-)plugin:define( 'THE_SEO_FRAMEWORK_HEADLESS', true );
- This constant must be defined before action
plugins_loaded
priority5
. - This constant also listens to granular control by setting an array instead of a boolean.
- This constant denies access to database calls, speeding up your website tremendously. However, your SEO (meta) settings won’t work.
- (But…) This constant can be used conditionally when used in a (mu-)plugin; for example, only super-admins can modify the settings/meta-data. Then, you must disable headlessness on the front-end for them to have an effect.
- Learn more about headless mode.
- This constant must be defined before action
- Don’t care about SEO? Now you can use TSF headlessly. Simply add the following snippet to your
- Changed:
- Option defaults:
- “Apply
noindex
to every second or later archive page?” is now disabled by default. We found that, even though it helps search engines crawl your website more quickly, blocking paginated archives could actually be deleterious to indexing old content in select scenarios.- We may consider reverting this decision once we can discern separate “post type archive” (including a separate “blog”) options for this.
- “Remove author name?” (from oEmbed) is now enabled by default. We asserted only Discord using this feature in the wild, and found those who care think Discord’s way of handling it undesired.
- “Sitemap Query Limit” is now lowered to 1000 items, from 3000.
- In combination with not applying
noindex
to every second or later archive page, this will improve indexing rate for almost all sites. This change also reduces the strain on your server, especially when you have many other plugins adding post meta to each post.
- In combination with not applying
- “Apply
- Redirected pages/posts/terms* are no longer included in the sitemap. For real this time.
- * WordPress Core sitemaps can display terms; our optimized version does not.
- Option defaults:
- Improved:
- Continuing the trend we set since TSF v4.0.0, we reduced the size of the main object of TSF significantly (7.8%) by offloading various methods to flyweight objects. This should improve load times, albeit only a little.
- The quick-edit column for the Visibiity setting is now 1.5x wider on non-mobile. This change might take some ‘getting used to,’ but now you can read the URLs you put in.
- Removed:
- Object caching of the HTML output of TSF.
- We theorized no benefit having this feature, and thought it rather harmful; later, we found this true empirically.
- This does not mean we removed basic object caching support! We only purged our very custom implementation for it. You do not need to clear your object cache after updating, and you won’t find performance altered.
- Object caching of the HTML output of TSF.
- Updated:
[?]
/Documentation links to various official Google and Bing resources no longer need to redirect to their latest articles.
- Other:
- It’s 2021 now… so, we extended the plugin’s copyright year notes.
- TSF’s ownership has been transferred from CyberWire to CyberWire B.V… Or, did the company itself transfer its own ownership? Who owns what? Do I own me? Technicalities aside, the new company will ruin everything.
- “The SEO Framework Title Fix” plugin (from WordPress.org, no longer available) no longer works with The SEO Framework from this version onward.
- Use our Extension Manager’s Title Fix instead. Or, better yet, update your theme to follow the latest standards at least once every decade.
- WordPress 5.7 came with improved robots-directives support. The improvements only go so far, and although WordPress seems dogmatic about bringing the editor experience to the front-end, it falls completely flat recognizing that the back-end query-system is disjointed from the front-end, as such is the newly ‘improved’ robots-directives support. Because we reliably rely on reliable queries with reliance, we are left with no choice but to strip some of its conflicting functionality from Core, without changing the fully fledged experience you’ve come to expect from TSF. To summarize our actions:
- TSF will not integrate with this new Core feature for now.
- TSF will remove some of WordPress 5.7 robots support on these pages:
noindex
for search. TSF supports this feature with a toggle already. When TSF doesn’t support the search query’s query (yes, that’s a thing), this directive can be reinstated via Core.- The new robots-directive support for
maximum-image-preview
-directives throughout your site, regardless whether a page is supported by TSF or not. TSF allows you much greater control and supports this directive quintessentially in principle on all pages it supports. We removed this output from WordPress as an enforced opt-out of this directive because there’s a conflict of policy, exempting foreboding legal issues from your potential clients for whatever TSF promises on its settings-pages to the site owners. ELI2: no copyright WP-robots, yes copyright TSF-robots. “But… but…!”, – no, talk to your lawyer.
- TSF will not remove the newly added
noindex
for embed pages. WordPress can detect embeds, and plugins or themes might respond differently (unpredictably) when embedded. - TSF will not remove the refactored WordPress method for outputting
noindex
when the blog isn’t set to public. TSF will warn you about this still – very much so. - TSF will not remove any implementation from other plugins or themes. First, this is definitely not the domain of themes. Secondly, we cannot predict what others plugin authors might do, and we’re very much inclinded to shift the blame… always.
- WordPress 5.8 brings no conflicting changes.
- Fixed:
- Addressed an issue where “Apply
noindex
to every second or later page on the homepage?” wasn’t honored when the homepage is force-indexed via the homepage’s post meta. - Addressed an issue where the character counter wasn’t aligned pixel-perfectly on RTL-language sites.
- Addressed an issue where image-tooltips didn’t load correctly when reloading the DOM, such as with our Local extension.
- Addressed an issue where in quick-edit, the homepage title wasn’t locked correctly, causing it-and its counters to render incorrectly.
- Removed double-deletion of term meta when deleting a term. That can’t happen of course… so, uh… yeah, we removed a redundant database call during term deletion. Cool.
- Resolved a memory leak in image-tooltips.
- Resolved an issue where excluded post types could still have their search-archives adjusted.
- The following was done to achieve this (bear with me – “excluding posts from excluded post types…” yea, I don’t get it either):
- Post meta cannot be fetched from excluded post types. This prevents “on the site”-queries filtering excluded post-type-posts.
- A
LEFT JOIN
…WHERE $wpdb->posts.post_type IN ('post_types[]')
query is now executed for fetching excluded posts, but only when post types are excluded. This type of join should minimize the query impact, because we expect users only to exclude few posts, so only a few posts are parsed. The parent query should be ‘slowest’, but ought to be cached, thence actually ‘fastest’.- As before, this query is executed only once after saving the SEO settings, or after updating/publishing any post or page. The result of the query is stored in a non-expiring “transient” option.
- The following was done to achieve this (bear with me – “excluding posts from excluded post types…” yea, I don’t get it either):
- Resolved an issue where excluded taxonomies could still have their archives adjusted.
- This means your ‘excluded’ posts may still show up in the excluded taxonomies’ terms.
- Resolved an issue where post-link queries via the Block Editor were filtered by post-search exclusions settings.
- The expected sitemap URL now generates a correct URL for (WordPress, WordPress Multisite, Polylang, WPML, etc.) subdirectories.
- TSF now disables WooCommerce’s robots-meta output, and its new WP 5.7 implementation thereof. In turn, TSF will hint
noindex
as default for the Cart, Checkout, and Profile (My Account) pages, regardless of your SEO settings otherwise.- Now, you can also overwrite these settings effectively.
- Cool: TSF will warn that WooCommerce recommends otherwise via the SEO Bar.
- When you don’t overwrite its state, TSF will then tell WooCommerce recommended this state.
- Cool: TSF will warn that WooCommerce recommends otherwise via the SEO Bar.
- Because of this change, TSF will now remove these pages from the sitemap by default. When you force-index these pages, they’ll get added back to the sitemap.
- Nota bene: This only works when setting a page ID via WooCommerce’s settings UI. The setting is accessible via
wc_get_page_id()
(WC 3.0 and later).
- Now, you can also overwrite these settings effectively.
- When using WPML, the main sitemap no longer contains “display translatable” post types, and the translated sitemaps no longer contain “not translatable” post types nor untranslated posts from “display translatable” post types.
- Addressed an issue where “Apply
- Not fixed:
- Polylang transforms (ruins) the home URL, and depending on your Polylang configuration, sitemaps may or may not output as expected. We’ve been beating this same horse iterably, and our spirit is dying, for their developers have been reluctant to communicate about this hitherto – even deleting our comments. Luckily, WordPress will eventually support translatable content natively, after which we can all sigh in relief.
- Briefly, when using Polylang, do not set “URL Modifications” to “The language is set from content”; use any other setting instead. You’ll encounter fewer issues, but using any other setting is also much better for SEO regardless.
- Polylang transforms (ruins) the home URL, and depending on your Polylang configuration, sitemaps may or may not output as expected. We’ve been beating this same horse iterably, and our spirit is dying, for their developers have been reluctant to communicate about this hitherto – even deleting our comments. Luckily, WordPress will eventually support translatable content natively, after which we can all sigh in relief.
For translators
- Added:
- A few new strings require translation.
- You may find a few strings fuzzy because we updated the links to the documentation of Google and Bing.
- Updated:
- Translation POT file.
For developers
- Option notes:
- For
autodescription-site-settings
(constantTHE_SEO_FRAMEWORK_SITE_OPTIONS
):- Changed:
oembed_remove_author
now defaults to1
, from0
.paged_noindex
now defaults to0
, from1
.sitemap_query_limit
now defaults to1000
, from3000
.
- Removed:
cache_object
, this value will not be deleted when upgrading to this version for backward-compatible reasons.- During a manual option-save, however, this value will vanish.
- Changed:
- For
- Object notes:
- Added:
\The_SEO_Framework\Bridges\Ajax
, private, internal use only.- This class is used to handle AJAX requests for The SEO Framework.
- The methods it contains were moved from
\The_SEO_Famework\Admin_Init
class. - This class and its methods are static, and cannot be instantiated.
- All methods in the class are marked private, and should not be integrated in your software.
- This class is final, and cannot be extended.
- The class may contain some filters and actions (callbacks), which are public.
- This class is autoloaded when admin-ajax requests are recorded. Since we do not handle “nopriv” requests, these are thusly ignored.
- In preparation for the UI redesign project, and to compact the main facade object, these new classes are new and marked as protected; all methods therein can disappear or change behavior without notice:
\The_SEO_Framework\Interpreters\HTML
\The_SEO_Framework\Interpreters\Form
\The_SEO_Framework\Interpreters\Markdown
\The_SEO_Framework\Bridges\PluginTable
- This class is used to take care of our implementations for the WordPress plugins overview table.
- This class is meant to be used internally only.
- If you wish to decouple our plugin links, you may need to update your filters now (gone are private methods
_add_plugin_action_links()
and_add_plugin_row_meta()
fromthe_seo_framework()
).
\The_SEO_Framework\Bridges\UserSettings
- This class is used to take care of our implemention for the WordPress profile/user edit page.
- This class is meant to be used internally only.
- If you wish to remove the user settings, you may need to update your filters now (gone are private methods
_update_user_settings
and_add_user_author_fields
fromthe_seo_framework()
).
- Added:
- Method notes:
- For object
\The_SEO_Framework\Load
(the_seo_framework()
):- Added:
advanced_query_protection()
, returns thetsf:aqp
meta tag.- This tag is used to indicate a malformed query was detected. It’s tilt-proof.
append_url_query()
, replacesappend_php_query()
.array_merge_recursive_distinct()
, a glorifiedarray_merge()
that allows unlimited multidimensional arrays inputted combobulated by yours truly.do_meta_output()
, outputs all meta tags for the current query.generate_robots_meta()
, returns generated robots metadata.get_current_post_type()
, shorthand for two other methods to (1) get an estimated post type from the front-end query, with page-as-archive support. (2) Also works extensively in the admin area.get_image_uploader_form()
, returns image uploader form button.- This is an abstraction of the
get_social_image_uploader_form()
andget_logo_uploader_form()
methods, both now call the new one for it has an interpolable API.
- This is an abstraction of the
get_modified_time()
, returns the modified time of the current post.get_redirect_url()
, returns the redirect URL for the current query. Also accepts arguments.get_seo_settings_page_url()
, replacesseo_settings_page_url()
.is_profile_edit()
, tells if (true/false) we’re on a user or profile edit page.og_updated_time()
, disjointed fromarticle_modified_time()
, outputsog:updated_time
meta tag.render_element
, do not use. Useful for generating meta tags with few inputs. Escapes for you.- This method is marked ‘protected’ for we have other plans with it in the future.
s_user_meta()
, sanitizes (registered) TSF profile metadata.save_user_meta()
, sanitizes and saves the user metadata for TSF.- We added these to get the names in sync with post-and term meta:
get_current_post_author_meta()
, returns all TSF author metadata.get_current_post_author_meta_item()
, replaces the now deprecatedget_current_author_option()
.update_single_user_meta_item()
, replacesupdate_user_option
, but it now filters through your input, and it doesn’t expect your input to be escaped.get_user_meta_defaults()
, replaces the now deprecatedget_default_user_data()
.get_user_meta_item()
, replaces the now deprecatedget_user_option()
.
- Improved:
can_i_use()
, fixed sorting algorithm from fribbling-me to resolving-me. Nothing changed but legibility.get_excluded_ids_from_cache()
- Now tests against post type exclusions.
- Now considers headlessness. This method runs only on the front-end.
get_post_meta()
:- Now considers headlessness.
- now returns an empty array when post type isn’t supported. This improvement also affects
get_post_meta_item()
, which will returnnull
.
get_timestamp_format()
:- Added options-override parameter.
- Added return value filter.
get_term_meta()
now considers headlessness.is_blog_page_by_id()
should now be faster thanks to bypassing the options when the input ID is0
.is_static_frontpage()
now memoizes the front page ID option.s_image_details()
, fixed theoretical issue where a different image could be set when width and height are supplied and either over 4K, but no ID is given.
- Changed:
article_modified_time()
no longer outputsog:updated_time
. Useog_updated_time()
instead.delete_cache()
, the following$types
are no longer supported:front
,post
,term
,author
,robots
,object
.delete_main_cache()
, no longer flushesfront
,robots
, andobject
cache.delete_post_cache()
, no longer flushespost
object cache.generate_cache_key()
no longer returns a key when no$type
is supplied.generate_cache_key_by_type()
, removed support forauthor
,frontpage
,page
,post
,attachment
,singular
, andterm
.is_product()
now has its return value memoized.is_product_admin()
now has its return value memoized.is_shop()
now has its return value memoized.robots_txt()
, removed object caching support.
- Removed:
- Object caching support: These methods were removed with no alternative available. Calling these methods will output a deprecation notice.
delete_author_cache()
delete_object_cache()
generate_cache_key_by_query()
generate_front_page_cache_key()
get_meta_output_cache_key_by_query()
get_meta_output_cache_key_by_type()
get_robots_txt_cache_key()
object_cache_set()
object_cache_get()
object_cache_delete()
- Deprecated methods:
- All methods deprecated in TSF v4.0.0 or earlier are no longer available. Calling those will result null and void:
doing_ajax()
enqueue_gutenberg_compat_scripts()
enqueue_media_scripts()
enqueue_primaryterm_scripts()
get_custom_field()
get_default_scripts()
get_header_image()
get_image_from_woocommerce_gallery()
get_schema_image()
get_seo_bar()
get_site_icon()
get_site_logo()
get_sitemap_xsl_url()
get_sitemap_xml_url()
get_social_image()
get_social_image_url_from_attachment()
get_social_image_url_from_home_meta()
get_social_image_url_from_post_meta()
get_social_image_url_from_post_thumbnail()
get_social_image_url_from_seo_settings()
get_taxonomial_canonical_url()
initialize_term_meta()
metabox_scripts()
output_sitemap_xsl_stylesheet()
ping_google()
ping_bing()
ping_searchengines()
post_status()
post_type_supports_custom_seo()
sanitize_field_id()
Scripts()
taxonomy_supports_custom_seo()
- All methods deprecated in TSF v4.0.0 or earlier are no longer available. Calling those will result null and void:
- Protected:
init_term_meta()
is now protected; it always should’ve been.
- Object caching support: These methods were removed with no alternative available. Calling these methods will output a deprecation notice.
- Info:
- The following methods are now marked for deprecation:
- “Marked” means that you should treat them as deprecated, but we honor them as maintained until the next major update.
append_php_query()
. Useappend_url_query()
instead.attention()
attention_description()
attention_description_noesc()
attention_noesc()
can_do_sitemap_robots()
.code_wrap()
code_wrap_noesc()
description()
description_noesc()
field_id()
field_name()
get_author_option()
. Useget_user_meta_item()
instead.get_current_author_option()
. Useget_current_post_author_meta_item()
instead.get_default_user_data()
. Useget_user_meta_defaults()
instead.get_field_id()
get_field_name()
get_html_output()
.get_image_uploader_form()
get_is_conditional_checked()
get_logo_uploader_form()
. Useget_logo_uploader_form()
instead.get_social_image_uploader_form()
. Useget_image_uploader_form()
instead.get_user_option()
. Useget_user_meta()
orget_user_meta_item()
instead.is_conditional_checked()
is_default_checked()
is_robots_meta_noindex_set_by_args()
. Usegenerate_robots_meta()
instead.is_warning_checked()
nav_tab_wrapper()
.inpost_flex_nav_tab_wrapper()
.seo_settings_page_url()
. Useget_seo_settings_page_url()
instead.make_data_attributes()
make_checkbox()
make_checkbox_array()
make_single_select_form()
make_info()
output_character_counter_wrap()
output_pixel_counter_wrap()
robots_meta()
, usegenerate_robots_meta()
instead.update_user_option()
. Useupdate_single_user_meta_item()
instead.wrap_fields()
- The following methods are now marked for deletion:
proportionate_dimensions()
.
- The following methods are now marked for deprecation:
- Added:
- For object
The_SEO_Framework\Builders\SeoBar
:- Added:
clear_query_cache()
, clears the query cache.get_query_cache()
, presents an unalterable form of query cache.
- Added:
- For object
The_SEO_Framework\Builders\Sitemap
:- Changed:
is_post_included_in_sitemap()
now returnsfalse
when the post has a redirect set via The SEO Framework. For real this time.is_term_included_in_sitemap()
now returnsfalse
when the term has a redirect set via The SEO Framework.
- Changed:
- For object
The_SEO_Framework\Bridges\Sitemap
:- Changed:
get_expected_sitemap_endpoint_url()
now assimilates the output using the base path, so that filterthe_seo_framework_sitemap_base_path
also works. Glues the pieces together using theget_home_host
value.
- Changed:
- For object
- Function notes:
- Removed (thank you, WP Directory, for helping us verify):
the_seo_framework_active()
the_seo_framework_description_from_cache()
the_seo_framework_dot_version()
the_seo_framework_get_option()
the_seo_framework_is_settings_page()
the_seo_framework_options_page_slug()
the_seo_framework_options_pagehook()
the_seo_framework_the_url_from_cache()
the_seo_framework_title_from_cache()
the_seo_framework_update_option()
the_seo_framework_version()
- Removed (thank you, WP Directory, for helping us verify):
- Property notes:
- For object
\The_SEO_Framework\Load
(the_seo_framework()
):- Marked for deprecation:
$load_options
. Use$is_headless['settings']
instead.- When you set this method, you’ll find it more restricting. Define
THE_SEO_FRAMEWORK_HEADLESS
instead.
- When you set this method, you’ll find it more restricting. Define
- Marked for deprecation:
- For object
- Constant notes:
- Added:
THE_SEO_FRAMEWORK_HEADLESS
, accepts boolean (true
/false
) or array, with boolean indexes'meta' => true/false
,'settings' => true/false
, and'user' => true/false
.
- Changed:
THE_SEO_FRAMEWORK_AUTHOR_INFO_CAP
applies now only to the user editable; instead of the editor (user self, or administrator) of the user.- This change should not affect anyone; it merely increases sanity in the code.
- Use the new headless mode to hide fields from users when not admin.
- Added:
- Filter notes:
- Added:
the_seo_framework_enable_noindex_no_posts
, useful for overriding the 404-protection for “empty” archives.the_seo_framework_kill_core_robots
; mind that this filter can run twice per page! Use (our) action-hooks to target one or the other… or both.the_seo_framework_post_meta_defaults
, replacesthe_seo_framework_inpost_seo_save_defaults
.the_seo_framework_timestamp_format
, used to change timestamp formats. Example usage. We advise not sending seconds, because small automated atomic-time fixes on your server may cause changes noted unscrupulously.the_seo_framework_save_user_data
, allows you to modify or add to the user meta saved by The SEO Framework.the_seo_framework_user_meta_defaults
, allows you to adjust the user meta defaults.
- Changed:
the_seo_framework_get_options
,the_seo_framework_post_meta
, &the_seo_framework_term_meta
:- Now considers headlessness.
- Now returns a 3rd parameter: boolean
$headless
.
the_seo_framework_is_shop
,the_seo_framework_is_product
, &the_seo_framework_is_product_admin
now have their return values memoized.the_seo_framework_robots_meta_array
now affects the sitemap. Be wary of performance issues! This filter will be executed thousands of times in one load.the_seo_framework_sitemap_nhpt_query_args
&the_seo_framework_sitemap_hpt_query_args
:- No longer pass the superfluously redundant
suppress_filters
index. - Can now have index
post_type
set to[]
or''
to cancel the query.
- No longer pass the superfluously redundant
- Deprecated:
the_seo_framework_inpost_seo_save_defaults
, use filterthe_seo_framework_post_meta_defaults
instead.the_seo_framework_load_options
, use constantTHE_SEO_FRAMEWORK_HEADLESS
instead.
- Removed:
the_seo_framework_current_term_meta
, this filter was deprecated since 4.0.0.the_seo_framework_save_custom_fields
, this filter was deprecated since 4.0.0.the_seo_framework_use_object_cache
, feature no longer available.
- Added:
- Action notes:
- Added:
- The following actions are now also available on the front-end (instead of only the back-end), and have their callbacks changed.
wp_ajax_tsf_crop_image
wp_ajax_tsf_dismiss_notice
wp_ajax_tsf_update_counter
wp_ajax_tsf_update_post_data
the_seo_framework_after_author_fields
allows you to do stuff a little later.the_seo_framework_before_author_fields
allows you to do stuff here.
- The following actions are now also available on the front-end (instead of only the back-end), and have their callbacks changed.
- Removed:
wp_ajax_the_seo_framework_update_counter
, usewp_ajax_tsf_update_counter
instead.wp_ajax_the_seo_framework_update_post_data
, usewp_ajax_tsf_update_post_data
instead.wp_ajax_tsf-crop-image
, usewp_ajax_tsf_crop_image
instead (underscore vs hyphens).wp_ajax_tsf-dismiss-notice
, usewp_ajax_tsf_dismiss_notice
instead (underscore vs hyphens).
- Added:
- Other:
- Cleaned up code, removed dumb quirks.
- Introduced the
tsfLePostData
-container. This helps assert post data for list-edit, such as whether the post is the front page. - We now use static anonymous functions where appropriate, instead of simple lambda functions, to improve performance and reduce memory consumption.
tsf_facebook_page
is nowtsf-user-meta[facebook_page]
.tsf_twitter_page
is nowtsf-user-meta[twitter_page]
.tsf-dismiss-key
is nowtsf_dismiss_key
tsf-dismiss-nonce
is nowtsf_dismiss_nonce
tsf-notice-nonce
is nowtsf_notice_nonce