• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Yoast / wordpress-seo / 49cdf70e861509c37a279de77f1105ee59c0f57f

20 Feb 2024 09:17AM UTC coverage: 53.172%. First build
49cdf70e861509c37a279de77f1105ee59c0f57f

push

github

YoastBot
Bump version to 22.1 on free

7564 of 13903 branches covered (54.41%)

Branch coverage included in aggregate %.

29067 of 54988 relevant lines covered (52.86%)

40245.15 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/admin/ajax.php
1
<?php
2
/**
3
 * WPSEO plugin file.
4
 *
5
 * @package WPSEO\Admin
6
 */
7

8
if ( ! defined( 'WPSEO_VERSION' ) ) {
×
9
        header( 'Status: 403 Forbidden' );
×
10
        header( 'HTTP/1.1 403 Forbidden' );
×
11
        exit();
×
12
}
13

14
/**
15
 * Convenience function to JSON encode and echo results and then die.
16
 *
17
 * @param array $results Results array for encoding.
18
 *
19
 * @return void
20
 */
21
function wpseo_ajax_json_echo_die( $results ) {
22
        // phpcs:ignore WordPress.Security.EscapeOutput -- Reason: WPSEO_Utils::format_json_encode is safe.
23
        echo WPSEO_Utils::format_json_encode( $results );
×
24
        die();
×
25
}
26

27
/**
28
 * Function used from AJAX calls, takes it variables from $_POST, dies on exit.
29
 *
30
 * @return void
31
 */
32
function wpseo_set_option() {
33
        if ( ! current_user_can( 'manage_options' ) ) {
×
34
                die( '-1' );
×
35
        }
36

37
        check_ajax_referer( 'wpseo-setoption' );
×
38

39
        if ( ! isset( $_POST['option'] ) || ! is_string( $_POST['option'] ) ) {
×
40
                die( '-1' );
×
41
        }
42

43
        $option = sanitize_text_field( wp_unslash( $_POST['option'] ) );
×
44
        if ( $option !== 'page_comments' ) {
×
45
                die( '-1' );
×
46
        }
47

48
        update_option( $option, 0 );
×
49
        die( '1' );
×
50
}
51

52
add_action( 'wp_ajax_wpseo_set_option', 'wpseo_set_option' );
×
53

54
/**
55
 * Since 3.2 Notifications are dismissed in the Notification Center.
56
 */
57
add_action( 'wp_ajax_yoast_dismiss_notification', [ 'Yoast_Notification_Center', 'ajax_dismiss_notification' ] );
×
58

59
/**
60
 * Function used to remove the admin notices for several purposes, dies on exit.
61
 *
62
 * @return void
63
 */
64
function wpseo_set_ignore() {
65
        if ( ! current_user_can( 'manage_options' ) ) {
×
66
                die( '-1' );
×
67
        }
68

69
        check_ajax_referer( 'wpseo-ignore' );
×
70

71
        if ( ! isset( $_POST['option'] ) || ! is_string( $_POST['option'] ) ) {
×
72
                die( '-1' );
×
73
        }
74

75
        $ignore_key = sanitize_text_field( wp_unslash( $_POST['option'] ) );
×
76
        WPSEO_Options::set( 'ignore_' . $ignore_key, true );
×
77

78
        die( '1' );
×
79
}
80

81
add_action( 'wp_ajax_wpseo_set_ignore', 'wpseo_set_ignore' );
×
82

83
/**
84
 * Save an individual SEO title from the Bulk Editor.
85
 *
86
 * @return void
87
 */
88
function wpseo_save_title() {
89
        wpseo_save_what( 'title' );
×
90
}
91

92
add_action( 'wp_ajax_wpseo_save_title', 'wpseo_save_title' );
×
93

94
/**
95
 * Save an individual meta description from the Bulk Editor.
96
 *
97
 * @return void
98
 */
99
function wpseo_save_description() {
100
        wpseo_save_what( 'metadesc' );
×
101
}
102

103
add_action( 'wp_ajax_wpseo_save_metadesc', 'wpseo_save_description' );
×
104

105
/**
106
 * Save titles & descriptions.
107
 *
108
 * @param string $what Type of item to save (title, description).
109
 *
110
 * @return void
111
 */
