← All changes
|
includes/abilities/content/class-update-term-seo.php
+47
-7
2.1.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 | |