PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 2.9.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v2.9.0
2.9.0 2.8.0 2.7.0 2.6.0 2.5.0 2.4.0 2.3.0 2.2.0 2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.32.0 1.31.0 1.30.0 1.29.0 1.28.0 1.27.0 1.26.0 1.25.0 trunk 1.0.0 1.0.1 1.0.2 All 50 releases
← All changes | includes/abilities/content/class-update-term-seo.php +47 -7 2.0.0 → 2.9.0 View file →
@@ -9,8 +9,10 @@
9 9
10 10 namespace ThinkRank\Abilities\Content;
11 11
12 12 use ThinkRank\Abilities\Ability_Base;
13 +use ThinkRank\SEO\Object_Redirect;
14 +use ThinkRank\SEO\Pattern_Resolver;
13 15
14 16 if ( ! defined( 'ABSPATH' ) ) {
15 17 exit; // Exit if accessed directly.
16 18 }
@@ -24,9 +26,9 @@
24 26 */
25 27 public function __construct() {
26 28 $this->id = 'thinkrank/update-term-seo';
27 29 $this->label = __( 'Update ThinkRank Term SEO', 'thinkrank' );
28 - $this->description = __( 'Update ThinkRank SEO metadata for a taxonomy term.', 'thinkrank' );
30 + $this->description = __( 'Update ThinkRank SEO metadata for a taxonomy term. Read the current values with get-term-seo first; only the fields you pass are changed.', 'thinkrank' );
29 31 }
30 32
31 33 /**
32 34 * {@inheritDoc}
@@ -49,8 +51,17 @@
49 51 'properties' => [
50 52 'title' => [ 'type' => 'string' ],
51 53 'description' => [ 'type' => 'string' ],
52 54 'canonical_url' => [ 'type' => 'string' ],
55 + 'redirect_url' => [
56 + 'type' => 'string',
57 + 'description' => __( 'Send visitors from this term archive to this URL. Empty string removes the redirect. Requires ThinkRank Pro.', 'thinkrank' ),
58 + ],
59 + 'redirect_type' => [
60 + 'type' => 'integer',
61 + 'enum' => Object_Redirect::TYPES,
62 + 'description' => __( 'Redirect status code. Defaults to 301.', 'thinkrank' ),
63 + ],
53 64 'focus_keyword' => [ 'type' => 'string' ],
54 65 'robots_meta_enabled' => [ 'type' => 'boolean' ],
55 66 'robots_meta' => [ 'type' => 'object' ],
56 67 'advanced_robots_meta' => [ 'type' => 'object' ],
@@ -118,9 +129,29 @@
118 129 }
119 130
120 131 $touched = $this->save_term_meta( $term_id, $settings );
121 132
122 - if ( ! $touched ) {
133 + // The redirect is not term meta — Pro's rules table holds it — so it is
134 + // saved separately, and unlike the meta fields it can be refused.
135 + $redirect_touched = array_key_exists( 'redirect_url', $settings );
136 + if ( $redirect_touched ) {
137 + $redirect_result = Object_Redirect::save(
138 + 'term',
139 + $term_id,
140 + (string) $settings['redirect_url'],
141 + $settings['redirect_type'] ?? Object_Redirect::DEFAULT_TYPE
142 + );
143 +
144 + if ( is_wp_error( $redirect_result ) ) {
145 + return new \WP_Error(
146 + $redirect_result->get_error_code(),
147 + $redirect_result->get_error_message(),
148 + [ 'status' => 400 ]
149 + );
150 + }
151 + }
152 +
153 + if ( ! $touched && ! $redirect_touched ) {
123 154 return new \WP_Error(
124 155 'thinkrank_no_valid_term_meta_keys',
125 156 __( 'No valid ThinkRank term SEO keys were provided.', 'thinkrank' ),
126 157 [ 'status' => 400 ]
@@ -143,21 +174,30 @@
143 174 */
144 175 private function save_term_meta( $term_id, array $settings ) {
145 176 $touched = false;
146 177
147 - $text_fields = [
178 + // The title/description fields are variable-tag templates — the frontend
179 + // runs each through Pattern_Resolver::resolve_term_value() — so they are
180 + // sanitized as templates. sanitize_text_field() would strip %date% and
181 + // %category% as percent-encoding and store "te%" / "tegory%" (#521).
182 + $template_fields = [
148 183 'title' => '_thinkrank_seo_title',
149 - 'focus_keyword' => '_thinkrank_focus_keyword',
150 184 'og_title' => '_thinkrank_og_title',
151 185 'twitter_title' => '_thinkrank_twitter_title',
152 186 ];
153 - foreach ( $text_fields as $key => $meta_key ) {
187 + foreach ( $template_fields as $key => $meta_key ) {
154 188 if ( array_key_exists( $key, $settings ) ) {
155 - $this->save_meta( $term_id, $meta_key, sanitize_text_field( (string) $settings[ $key ] ) );
189 + $this->save_meta( $term_id, $meta_key, Pattern_Resolver::sanitize_template( (string) $settings[ $key ] ) );
156 190 $touched = true;
157 191 }
158 192 }
159 193
194 + // Plain text, not a template.
195 + if ( array_key_exists( 'focus_keyword', $settings ) ) {
196 + $this->save_meta( $term_id, '_thinkrank_focus_keyword', sanitize_text_field( (string) $settings['focus_keyword'] ) );
197 + $touched = true;
198 + }
199 +
160 200 $textarea_fields = [
161 201 'description' => '_thinkrank_meta_description',
162 202 'og_description' => '_thinkrank_og_description',
163 203 'twitter_description' => '_thinkrank_twitter_description',
@@ -163,9 +203,9 @@
163 203 'twitter_description' => '_thinkrank_twitter_description',
164 204 ];
165 205 foreach ( $textarea_fields as $key => $meta_key ) {
166 206 if ( array_key_exists( $key, $settings ) ) {
167 - $this->save_meta( $term_id, $meta_key, sanitize_textarea_field( (string) $settings[ $key ] ) );
207 + $this->save_meta( $term_id, $meta_key, Pattern_Resolver::sanitize_template_textarea( (string) $settings[ $key ] ) );
168 208 $touched = true;
169 209 }
170 210 }
171 211