112
function wpseo_save_what( $what ) {
113
        check_ajax_referer( 'wpseo-bulk-editor' );
×
114

115
        if ( ! isset( $_POST['new_value'], $_POST['wpseo_post_id'], $_POST['existing_value'] ) || ! is_string( $_POST['new_value'] ) || ! is_string( $_POST['existing_value'] ) ) {
×
116
                die( '-1' );
×
117
        }
118

119
        $new = sanitize_text_field( wp_unslash( $_POST['new_value'] ) );
×
120
        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason: We are casting the unsafe value to an integer.
121
        $post_id  = (int) wp_unslash( $_POST['wpseo_post_id'] );
×
122
        $original = sanitize_text_field( wp_unslash( $_POST['existing_value'] ) );
×
123

124
        if ( $post_id === 0 ) {
×
125
                die( '-1' );
×
126
        }
127

128
        $results = wpseo_upsert_new( $what, $post_id, $new, $original );
×
129

130
        wpseo_ajax_json_echo_die( $results );
×
131
}
132

133
/**
134
 * Helper function to update a post's meta data, returning relevant information
135
 * about the information updated and the results or the meta update.
136
 *
137
 * @param int    $post_id         Post ID.
138
 * @param string $new_meta_value  New meta value to record.
139
 * @param string $orig_meta_value Original meta value.
140
 * @param string $meta_key        Meta key string.
141
 * @param string $return_key      Return key string to use in results.
142
 *
143
 * @return array
144
 */
145
function wpseo_upsert_meta( $post_id, $new_meta_value, $orig_meta_value, $meta_key, $return_key ) {
146

147
        $post_id                  = intval( $post_id );
×
148
        $sanitized_new_meta_value = wp_strip_all_tags( $new_meta_value );
×
149
        $orig_meta_value          = wp_strip_all_tags( $orig_meta_value );
×
150

151
        $upsert_results = [
×
152
                'status'                 => 'success',
×
153
                'post_id'                => $post_id,
×
154
                "new_{$return_key}"      => $sanitized_new_meta_value,
×
155
                "original_{$return_key}" => $orig_meta_value,
×
156
        ];
×
157

158
        $the_post = get_post( $post_id );
×
159
        if ( empty( $the_post ) ) {
×
160

161
                $upsert_results['status']  = 'failure';
×
162
                $upsert_results['results'] = __( 'Post doesn\'t exist.', 'wordpress-seo' );
×
163

164
                return $upsert_results;
×
165
        }
166

167
        $post_type_object = get_post_type_object( $the_post->post_type );
×
168
        if ( ! $post_type_object ) {
×
169

170
                $upsert_results['status']  = 'failure';
×
171
                $upsert_results['results'] = sprintf(
×
172
                        /* translators: %s expands to post type. */
173
                        __( 'Post has an invalid Content Type: %s.', 'wordpress-seo' ),
×
174
                        $the_post->post_type
×
175
                );
×
176

177
                return $upsert_results;
×
178
        }
179

180
        if ( ! current_user_can( $post_type_object->cap->edit_posts ) ) {
×
181

182
                $upsert_results['status']  = 'failure';
×
183
                $upsert_results['results'] = sprintf(
×
184
                        /* translators: %s expands to post type name. */
185
                        __( 'You can\'t edit %s.', 'wordpress-seo' ),
×
186
                        $post_type_object->label
×
187
                );
×
188

189
                return $upsert_results;
×
190
        }
191

192
        if ( ! current_user_can( $post_type_object->cap->edit_others_posts ) && (int) $the_post->post_author !== get_current_user_id() ) {
×
193

194
                $upsert_results['status']  = 'failure';
×
195
                $upsert_results['results'] = sprintf(
×
196
                        /* translators: %s expands to the name of a post type (plural). */
197
                        __( 'You can\'t edit %s that aren\'t yours.', 'wordpress-seo' ),
×
198
                        $post_type_object->label
×
199
                );
×
200

201
                return $upsert_results;
×
202
        }
203

204
        if ( $sanitized_new_meta_value === $orig_meta_value && $sanitized_new_meta_value !== $new_meta_value ) {
×
205
                $upsert_results['status']  = 'failure';
×
206
                $upsert_results['results'] = __( 'You have used HTML in your value which is not allowed.', 'wordpress-seo' );
×
207

208
                return $upsert_results;
×
209
        }
210

211
        $res = update_post_meta( $post_id, $meta_key, $sanitized_new_meta_value );
×
212

213
        $upsert_results['status']  = ( $res !== false ) ? 'success' : 'failure';
×
214
        $upsert_results['results'] = $res;
×
215

216
        return $upsert_results;
×
217
}
218

