PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 1.32.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v1.32.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 1.1.0 1.10.0 All 48 releases
thinkrank / includes / abilities / content / class-update-term-seo.php

class-update-term-seo.php in ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO 1.32.0, at includes/abilities/content/class-update-term-seo.php

219 lines 6.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Update term SEO ability.
4 *
5 * @package ThinkRank\Abilities\Content
6 */
7
8 declare(strict_types=1);
9
10 namespace ThinkRank\Abilities\Content;
11
12 use ThinkRank\Abilities\Ability_Base;
13
14 if ( ! defined( 'ABSPATH' ) ) {
15 exit; // Exit if accessed directly.
16 }
17
18 /**
19 * Updates ThinkRank SEO data for a taxonomy term.
20 */
21 class Update_Term_Seo extends Ability_Base {
22 /**
23 * Constructor.
24 */
25 public function __construct() {
26 $this->id = 'thinkrank/update-term-seo';
27 $this->label = __( 'Update ThinkRank Term SEO', 'thinkrank' );
28 $this->description = __( 'Update ThinkRank SEO metadata for a taxonomy term.', 'thinkrank' );
29 }
30
31 /**
32 * {@inheritDoc}
33 *
34 * @return array<string, mixed>
35 */
36 public function get_input_schema() {
37 return [
38 'type' => 'object',
39 'additionalProperties' => false,
40 'properties' => [
41 'term_id' => [
42 'type' => 'integer',
43 'description' => __( 'The target term ID.', 'thinkrank' ),
44 ],
45 'settings' => [
46 'type' => 'object',
47 'additionalProperties' => true,
48 'description' => __( 'ThinkRank term SEO fields to update.', 'thinkrank' ),
49 'properties' => [
50 'title' => [ 'type' => 'string' ],
51 'description' => [ 'type' => 'string' ],
52 'canonical_url' => [ 'type' => 'string' ],
53 'focus_keyword' => [ 'type' => 'string' ],
54 'robots_meta_enabled' => [ 'type' => 'boolean' ],
55 'robots_meta' => [ 'type' => 'object' ],
56 'advanced_robots_meta' => [ 'type' => 'object' ],
57 'og_title' => [ 'type' => 'string' ],
58 'og_description' => [ 'type' => 'string' ],
59 'og_image' => [ 'type' => 'string' ],
60 'twitter_title' => [ 'type' => 'string' ],
61 'twitter_description' => [ 'type' => 'string' ],
62 'twitter_image' => [ 'type' => 'string' ],
63 ],
64 ],
65 ],
66 'required' => [ 'term_id', 'settings' ],
67 ];
68 }
69
70 /**
71 * {@inheritDoc}
72 *
73 * @return array<string, mixed>
74 */
75 public function get_output_schema() {
76 return [
77 'type' => 'object',
78 'properties' => [
79 'success' => [ 'type' => 'boolean' ],
80 'message' => [ 'type' => 'string' ],
81 'term_id' => [ 'type' => 'integer' ],
82 ],
83 ];
84 }
85
86 /**
87 * Execute ability.
88 *
89 * @param array<string, mixed> $input Ability input payload.
90 * @return array<string, mixed>|\WP_Error
91 */
92 public function execute( $input ) {
93 $term_id = isset( $input['term_id'] ) ? absint( $input['term_id'] ) : 0;
94 $settings = isset( $input['settings'] ) && is_array( $input['settings'] ) ? $input['settings'] : [];
95
96 if ( ! $term_id || empty( $settings ) ) {
97 return new \WP_Error(
98 'thinkrank_invalid_term_update',
99 __( 'A valid term ID and settings payload are required.', 'thinkrank' ),
100 [ 'status' => 400 ]
101 );
102 }
103
104 if ( ! get_term( $term_id ) instanceof \WP_Term ) {
105 return new \WP_Error(
106 'thinkrank_term_not_found',
107 __( 'No term was found for the provided ID.', 'thinkrank' ),
108 [ 'status' => 404 ]
109 );
110 }
111
112 if ( ! current_user_can( 'manage_options' ) ) {
113 return new \WP_Error(
114 'thinkrank_cannot_edit_term',
115 __( 'You are not allowed to edit term SEO settings.', 'thinkrank' ),
116 [ 'status' => 403 ]
117 );
118 }
119
120 $touched = $this->save_term_meta( $term_id, $settings );
121
122 if ( ! $touched ) {
123 return new \WP_Error(
124 'thinkrank_no_valid_term_meta_keys',
125 __( 'No valid ThinkRank term SEO keys were provided.', 'thinkrank' ),
126 [ 'status' => 400 ]
127 );
128 }
129
130 return [
131 'success' => true,
132 'message' => __( 'Term SEO metadata updated.', 'thinkrank' ),
133 'term_id' => $term_id,
134 ];
135 }
136
137 /**
138 * Persist provided term SEO meta, sanitizing per field type.
139 *
140 * @param int $term_id Term ID.
141 * @param array<string, mixed> $settings Normalized settings payload.
142 * @return bool Whether any meta key was written or deleted.
143 */
144 private function save_term_meta( $term_id, array $settings ) {
145 $touched = false;
146
147 $text_fields = [
148 'title' => '_thinkrank_seo_title',
149 'focus_keyword' => '_thinkrank_focus_keyword',
150 'og_title' => '_thinkrank_og_title',
151 'twitter_title' => '_thinkrank_twitter_title',
152 ];
153 foreach ( $text_fields as $key => $meta_key ) {
154 if ( array_key_exists( $key, $settings ) ) {
155 $this->save_meta( $term_id, $meta_key, sanitize_text_field( (string) $settings[ $key ] ) );
156 $touched = true;
157 }
158 }
159
160 $textarea_fields = [
161 'description' => '_thinkrank_meta_description',
162 'og_description' => '_thinkrank_og_description',
163 'twitter_description' => '_thinkrank_twitter_description',
164 ];
165 foreach ( $textarea_fields as $key => $meta_key ) {
166 if ( array_key_exists( $key, $settings ) ) {
167 $this->save_meta( $term_id, $meta_key, sanitize_textarea_field( (string) $settings[ $key ] ) );
168 $touched = true;
169 }
170 }
171
172 $url_fields = [
173 'canonical_url' => '_thinkrank_canonical_url',
174 'og_image' => '_thinkrank_og_image',
175 'twitter_image' => '_thinkrank_twitter_image',
176 ];
177 foreach ( $url_fields as $key => $meta_key ) {
178 if ( array_key_exists( $key, $settings ) ) {
179 $this->save_meta( $term_id, $meta_key, esc_url_raw( (string) $settings[ $key ] ) );
180 $touched = true;
181 }
182 }
183
184 if ( array_key_exists( 'robots_meta_enabled', $settings ) ) {
185 update_term_meta( $term_id, '_thinkrank_robots_meta_enabled', (int) (bool) $settings['robots_meta_enabled'] );
186 $touched = true;
187 }
188
189 if ( array_key_exists( 'robots_meta', $settings ) ) {
190 update_term_meta( $term_id, '_thinkrank_robots_meta', (string) wp_json_encode( (array) $settings['robots_meta'] ) );
191 $touched = true;
192 }
193
194 if ( array_key_exists( 'advanced_robots_meta', $settings ) ) {
195 update_term_meta( $term_id, '_thinkrank_advanced_robots_meta', (string) wp_json_encode( (array) $settings['advanced_robots_meta'] ) );
196 $touched = true;
197 }
198
199 return $touched;
200 }
201
202 /**
203 * Update a term meta value, deleting it when the sanitized value is empty.
204 *
205 * @param int $term_id Term ID.
206 * @param string $meta_key Meta key.
207 * @param string $value Sanitized value.
208 * @return void
209 */
210 private function save_meta( $term_id, $meta_key, $value ) {
211 if ( '' === $value ) {
212 delete_term_meta( $term_id, $meta_key );
213 return;
214 }
215
216 update_term_meta( $term_id, $meta_key, $value );
217 }
218 }
219