PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 28.6
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v28.6
28.6 28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 All 130 releases
wordpress-seo / src / actions / importing / aioseo / abstract-aioseo-settings-importing-action.php

abstract-aioseo-settings-importing-action.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI 28.6, at src/actions/importing/aioseo/abstract-aioseo-settings-importing-action.php

343 lines 10.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 // phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Given it's a very specific case.
4 namespace Yoast\WP\SEO\Actions\Importing\Aioseo;
5
6 use Exception;
7 use Yoast\WP\SEO\Actions\Importing\Abstract_Aioseo_Importing_Action;
8 use Yoast\WP\SEO\Helpers\Import_Helper;
9
10 /**
11 * Abstract class for importing AIOSEO settings.
12 */
13 abstract class Abstract_Aioseo_Settings_Importing_Action extends Abstract_Aioseo_Importing_Action {
14
15 /**
16 * The plugin the class deals with.
17 *
18 * @var string
19 */
20 public const PLUGIN = null;
21
22 /**
23 * The type the class deals with.
24 *
25 * @var string
26 */
27 public const TYPE = null;
28
29 /**
30 * The option_name of the AIOSEO option that contains the settings.
31 */
32 public const SOURCE_OPTION_NAME = null;
33
34 /**
35 * The map of aioseo_options to yoast settings.
36 *
37 * @var array
38 */
39 protected $aioseo_options_to_yoast_map = [];
40
41 /**
42 * The tab of the aioseo settings we're working with, eg. taxonomies, posttypes.
43 *
44 * @var string
45 */
46 protected $settings_tab = '';
47
48 /**
49 * Additional mapping between AiOSEO replace vars and Yoast replace vars.
50 *
51 * @see https://yoast.com/help/list-available-snippet-variables-yoast-seo/
52 *
53 * @var array
54 */
55 protected $replace_vars_edited_map = [];
56
57 /**
58 * The import helper.
59 *
60 * @var Import_Helper
61 */
62 protected $import_helper;
63
64 /**
65 * Builds the mapping that ties AOISEO option keys with Yoast ones and their data transformation method.
66 *
67 * @return void
68 */
69 abstract protected function build_mapping();
70
71 /**
72 * Sets the import helper.
73 *
74 * @required
75 *
76 * @param Import_Helper $import_helper The import helper.
77 *
78 * @return void
79 */
80 public function set_import_helper( Import_Helper $import_helper ) {
81 $this->import_helper = $import_helper;
82 }
83
84 /**
85 * Retrieves the source option_name.
86 *
87 * @return string The source option_name.
88 *
89 * @throws Exception If the SOURCE_OPTION_NAME constant is not set in the child class.
90 */
91 public function get_source_option_name() {
92 $source_option_name = static::SOURCE_OPTION_NAME;
93
94 if ( empty( $source_option_name ) ) {
95 throw new Exception( 'Importing settings action without explicit source option_name' );
96 }
97
98 return $source_option_name;
99 }
100
101 /**
102 * Returns the total number of unimported objects.
103 *
104 * @return int The total number of unimported objects.
105 */
106 public function get_total_unindexed() {
107 return $this->get_unindexed_count();
108 }
109
110 /**
111 * Returns the limited number of unimported objects.
112 *
113 * @param int $limit The maximum number of unimported objects to be returned.
114 *
115 * @return int The limited number of unindexed posts.
116 */
117 public function get_limited_unindexed_count( $limit ) {
118 return $this->get_unindexed_count( $limit );
119 }
120
121 /**
122 * Returns the number of unimported objects (limited if limit is applied).
123 *
124 * @param int|null $limit The maximum number of unimported objects to be returned.
125 *
126 * @return int The number of unindexed posts.
127 */
128 protected function get_unindexed_count( $limit = null ) {
129 if ( ! \is_int( $limit ) || $limit < 1 ) {
130 $limit = null;
131 }
132
133 $settings_to_create = $this->query( $limit );
134
135 $number_of_settings_to_create = \count( $settings_to_create );
136 $completed = $number_of_settings_to_create === 0;
137 $this->set_completed( $completed );
138
139 return $number_of_settings_to_create;
140 }
141
142 /**
143 * Imports AIOSEO settings.
144 *
145 * @return array|false An array of the AIOSEO settings that were imported or false if aioseo data was not found.
146 */
147 public function index() {
148 $limit = $this->get_limit();
149 $aioseo_settings = $this->query( $limit );
150 $created_settings = [];
151
152 $completed = \count( $aioseo_settings ) === 0;
153 $this->set_completed( $completed );
154
155 // Prepare the setting keys mapping.
156 $this->build_mapping();
157
158 // Prepare the replacement var mapping.
159 foreach ( $this->replace_vars_edited_map as $aioseo_var => $yoast_var ) {
160 $this->replacevar_handler->compose_map( $aioseo_var, $yoast_var );
161 }
162
163 $last_imported_setting = '';
164 try {
165 foreach ( $aioseo_settings as $setting => $setting_value ) {
166 // Map and import the values of the setting we're working with (eg. post, book-category, etc.) to the respective Yoast option.
167 $this->map( $setting_value, $setting );
168
169 // Save the type of the settings that were just imported, so that we can allow chunked imports.
170 $last_imported_setting = $setting;
171
172 $created_settings[] = $setting;
173 }
174 }
175 finally {
176 $cursor_id = $this->get_cursor_id();
177 $this->import_cursor->set_cursor( $cursor_id, $last_imported_setting );
178 }
179
180 return $created_settings;
181 }
182
183 /**
184 * Checks if the settings tab subsetting is set in the AIOSEO option.
185 *
186 * @param string $aioseo_settings The AIOSEO option.
187 *
188 * @return bool Whether the settings are set.
189 */
190 public function isset_settings_tab( $aioseo_settings ) {
191 return isset( $aioseo_settings['searchAppearance'][ $this->settings_tab ] );
192 }
193
194 /**
195 * Queries the database and retrieves unimported AiOSEO settings (in chunks if a limit is applied).
196 *
197 * @param int|null $limit The maximum number of unimported objects to be returned.
198 *
199 * @return array The (maybe chunked) unimported AiOSEO settings to import.
200 */
201 protected function query( $limit = null ) {
202 $aioseo_settings = \json_decode( \get_option( $this->get_source_option_name(), '' ), true );
203
204 if ( empty( $aioseo_settings ) ) {
205 return [];
206 }
207
208 // We specifically want the setttings of the tab we're working with, eg. postTypes, taxonomies, etc.
209 $settings_values = $aioseo_settings['searchAppearance'][ $this->settings_tab ];
210 if ( ! \is_array( $settings_values ) ) {
211 return [];
212 }
213
214 $flattened_settings = $this->import_helper->flatten_settings( $settings_values );
215
216 return $this->get_unimported_chunk( $flattened_settings, $limit );
217 }
218
219 /**
220 * Retrieves (a chunk of, if limit is applied) the unimported AIOSEO settings.
221 * To apply a chunk, we manipulate the cursor to the keys of the AIOSEO settings.
222 *
223 * @param array $importable_data All of the available AIOSEO settings.
224 * @param int $limit The maximum number of unimported objects to be returned.
225 *
226 * @return array The (chunk of, if limit is applied)) unimported AIOSEO settings.
227 */
228 protected function get_unimported_chunk( $importable_data, $limit ) {
229 \ksort( $importable_data );
230
231 $cursor_id = $this->get_cursor_id();
232 $cursor = $this->import_cursor->get_cursor( $cursor_id, '' );
233
234 /**
235 * Filter 'wpseo_aioseo_<identifier>_import_cursor' - Allow filtering the value of the aioseo settings import cursor.
236 *
237 * @param int $import_cursor The value of the aioseo posttype default settings import cursor.
238 */
239 $cursor = \apply_filters( 'wpseo_aioseo_' . $this->get_type() . '_import_cursor', $cursor );
240
241 if ( $cursor === '' ) {
242 return \array_slice( $importable_data, 0, $limit, true );
243 }
244
245 // Let's find the position of the cursor in the alphabetically sorted importable data, so we can return only the unimported data.
246 $keys = \array_flip( \array_keys( $importable_data ) );
247 // If the stored cursor now no longer exists in the data, we have no choice but to start over.
248 $position = ( isset( $keys[ $cursor ] ) ) ? ( $keys[ $cursor ] + 1 ) : 0;
249
250 return \array_slice( $importable_data, $position, $limit, true );
251 }
252
253 /**
254 * Returns the number of objects that will be imported in a single importing pass.
255 *
256 * @return int The limit.
257 */
258 public function get_limit() {
259 /**
260 * Filter 'wpseo_aioseo_<identifier>_indexation_limit' - Allow filtering the number of settings imported during each importing pass.
261 *
262 * @param int $max_posts The maximum number of posts indexed.
263 */
264 $limit = \apply_filters( 'wpseo_aioseo_' . $this->get_type() . '_indexation_limit', 25 );
265
266 if ( ! \is_int( $limit ) || $limit < 1 ) {
267 $limit = 25;
268 }
269
270 return $limit;
271 }
272
273 /**
274 * Maps/imports AIOSEO settings into the respective Yoast settings.
275 *
276 * @param string|array $setting_value The value of the AIOSEO setting at hand.
277 * @param string $setting The setting at hand, eg. post or movie-category, separator etc.
278 *
279 * @return void
280 */
281 protected function map( $setting_value, $setting ) {
282 $aioseo_options_to_yoast_map = $this->aioseo_options_to_yoast_map;
283
284 if ( isset( $aioseo_options_to_yoast_map[ $setting ] ) ) {
285 $this->import_single_setting( $setting, $setting_value, $aioseo_options_to_yoast_map[ $setting ] );
286 }
287 }
288
289 /**
290 * Imports a single setting in the db after transforming it to adhere to Yoast conventions.
291 *
292 * @param string $setting The name of the setting.
293 * @param string $setting_value The values of the setting.
294 * @param array $setting_mapping The mapping of the setting to Yoast formats.
295 *
296 * @return void
297 */
298 protected function import_single_setting( $setting, $setting_value, $setting_mapping ) {
299 $yoast_key = $setting_mapping['yoast_name'];
300
301 // Check if we're supposed to save the setting.
302 if ( $this->options->get_default( 'wpseo_titles', $yoast_key ) !== null ) {
303 // Then, do any needed data transfomation before actually saving the incoming data.
304 $transformed_data = \call_user_func( [ $this, $setting_mapping['transform_method'] ], $setting_value, $setting_mapping );
305
306 $this->options->set( $yoast_key, $transformed_data );
307 }
308 }
309
310 /**
311 * Minimally transforms boolean data to be imported.
312 *
313 * @param bool $meta_data The boolean meta data to be imported.
314 *
315 * @return bool The transformed boolean meta data.
316 */
317 public function simple_boolean_import( $meta_data ) {
318 return $meta_data;
319 }
320
321 /**
322 * Imports the noindex setting, taking into consideration whether they defer to global defaults.
323 *
324 * @param bool $noindex The noindex of the type, without taking into consideration whether the type defers to global defaults.
325 * @param array $mapping The mapping of the setting we're working with.
326 *
327 * @return bool The noindex setting.
328 */
329 public function import_noindex( $noindex, $mapping ) {
330 return $this->robots_transformer->transform_robot_setting( 'noindex', $noindex, $mapping );
331 }
332
333 /**
334 * Returns a setting map of the robot setting for one subset of post types/taxonomies/archives.
335 * For custom archives, it returns an empty array because AIOSEO excludes some custom archives from this option structure, eg. WooCommerce's products and we don't want to raise a false alarm.
336 *
337 * @return array The setting map of the robot setting for one subset of post types/taxonomies/archives or an empty array.
338 */
339 public function pluck_robot_setting_from_mapping() {
340 return [];
341 }
342 }
343