219
/**
220
 * Save all titles sent from the Bulk Editor.
221
 *
222
 * @return void
223
 */
224
function wpseo_save_all_titles() {
225
        wpseo_save_all( 'title' );
×
226
}
227

228
add_action( 'wp_ajax_wpseo_save_all_titles', 'wpseo_save_all_titles' );
×
229

230
/**
231
 * Save all description sent from the Bulk Editor.
232
 *
233
 * @return void
234
 */
235
function wpseo_save_all_descriptions() {
236
        wpseo_save_all( 'metadesc' );
×
237
}
238

239
add_action( 'wp_ajax_wpseo_save_all_descriptions', 'wpseo_save_all_descriptions' );
×
240

241
/**
242
 * Utility function to save values.
243
 *
244
 * @param string $what Type of item so save.
245
 *
246
 * @return void
247
 */
248
function wpseo_save_all( $what ) {
249
        check_ajax_referer( 'wpseo-bulk-editor' );
×
250

251
        $results = [];
×
252
        if ( ! isset( $_POST['items'], $_POST['existingItems'] ) ) {
×
253
                wpseo_ajax_json_echo_die( $results );
×
254
        }
255

256
        $new_values      = array_map( [ 'WPSEO_Utils', 'sanitize_text_field' ], wp_unslash( (array) $_POST['items'] ) );
×
257
        $original_values = array_map( [ 'WPSEO_Utils', 'sanitize_text_field' ], wp_unslash( (array) $_POST['existingItems'] ) );
×
258

259
        foreach ( $new_values as $post_id => $new_value ) {
×
260
                $original_value = $original_values[ $post_id ];
×
261
                $results[]      = wpseo_upsert_new( $what, $post_id, $new_value, $original_value );
×
262
        }
263

264
        wpseo_ajax_json_echo_die( $results );
×
265
}
266

267
/**
268
 * Insert a new value.
269
 *
270
 * @param string $what      Item type (such as title).
271
 * @param int    $post_id   Post ID.
272
 * @param string $new_value New value to record.
273
 * @param string $original  Original value.
274
 *
275
 * @return string
276
 */
277
function wpseo_upsert_new( $what, $post_id, $new_value, $original ) {
278
        $meta_key = WPSEO_Meta::$meta_prefix . $what;
×
279

280
        return wpseo_upsert_meta( $post_id, $new_value, $original, $meta_key, $what );
×
281
}
282

283
/**
284
 * Retrieves the post ids where the keyword is used before as well as the types of those posts.
285
 *
286
 * @return void
287
 */
288
function ajax_get_keyword_usage_and_post_types() {
289
        check_ajax_referer( 'wpseo-keyword-usage-and-post-types', 'nonce' );
×
290

291
        if ( ! isset( $_POST['post_id'], $_POST['keyword'] ) || ! is_string( $_POST['keyword'] ) ) {
×
292
                die( '-1' );
×
293
        }
294

295
        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- We are casting to an integer.
296
        $post_id = (int) wp_unslash( $_POST['post_id'] );
×
297

298
        if ( $post_id === 0 || ! current_user_can( 'edit_post', $post_id ) ) {
×
299
                die( '-1' );
×
300
        }
301

302
        $keyword = sanitize_text_field( wp_unslash( $_POST['keyword'] ) );
×
303

304
        $post_ids = WPSEO_Meta::keyword_usage( $keyword, $post_id );
×
305

306
        if ( ! empty( $post_ids ) ) {
×
307
                $post_types = WPSEO_Meta::post_types_for_ids( $post_ids );
×
308
        }
309
        else {
310
                $post_types = [];
×
311
        }
312

313
        $return_object = [
×
314
                'keyword_usage' => $post_ids,
×
315
                'post_types'    => $post_types,
×
316
        ];
×
317

318
        wp_die(
×
319
                // phpcs:ignore WordPress.Security.EscapeOutput -- Reason: WPSEO_Utils::format_json_encode is safe.
320
                WPSEO_Utils::format_json_encode( $return_object )
×
321
        );
×
322
}
323

