Version Description
This minor update adds support for Gutenberg 11.3.0 and fixes a few bugs.
Download this release
Release Info
Developer | Cybr |
Plugin | The SEO Framework |
Version | 4.1.5 |
Comparing to | |
See all releases |
Code changes from version 4.1.4 to 4.1.5
- autodescription.php +2 -2
- inc/classes/core.class.php +3 -0
- inc/classes/generate-description.class.php +31 -8
- inc/classes/generate-url.class.php +1 -2
- inc/classes/post-data.class.php +27 -14
- lib/js/gbc.js +5 -3
- lib/js/gbc.min.js +1 -1
- lib/js/pt-gb.js +188 -247
- lib/js/pt-gb.min.js +1 -1
- readme.txt +5 -1
autodescription.php
CHANGED
@@ -3,7 +3,7 @@
|
|
3 |
* Plugin Name: The SEO Framework
|
4 |
* Plugin URI: https://theseoframework.com/
|
5 |
* Description: An automated, advanced, accessible, unbranded and extremely fast SEO solution for your WordPress website.
|
6 |
-
* Version: 4.1.
|
7 |
* Author: The SEO Framework Team
|
8 |
* Author URI: https://theseoframework.com/
|
9 |
* License: GPLv3
|
@@ -46,7 +46,7 @@ defined( 'ABSPATH' ) or die;
|
|
46 |
*
|
47 |
* @since 2.3.5
|
48 |
*/
|
49 |
-
define( 'THE_SEO_FRAMEWORK_VERSION', '4.1.
|
50 |
|
51 |
/**
|
52 |
* The plugin Database version.
|
3 |
* Plugin Name: The SEO Framework
|
4 |
* Plugin URI: https://theseoframework.com/
|
5 |
* Description: An automated, advanced, accessible, unbranded and extremely fast SEO solution for your WordPress website.
|
6 |
+
* Version: 4.1.5
|
7 |
* Author: The SEO Framework Team
|
8 |
* Author URI: https://theseoframework.com/
|
9 |
* License: GPLv3
|
46 |
*
|
47 |
* @since 2.3.5
|
48 |
*/
|
49 |
+
define( 'THE_SEO_FRAMEWORK_VERSION', '4.1.5' );
|
50 |
|
51 |
/**
|
52 |
* The plugin Database version.
|
inc/classes/core.class.php
CHANGED
@@ -229,6 +229,9 @@ class Core {
|
|
229 |
* Gets view location.
|
230 |
*
|
231 |
* @since 3.1.0
|
|
|
|
|
|
|
232 |
*
|
233 |
* @param string $file The file name.
|
234 |
* @return string The view location.
|
229 |
* Gets view location.
|
230 |
*
|
231 |
* @since 3.1.0
|
232 |
+
* @access private
|
233 |
+
* @TODO add path traversal mitigation via realpath()?
|
234 |
+
* -> $file must always be dev-supplied, never user-.
|
235 |
*
|
236 |
* @param string $file The file name.
|
237 |
* @return string The view location.
|
inc/classes/generate-description.class.php
CHANGED
@@ -829,7 +829,7 @@ class Generate_Description extends Generate {
|
|
829 |
* @since 4.0.5 : 1. Now decodes the excerpt input, improving accuracy, and so that HTML entities at
|
830 |
* the end won't be transformed into gibberish.
|
831 |
* @since 4.1.0 : 1. Now texturizes the excerpt input, improving accuracy with included closing & final punctuation support.
|
832 |
-
* 2. Now performs even faster queries, in most situations. (0.2ms/0.02ms total (worst/best) @ PHP 7.3/PCRE 11
|
833 |
* Mind you, this method probably boots PCRE and wptexturize; so, it'll be slower than what we noted--it's
|
834 |
* overhead that otherwise WP, the theme, or other plugin would cause anyway. So, deduct that.
|
835 |
* 3. Now recognizes connector and final punctuations for preliminary sentence bounding.
|
@@ -840,37 +840,56 @@ class Generate_Description extends Generate {
|
|
840 |
* 7. It will now stop counting trailing words towards new sentences when a connector, dash, mark, or ¡¿ is found.
|
841 |
* 8. Now returns encoded entities once more. So that the return value can be treated the same as anything else
|
842 |
* revolving around descriptions--preventing double transcoding like `& > & > &` instead of `&`.
|
|
|
|
|
|
|
|
|
|
|
843 |
* @see https://secure.php.net/manual/en/regexp.reference.unicode.php
|
844 |
*
|
845 |
* We use `[^\P{Po}\'\"]` because WordPress texturizes ' and " to fall under `\P{Po}`.
|
846 |
* This is perfect. Please have the courtesy to credit us when taking it. :)
|
847 |
*
|
848 |
* @param string $excerpt The untrimmed excerpt. Expected not to contain any HTML operators.
|
849 |
-
* @param int $
|
|
|
850 |
* @param int $max_char_length At what point to shave off the excerpt.
|
851 |
* @return string The trimmed excerpt with encoded entities. Needs escaping prior printing.
|
852 |
*/
|
853 |
-
public function trim_excerpt( $excerpt, $
|
|
|
|
|
|
|
|
|
|
|
|
|
854 |
|
855 |
// Decode to get a more accurate character length in Unicode.
|
856 |
$excerpt = html_entity_decode( $excerpt, ENT_QUOTES, 'UTF-8' );
|
857 |
|
858 |
// Find all words with $max_char_length, and trim when the last word boundary or punctuation is found.
|
859 |
-
preg_match( sprintf( '/.{0,%d}([^\P{Po}\'\":]|[\p{Pc}\p{Pd}\p{Pf}\p{Z}]
|
860 |
$excerpt = isset( $matches[0] ) ? ( $matches[0] ?: '' ) : '';
|
861 |
|
862 |
$excerpt = trim( $excerpt );
|
863 |
|
864 |
-
if (
|
865 |
|
866 |
// Texturize to recognize the sentence structure. Decode thereafter since we get HTML returned.
|
867 |
$excerpt = htmlentities( $excerpt, ENT_QUOTES, 'UTF-8' );
|
868 |
$excerpt = \wptexturize( $excerpt );
|
869 |
$excerpt = html_entity_decode( $excerpt, ENT_QUOTES, 'UTF-8' );
|
870 |
/**
|
871 |
-
* Play with it here: https://regex101.com/r/u0DIgx/5/
|
|
|
|
|
872 |
*
|
873 |
-
* TODO
|
|
|
|
|
|
|
|
|
|
|
874 |
*
|
875 |
* Critically optimized, so the $matches don't make much sense. Bear with me:
|
876 |
*
|
@@ -884,7 +903,7 @@ class Generate_Description extends Generate {
|
|
884 |
* }
|
885 |
*/
|
886 |
preg_match(
|
887 |
-
'/(
|
888 |
$excerpt,
|
889 |
$matches
|
890 |
);
|
@@ -900,6 +919,8 @@ class Generate_Description extends Generate {
|
|
900 |
$excerpt = $matches[1];
|
901 |
}
|
902 |
|
|
|
|
|
903 |
/**
|
904 |
* @param array $matches: {
|
905 |
* 1 : Full match until leading punctuation.
|
@@ -923,6 +944,8 @@ class Generate_Description extends Generate {
|
|
923 |
$excerpt = '';
|
924 |
}
|
925 |
|
|
|
|
|
926 |
return trim( htmlentities( $excerpt, ENT_QUOTES, 'UTF-8' ) );
|
927 |
}
|
928 |
|
829 |
* @since 4.0.5 : 1. Now decodes the excerpt input, improving accuracy, and so that HTML entities at
|
830 |
* the end won't be transformed into gibberish.
|
831 |
* @since 4.1.0 : 1. Now texturizes the excerpt input, improving accuracy with included closing & final punctuation support.
|
832 |
+
* 2. Now performs even faster queries, in most situations. (0.2ms/0.02ms total (worst/best) @ PHP 7.3/PCRE 11).
|
833 |
* Mind you, this method probably boots PCRE and wptexturize; so, it'll be slower than what we noted--it's
|
834 |
* overhead that otherwise WP, the theme, or other plugin would cause anyway. So, deduct that.
|
835 |
* 3. Now recognizes connector and final punctuations for preliminary sentence bounding.
|
840 |
* 7. It will now stop counting trailing words towards new sentences when a connector, dash, mark, or ¡¿ is found.
|
841 |
* 8. Now returns encoded entities once more. So that the return value can be treated the same as anything else
|
842 |
* revolving around descriptions--preventing double transcoding like `& > & > &` instead of `&`.
|
843 |
+
* @since 4.1.5 : 1. The second parameter now accepts values again. From "current description length" to minimum accepted char length.
|
844 |
+
* 2. Can now return an empty string when the input string doesn't satisfy the minimum character length.
|
845 |
+
* 3. The third parameter now defaults to 4096, so no longer unexpected results are created.
|
846 |
+
* 4. Resolved some backtracking issues.
|
847 |
+
* 5. Resolved an issue where a character followed by punctuation would cause the match to fail.
|
848 |
* @see https://secure.php.net/manual/en/regexp.reference.unicode.php
|
849 |
*
|
850 |
* We use `[^\P{Po}\'\"]` because WordPress texturizes ' and " to fall under `\P{Po}`.
|
851 |
* This is perfect. Please have the courtesy to credit us when taking it. :)
|
852 |
*
|
853 |
* @param string $excerpt The untrimmed excerpt. Expected not to contain any HTML operators.
|
854 |
+
* @param int $min_char_length The minimum character length. Set to 0 to ignore the requirement.
|
855 |
+
* This is read as a SUGGESTION. Multibyte characters will create inaccuracies.
|
856 |
* @param int $max_char_length At what point to shave off the excerpt.
|
857 |
* @return string The trimmed excerpt with encoded entities. Needs escaping prior printing.
|
858 |
*/
|
859 |
+
public function trim_excerpt( $excerpt, $min_char_length = 1, $max_char_length = 4096 ) {
|
860 |
+
|
861 |
+
// We should _actually_ use mb_strlen, but that's wasteful on resources for something benign.
|
862 |
+
// We'll rectify that later, somewhat, where characters are transformed.
|
863 |
+
// We could also use preg_match_all( '/./u' ); or count( preg_split( '/./u', $excerpt, $min_char_length ) );
|
864 |
+
// But, again, that'll eat CPU cycles.
|
865 |
+
if ( \strlen( $excerpt ) < $min_char_length ) return '';
|
866 |
|
867 |
// Decode to get a more accurate character length in Unicode.
|
868 |
$excerpt = html_entity_decode( $excerpt, ENT_QUOTES, 'UTF-8' );
|
869 |
|
870 |
// Find all words with $max_char_length, and trim when the last word boundary or punctuation is found.
|
871 |
+
preg_match( sprintf( '/.{0,%d}([^\P{Po}\'\":]|[\p{Pc}\p{Pd}\p{Pf}\p{Z}]|\Z){1}/su', $max_char_length ), trim( $excerpt ), $matches );
|
872 |
$excerpt = isset( $matches[0] ) ? ( $matches[0] ?: '' ) : '';
|
873 |
|
874 |
$excerpt = trim( $excerpt );
|
875 |
|
876 |
+
if ( \strlen( $excerpt ) < $min_char_length ) return '';
|
877 |
|
878 |
// Texturize to recognize the sentence structure. Decode thereafter since we get HTML returned.
|
879 |
$excerpt = htmlentities( $excerpt, ENT_QUOTES, 'UTF-8' );
|
880 |
$excerpt = \wptexturize( $excerpt );
|
881 |
$excerpt = html_entity_decode( $excerpt, ENT_QUOTES, 'UTF-8' );
|
882 |
/**
|
883 |
+
* Play with it here: https://regex101.com/r/u0DIgx/5/ (old) https://regex101.com/r/G92lUt/3 (new)
|
884 |
+
*
|
885 |
+
* TODO Group 4's match is repeated. However, referring to it as (4) will cause it to congeal into 3.
|
886 |
*
|
887 |
+
* TODO .+[\p{Pe}\p{Pf}](*THEN)\Z still backtracks; it should just find \Z and see if one char is in front of it.
|
888 |
+
* -> [^\p{Pe}\p{Pf}]++.*?[\p{Pe}\p{Pf}]+?\Z would solve it... but I don't trust it; it's populating 4 and 5 in edge-cases.
|
889 |
+
*
|
890 |
+
* TODO we can futher optimize this by capturing the last 4 words and refer to that. Of thence more than 3 words
|
891 |
+
* found, we could simply end the query, mitigating all forms of backtracking. For now, backtracking cannot
|
892 |
+
* exceed step-count=($max_char_length*2+56) = 160*2+56 = 376, which is perfectly acceptable as a 'worst case'.
|
893 |
*
|
894 |
* Critically optimized, so the $matches don't make much sense. Bear with me:
|
895 |
*
|
903 |
* }
|
904 |
*/
|
905 |
preg_match(
|
906 |
+
'/(?:\A[\p{P}\p{Z}]*?)?([\P{Po}\p{M}\xBF\xA1:\p{Z}]+[\p{Z}\w])(?:([^\P{Po}\p{M}\xBF\xA1:]\Z(*ACCEPT))|((?(?=.+(?:\w+[\p{Pc}\p{Pd}\p{Pf}\p{Z}]*){1,3}|[\p{Po}]\Z)(?:.+[\p{Pe}\p{Pf}](*THEN)\Z(*ACCEPT)|.*[^\P{Po}\p{M}\xBF\xA1:])|.*\Z(*ACCEPT)))(?>(.+?\p{Z}*(?:\w+[\p{Pc}\p{Pd}\p{Pf}\p{Z}]*){1,3})|[^\p{Pc}\p{Pd}\p{M}\xBF\xA1:])?)(.+)?/su',
|
907 |
$excerpt,
|
908 |
$matches
|
909 |
);
|
919 |
$excerpt = $matches[1];
|
920 |
}
|
921 |
|
922 |
+
if ( \strlen( $excerpt ) < $min_char_length ) return '';
|
923 |
+
|
924 |
/**
|
925 |
* @param array $matches: {
|
926 |
* 1 : Full match until leading punctuation.
|
944 |
$excerpt = '';
|
945 |
}
|
946 |
|
947 |
+
if ( \strlen( $excerpt ) < $min_char_length ) return '';
|
948 |
+
|
949 |
return trim( htmlentities( $excerpt, ENT_QUOTES, 'UTF-8' ) );
|
950 |
}
|
951 |
|
inc/classes/generate-url.class.php
CHANGED
@@ -801,9 +801,8 @@ class Generate_Url extends Generate_Title {
|
|
801 |
*/
|
802 |
public function _adjust_post_link_category( $term, $terms = null, $post = null ) {
|
803 |
|
804 |
-
if ( null === $post )
|
805 |
$post = \get_post( $this->get_the_real_ID() );
|
806 |
-
}
|
807 |
|
808 |
return $this->get_primary_term( $post->ID, $term->taxonomy ) ?: $term;
|
809 |
}
|
801 |
*/
|
802 |
public function _adjust_post_link_category( $term, $terms = null, $post = null ) {
|
803 |
|
804 |
+
if ( null === $post )
|
805 |
$post = \get_post( $this->get_the_real_ID() );
|
|
|
806 |
|
807 |
return $this->get_primary_term( $post->ID, $term->taxonomy ) ?: $term;
|
808 |
}
|
inc/classes/post-data.class.php
CHANGED
@@ -298,6 +298,7 @@ class Post_Data extends Detect {
|
|
298 |
if ( $value || ( \is_string( $value ) && \strlen( $value ) ) ) {
|
299 |
\update_post_meta( $post->ID, $field, $value );
|
300 |
} else {
|
|
|
301 |
// This is fine for as long as we merge the getter values with the defaults.
|
302 |
\delete_post_meta( $post->ID, $field );
|
303 |
}
|
@@ -557,10 +558,10 @@ class Post_Data extends Detect {
|
|
557 |
];
|
558 |
}
|
559 |
|
560 |
-
foreach ( $values as $
|
561 |
if ( ! isset( $_POST[ $v['name'] ] ) ) continue;
|
562 |
if ( \wp_verify_nonce( $_POST[ $v['name'] ], $v['action'] ) ) { // Redundant. Fortified.
|
563 |
-
$this->update_primary_term_id( $post->ID, $
|
564 |
}
|
565 |
}
|
566 |
}
|
@@ -820,20 +821,29 @@ class Post_Data extends Detect {
|
|
820 |
* Returns the primary term for post.
|
821 |
*
|
822 |
* @since 3.0.0
|
|
|
|
|
823 |
*
|
824 |
-
* @param int
|
825 |
-
* @param string
|
826 |
* @return \WP_Term|false The primary term. False if not set.
|
827 |
*/
|
828 |
-
public function get_primary_term( $post_id
|
|
|
|
|
829 |
|
830 |
-
|
|
|
831 |
|
832 |
-
|
|
|
|
|
833 |
|
834 |
// Users can alter the term list via quick/bulk edit, but cannot set a primary term that way.
|
835 |
// Users can also delete a term from the site that was previously assigned as primary.
|
836 |
// So, test if the term still exists for the post.
|
|
|
|
|
837 |
$terms = \get_the_terms( $post_id, $taxonomy );
|
838 |
$primary_term = false;
|
839 |
|
@@ -844,20 +854,23 @@ class Post_Data extends Detect {
|
|
844 |
}
|
845 |
}
|
846 |
|
847 |
-
return $primary_term;
|
848 |
}
|
849 |
|
850 |
/**
|
851 |
* Returns the primary term ID for post.
|
852 |
*
|
853 |
* @since 3.0.0
|
|
|
|
|
854 |
*
|
855 |
-
* @param int
|
856 |
-
* @param string
|
857 |
-
* @return int
|
858 |
*/
|
859 |
-
public function get_primary_term_id( $post_id
|
860 |
-
|
|
|
861 |
}
|
862 |
|
863 |
/**
|
@@ -871,7 +884,7 @@ class Post_Data extends Detect {
|
|
871 |
* @return bool True on success, false on failure.
|
872 |
*/
|
873 |
public function update_primary_term_id( $post_id = null, $taxonomy = '', $value = 0 ) {
|
874 |
-
if (
|
875 |
$success = \delete_post_meta( $post_id, '_primary_term_' . $taxonomy );
|
876 |
} else {
|
877 |
$success = \update_post_meta( $post_id, '_primary_term_' . $taxonomy, $value );
|
298 |
if ( $value || ( \is_string( $value ) && \strlen( $value ) ) ) {
|
299 |
\update_post_meta( $post->ID, $field, $value );
|
300 |
} else {
|
301 |
+
// All empty values are deleted here, even if they never existed... is this the best way to handle this?
|
302 |
// This is fine for as long as we merge the getter values with the defaults.
|
303 |
\delete_post_meta( $post->ID, $field );
|
304 |
}
|
558 |
];
|
559 |
}
|
560 |
|
561 |
+
foreach ( $values as $_taxonomy => $v ) {
|
562 |
if ( ! isset( $_POST[ $v['name'] ] ) ) continue;
|
563 |
if ( \wp_verify_nonce( $_POST[ $v['name'] ], $v['action'] ) ) { // Redundant. Fortified.
|
564 |
+
$this->update_primary_term_id( $post->ID, $_taxonomy, $v['value'] );
|
565 |
}
|
566 |
}
|
567 |
}
|
821 |
* Returns the primary term for post.
|
822 |
*
|
823 |
* @since 3.0.0
|
824 |
+
* @since 4.1.5 1. Added memoization.
|
825 |
+
* 2. The first and second parameters are now required.
|
826 |
*
|
827 |
+
* @param int $post_id The post ID.
|
828 |
+
* @param string $taxonomy The taxonomy name.
|
829 |
* @return \WP_Term|false The primary term. False if not set.
|
830 |
*/
|
831 |
+
public function get_primary_term( $post_id, $taxonomy ) {
|
832 |
+
|
833 |
+
static $primary_terms = [];
|
834 |
|
835 |
+
if ( isset( $primary_terms[ $post_id ][ $taxonomy ] ) )
|
836 |
+
return $primary_terms[ $post_id ][ $taxonomy ];
|
837 |
|
838 |
+
$primary_id = \get_post_meta( $post_id, '_primary_term_' . $taxonomy, true ) ?: 0;
|
839 |
+
|
840 |
+
if ( ! $primary_id ) return $primary_terms[ $post_id ][ $taxonomy ] = false;
|
841 |
|
842 |
// Users can alter the term list via quick/bulk edit, but cannot set a primary term that way.
|
843 |
// Users can also delete a term from the site that was previously assigned as primary.
|
844 |
// So, test if the term still exists for the post.
|
845 |
+
// Although 'get_the_terms()' is an expensive function, it memoizes, and
|
846 |
+
// is always called by WP before we fetch a primary term. So, 0 overhead here.
|
847 |
$terms = \get_the_terms( $post_id, $taxonomy );
|
848 |
$primary_term = false;
|
849 |
|
854 |
}
|
855 |
}
|
856 |
|
857 |
+
return $primary_terms[ $post_id ][ $taxonomy ] = $primary_term;
|
858 |
}
|
859 |
|
860 |
/**
|
861 |
* Returns the primary term ID for post.
|
862 |
*
|
863 |
* @since 3.0.0
|
864 |
+
* @since 4.1.5 1. Now validates if the stored term ID's term exists (for the post or at all).
|
865 |
+
* 2. The first and second parameters are now required.
|
866 |
*
|
867 |
+
* @param int $post_id The post ID.
|
868 |
+
* @param string $taxonomy The taxonomy name.
|
869 |
+
* @return int The primary term ID. 0 if not found.
|
870 |
*/
|
871 |
+
public function get_primary_term_id( $post_id, $taxonomy ) {
|
872 |
+
$primary_term = $this->get_primary_term( $post_id, $taxonomy );
|
873 |
+
return isset( $primary_term->term_id ) ? $primary_term->term_id : 0;
|
874 |
}
|
875 |
|
876 |
/**
|
884 |
* @return bool True on success, false on failure.
|
885 |
*/
|
886 |
public function update_primary_term_id( $post_id = null, $taxonomy = '', $value = 0 ) {
|
887 |
+
if ( ! $value ) {
|
888 |
$success = \delete_post_meta( $post_id, '_primary_term_' . $taxonomy );
|
889 |
} else {
|
890 |
$success = \update_post_meta( $post_id, '_primary_term_' . $taxonomy, $value );
|
lib/js/gbc.js
CHANGED
@@ -329,9 +329,11 @@ window.tsfGBC = function( $ ) {
|
|
329 |
*/
|
330 |
const _initCompat = () => {
|
331 |
|
332 |
-
|
333 |
-
|
334 |
-
|
|
|
|
|
335 |
|
336 |
// Set all values prior debouncing.
|
337 |
setTimeout( () => {
|
329 |
*/
|
330 |
const _initCompat = () => {
|
331 |
|
332 |
+
const { subscribe } = wp.data;
|
333 |
+
|
334 |
+
subscribe( debounce( sidebarDispatcher, 500 ) );
|
335 |
+
subscribe( debounce( assessData, 300 ) );
|
336 |
+
subscribe( saveDispatcher );
|
337 |
|
338 |
// Set all values prior debouncing.
|
339 |
setTimeout( () => {
|
lib/js/gbc.min.js
CHANGED
@@ -1 +1 @@
|
|
1 |
-
'use strict';window.tsfGBC=function(a){function b(a){return k.getEditedPostAttribute(a)}function c(){n={title:b("title"),link:k.getPermalink(),content:b("content"),excerpt:b("excerpt"),visibility:k.getEditedPostVisibility()}}function d(a){return n[a]||null}function e(){let a=n;c(),a.title!==n.title&&o("title"),a.link!==n.link&&o("link"),a.content!==n.content&&o("content"),a.excerpt!==n.excerpt&&o("excerpt"),a.visibility!==n.visibility&&o("visibility")}function f(){p?k.didPostSaveRequestSucceed()?(s(),r()&&r().cancel(),g()):r():k.isSavingPost()&&(k.isPreviewingPost()?(p=!0,q="preview"):k.isAutosavingPost()?(p=!0,q="autosave"):(p=!0,q="save"))}function g(){p=!1}function h(){if(k.isPostSavingLocked())3>++t?s():(s()&&s().cancel(),t=0);else{t=0;let a=!k.hasChangedContent();"preview"===q?document.dispatchEvent(new CustomEvent("tsf-gutenberg-onpreview")):"autosave"===q?document.dispatchEvent(new CustomEvent("tsf-gutenberg-onautosave")):"save"===q?a=!0:void 0;a&&document.dispatchEvent(new CustomEvent("tsf-gutenberg-onsave"))&&document.dispatchEvent(new CustomEvent("tsf-gutenberg-onsave-completed")),document.dispatchEvent(new CustomEvent("tsf-gutenberg-saved-document",{detail:{savedType:q}})),q=""}}function i(){l.isEditorSidebarOpened()?!u.opened&&(u.opened=!0,document.dispatchEvent(new CustomEvent("tsf-gutenberg-sidebar-opened"))):u.opened&&(u.opened=!1,document.dispatchEvent(new CustomEvent("tsf-gutenberg-sidebar-closed")))}const j="undefined"!=typeof tsfGBCL10n&&tsfGBCL10n,k=wp.data.select("core/editor"),l=wp.data.select("core/edit-post"),{debounce:m}=lodash;let n;const o=b=>{a(document).trigger("tsf-updated-gutenberg-"+b,[d(b)])};let p=!1,q="";const r=m(g,7e3),s=m(h,500);let t=0,u={opened:!1};const v=()=>{wp.data
|
1 |
+
'use strict';window.tsfGBC=function(a){function b(a){return k.getEditedPostAttribute(a)}function c(){n={title:b("title"),link:k.getPermalink(),content:b("content"),excerpt:b("excerpt"),visibility:k.getEditedPostVisibility()}}function d(a){return n[a]||null}function e(){let a=n;c(),a.title!==n.title&&o("title"),a.link!==n.link&&o("link"),a.content!==n.content&&o("content"),a.excerpt!==n.excerpt&&o("excerpt"),a.visibility!==n.visibility&&o("visibility")}function f(){p?k.didPostSaveRequestSucceed()?(s(),r()&&r().cancel(),g()):r():k.isSavingPost()&&(k.isPreviewingPost()?(p=!0,q="preview"):k.isAutosavingPost()?(p=!0,q="autosave"):(p=!0,q="save"))}function g(){p=!1}function h(){if(k.isPostSavingLocked())3>++t?s():(s()&&s().cancel(),t=0);else{t=0;let a=!k.hasChangedContent();"preview"===q?document.dispatchEvent(new CustomEvent("tsf-gutenberg-onpreview")):"autosave"===q?document.dispatchEvent(new CustomEvent("tsf-gutenberg-onautosave")):"save"===q?a=!0:void 0;a&&document.dispatchEvent(new CustomEvent("tsf-gutenberg-onsave"))&&document.dispatchEvent(new CustomEvent("tsf-gutenberg-onsave-completed")),document.dispatchEvent(new CustomEvent("tsf-gutenberg-saved-document",{detail:{savedType:q}})),q=""}}function i(){l.isEditorSidebarOpened()?!u.opened&&(u.opened=!0,document.dispatchEvent(new CustomEvent("tsf-gutenberg-sidebar-opened"))):u.opened&&(u.opened=!1,document.dispatchEvent(new CustomEvent("tsf-gutenberg-sidebar-closed")))}const j="undefined"!=typeof tsfGBCL10n&&tsfGBCL10n,k=wp.data.select("core/editor"),l=wp.data.select("core/edit-post"),{debounce:m}=lodash;let n;const o=b=>{a(document).trigger("tsf-updated-gutenberg-"+b,[d(b)])};let p=!1,q="";const r=m(g,7e3),s=m(h,500);let t=0,u={opened:!1};const v=()=>{const{subscribe:a}=wp.data;a(m(i,500)),a(m(e,300)),a(f),setTimeout(()=>{c(),o("title"),o("link"),o("content"),o("excerpt"),o("visibility"),document.dispatchEvent(new CustomEvent("tsf-subscribed-to-gutenberg"))})};return Object.assign({load:()=>{document.body.addEventListener("tsf-onload",v)}},{triggerUpdate:o},{l10n:j})}(jQuery),window.tsfGBC.load();
|
lib/js/pt-gb.js
CHANGED
@@ -30,6 +30,7 @@
|
|
30 |
* This is a self-constructed function assigned as an object.
|
31 |
*
|
32 |
* @since 3.2.0
|
|
|
33 |
*
|
34 |
* @constructor
|
35 |
* @param {!jQuery} $ jQuery object.
|
@@ -46,18 +47,30 @@ window.tsfPTGB = function( $ ) {
|
|
46 |
const l10n = 'undefined' !== typeof tsfPTL10n && tsfPTL10n;
|
47 |
|
48 |
/**
|
49 |
-
* @since
|
|
|
|
|
|
|
|
|
|
|
|
|
50 |
* @access private
|
51 |
*/
|
52 |
-
const {
|
53 |
-
const { createElement, Fragment } = wp.element;
|
54 |
const { SelectControl } = wp.components;
|
55 |
-
const {
|
56 |
-
const
|
57 |
-
const { invoke, unescape } = lodash;
|
58 |
|
59 |
/**
|
60 |
-
*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
61 |
* @access private
|
62 |
*/
|
63 |
const DEFAULT_QUERY = {
|
@@ -68,287 +81,215 @@ window.tsfPTGB = function( $ ) {
|
|
68 |
};
|
69 |
|
70 |
/**
|
71 |
-
*
|
72 |
-
*
|
73 |
-
* @since 3.2.0
|
74 |
* @access private
|
75 |
-
*
|
76 |
-
* @
|
77 |
-
* @return {undefined}
|
78 |
*/
|
79 |
-
const
|
80 |
|
81 |
-
|
82 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
83 |
|
84 |
-
|
85 |
-
inputTemplate = wp.template( 'tsf-primary-term-selector' ),
|
86 |
-
registeredFields = {};
|
87 |
|
88 |
-
const
|
|
|
89 |
|
90 |
-
const
|
91 |
-
|
92 |
-
const setPrimaryTermID = ( taxonomy, id ) => +( getPrimaryTermHolder( taxonomy ).value = +id );
|
93 |
|
94 |
-
|
95 |
-
|
96 |
-
if ( ! wrap ) return registeredFields[ taxonomy ] = false;
|
97 |
|
98 |
-
|
99 |
-
return registeredFields[ taxonomy ] = !! $( template ).appendTo( wrap );
|
100 |
-
}
|
101 |
-
const hasDataInput = ( taxonomy ) => registeredFields[ taxonomy ];
|
102 |
-
|
103 |
-
const revalidatePrimaryTerm = ( taxonomy, terms ) => {
|
104 |
-
if ( terms.indexOf( getPrimaryTermID( taxonomy ) ) < 0 ) {
|
105 |
-
// Set to first found term, or empty the value if no term is selected.
|
106 |
-
if ( 0 in terms ) {
|
107 |
-
setPrimaryTermID( taxonomy, terms[0] );
|
108 |
-
} else {
|
109 |
-
setPrimaryTermID( taxonomy, 0 );
|
110 |
-
}
|
111 |
-
}
|
112 |
}
|
113 |
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
this.slug = slug;
|
118 |
-
this.reset();
|
119 |
-
}
|
120 |
|
121 |
-
|
122 |
-
|
123 |
-
|
|
|
124 |
|
125 |
-
|
126 |
-
|
127 |
-
}
|
128 |
|
129 |
-
|
130 |
-
|
131 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
132 |
|
133 |
-
|
134 |
-
return what in this.data && this.data[ what ] || null;
|
135 |
-
}
|
136 |
|
137 |
-
|
138 |
-
return this.data[ what ] = value;
|
139 |
-
}
|
140 |
-
}
|
141 |
-
const createStore = slug => dataStores[ slug ] = new DataStore( slug );
|
142 |
-
const getStore = slug => dataStores[ slug ] || createStore( slug );
|
143 |
-
|
144 |
-
class PrimaryTermSelectorHandler extends React.Component {
|
145 |
-
componentDidMount() {
|
146 |
-
// Mounted for the first time.
|
147 |
-
if ( ! this.dsAccess().registeredData() )
|
148 |
-
this.registerData();
|
149 |
-
|
150 |
-
if ( this.dsAccess().registeredData() ) {
|
151 |
-
// Remounted thanks to adding a new term (from 1 selected, now 2).
|
152 |
-
if ( this.hasNewTerms() ) {
|
153 |
-
this.fetchTerms();
|
154 |
-
} else {
|
155 |
-
this.setState( {
|
156 |
-
loading: false,
|
157 |
-
} );
|
158 |
-
}
|
159 |
-
}
|
160 |
-
}
|
161 |
|
162 |
-
|
163 |
-
|
164 |
-
}
|
165 |
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
|
|
|
|
|
|
|
|
176 |
|
177 |
-
|
178 |
-
|
179 |
-
this.fetchTerms();
|
180 |
-
}
|
181 |
-
|
182 |
-
dsAccess() {
|
183 |
-
return getStore( this.props.slug );
|
184 |
-
}
|
185 |
-
|
186 |
-
hasNewTerms() {
|
187 |
-
let availableTermsIds = ( this.dsAccess().get( 'availableTerms' ) || [] ).map( x => x.id );
|
188 |
-
return ! this.props.terms.every( id => availableTermsIds.includes( id ) );
|
189 |
-
}
|
190 |
-
isTermAvailable( id ) {
|
191 |
-
return this.dsAccess().get( 'availableTerms' ).some( term => term.id === id );
|
192 |
-
}
|
193 |
|
194 |
-
|
195 |
-
|
196 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
197 |
|
198 |
-
|
199 |
-
|
200 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
201 |
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
221 |
}
|
222 |
-
this.fetchRequest = null;
|
223 |
-
this.setState( {
|
224 |
-
loading: false,
|
225 |
-
} );
|
226 |
-
// Users may see empty select fields now... Strip them? See getTermName instead?
|
227 |
-
// if ( ! this.dsAccess().get( 'availableTerms' ).length ) {}
|
228 |
}
|
229 |
-
);
|
230 |
-
}
|
231 |
-
}
|
232 |
-
|
233 |
-
class TermSelector extends PrimaryTermSelectorHandler {
|
234 |
-
constructor() {
|
235 |
-
super( ...arguments );
|
236 |
-
this.onChange = this.onChange.bind( this );
|
237 |
-
this.state = {
|
238 |
-
loading: true,
|
239 |
-
}
|
240 |
-
}
|
241 |
-
|
242 |
-
getTermName( id ) {
|
243 |
-
let availableTerms = this.dsAccess().get( 'availableTerms' );
|
244 |
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
}
|
250 |
-
|
251 |
-
getSelectOptions() {
|
252 |
-
// Terms might not've been registered (yet).
|
253 |
-
if ( ! Array.isArray( this.props.terms ) ) return '';
|
254 |
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
};
|
261 |
-
} );
|
262 |
}
|
263 |
|
264 |
-
onChange
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
} );
|
269 |
'tsfAys' in window && tsfAys.registerChange();
|
270 |
-
}
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
|
|
|
|
295 |
}
|
296 |
|
297 |
-
const
|
298 |
-
initSelectors() {
|
299 |
-
const { slug, terms } = this.props;
|
300 |
-
if ( hasDataInput( slug ) ) {
|
301 |
-
revalidatePrimaryTerm( slug, terms );
|
302 |
-
if ( terms.length > 1 ) {
|
303 |
-
return createElement(
|
304 |
-
Fragment,
|
305 |
-
{},
|
306 |
-
createElement(
|
307 |
-
TermSelector,
|
308 |
-
this.props,
|
309 |
-
)
|
310 |
-
);
|
311 |
-
}
|
312 |
-
}
|
313 |
-
return null;
|
314 |
-
}
|
315 |
-
|
316 |
render() {
|
317 |
-
|
318 |
-
|
319 |
-
return createElement(
|
320 |
-
|
321 |
-
this.props,
|
322 |
-
);
|
323 |
-
}
|
324 |
-
|
325 |
-
// React causes this to loop back to render() once because of debugRenderPhaseSideEffectsForStrictMode...
|
326 |
return createElement(
|
327 |
Fragment,
|
328 |
-
|
329 |
createElement(
|
330 |
-
|
331 |
-
this.props,
|
332 |
),
|
333 |
-
|
334 |
-
|
|
|
|
|
|
|
|
|
|
|
335 |
);
|
336 |
}
|
337 |
}
|
338 |
|
339 |
-
|
340 |
-
|
341 |
-
addDataInput( taxonomy );
|
342 |
-
}
|
343 |
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
);
|
350 |
-
}
|
351 |
-
_init();
|
352 |
}
|
353 |
|
354 |
return Object.assign( {
|
30 |
* This is a self-constructed function assigned as an object.
|
31 |
*
|
32 |
* @since 3.2.0
|
33 |
+
* @since 4.1.5 Rewritten to support Gutenberg 11.3.0+ / WP 5.9+
|
34 |
*
|
35 |
* @constructor
|
36 |
* @param {!jQuery} $ jQuery object.
|
47 |
const l10n = 'undefined' !== typeof tsfPTL10n && tsfPTL10n;
|
48 |
|
49 |
/**
|
50 |
+
* @since 4.1.5
|
51 |
+
* @access private
|
52 |
+
*/
|
53 |
+
const supportedTaxonomies = l10n?.taxonomies;
|
54 |
+
|
55 |
+
/**
|
56 |
+
* @since 4.1.5
|
57 |
* @access private
|
58 |
*/
|
59 |
+
const { createElement, Fragment, Component, useState, useEffect } = wp.element;
|
|
|
60 |
const { SelectControl } = wp.components;
|
61 |
+
const { useSelect } = wp.data;
|
62 |
+
const { unescape } = lodash;
|
|
|
63 |
|
64 |
/**
|
65 |
+
* Arrays are unique objects, meaning [] !== [].
|
66 |
+
* Let's store one that's immutable, so we have [] === [], preventing state changes.
|
67 |
+
* @since 4.1.5
|
68 |
+
* @access private
|
69 |
+
*/
|
70 |
+
const EMPTY_ARRAY = [];
|
71 |
+
|
72 |
+
/**
|
73 |
+
* @since 4.1.5
|
74 |
* @access private
|
75 |
*/
|
76 |
const DEFAULT_QUERY = {
|
81 |
};
|
82 |
|
83 |
/**
|
84 |
+
* @since 4.1.5
|
|
|
|
|
85 |
* @access private
|
86 |
+
* @param {String} taxonomySlug
|
87 |
+
* @param {String} what The i18n to get.
|
|
|
88 |
*/
|
89 |
+
const _geti18n = ( taxonomySlug, what ) => supportedTaxonomies[ taxonomySlug ].i18n[ what ] || '';
|
90 |
|
91 |
+
let _registeredFields = {}; // memo. TODO Make Map()? Meh, this gets called like 3 to 5x per page.
|
92 |
+
/**
|
93 |
+
* @since 4.1.5
|
94 |
+
* @access private
|
95 |
+
* @param {String} taxonomySlug
|
96 |
+
* @return {(<Object<String,Function>)}
|
97 |
+
*/
|
98 |
+
const _primaryTerm = taxonomySlug => {
|
99 |
|
100 |
+
const _dataHolder = () => document.getElementById( `autodescription[_primary_term_${taxonomySlug}]` );
|
|
|
|
|
101 |
|
102 |
+
const get = () => +_dataHolder().value;
|
103 |
+
const set = id => +( _dataHolder().value = +id );
|
104 |
|
105 |
+
const revalidate = selectedTerms => {
|
106 |
+
let primaryTerm = get();
|
|
|
107 |
|
108 |
+
if ( selectedTerms.includes( primaryTerm ) )
|
109 |
+
return primaryTerm;
|
|
|
110 |
|
111 |
+
return set( selectedTerms?.[0] || 0 );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
112 |
}
|
113 |
|
114 |
+
const register = () => {
|
115 |
+
let wrap = document.getElementById( 'tsf-gutenberg-data-holder' );
|
116 |
+
if ( ! wrap ) return _registeredFields[ taxonomySlug ] = false;
|
|
|
|
|
|
|
117 |
|
118 |
+
let template = wp.template( 'tsf-primary-term-selector' )( { taxonomy: supportedTaxonomies[ taxonomySlug ] } );
|
119 |
+
return _registeredFields[ taxonomySlug ] = !! $( template ).appendTo( wrap );
|
120 |
+
}
|
121 |
+
const isRegistered = () => _registeredFields[ taxonomySlug ] || false;
|
122 |
|
123 |
+
return { get, set, revalidate, register, isRegistered };
|
124 |
+
};
|
|
|
125 |
|
126 |
+
/**
|
127 |
+
* Initializes primary term selection for Gutenberg.
|
128 |
+
*
|
129 |
+
* @since 3.2.0
|
130 |
+
* @access private
|
131 |
+
*
|
132 |
+
* @function
|
133 |
+
* @return {undefined}
|
134 |
+
*/
|
135 |
+
const _initPrimaryTerm = () => {
|
136 |
|
137 |
+
if ( ! Object.keys( supportedTaxonomies ).length ) return;
|
|
|
|
|
138 |
|
139 |
+
function primaryTermSelector( { taxonomySlug, _legacySelectedTerms } ) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
140 |
|
141 |
+
const primaryTerm = _primaryTerm( taxonomySlug );
|
142 |
+
const [ selection, setSelection ] = useState( primaryTerm.get() );
|
|
|
143 |
|
144 |
+
// Legacy Gutenberg (<11.3.0) support
|
145 |
+
const [ _legacyAvailableTerms, _setLegacyAvailableTerms ] = useState( false );
|
146 |
+
const [ _legacyIsResolving, _setLegacyIsResolving ] = useState( false );
|
147 |
|
148 |
+
// Ref: <https://github.com/WordPress/gutenberg/pull/33418#issuecomment-903686737>
|
149 |
+
const {
|
150 |
+
selectedTerms,
|
151 |
+
loading,
|
152 |
+
availableTerms,
|
153 |
+
_taxonomy,
|
154 |
+
} = useSelect(
|
155 |
+
select => {
|
156 |
+
const { getTaxonomy, getEntityRecords, isResolving } = select( 'core' );
|
157 |
+
const { getEditedPostAttribute } = select( 'core/editor' );
|
158 |
|
159 |
+
const _taxonomy = getTaxonomy( taxonomySlug );
|
160 |
+
const _query = [ 'taxonomy', taxonomySlug, DEFAULT_QUERY ];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
161 |
|
162 |
+
return {
|
163 |
+
selectedTerms: getEditedPostAttribute( _taxonomy?.rest_base ) || EMPTY_ARRAY, // GB bug: causes 70 useSelect-calls on-load.
|
164 |
+
loading: _legacyIsResolving || isResolving( 'getEntityRecords', _query ),
|
165 |
+
availableTerms: _legacyAvailableTerms || getEntityRecords( ..._query ) || EMPTY_ARRAY,
|
166 |
+
_taxonomy,
|
167 |
+
};
|
168 |
+
},
|
169 |
+
[ taxonomySlug, _legacyIsResolving, _legacyAvailableTerms ]
|
170 |
+
);
|
171 |
|
172 |
+
// Forward data to our store based on mutability of "selection".
|
173 |
+
// TODO add test for availableTerms.includes( +selection ) -> User deletes terms while in Gutenberg?
|
174 |
+
// -> `! availableTerms.map( term => term.id ).includes( id )`
|
175 |
+
// = Mega edge case Gutenberg doesn't handle (properly) either.
|
176 |
+
useEffect(
|
177 |
+
() => {
|
178 |
+
if ( ! selectedTerms.includes( +selection ) || primaryTerm.get() !== +selection ) {
|
179 |
+
primaryTerm.revalidate( selectedTerms );
|
180 |
+
setSelection( primaryTerm.get() );
|
181 |
+
}
|
182 |
+
},
|
183 |
+
[ selectedTerms ]
|
184 |
+
);
|
185 |
|
186 |
+
// This effect depends on the mutable state of _legacySelectedTerms.
|
187 |
+
useEffect(
|
188 |
+
() => {
|
189 |
+
// Legacy Gutenberg (<11.3.0) support.
|
190 |
+
if ( _taxonomy?.rest_base && _legacySelectedTerms?.length && ! _legacyIsResolving ) {
|
191 |
+
if ( ! _legacyAvailableTerms?.length
|
192 |
+
// Find differences in stored/selected ids.
|
193 |
+
|| _legacySelectedTerms.filter( id => ! availableTerms.map( term => term.id ).includes( id ) ).length
|
194 |
+
) {
|
195 |
+
_setLegacyIsResolving( true );
|
196 |
+
wp.apiFetch(
|
197 |
+
{
|
198 |
+
path: wp.url?.addQueryArgs(
|
199 |
+
`/wp/v2/${ _taxonomy.rest_base }`,
|
200 |
+
DEFAULT_QUERY
|
201 |
+
),
|
202 |
+
}
|
203 |
+
).then(
|
204 |
+
terms => {
|
205 |
+
_setLegacyAvailableTerms( terms );
|
206 |
+
},
|
207 |
+
).finally(
|
208 |
+
() => {
|
209 |
+
_setLegacyIsResolving( false );
|
210 |
+
}
|
211 |
+
);
|
212 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
213 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
214 |
|
215 |
+
return () => { _setLegacyIsResolving( false ); };
|
216 |
+
},
|
217 |
+
[ _legacySelectedTerms ]
|
218 |
+
);
|
|
|
|
|
|
|
|
|
|
|
219 |
|
220 |
+
if ( selectedTerms?.length < 2 ) {
|
221 |
+
// Delete entry.
|
222 |
+
primaryTerm.set( 0 );
|
223 |
+
// Hide selector. Halt function.
|
224 |
+
return null;
|
|
|
|
|
225 |
}
|
226 |
|
227 |
+
const onChange = termId => {
|
228 |
+
if ( ! selectedTerms.includes( +termId ) ) return;
|
229 |
+
primaryTerm.set( termId );
|
230 |
+
setSelection( primaryTerm.get() );
|
|
|
231 |
'tsfAys' in window && tsfAys.registerChange();
|
232 |
+
};
|
233 |
+
|
234 |
+
const getSelectOptions = () => {
|
235 |
+
return availableTerms.map( term =>
|
236 |
+
selectedTerms.includes( term?.id )
|
237 |
+
&& {
|
238 |
+
value: term.id,
|
239 |
+
label: unescape( term?.name )
|
240 |
+
}
|
241 |
+
).filter( Boolean ) || '';
|
242 |
+
};
|
243 |
+
|
244 |
+
const isDisabled = () => ! ( selectedTerms.length && availableTerms.length && ! loading );
|
245 |
+
|
246 |
+
return createElement(
|
247 |
+
SelectControl,
|
248 |
+
{
|
249 |
+
label: _geti18n( taxonomySlug, 'selectPrimary' ),
|
250 |
+
value: selection,
|
251 |
+
className: 'tsf-pt-gb-selector',
|
252 |
+
onChange: onChange,
|
253 |
+
options: getSelectOptions(),
|
254 |
+
disabled: isDisabled(),
|
255 |
+
// Yes, it's neater. No, we shouldn't. Not our bug.
|
256 |
+
// style: { lineHeight: 'unset' }, // <https://github.com/WordPress/gutenberg/issues/27194>
|
257 |
+
},
|
258 |
+
);
|
259 |
}
|
260 |
|
261 |
+
const PrimaryTermSelectorFilter = OriginalComponent => class extends Component {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
262 |
render() {
|
263 |
+
// If we cannot store the primary term for this taxonomy, bail.
|
264 |
+
if ( ! _primaryTerm( this.props?.slug ).isRegistered() )
|
265 |
+
return createElement( OriginalComponent, { ...this.props } );
|
266 |
+
|
|
|
|
|
|
|
|
|
|
|
267 |
return createElement(
|
268 |
Fragment,
|
269 |
+
null,
|
270 |
createElement(
|
271 |
+
OriginalComponent,
|
272 |
+
{ ...this.props },
|
273 |
),
|
274 |
+
createElement(
|
275 |
+
primaryTermSelector,
|
276 |
+
{
|
277 |
+
taxonomySlug: this.props?.slug,
|
278 |
+
_legacySelectedTerms: this.props?.terms,
|
279 |
+
},
|
280 |
+
)
|
281 |
);
|
282 |
}
|
283 |
}
|
284 |
|
285 |
+
for ( let taxonomySlug in supportedTaxonomies )
|
286 |
+
_primaryTerm( taxonomySlug ).register();
|
|
|
|
|
287 |
|
288 |
+
wp.hooks.addFilter(
|
289 |
+
'editor.PostTaxonomyType',
|
290 |
+
'tsf/pt',
|
291 |
+
PrimaryTermSelectorFilter
|
292 |
+
);
|
|
|
|
|
|
|
293 |
}
|
294 |
|
295 |
return Object.assign( {
|
lib/js/pt-gb.min.js
CHANGED
@@ -1 +1 @@
|
|
1 |
-
'use strict';window.tsfPTGB=function(a){const b="undefined"!=typeof tsfPTL10n&&tsfPTL10n,
|
1 |
+
'use strict';window.tsfPTGB=function(a){const b="undefined"!=typeof tsfPTL10n&&tsfPTL10n,c=null===b||void 0===b?void 0:b.taxonomies,{createElement:d,Fragment:e,Component:f,useState:g,useEffect:h}=wp.element,{SelectControl:i}=wp.components,{useSelect:j}=wp.data,{unescape:k}=lodash,l=[],m={per_page:-1,orderby:"id",order:"asc",_fields:"id,name"},n=(a,b)=>c[a].i18n[b]||"";let o={};const p=b=>{const d=()=>document.getElementById(`autodescription[_primary_term_${b}]`),e=()=>+d().value,f=a=>+(d().value=+a);return{get:e,set:f,revalidate:a=>{let b=e();return a.includes(b)?b:f((null===a||void 0===a?void 0:a[0])||0)},register:()=>{let d=document.getElementById("tsf-gutenberg-data-holder");if(!d)return o[b]=!1;let e=wp.template("tsf-primary-term-selector")({taxonomy:c[b]});return o[b]=!!a(e).appendTo(d)},isRegistered:()=>o[b]||!1}},q=()=>{function a({taxonomySlug:a,_legacySelectedTerms:b}){const c=p(a),[e,f]=g(c.get()),[o,q]=g(!1),[r,s]=g(!1),{selectedTerms:t,loading:u,availableTerms:v,_taxonomy:w}=j(b=>{const{getTaxonomy:c,getEntityRecords:d,isResolving:e}=b("core"),{getEditedPostAttribute:f}=b("core/editor"),g=c(a),h=["taxonomy",a,m];return{selectedTerms:f(null===g||void 0===g?void 0:g.rest_base)||l,loading:r||e("getEntityRecords",h),availableTerms:o||d(...h)||l,_taxonomy:g}},[a,r,o]);if(h(()=>{t.includes(+e)&&c.get()===+e||(c.revalidate(t),f(c.get()))},[t]),h(()=>{if((null===w||void 0===w?void 0:w.rest_base)&&(null===b||void 0===b?void 0:b.length)&&!r&&(null===o||void 0===o||!o.length||b.filter(a=>!v.map(a=>a.id).includes(a)).length)){var a;s(!0),wp.apiFetch({path:null===(a=wp.url)||void 0===a?void 0:a.addQueryArgs(`/wp/v2/${w.rest_base}`,m)}).then(a=>{q(a)}).finally(()=>{s(!1)})}return()=>{s(!1)}},[b]),2>(null===t||void 0===t?void 0:t.length))return c.set(0),null;const x=a=>{t.includes(+a)&&(c.set(a),f(c.get()),"tsfAys"in window&&tsfAys.registerChange())};return d(i,{label:n(a,"selectPrimary"),value:e,className:"tsf-pt-gb-selector",onChange:x,options:(()=>v.map(a=>t.includes(null===a||void 0===a?void 0:a.id)&&{value:a.id,label:k(null===a||void 0===a?void 0:a.name)}).filter(Boolean)||"")(),disabled:(()=>!(t.length&&v.length&&!u))()})}if(Object.keys(c).length){for(let a in c)p(a).register();wp.hooks.addFilter("editor.PostTaxonomyType","tsf/pt",b=>class extends f{render(){var c,f,g;return p(null===(c=this.props)||void 0===c?void 0:c.slug).isRegistered()?d(e,null,d(b,{...this.props}),d(a,{taxonomySlug:null===(f=this.props)||void 0===f?void 0:f.slug,_legacySelectedTerms:null===(g=this.props)||void 0===g?void 0:g.terms})):d(b,{...this.props})}})}};return Object.assign({load:()=>{document.body.addEventListener("tsf-onload",q)}},{},{l10n:b})}(jQuery),window.tsfPTGB.load();
|
readme.txt
CHANGED
@@ -5,7 +5,7 @@ Tags: seo, xml sitemap, google search, open graph, schema.org, twitter card, per
|
|
5 |
Requires at least: 5.1.0
|
6 |
Tested up to: 5.8
|
7 |
Requires PHP: 5.6.0
|
8 |
-
Stable tag: 4.1.
|
9 |
License: GPLv3
|
10 |
License URI: http://www.gnu.org/licenses/gpl-3.0.html
|
11 |
|
@@ -247,6 +247,10 @@ If you wish to display breadcrumbs, then your theme should provide this. Alterna
|
|
247 |
|
248 |
== Changelog ==
|
249 |
|
|
|
|
|
|
|
|
|
250 |
= 4.1.4 =
|
251 |
|
252 |
This minor update packs a major punch. TSF now supports [headless mode](https://kb.theseoframework.com/?p=136), 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](https://theseoframework.com/?p=3727#detailed).
|
5 |
Requires at least: 5.1.0
|
6 |
Tested up to: 5.8
|
7 |
Requires PHP: 5.6.0
|
8 |
+
Stable tag: 4.1.5
|
9 |
License: GPLv3
|
10 |
License URI: http://www.gnu.org/licenses/gpl-3.0.html
|
11 |
|
247 |
|
248 |
== Changelog ==
|
249 |
|
250 |
+
= 4.1.5 =
|
251 |
+
|
252 |
+
This minor update adds support for Gutenberg 11.3.0 and [fixes a few bugs](https://theseoframework.com/?p=3756#detailed).
|
253 |
+
|
254 |
= 4.1.4 =
|
255 |
|
256 |
This minor update packs a major punch. TSF now supports [headless mode](https://kb.theseoframework.com/?p=136), 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](https://theseoframework.com/?p=3727#detailed).
|