324
add_action( 'wp_ajax_get_focus_keyword_usage_and_post_types', 'ajax_get_keyword_usage_and_post_types' );
×
325

326

327
/**
328
 * Retrieves the keyword for the keyword doubles of the termpages.
329
 *
330
 * @return void
331
 */
332
function ajax_get_term_keyword_usage() {
333
        check_ajax_referer( 'wpseo-keyword-usage', 'nonce' );
×
334

335
        if ( ! isset( $_POST['post_id'], $_POST['keyword'], $_POST['taxonomy'] ) || ! is_string( $_POST['keyword'] ) || ! is_string( $_POST['taxonomy'] ) ) {
×
336
                wp_die( -1 );
×
337
        }
338

339
        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reason: We are casting the unsafe input to an integer.
340
        $post_id = (int) wp_unslash( $_POST['post_id'] );
×
341

342
        if ( $post_id === 0 ) {
×
343
                wp_die( -1 );
×
344
        }
345

346
        $keyword       = sanitize_text_field( wp_unslash( $_POST['keyword'] ) );
×
347
        $taxonomy_name = sanitize_text_field( wp_unslash( $_POST['taxonomy'] ) );
×
348

349
        $taxonomy = get_taxonomy( $taxonomy_name );
×
350

351
        if ( ! $taxonomy ) {
×
352
                wp_die( 0 );
×
353
        }
354

355
        if ( ! current_user_can( $taxonomy->cap->edit_terms ) ) {
×
356
                wp_die( -1 );
×
357
        }
358

359
        $usage = WPSEO_Taxonomy_Meta::get_keyword_usage( $keyword, $post_id, $taxonomy_name );
×
360

361
        // Normalize the result so it is the same as the post keyword usage AJAX request.
362
        $usage = $usage[ $keyword ];
×
363

364
        wp_die(
×
365
                // phpcs:ignore WordPress.Security.EscapeOutput -- Reason: WPSEO_Utils::format_json_encode is safe.
366
                WPSEO_Utils::format_json_encode( $usage )
×
367
        );
×
368
}
369

370
add_action( 'wp_ajax_get_term_keyword_usage', 'ajax_get_term_keyword_usage' );
×
371

372
/**
373
 * Registers hooks for all AJAX integrations.
374
 *
375
 * @return void
376
 */
377
function wpseo_register_ajax_integrations() {
378
        $integrations = [ new Yoast_Network_Admin() ];
×
379

380
        foreach ( $integrations as $integration ) {
×
381
                $integration->register_ajax_hooks();
×
382
        }
383
}
384

385
wpseo_register_ajax_integrations();
×
386

387
new WPSEO_Shortcode_Filter();
×
388

389
new WPSEO_Taxonomy_Columns();
×
390

391
/* ********************* DEPRECATED FUNCTIONS ********************* */
392

393
/**
394
 * Retrieves the keyword for the keyword doubles.
395
 *
396
 * @return void
397
 */
398
function ajax_get_keyword_usage() {
399
        _deprecated_function( __METHOD__, 'WPSEO 20.4' );
×
400
        check_ajax_referer( 'wpseo-keyword-usage', 'nonce' );
×
401

402
        if ( ! isset( $_POST['post_id'], $_POST['keyword'] ) || ! is_string( $_POST['keyword'] ) ) {
×
403
                die( '-1' );
×
404
        }
405

406
        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- We are casting to an integer.
407
        $post_id = (int) wp_unslash( $_POST['post_id'] );
×
408

409
        if ( $post_id === 0 || ! current_user_can( 'edit_post', $post_id ) ) {
×
410
                die( '-1' );
×
411
        }
412

413
        $keyword = sanitize_text_field( wp_unslash( $_POST['keyword'] ) );
×
414

415
        wp_die(
×
416
                // phpcs:ignore WordPress.Security.EscapeOutput -- Reason: WPSEO_Utils::format_json_encode is safe.
417
                WPSEO_Utils::format_json_encode( WPSEO_Meta::keyword_usage( $keyword, $post_id ) )
×
418
        );
×
419
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc