Yoast SEO - Version 8.0

Version Description

Download this release

Release Info

Developer Yoast
Plugin Icon 128x128 Yoast SEO
Version 8.0
Comparing to
See all releases

Code changes from version 7.9.1 to 8.0

Files changed (82) hide show
  1. admin/ajax.php +15 -0
  2. admin/class-admin-asset-dev-server-location.php +1 -20
  3. admin/class-admin-asset-manager.php +86 -35
  4. admin/class-admin-gutenberg-compatibility-notification.php +89 -0
  5. admin/class-admin.php +7 -1
  6. admin/class-cornerstone-field.php +0 -57
  7. admin/class-cornerstone.php +1 -1
  8. admin/class-expose-shortlinks.php +32 -0
  9. admin/class-gutenberg-compatibility.php +101 -0
  10. admin/class-keyword-synonyms-modal.php +7 -2
  11. admin/class-multiple-keywords-modal.php +66 -0
  12. admin/class-structured-data-blocks.php +50 -0
  13. admin/class-yoast-form.php +13 -2
  14. admin/class-yoast-network-admin.php +299 -0
  15. admin/class-yoast-network-settings-api.php +157 -0
  16. admin/formatter/class-metabox-formatter.php +36 -2
  17. admin/menu/class-admin-menu.php +19 -150
  18. admin/menu/class-base-menu.php +258 -0
  19. admin/menu/class-network-admin-menu.php +47 -35
  20. admin/metabox/class-metabox-add-keyword-tab.php +0 -69
  21. admin/metabox/{class-metabox-keyword-synonyms-button.php → class-metabox-keyword-synonyms-config.php} +9 -15
  22. admin/metabox/class-metabox-multiple-keywords-config.php +44 -0
  23. admin/metabox/class-metabox-section-react.php +99 -0
  24. admin/metabox/class-metabox-tab-section.php +9 -0
  25. admin/metabox/class-metabox.php +24 -212
  26. admin/pages/network.php +6 -139
  27. admin/taxonomy/class-taxonomy-content-fields.php +6 -25
  28. admin/taxonomy/class-taxonomy-fields-presenter.php +0 -38
  29. admin/taxonomy/class-taxonomy-metabox.php +5 -61
  30. admin/taxonomy/class-taxonomy.php +1 -2
  31. admin/views/tabs/network/general.php +51 -0
  32. admin/views/tabs/network/restore-site.php +34 -0
  33. css/dist/{admin-global-791-rtl.min.css → admin-global-800-rtl.min.css} +0 -0
  34. css/dist/{admin-global-791.min.css → admin-global-800.min.css} +0 -0
  35. css/dist/{adminbar-791-rtl.min.css → adminbar-800-rtl.min.css} +0 -0
  36. css/dist/{adminbar-791.min.css → adminbar-800.min.css} +0 -0
  37. css/dist/{alerts-791-rtl.min.css → alerts-800-rtl.min.css} +0 -0
  38. css/dist/{alerts-791.min.css → alerts-800.min.css} +0 -0
  39. css/dist/{dashboard-791-rtl.min.css → dashboard-800-rtl.min.css} +0 -0
  40. css/dist/{dashboard-791.min.css → dashboard-800.min.css} +0 -0
  41. css/dist/{edit-page-791-rtl.min.css → edit-page-800-rtl.min.css} +0 -0
  42. css/dist/{edit-page-791.min.css → edit-page-800.min.css} +0 -0
  43. css/dist/{featured-image-791-rtl.min.css → featured-image-800-rtl.min.css} +0 -0
  44. css/dist/{featured-image-791.min.css → featured-image-800.min.css} +0 -0
  45. css/dist/{filter-explanation-791-rtl.min.css → filter-explanation-800-rtl.min.css} +0 -0
  46. css/dist/{filter-explanation-791.min.css → filter-explanation-800.min.css} +0 -0
  47. css/dist/{inside-editor-791-rtl.min.css → inside-editor-800-rtl.min.css} +0 -0
  48. css/dist/{inside-editor-791.min.css → inside-editor-800.min.css} +0 -0
  49. css/dist/metabox-791-rtl.min.css +0 -1
  50. css/dist/metabox-791.min.css +0 -1
  51. css/dist/metabox-800-rtl.min.css +1 -0
  52. css/dist/metabox-800.min.css +1 -0
  53. css/dist/{metabox-primary-category-791-rtl.min.css → metabox-primary-category-800-rtl.min.css} +0 -0
  54. css/dist/{metabox-primary-category-791.min.css → metabox-primary-category-800.min.css} +0 -0
  55. css/dist/{search-appearance-791-rtl.min.css → search-appearance-800-rtl.min.css} +0 -0
  56. css/dist/{search-appearance-791.min.css → search-appearance-800.min.css} +0 -0
  57. css/dist/snippet-791-rtl.min.css +0 -1
  58. css/dist/snippet-791.min.css +0 -1
  59. css/dist/structured-data-blocks-800-rtl.min.css +1 -0
  60. css/dist/structured-data-blocks-800.min.css +1 -0
  61. css/dist/{toggle-switch-791-rtl.min.css → toggle-switch-800-rtl.min.css} +0 -0
  62. css/dist/{toggle-switch-791.min.css → toggle-switch-800.min.css} +0 -0
  63. css/dist/{wpseo-dismissible-791-rtl.min.css → wpseo-dismissible-800-rtl.min.css} +0 -0
  64. css/dist/{wpseo-dismissible-791.min.css → wpseo-dismissible-800.min.css} +0 -0
  65. css/dist/{yoast-components-791-rtl.min.css → yoast-components-800-rtl.min.css} +0 -0
  66. css/dist/{yoast-components-791.min.css → yoast-components-800.min.css} +0 -0
  67. css/dist/{yoast-extensions-791-rtl.min.css → yoast-extensions-800-rtl.min.css} +0 -0
  68. css/dist/{yoast-extensions-791.min.css → yoast-extensions-800.min.css} +0 -0
  69. css/dist/{yst_plugin_tools-791-rtl.min.css → yst_plugin_tools-800-rtl.min.css} +0 -0
  70. css/dist/{yst_plugin_tools-791.min.css → yst_plugin_tools-800.min.css} +0 -0
  71. css/dist/{yst_seo_score-791-rtl.min.css → yst_seo_score-800-rtl.min.css} +0 -0
  72. css/dist/{yst_seo_score-791.min.css → yst_seo_score-800.min.css} +0 -0
  73. inc/class-wpseo-admin-bar-menu.php +626 -0
  74. inc/class-wpseo-meta.php +32 -47
  75. inc/class-wpseo-utils.php +17 -0
  76. inc/interface-wpseo-wordpress-ajax-integration.php +19 -0
  77. inc/options/class-wpseo-option.php +12 -2
  78. inc/options/class-wpseo-options.php +1 -1
  79. inc/sitemaps/class-sitemaps-cache-validator.php +2 -0
  80. inc/sitemaps/class-sitemaps-router.php +6 -1
  81. inc/wpseo-non-ajax-functions.php +85 -303
  82. js/dist/analysis-791.min.js +0 -13
admin/ajax.php CHANGED
@@ -332,6 +332,21 @@ function ajax_get_term_keyword_usage() {
332
 
333
  add_action( 'wp_ajax_get_term_keyword_usage', 'ajax_get_term_keyword_usage' );
334
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
335
  // Crawl Issue Manager AJAX hooks.
336
  new WPSEO_GSC_Ajax();
337
 
332
 
333
  add_action( 'wp_ajax_get_term_keyword_usage', 'ajax_get_term_keyword_usage' );
334
 
335
+ /**
336
+ * Registers hooks for all AJAX integrations.
337
+ *
338
+ * @return void
339
+ */
340
+ function wpseo_register_ajax_integrations() {
341
+ $integrations = array( new Yoast_Network_Admin() );
342
+
343
+ foreach ( $integrations as $integration ) {
344
+ $integration->register_ajax_hooks();
345
+ }
346
+ }
347
+
348
+ wpseo_register_ajax_integrations();
349
+
350
  // Crawl Issue Manager AJAX hooks.
351
  new WPSEO_GSC_Ajax();
352
 
admin/class-admin-asset-dev-server-location.php CHANGED
@@ -11,25 +11,6 @@
11
  final class WPSEO_Admin_Asset_Dev_Server_Location implements WPSEO_Admin_Asset_Location {
12
  const DEFAULT_URL = 'http://localhost:8080';
13
 
14
- /**
15
- * @var array
16
- */
17
- private static $dev_server_script = array(
18
- 'analysis',
19
- 'commons',
20
- 'configuration-wizard',
21
- 'search-appearance',
22
- 'wp-seo-dashboard-widget',
23
- 'wp-seo-help-center',
24
- 'wp-seo-metabox',
25
- 'wp-seo-modal',
26
- 'wp-seo-post-scraper',
27
- 'wp-seo-replacevar-plugin',
28
- 'wp-seo-term-scraper',
29
- 'wp-seo-modal',
30
- 'wp-seo-wp-globals-backport',
31
- );
32
-
33
  /**
34
  * @var string
35
  */
@@ -63,7 +44,7 @@ final class WPSEO_Admin_Asset_Dev_Server_Location implements WPSEO_Admin_Asset_L
63
  $flat_version = $asset_manager->flatten_version( WPSEO_VERSION );
64
  $version_less_source = str_replace( '-' . $flat_version, '', $asset->get_src() );
65
 
66
- if ( ! in_array( $version_less_source, self::$dev_server_script, true ) ) {
67
  return $this->get_default_url( $asset, $type );
68
  }
69
 
11
  final class WPSEO_Admin_Asset_Dev_Server_Location implements WPSEO_Admin_Asset_Location {
12
  const DEFAULT_URL = 'http://localhost:8080';
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  /**
15
  * @var string
16
  */
44
  $flat_version = $asset_manager->flatten_version( WPSEO_VERSION );
45
  $version_less_source = str_replace( '-' . $flat_version, '', $asset->get_src() );
46
 
47
+ if ( false !== strpos( $version_less_source, 'select2' ) ) {
48
  return $this->get_default_url( $asset, $type );
49
  }
50
 
admin/class-admin-asset-manager.php CHANGED
@@ -193,9 +193,10 @@ class WPSEO_Admin_Asset_Manager {
193
  $backport_wp_dependencies = array( self::PREFIX . 'react-dependencies' );
194
 
195
  // If Gutenberg is present we can borrow their globals for our own.
196
- if ( function_exists( 'gutenberg_register_scripts_and_styles' ) ) {
197
  $backport_wp_dependencies[] = 'wp-element';
198
  $backport_wp_dependencies[] = 'wp-data';
 
199
 
200
  /*
201
  * The version of TinyMCE that Gutenberg uses is incompatible with
@@ -210,15 +211,24 @@ class WPSEO_Admin_Asset_Manager {
210
  wp_register_script( 'tinymce-latest', includes_url( 'js/tinymce/' ) . 'wp-tinymce.php', array( 'jquery' ), false, true );
211
  }
212
  }
 
 
 
 
 
 
 
 
 
 
 
 
213
 
214
  return array(
215
  array(
216
  'name' => 'react-dependencies',
217
  // Load webpack-commons for bundle support.
218
  'src' => 'commons-' . $flat_version,
219
- 'deps' => array(
220
- self::PREFIX . 'babel-polyfill',
221
- ),
222
  ),
223
  array(
224
  'name' => 'search-appearance',
@@ -255,7 +265,7 @@ class WPSEO_Admin_Asset_Manager {
255
  'jquery-ui-progressbar',
256
  self::PREFIX . 'select2',
257
  self::PREFIX . 'select2-translations',
258
- self::PREFIX . 'babel-polyfill',
259
  ),
260
  ),
261
  array(
@@ -264,23 +274,29 @@ class WPSEO_Admin_Asset_Manager {
264
  'deps' => array(
265
  'jquery',
266
  'jquery-ui-core',
267
- self::PREFIX . 'babel-polyfill',
268
  ),
269
  ),
270
  array(
271
- 'name' => 'bulk-editor',
272
- 'src' => 'wp-seo-bulk-editor-' . $flat_version,
273
- 'deps' => array( 'jquery', self::PREFIX . 'babel-polyfill' ),
274
  ),
275
  array(
276
- 'name' => 'dismissible',
277
- 'src' => 'wp-seo-dismissible-' . $flat_version,
278
- 'deps' => array( 'jquery', self::PREFIX . 'babel-polyfill' ),
 
 
 
279
  ),
280
  array(
281
  'name' => 'admin-global-script',
282
  'src' => 'wp-seo-admin-global-' . $flat_version,
283
- 'deps' => array( 'jquery', self::PREFIX . 'babel-polyfill' ),
 
 
 
284
  ),
285
  array(
286
  'name' => 'metabox',
@@ -298,13 +314,15 @@ class WPSEO_Admin_Asset_Manager {
298
  'src' => 'wp-seo-featured-image-' . $flat_version,
299
  'deps' => array(
300
  'jquery',
301
- self::PREFIX . 'babel-polyfill',
302
  ),
303
  ),
304
  array(
305
  'name' => 'admin-gsc',
306
  'src' => 'wp-seo-admin-gsc-' . $flat_version,
307
- 'deps' => array( self::PREFIX . 'babel-polyfill' ),
 
 
308
  'in_footer' => false,
309
  ),
310
  array(
@@ -316,6 +334,7 @@ class WPSEO_Admin_Asset_Manager {
316
  'wp-util',
317
  self::PREFIX . 'wp-globals-backport',
318
  self::PREFIX . 'analysis',
 
319
  ),
320
  ),
321
  array(
@@ -331,7 +350,7 @@ class WPSEO_Admin_Asset_Manager {
331
  'name' => 'replacevar-plugin',
332
  'src' => 'wp-seo-replacevar-plugin-' . $flat_version,
333
  'deps' => array(
334
- self::PREFIX . 'babel-polyfill',
335
  self::PREFIX . 'analysis',
336
  ),
337
  ),
@@ -339,7 +358,7 @@ class WPSEO_Admin_Asset_Manager {
339
  'name' => 'shortcode-plugin',
340
  'src' => 'wp-seo-shortcode-plugin-' . $flat_version,
341
  'deps' => array(
342
- self::PREFIX . 'babel-polyfill',
343
  self::PREFIX . 'analysis',
344
  ),
345
  ),
@@ -350,8 +369,8 @@ class WPSEO_Admin_Asset_Manager {
350
  'jquery',
351
  'jquery-ui-core',
352
  'jquery-ui-progressbar',
353
- self::PREFIX . 'babel-polyfill',
354
  self::PREFIX . 'analysis',
 
355
  ),
356
  ),
357
  array(
@@ -360,7 +379,7 @@ class WPSEO_Admin_Asset_Manager {
360
  'deps' => array(
361
  'jquery',
362
  'wp-util',
363
- self::PREFIX . 'babel-polyfill',
364
  ),
365
  ),
366
  array(
@@ -390,15 +409,6 @@ class WPSEO_Admin_Asset_Manager {
390
  self::PREFIX . 'wp-globals-backport',
391
  ),
392
  ),
393
- // Register for backwards-compatiblity.
394
- array(
395
- 'name' => 'polyfill',
396
- 'src' => 'wp-seo-babel-polyfill-' . $flat_version,
397
- ),
398
- array(
399
- 'name' => 'babel-polyfill',
400
- 'src' => 'wp-seo-babel-polyfill-' . $flat_version,
401
- ),
402
  array(
403
  'name' => 'reindex-links',
404
  'src' => 'wp-seo-reindex-links-' . $flat_version,
@@ -406,17 +416,24 @@ class WPSEO_Admin_Asset_Manager {
406
  'jquery',
407
  'jquery-ui-core',
408
  'jquery-ui-progressbar',
 
409
  ),
410
  ),
411
  array(
412
  'name' => 'edit-page-script',
413
  'src' => 'wp-seo-edit-page-' . $flat_version,
414
- 'deps' => array( 'jquery' ),
 
 
 
415
  ),
416
  array(
417
  'name' => 'quick-edit-handler',
418
  'src' => 'wp-seo-quick-edit-handler-' . $flat_version,
419
- 'deps' => array( 'jquery' ),
 
 
 
420
  'in_footer' => true,
421
  ),
422
  array(
@@ -425,6 +442,7 @@ class WPSEO_Admin_Asset_Manager {
425
  'deps' => array(
426
  'wp-api',
427
  'jquery',
 
428
  ),
429
  ),
430
  array(
@@ -439,12 +457,20 @@ class WPSEO_Admin_Asset_Manager {
439
  array(
440
  'name' => 'filter-explanation',
441
  'src' => 'wp-seo-filter-explanation-' . $flat_version,
442
- 'deps' => array( 'jquery' ),
 
 
 
443
  ),
444
  array(
445
  'name' => 'analysis',
446
  'src' => 'analysis-' . $flat_version,
447
  ),
 
 
 
 
 
448
  );
449
  }
450
 
@@ -499,10 +525,6 @@ class WPSEO_Admin_Asset_Manager {
499
  'name' => 'scoring',
500
  'src' => 'yst_seo_score-' . $flat_version,
501
  ),
502
- array(
503
- 'name' => 'snippet',
504
- 'src' => 'snippet-' . $flat_version,
505
- ),
506
  array(
507
  'name' => 'adminbar',
508
  'src' => 'adminbar-' . $flat_version,
@@ -538,6 +560,35 @@ class WPSEO_Admin_Asset_Manager {
538
  'name' => 'search-appearance',
539
  'src' => 'search-appearance-' . $flat_version,
540
  ),
 
 
 
 
 
541
  );
542
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
543
  }
193
  $backport_wp_dependencies = array( self::PREFIX . 'react-dependencies' );
194
 
195
  // If Gutenberg is present we can borrow their globals for our own.
196
+ if ( $this->should_load_gutenberg_assets() ) {
197
  $backport_wp_dependencies[] = 'wp-element';
198
  $backport_wp_dependencies[] = 'wp-data';
199
+ $backport_wp_dependencies[] = 'wp-components';
200
 
201
  /*
202
  * The version of TinyMCE that Gutenberg uses is incompatible with
211
  wp_register_script( 'tinymce-latest', includes_url( 'js/tinymce/' ) . 'wp-tinymce.php', array( 'jquery' ), false, true );
212
  }
213
  }
214
+ else {
215
+ if ( wp_script_is( 'lodash', 'registered' ) ) {
216
+ $backport_wp_dependencies[] = 'lodash';
217
+ }
218
+ else {
219
+ if ( ! wp_script_is( self::PREFIX . 'lodash', 'registered' ) ) {
220
+ wp_register_script( self::PREFIX . 'lodash', plugins_url( 'js/vendor/lodash.min.js', WPSEO_FILE ) );
221
+ wp_add_inline_script( self::PREFIX . 'lodash', 'window.lodash = _.noConflict();', 'after' );
222
+ }
223
+ $backport_wp_dependencies[] = self::PREFIX . 'lodash';
224
+ }
225
+ }
226
 
227
  return array(
228
  array(
229
  'name' => 'react-dependencies',
230
  // Load webpack-commons for bundle support.
231
  'src' => 'commons-' . $flat_version,
 
 
 
232
  ),
233
  array(
234
  'name' => 'search-appearance',
265
  'jquery-ui-progressbar',
266
  self::PREFIX . 'select2',
267
  self::PREFIX . 'select2-translations',
268
+ self::PREFIX . 'react-dependencies',
269
  ),
270
  ),
271
  array(
274
  'deps' => array(
275
  'jquery',
276
  'jquery-ui-core',
277
+ self::PREFIX . 'react-dependencies',
278
  ),
279
  ),
280
  array(
281
+ 'name' => 'network-admin-script',
282
+ 'src' => 'wp-seo-network-admin-' . $flat_version,
283
+ 'deps' => array( 'jquery' ),
284
  ),
285
  array(
286
+ 'name' => 'bulk-editor',
287
+ 'src' => 'wp-seo-bulk-editor-' . $flat_version,
288
+ 'deps' => array(
289
+ 'jquery',
290
+ self::PREFIX . 'react-dependencies',
291
+ ),
292
  ),
293
  array(
294
  'name' => 'admin-global-script',
295
  'src' => 'wp-seo-admin-global-' . $flat_version,
296
+ 'deps' => array(
297
+ 'jquery',
298
+ self::PREFIX . 'react-dependencies',
299
+ ),
300
  ),
301
  array(
302
  'name' => 'metabox',
314
  'src' => 'wp-seo-featured-image-' . $flat_version,
315
  'deps' => array(
316
  'jquery',
317
+ self::PREFIX . 'react-dependencies',
318
  ),
319
  ),
320
  array(
321
  'name' => 'admin-gsc',
322
  'src' => 'wp-seo-admin-gsc-' . $flat_version,
323
+ 'deps' => array(
324
+ self::PREFIX . 'react-dependencies',
325
+ ),
326
  'in_footer' => false,
327
  ),
328
  array(
334
  'wp-util',
335
  self::PREFIX . 'wp-globals-backport',
336
  self::PREFIX . 'analysis',
337
+ self::PREFIX . 'react-dependencies',
338
  ),
339
  ),
340
  array(
350
  'name' => 'replacevar-plugin',
351
  'src' => 'wp-seo-replacevar-plugin-' . $flat_version,
352
  'deps' => array(
353
+ self::PREFIX . 'react-dependencies',
354
  self::PREFIX . 'analysis',
355
  ),
356
  ),
358
  'name' => 'shortcode-plugin',
359
  'src' => 'wp-seo-shortcode-plugin-' . $flat_version,
360
  'deps' => array(
361
+ self::PREFIX . 'react-dependencies',
362
  self::PREFIX . 'analysis',
363
  ),
364
  ),
369
  'jquery',
370
  'jquery-ui-core',
371
  'jquery-ui-progressbar',
 
372
  self::PREFIX . 'analysis',
373
+ self::PREFIX . 'react-dependencies',
374
  ),
375
  ),
376
  array(
379
  'deps' => array(
380
  'jquery',
381
  'wp-util',
382
+ self::PREFIX . 'react-dependencies',
383
  ),
384
  ),
385
  array(
409
  self::PREFIX . 'wp-globals-backport',
410
  ),
411
  ),
 
 
 
 
 
 
 
 
 
412
  array(
413
  'name' => 'reindex-links',
414
  'src' => 'wp-seo-reindex-links-' . $flat_version,
416
  'jquery',
417
  'jquery-ui-core',
418
  'jquery-ui-progressbar',
419
+ self::PREFIX . 'react-dependencies',
420
  ),
421
  ),
422
  array(
423
  'name' => 'edit-page-script',
424
  'src' => 'wp-seo-edit-page-' . $flat_version,
425
+ 'deps' => array(
426
+ 'jquery',
427
+ self::PREFIX . 'react-dependencies',
428
+ ),
429
  ),
430
  array(
431
  'name' => 'quick-edit-handler',
432
  'src' => 'wp-seo-quick-edit-handler-' . $flat_version,
433
+ 'deps' => array(
434
+ 'jquery',
435
+ self::PREFIX . 'react-dependencies',
436
+ ),
437
  'in_footer' => true,
438
  ),
439
  array(
442
  'deps' => array(
443
  'wp-api',
444
  'jquery',
445
+ self::PREFIX . 'react-dependencies',
446
  ),
447
  ),
448
  array(
457
  array(
458
  'name' => 'filter-explanation',
459
  'src' => 'wp-seo-filter-explanation-' . $flat_version,
460
+ 'deps' => array(
461
+ 'jquery',
462
+ self::PREFIX . 'react-dependencies',
463
+ ),
464
  ),
465
  array(
466
  'name' => 'analysis',
467
  'src' => 'analysis-' . $flat_version,
468
  ),
469
+ array(
470
+ 'name' => 'structured-data-blocks',
471
+ 'src' => 'wp-seo-structured-data-blocks-' . $flat_version,
472
+ 'deps' => array( 'wp-blocks', 'wp-i18n', 'wp-element' ),
473
+ ),
474
  );
475
  }
476
 
525
  'name' => 'scoring',
526
  'src' => 'yst_seo_score-' . $flat_version,
527
  ),
 
 
 
 
528
  array(
529
  'name' => 'adminbar',
530
  'src' => 'adminbar-' . $flat_version,
560
  'name' => 'search-appearance',
561
  'src' => 'search-appearance-' . $flat_version,
562
  ),
563
+ array(
564
+ 'name' => 'structured-data-blocks',
565
+ 'src' => 'structured-data-blocks-' . $flat_version,
566
+ 'deps' => array( 'wp-edit-blocks' ),
567
+ ),
568
  );
569
  }
570
+
571
+ /**
572
+ * Checks if the Gutenberg assets must be loaded.
573
+ *
574
+ * @return bool True wheter Gutenberg assets must be loaded.
575
+ */
576
+ protected function should_load_gutenberg_assets() {
577
+ // When Gutenberg is not active, just return false.
578
+ if ( ! function_exists( 'gutenberg_register_scripts_and_styles' ) ) {
579
+ return false;
580
+ }
581
+
582
+ // When working in the classic editor shipped with Gutenberg, the assets shouldn't be loaded. Fixes IE11 bug.
583
+ if ( isset( $_GET['classic-editor'] ) ) {
584
+ return false;
585
+ }
586
+
587
+ // When classic editor plugin and Gutenberg are active, the Gutenberg assets shouldn't be loaded.
588
+ if ( function_exists( 'classic_editor_is_gutenberg_active' ) && classic_editor_is_gutenberg_active() ) {
589
+ return false;
590
+ }
591
+
592
+ return true;
593
+ }
594
  }
admin/class-admin-gutenberg-compatibility-notification.php ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WPSEO plugin file.
4
+ *
5
+ * @package WPSEO\Admin
6
+ */
7
+
8
+ /**
9
+ * Handles the Gutenberg Compatibility notification showing and hiding.
10
+ */
11
+ class WPSEO_Admin_Gutenberg_Compatibility_Notification implements WPSEO_WordPress_Integration {
12
+
13
+ /**
14
+ * Notification ID to use.
15
+ *
16
+ * @var string
17
+ */
18
+ private $notification_id = 'wpseo-outdated-gutenberg-plugin';
19
+
20
+ /**
21
+ * @var WPSEO_Gutenberg_Compatibility
22
+ */
23
+ private $compatibility_checker;
24
+
25
+ /**
26
+ * @var Yoast_Notification_Center
27
+ */
28
+ private $notification_center;
29
+
30
+ /**
31
+ * WPSEO_Admin_Gutenberg_Compatibility_Notification constructor.
32
+ */
33
+ public function __construct() {
34
+ $this->compatibility_checker = new WPSEO_Gutenberg_Compatibility();
35
+ $this->notification_center = Yoast_Notification_Center::get();
36
+ }
37
+
38
+ /**
39
+ * Registers all hooks to WordPress.
40
+ *
41
+ * @return void
42
+ */
43
+ public function register_hooks() {
44
+ add_action( 'admin_init', array( $this, 'manage_notification' ) );
45
+ }
46
+
47
+ /**
48
+ * Manages if the notification should be shown or removed.
49
+ *
50
+ * @return void
51
+ */
52
+ public function manage_notification() {
53
+ if ( ! $this->compatibility_checker->is_installed() || $this->compatibility_checker->is_fully_compatible() ) {
54
+ $this->notification_center->remove_notification_by_id( $this->notification_id );
55
+
56
+ return;
57
+ }
58
+
59
+ $this->add_notification();
60
+ }
61
+
62
+ /**
63
+ * Adds the notification to the notificaton center.
64
+ *
65
+ * @return void
66
+ */
67
+ private function add_notification() {
68
+ $level = $this->compatibility_checker->is_below_minimum() ? Yoast_Notification::ERROR : Yoast_Notification::WARNING;
69
+
70
+ $message = sprintf(
71
+ /* translators: %1$s expands to Yoast SEO, %2$s expands to the installed version, %3$s expands to Gutenberg */
72
+ __( '%1$s detected you are using version %2$s of %3$s, please update to the latest version to prevent compatibility issues.', 'wordpress-seo' ),
73
+ 'Yoast SEO',
74
+ $this->compatibility_checker->get_installed_version(),
75
+ 'Gutenberg'
76
+ );
77
+
78
+ $notification = new Yoast_Notification(
79
+ $message,
80
+ array(
81
+ 'id' => $this->notification_id,
82
+ 'type' => $level,
83
+ 'priority' => 1,
84
+ )
85
+ );
86
+
87
+ $this->notification_center->add_notification( $notification );
88
+ }
89
+ }
admin/class-admin.php CHANGED
@@ -48,7 +48,10 @@ class WPSEO_Admin {
48
  );
49
 
50
  if ( WPSEO_Metabox::is_post_overview( $pagenow ) || WPSEO_Metabox::is_post_edit( $pagenow ) ) {
51
- $this->admin_features['primary_category'] = new WPSEO_Primary_Term_Admin();
 
 
 
52
  }
53
 
54
  if ( filter_input( INPUT_GET, 'page' ) === 'wpseo_tools' && filter_input( INPUT_GET, 'tool' ) === null ) {
@@ -92,11 +95,14 @@ class WPSEO_Admin {
92
 
93
  new Yoast_Modal();
94
 
 
95
  $integrations[] = new WPSEO_Yoast_Columns();
96
  $integrations[] = new WPSEO_License_Page_Manager();
97
  $integrations[] = new WPSEO_Statistic_Integration();
98
  $integrations[] = new WPSEO_Capability_Manager_Integration( WPSEO_Capability_Manager_Factory::get() );
99
  $integrations[] = new WPSEO_Admin_Media_Purge_Notification();
 
 
100
  $integrations = array_merge( $integrations, $this->initialize_seo_links() );
101
 
102
  /** @var WPSEO_WordPress_Integration $integration */
48
  );
49
 
50
  if ( WPSEO_Metabox::is_post_overview( $pagenow ) || WPSEO_Metabox::is_post_edit( $pagenow ) ) {
51
+ $this->admin_features['primary_category'] = new WPSEO_Primary_Term_Admin();
52
+ if ( defined( 'YOAST_FEATURE_GUTENBERG_STRUCTURED_DATA_BLOCKS' ) ) {
53
+ $this->admin_features['structured_data_blocks'] = new WPSEO_Structured_Data_Blocks();
54
+ }
55
  }
56
 
57
  if ( filter_input( INPUT_GET, 'page' ) === 'wpseo_tools' && filter_input( INPUT_GET, 'tool' ) === null ) {
95
 
96
  new Yoast_Modal();
97
 
98
+ $integrations[] = new Yoast_Network_Admin();
99
  $integrations[] = new WPSEO_Yoast_Columns();
100
  $integrations[] = new WPSEO_License_Page_Manager();
101
  $integrations[] = new WPSEO_Statistic_Integration();
102
  $integrations[] = new WPSEO_Capability_Manager_Integration( WPSEO_Capability_Manager_Factory::get() );
103
  $integrations[] = new WPSEO_Admin_Media_Purge_Notification();
104
+ $integrations[] = new WPSEO_Admin_Gutenberg_Compatibility_Notification();
105
+ $integrations[] = new WPSEO_Expose_Shortlinks();
106
  $integrations = array_merge( $integrations, $this->initialize_seo_links() );
107
 
108
  /** @var WPSEO_WordPress_Integration $integration */
admin/class-cornerstone-field.php DELETED
@@ -1,57 +0,0 @@
1
- <?php
2
- /**
3
- * WPSEO plugin file.
4
- *
5
- * @package WPSEO\Admin
6
- */
7
-
8
- /**
9
- * Adds a checkbox to the focus keyword section.
10
- */
11
- class WPSEO_Cornerstone_Field {
12
-
13
- /**
14
- * Returns a label with a checkbox in it. Make it possible to mark the page as cornerstone content.
15
- *
16
- * @param WP_POST $post The post object.
17
- *
18
- * @return string The HTML to show.
19
- */
20
- public function get_html( $post ) {
21
-
22
- $post_types = apply_filters( 'wpseo_cornerstone_post_types', WPSEO_Post_Type::get_accessible_post_types() );
23
- if ( ! is_array( $post_types ) || ! isset( $post_types[ get_post_type( $post ) ] ) ) {
24
- return '';
25
- }
26
-
27
- $return = '';
28
- $return .= sprintf(
29
- '<input id="%1$s" class="wpseo-cornerstone-checkbox" type="checkbox" value="1" name="%1$s" %2$s/>',
30
- WPSEO_Cornerstone::FIELD_NAME,
31
- checked( $this->get_meta_value( $post->ID ), '1', false )
32
- );
33
-
34
- $return .= sprintf( '<label for="%1$s">', WPSEO_Cornerstone::FIELD_NAME );
35
-
36
- $return .= sprintf(
37
- /* translators: 1: link open tag; 2: link close tag. */
38
- __( 'This article is %1$scornerstone content%2$s', 'wordpress-seo' ),
39
- '<a href="' . WPSEO_Shortlinker::get( 'https://yoa.st/metabox-help-cornerstone' ) . '" target="_blank">',
40
- '</a>'
41
- );
42
- $return .= '</label>';
43
-
44
- return $return;
45
- }
46
-
47
- /**
48
- * Gets the meta value from the database.
49
- *
50
- * @param int $post_id The post id to get the meta value for.
51
- *
52
- * @return null|string The meta value from the database.
53
- */
54
- protected function get_meta_value( $post_id ) {
55
- return WPSEO_Meta::get_value( WPSEO_Cornerstone::META_NAME, $post_id );
56
- }
57
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/class-cornerstone.php CHANGED
@@ -55,7 +55,7 @@ class WPSEO_Cornerstone {
55
  * @return bool True when checkbox is checked.
56
  */
57
  protected function is_cornerstone_content() {
58
- return filter_input( INPUT_POST, self::FIELD_NAME ) === '1';
59
  }
60
 
61
  /**
55
  * @return bool True when checkbox is checked.
56
  */
57
  protected function is_cornerstone_content() {
58
+ return filter_input( INPUT_POST, self::FIELD_NAME ) === 'true';
59
  }
60
 
61
  /**
admin/class-expose-shortlinks.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WPSEO plugin file.
4
+ *
5
+ * @package WPSEO\Admin
6
+ */
7
+
8
+ /**
9
+ * Exposes shortlinks in a global, so that we can pass them to our Javascript components.
10
+ */
11
+ class WPSEO_Expose_Shortlinks implements WPSEO_WordPress_Integration {
12
+
13
+ /**
14
+ * Registers all hooks to WordPress
15
+ */
16
+ public function register_hooks() {
17
+ add_filter( 'wpseo_admin_l10n', array( $this, 'expose_shortlinks' ) );
18
+ }
19
+
20
+ /**
21
+ * @param array $input The array to add shortlinks to.
22
+ *
23
+ * @return array
24
+ */
25
+ public function expose_shortlinks( $input ) {
26
+ $input['shortlinks.focus_keyword_info'] = WPSEO_Shortlinker::get( 'https://yoa.st/focus-keyword' );
27
+ $input['shortlinks.snippet_preview_info'] = WPSEO_Shortlinker::get( 'https://yoa.st/snippet-preview' );
28
+ $input['shortlinks.cornerstone_content_info'] = WPSEO_Shortlinker::get( 'https://yoa.st/1i9' );
29
+
30
+ return $input;
31
+ }
32
+ }
admin/class-gutenberg-compatibility.php ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WPSEO plugin file.
4
+ *
5
+ * @package WPSEO\Gutenberg_Compatibility
6
+ */
7
+
8
+ /**
9
+ * Class WPSEO_Gutenberg_Compatibility
10
+ */
11
+ class WPSEO_Gutenberg_Compatibility {
12
+
13
+ /**
14
+ * The currently released version of Gutenberg.
15
+ */
16
+ const CURRENT_RELEASE = '3.5.0';
17
+
18
+ /**
19
+ * The minimally supported version of Gutenberg by the plugin.
20
+ */
21
+ const MINIMUM_SUPPORTED = '2.8.0';
22
+
23
+ /**
24
+ * @var string
25
+ */
26
+ protected $current_version;
27
+
28
+ /**
29
+ * WPSEO_Gutenberg_Compatibility constructor.
30
+ */
31
+ public function __construct() {
32
+ $this->current_version = $this->detect_installed_gutenberg_version();
33
+ }
34
+
35
+ /**
36
+ * Determines whether or not Gutenberg is installed.
37
+ *
38
+ * @return bool Whether or not Gutenberg is installed.
39
+ */
40
+ public function is_installed() {
41
+ return $this->current_version !== '';
42
+ }
43
+
44
+ /**
45
+ * Determines whether or not the currently installed version of Gutenberg is below the minimum supported version.
46
+ *
47
+ * @return bool True if the currently installed version is below the minimum supported version. False otherwise.
48
+ */
49
+ public function is_below_minimum() {
50
+ return version_compare( $this->current_version, $this->get_minimum_supported_version(), '<' );
51
+ }
52
+
53
+ /**
54
+ * Gets the currently installed version.
55
+ *
56
+ * @return string The currently installed version.
57
+ */
58
+ public function get_installed_version() {
59
+ return $this->current_version;
60
+ }
61
+
62
+ /**
63
+ * Determines whether or not the currently installed version of Gutenberg is the latest, fully compatible version.
64
+ *
65
+ * @return bool Whether or not the currently installed version is fully compatible.
66
+ */
67
+ public function is_fully_compatible() {
68
+ return version_compare( $this->current_version, $this->get_latest_release(), '>=' );
69
+ }
70
+
71
+ /**
72
+ * Gets the latest released version of Gutenberg.
73
+ *
74
+ * @return string The latest release.
75
+ */
76
+ protected function get_latest_release() {
77
+ return self::CURRENT_RELEASE;
78
+ }
79
+
80
+ /**
81
+ * Gets the minimum supported version of Gutenberg.
82
+ *
83
+ * @return string The minumum supported release.
84
+ */
85
+ protected function get_minimum_supported_version() {
86
+ return self::MINIMUM_SUPPORTED;
87
+ }
88
+
89
+ /**
90
+ * Detects the currently installed Gutenberg version.
91
+ *
92
+ * @return string The currently installed Gutenberg version. Empty if the version couldn't be detected.
93
+ */
94
+ protected function detect_installed_gutenberg_version() {
95
+ if ( defined( 'GUTENBERG_VERSION' ) ) {
96
+ return GUTENBERG_VERSION;
97
+ }
98
+
99
+ return '';
100
+ }
101
+ }
admin/class-keyword-synonyms-modal.php CHANGED
@@ -27,12 +27,14 @@ class WPSEO_Keyword_Synonyms_Modal {
27
  'link' => WPSEO_Shortlinker::get( 'https://yoa.st/pe-premium-page' ),
28
  'other' => sprintf(
29
  /* translators: %s expands to 'Yoast SEO Premium'. */
30
- __( 'Other benefits of %s for you:', 'wordpress-seo' ), 'Yoast SEO Premium'
 
31
  ),
32
  'buylink' => WPSEO_Shortlinker::get( 'https://yoa.st/keyword-synonyms-popup' ),
33
  'buy' => sprintf(
34
  /* translators: %s expands to 'Yoast SEO Premium'. */
35
- __( 'Get %s now!', 'wordpress-seo' ), 'Yoast SEO Premium'
 
36
  ),
37
  'small' => __( '1 year free updates and upgrades included!', 'wordpress-seo' ),
38
  'a11yNotice.opensInNewTab' => __( '(Opens in a new browser tab)', 'wordpress-seo' ),
@@ -46,6 +48,7 @@ class WPSEO_Keyword_Synonyms_Modal {
46
  */
47
  public function get_translations_for_js() {
48
  $translations = $this->get_translations();
 
49
  return array(
50
  'locale' => WPSEO_Utils::get_user_locale(),
51
  'intl' => $translations,
@@ -54,6 +57,8 @@ class WPSEO_Keyword_Synonyms_Modal {
54
 
55
  /**
56
  * Prints the localized Keyword Synonyms modal translations for JS.
 
 
57
  */
58
  public function enqueue_translations() {
59
  wp_localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'admin-global-script', 'yoastKeywordSynonymsModalL10n', $this->get_translations_for_js() );
27
  'link' => WPSEO_Shortlinker::get( 'https://yoa.st/pe-premium-page' ),
28
  'other' => sprintf(
29
  /* translators: %s expands to 'Yoast SEO Premium'. */
30
+ __( 'Other benefits of %s for you:', 'wordpress-seo' ),
31
+ 'Yoast SEO Premium'
32
  ),
33
  'buylink' => WPSEO_Shortlinker::get( 'https://yoa.st/keyword-synonyms-popup' ),
34
  'buy' => sprintf(
35
  /* translators: %s expands to 'Yoast SEO Premium'. */
36
+ __( 'Get %s now!', 'wordpress-seo' ),
37
+ 'Yoast SEO Premium'
38
  ),
39
  'small' => __( '1 year free updates and upgrades included!', 'wordpress-seo' ),
40
  'a11yNotice.opensInNewTab' => __( '(Opens in a new browser tab)', 'wordpress-seo' ),
48
  */
49
  public function get_translations_for_js() {
50
  $translations = $this->get_translations();
51
+
52
  return array(
53
  'locale' => WPSEO_Utils::get_user_locale(),
54
  'intl' => $translations,
57
 
58
  /**
59
  * Prints the localized Keyword Synonyms modal translations for JS.
60
+ *
61
+ * @return void
62
  */
63
  public function enqueue_translations() {
64
  wp_localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'admin-global-script', 'yoastKeywordSynonymsModalL10n', $this->get_translations_for_js() );
admin/class-multiple-keywords-modal.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @package WPSEO\Admin
4
+ */
5
+
6
+ /**
7
+ * Class to print out the translatable strings for the Multiple Keywords modal.
8
+ */
9
+ class WPSEO_Multiple_Keywords_Modal {
10
+
11
+ /**
12
+ * Returns the translations for the Multiple Keywords modal.
13
+ *
14
+ * These strings are not escaped because they're meant to be used with React
15
+ * which already takes care of that. If used in PHP, they should be escaped.
16
+ *
17
+ * @return array Translated text strings for the Multiple Keywords modal.
18
+ */
19
+ public function get_translations() {
20
+ return array(
21
+ 'title' => __( 'Would you like to add another keyword?', 'wordpress-seo' ),
22
+ 'intro' => sprintf(
23
+ /* translators: %1$s expands to a 'Yoast SEO Premium' text linked to the yoast.com website. */
24
+ __( 'Great news: you can, with %1$s!', 'wordpress-seo' ),
25
+ '{{link}}Yoast SEO Premium{{/link}}'
26
+ ),
27
+ 'link' => WPSEO_Shortlinker::get( 'https://yoa.st/pe-premium-page' ),
28
+ 'other' => sprintf(
29
+ /* translators: %s expands to 'Yoast SEO Premium'. */
30
+ __( 'Other benefits of %s for you:', 'wordpress-seo' ),
31
+ 'Yoast SEO Premium'
32
+ ),
33
+ 'buylink' => WPSEO_Shortlinker::get( 'https://yoa.st/add-keywords-popup' ),
34
+ 'buy' => sprintf(
35
+ /* translators: %s expands to 'Yoast SEO Premium'. */
36
+ __( 'Get %s now!', 'wordpress-seo' ),
37
+ 'Yoast SEO Premium'
38
+ ),
39
+ 'small' => __( '1 year free updates and upgrades included!', 'wordpress-seo' ),
40
+ 'a11yNotice.opensInNewTab' => __( '(Opens in a new browser tab)', 'wordpress-seo' ),
41
+ );
42
+ }
43
+
44
+ /**
45
+ * Passes translations to JS for the Multiple Keywords modal component.
46
+ *
47
+ * @return array Translated text strings for the Multiple Keywords modal component.
48
+ */
49
+ public function get_translations_for_js() {
50
+ $translations = $this->get_translations();
51
+
52
+ return array(
53
+ 'locale' => WPSEO_Utils::get_user_locale(),
54
+ 'intl' => $translations,
55
+ );
56
+ }
57
+
58
+ /**
59
+ * Prints the localized Multiple Keywords modal translations for JS.
60
+ *
61
+ * @return void
62
+ */
63
+ public function enqueue_translations() {
64
+ wp_localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'admin-global-script', 'yoastMultipleKeywordsModalL10n', $this->get_translations_for_js() );
65
+ }
66
+ }
admin/class-structured-data-blocks.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WPSEO plugin file.
4
+ *
5
+ * @package WPSEO\Admin
6
+ */
7
+
8
+ /**
9
+ * Class to load assets required for structured data blocks.
10
+ */
11
+ class WPSEO_Structured_Data_Blocks {
12
+ /**
13
+ * @var WPSEO_Admin_Asset_Manager
14
+ */
15
+ protected $asset_manager;
16
+
17
+ /**
18
+ * WPSEO_Structured_Data_Blocks constructor.
19
+ */
20
+ public function __construct() {
21
+ $this->asset_manager = new WPSEO_Admin_Asset_Manager();
22
+
23
+ add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_block_editor_assets' ) );
24
+ add_filter( 'block_categories', array( $this, 'add_block_category' ) );
25
+ }
26
+
27
+ /**
28
+ * Enqueue Gutenberg block assets for backend editor.
29
+ */
30
+ public function enqueue_block_editor_assets() {
31
+ $this->asset_manager->enqueue_script( 'structured-data-blocks' );
32
+ $this->asset_manager->enqueue_style( 'structured-data-blocks' );
33
+ }
34
+
35
+ /**
36
+ * Adds the structured data blocks category to the Gutenberg categories.
37
+ *
38
+ * @param array $categories The current categories.
39
+ *
40
+ * @return array The updated categories.
41
+ */
42
+ public function add_block_category( $categories ) {
43
+ $categories[] = array(
44
+ 'slug' => 'yoast-structured-data-blocks',
45
+ 'title' => __( 'Structured Data Blocks', 'wordpress-seo' ),
46
+ );
47
+
48
+ return $categories;
49
+ }
50
+ }
admin/class-yoast-form.php CHANGED
@@ -76,8 +76,19 @@ class Yoast_Form {
76
  <?php
77
  if ( $form === true ) {
78
  $enctype = ( $contains_files ) ? ' enctype="multipart/form-data"' : '';
79
- echo '<form action="' . esc_url( admin_url( 'options.php' ) ) . '" method="post" id="wpseo-conf"' . $enctype . ' accept-charset="' . esc_attr( get_bloginfo( 'charset' ) ) . '">';
80
- settings_fields( $option_long_name );
 
 
 
 
 
 
 
 
 
 
 
81
  }
82
  $this->set_option( $option );
83
  }
76
  <?php
77
  if ( $form === true ) {
78
  $enctype = ( $contains_files ) ? ' enctype="multipart/form-data"' : '';
79
+
80
+ $network_admin = new Yoast_Network_Admin();
81
+ if ( $network_admin->meets_requirements() ) {
82
+ $action_url = network_admin_url( 'settings.php' );
83
+ $hidden_fields_cb = array( $network_admin, 'settings_fields' );
84
+ }
85
+ else {
86
+ $action_url = admin_url( 'options.php' );
87
+ $hidden_fields_cb = 'settings_fields';
88
+ }
89
+
90
+ echo '<form action="' . esc_url( $action_url ) . '" method="post" id="wpseo-conf"' . $enctype . ' accept-charset="' . esc_attr( get_bloginfo( 'charset' ) ) . '">';
91
+ call_user_func( $hidden_fields_cb, $option_long_name );
92
  }
93
  $this->set_option( $option );
94
  }
admin/class-yoast-network-admin.php ADDED
@@ -0,0 +1,299 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WPSEO plugin file.
4
+ *
5
+ * @package WPSEO\Internals
6
+ */
7
+
8
+ /**
9
+ * Multisite utility class for network admin functionality.
10
+ */
11
+ class Yoast_Network_Admin implements WPSEO_WordPress_Integration, WPSEO_WordPress_AJAX_Integration {
12
+
13
+ /**
14
+ * Action identifier for updating plugin network options.
15
+ */
16
+ const UPDATE_OPTIONS_ACTION = 'yoast_handle_network_options';
17
+
18
+ /**
19
+ * Action identifier for restoring a site.
20
+ */
21
+ const RESTORE_SITE_ACTION = 'yoast_restore_site';
22
+
23
+ /**
24
+ * Gets the available sites as choices, e.g. for a dropdown.
25
+ *
26
+ * @param bool $include_empty Optional. Whether to include an initial placeholder choice.
27
+ *
28
+ * @return array Choices as $site_id => $site_label pairs.
29
+ */
30
+ public function get_site_choices( $include_empty = false ) {
31
+ $choices = array();
32
+
33
+ if ( $include_empty ) {
34
+ $choices['-'] = __( 'None', 'wordpress-seo' );
35
+ }
36
+
37
+ $sites = get_sites( array( 'deleted' => 0 ) );
38
+ foreach ( $sites as $site ) {
39
+ $choices[ $site->blog_id ] = $site->blog_id . ': ' . $site->domain . $site->path;
40
+
41
+ $site_states = $this->get_site_states( $site );
42
+ if ( ! empty( $site_states ) ) {
43
+ $choices[ $site->blog_id ] .= ' [' . implode( ', ', $site_states ) . ']';
44
+ }
45
+ }
46
+
47
+ return $choices;
48
+ }
49
+
50
+ /**
51
+ * Gets the states of a site.
52
+ *
53
+ * @param WP_Site $site Site object.
54
+ *
55
+ * @return array Array of $state_slug => $state_label pairs.
56
+ */
57
+ public function get_site_states( $site ) {
58
+ $available_states = array(
59
+ 'public' => __( 'public', 'wordpress-seo' ),
60
+ 'archived' => __( 'archived', 'wordpress-seo' ),
61
+ 'mature' => __( 'mature', 'wordpress-seo' ),
62
+ 'spam' => __( 'spam', 'wordpress-seo' ),
63
+ 'deleted' => __( 'deleted', 'wordpress-seo' ),
64
+ );
65
+
66
+ $site_states = array();
67
+ foreach ( $available_states as $state_slug => $state_label ) {
68
+ if ( $site->$state_slug === '1' ) {
69
+ $site_states[ $state_slug ] = $state_label;
70
+ }
71
+ }
72
+
73
+ return $site_states;
74
+ }
75
+
76
+ /**
77
+ * Handles a request to update plugin network options.
78
+ *
79
+ * This method works similar to how option updates are handled in `wp-admin/options.php` and
80
+ * `wp-admin/network/settings.php`.
81
+ *
82
+ * @return void
83
+ */
84
+ public function handle_update_options_request() {
85
+ $option_group = filter_input( INPUT_POST, 'network_option_group', FILTER_SANITIZE_STRING );
86
+
87
+ $this->verify_request( "{$option_group}-network-options" );
88
+
89
+ $whitelist_options = Yoast_Network_Settings_API::get()->get_whitelist_options( $option_group );
90
+
91
+ if ( empty( $whitelist_options ) ) {
92
+ add_settings_error( $option_group, 'settings_updated', __( 'You are not allowed to modify unregistered network settings.', 'wordpress-seo' ), 'error' );
93
+
94
+ $this->terminate_request();
95
+ return;
96
+ }
97
+
98
+ foreach ( $whitelist_options as $option_name ) {
99
+ $value = null;
100
+ if ( isset( $_POST[ $option_name ] ) ) { // WPCS: CSRF ok.
101
+ $value = wp_unslash( $_POST[ $option_name ] ); // WPCS: CSRF ok.
102
+ }
103
+
104
+ WPSEO_Options::update_site_option( $option_name, $value );
105
+ }
106
+
107
+ $settings_errors = get_settings_errors();
108
+ if ( empty( $settings_errors ) ) {
109
+ add_settings_error( $option_group, 'settings_updated', __( 'Settings Updated.', 'wordpress-seo' ), 'updated' );
110
+ }
111
+
112
+ $this->terminate_request();
113
+ }
114
+
115
+ /**
116
+ * Handles a request to restore a site's default settings.
117
+ *
118
+ * @return void
119
+ */
120
+ public function handle_restore_site_request() {
121
+ $this->verify_request( 'wpseo-network-restore', 'restore_site_nonce' );
122
+
123
+ $option_group = 'wpseo_ms';
124
+
125
+ $site_id = ! empty( $_POST[ $option_group ]['site_id'] ) ? (int) $_POST[ $option_group ]['site_id'] : 0; // WPCS: CSRF ok.
126
+ if ( ! $site_id ) {
127
+ add_settings_error( $option_group, 'settings_updated', __( 'No site has been selected to restore.', 'wordpress-seo' ), 'error' );
128
+
129
+ $this->terminate_request();
130
+ return;
131
+ }
132
+
133
+ $site = get_site( $site_id );
134
+ if ( ! $site ) {
135
+ /* translators: %s expands to the ID of a site within a multisite network. */
136
+ add_settings_error( $option_group, 'settings_updated', sprintf( __( 'Site with ID %d not found.', 'wordpress-seo' ), $site_id ), 'error' );
137
+ }
138
+ else {
139
+ WPSEO_Options::reset_ms_blog( $site_id );
140
+
141
+ /* translators: %s expands to the name of a site within a multisite network. */
142
+ add_settings_error( $option_group, 'settings_updated', sprintf( __( '%s restored to default SEO settings.', 'wordpress-seo' ), esc_html( $site->blogname ) ), 'updated' );
143
+ }
144
+
145
+ $this->terminate_request();
146
+ }
147
+
148
+ /**
149
+ * Outputs nonce, action and option group fields for a network settings page in the plugin.
150
+ *
151
+ * @param string $option_group Option group name for the current page.
152
+ *
153
+ * @return void
154
+ */
155
+ public function settings_fields( $option_group ) {
156
+ ?>
157
+ <input type="hidden" name="network_option_group" value="<?php echo esc_attr( $option_group ); ?>" />
158
+ <input type="hidden" name="action" value="<?php echo esc_attr( self::UPDATE_OPTIONS_ACTION ); ?>" />
159
+ <?php
160
+ wp_nonce_field( "$option_group-network-options" );
161
+ }
162
+
163
+ /**
164
+ * Enqueues network admin assets.
165
+ *
166
+ * @return void
167
+ */
168
+ public function enqueue_assets() {
169
+ $asset_manager = new WPSEO_Admin_Asset_Manager();
170
+ $asset_manager->enqueue_script( 'network-admin-script' );
171
+
172
+ wp_localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'network-admin-script', 'wpseoNetworkAdminGlobalL10n', array(
173
+
174
+ /* translators: %s: success message */
175
+ 'success_prefix' => __( 'Success: %s', 'wordpress-seo' ),
176
+
177
+ /* translators: %s: error message */
178
+ 'error_prefix' => __( 'Error: %s', 'wordpress-seo' ),
179
+ ) );
180
+ }
181
+
182
+ /**
183
+ * Hooks in the necessary actions and filters.
184
+ *
185
+ * @return void
186
+ */
187
+ public function register_hooks() {
188
+
189
+ if ( ! $this->meets_requirements() ) {
190
+ return;
191
+ }
192
+
193
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
194
+
195
+ add_action( 'admin_action_' . self::UPDATE_OPTIONS_ACTION, array( $this, 'handle_update_options_request' ) );
196
+ add_action( 'admin_action_' . self::RESTORE_SITE_ACTION, array( $this, 'handle_restore_site_request' ) );
197
+ }
198
+
199
+ /**
200
+ * Hooks in the necessary AJAX actions.
201
+ *
202
+ * @return void
203
+ */
204
+ public function register_ajax_hooks() {
205
+ add_action( 'wp_ajax_' . self::UPDATE_OPTIONS_ACTION, array( $this, 'handle_update_options_request' ) );
206
+ add_action( 'wp_ajax_' . self::RESTORE_SITE_ACTION, array( $this, 'handle_restore_site_request' ) );
207
+ }
208
+
209
+ /**
210
+ * Checks whether the requirements to use this class are met.
211
+ *
212
+ * @return bool True if requirements are met, false otherwise.
213
+ */
214
+ public function meets_requirements() {
215
+ return is_multisite() && is_network_admin();
216
+ }
217
+
218
+ /**
219
+ * Verifies that the current request is valid.
220
+ *
221
+ * @param string $action Nonce action.
222
+ * @param string $query_arg Optional. Nonce query argument. Default '_wpnonce'.
223
+ *
224
+ * @return void
225
+ */
226
+ public function verify_request( $action, $query_arg = '_wpnonce' ) {
227
+ $has_access = current_user_can( 'wpseo_manage_network_options' );
228
+
229
+ if ( wp_doing_ajax() ) {
230
+ check_ajax_referer( $action, $query_arg );
231
+
232
+ if ( ! $has_access ) {
233
+ wp_die( -1, 403 );
234
+ }
235
+ return;
236
+ }
237
+
238
+ check_admin_referer( $action, $query_arg );
239
+
240
+ if ( ! $has_access ) {
241
+ wp_die( __( 'You are not allowed to perform this action.', 'wordpress-seo' ) );
242
+ }
243
+ }
244
+
245
+ /**
246
+ * Terminates the current request by either redirecting back or sending an AJAX response.
247
+ *
248
+ * @return void
249
+ */
250
+ public function terminate_request() {
251
+ if ( wp_doing_ajax() ) {
252
+ $settings_errors = get_settings_errors();
253
+
254
+ if ( ! empty( $settings_errors ) && $settings_errors[0]['type'] === 'updated' ) {
255
+ wp_send_json_success( $settings_errors, 200 );
256
+ }
257
+
258
+ wp_send_json_error( $settings_errors, 400 );
259
+ }
260
+
261
+ $this->persist_settings_errors();
262
+ $this->redirect_back( array( 'settings-updated' => 'true' ) );
263
+ }
264
+
265
+ /**
266
+ * Persists settings errors.
267
+ *
268
+ * Settings errors are stored in a transient for 30 seconds so that this transient
269
+ * can be retrieved on the next page load.
270
+ *
271
+ * @return void
272
+ */
273
+ protected function persist_settings_errors() {
274
+ /*
275
+ * A regular transient is used here, since it is automatically cleared right after the redirect.
276
+ * A network transient would be cleaner, but would require a lot of copied code from core for
277
+ * just a minor adjustment when displaying settings errors.
278
+ */
279
+ set_transient( 'settings_errors', get_settings_errors(), 30 );
280
+ }
281
+
282
+ /**
283
+ * Redirects back to the referer URL, with optional query arguments.
284
+ *
285
+ * @param array $query_args Optional. Query arguments to add to the redirect URL. Default none.
286
+ *
287
+ * @return void
288
+ */
289
+ protected function redirect_back( $query_args = array() ) {
290
+ $sendback = wp_get_referer();
291
+
292
+ if ( ! empty( $query_args ) ) {
293
+ $sendback = add_query_arg( $query_args, $sendback );
294
+ }
295
+
296
+ wp_safe_redirect( $sendback );
297
+ exit;
298
+ }
299
+ }
admin/class-yoast-network-settings-api.php ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WPSEO plugin file.
4
+ *
5
+ * @package WPSEO\Admin\Network
6
+ */
7
+
8
+ /**
9
+ * Implements a network settings API for the plugin's multisite settings.
10
+ */
11
+ class Yoast_Network_Settings_API {
12
+
13
+ /**
14
+ * @var array Registered network settings.
15
+ */
16
+ private $registered_settings = array();
17
+
18
+ /**
19
+ * @var array Options whitelist, keyed by option group.
20
+ */
21
+ private $whitelist_options = array();
22
+
23
+ /**
24
+ * @var Yoast_Network_Settings_API The singleton instance of this class.
25
+ */
26
+ private static $instance = null;
27
+
28
+ /**
29
+ * Registers a network setting and its data.
30
+ *
31
+ * @param string $option_group The group the network option is part of.
32
+ * @param string $option_name The name of the network option to sanitize and save.
33
+ * @param array $args {
34
+ * Optional. Data used to describe the network setting when registered.
35
+ *
36
+ * @type callable $sanitize_callback A callback function that sanitizes the network option's value.
37
+ * @type mixed $default Default value when calling `get_network_option()`.
38
+ * }
39
+ *
40
+ * @return void
41
+ */
42
+ public function register_setting( $option_group, $option_name, $args = array() ) {
43
+
44
+ $args = wp_parse_args( $args, array(
45
+ 'group' => $option_group,
46
+ 'sanitize_callback' => null,
47
+ ) );
48
+
49
+ if ( ! isset( $this->whitelist_options[ $option_group ] ) ) {
50
+ $this->whitelist_options[ $option_group ] = array();
51
+ }
52
+
53
+ $this->whitelist_options[ $option_group ][] = $option_name;
54
+
55
+ if ( ! empty( $args['sanitize_callback'] ) ) {
56
+ add_filter( "sanitize_option_{$option_name}", array( $this, 'filter_sanitize_option' ), 10, 2 );
57
+ }
58
+
59
+ if ( array_key_exists( 'default', $args ) ) {
60
+ add_filter( "default_site_option_{$option_name}", array( $this, 'filter_default_option' ), 10, 2 );
61
+ }
62
+
63
+ $this->registered_settings[ $option_name ] = $args;
64
+ }
65
+
66
+ /**
67
+ * Gets the registered settings and their data.
68
+ *
69
+ * @return array Array of $option_name => $data pairs.
70
+ */
71
+ public function get_registered_settings() {
72
+ return $this->registered_settings;
73
+ }
74
+
75
+ /**
76
+ * Gets the whitelisted options for a given option group.
77
+ *
78
+ * @param string $option_group Option group.
79
+ *
80
+ * @return array List of option names, or empty array if unknown option group.
81
+ */
82
+ public function get_whitelist_options( $option_group ) {
83
+ if ( ! isset( $this->whitelist_options[ $option_group ] ) ) {
84
+ return array();
85
+ }
86
+
87
+ return $this->whitelist_options[ $option_group ];
88
+ }
89
+
90
+ /**
91
+ * Filters sanitization for a network option value.
92
+ *
93
+ * This method is added as a filter to `sanitize_option_{$option}` for network options that are
94
+ * registered with a sanitize callback.
95
+ *
96
+ * @param string $value The sanitized option value.
97
+ * @param string $option The option name.
98
+ *
99
+ * @return string The filtered sanitized option value.
100
+ */
101
+ public function filter_sanitize_option( $value, $option ) {
102
+
103
+ if ( empty( $this->registered_settings[ $option ] ) ) {
104
+ return $value;
105
+ }
106
+
107
+ return call_user_func( $this->registered_settings[ $option ]['sanitize_callback'], $value );
108
+ }
109
+
110
+ /**
111
+ * Filters the default value for a network option.
112
+ *
113
+ * This function is added as a filter to `default_site_option_{$option}` for network options that
114
+ * are registered with a default.
115
+ *
116
+ * @param mixed $default Existing default value to return.
117
+ * @param string $option The option name.
118
+ *
119
+ * @return mixed The filtered default value.
120
+ */
121
+ public function filter_default_option( $default, $option ) {
122
+
123
+ // If a default value was manually passed to the function, allow it to override.
124
+ if ( $default !== false ) {
125
+ return $default;
126
+ }
127
+
128
+ if ( empty( $this->registered_settings[ $option ] ) ) {
129
+ return $default;
130
+ }
131
+
132
+ return $this->registered_settings[ $option ]['default'];
133
+ }
134
+
135
+ /**
136
+ * Checks whether the requirements to use this class are met.
137
+ *
138
+ * @return bool True if requirements are met, false otherwise.
139
+ */
140
+ public function meets_requirements() {
141
+ return is_multisite();
142
+ }
143
+
144
+ /**
145
+ * Gets the singleton instance of this class.
146
+ *
147
+ * @return Yoast_Network_Settings_API The singleton instance.
148
+ */
149
+ public static function get() {
150
+
151
+ if ( self::$instance === null ) {
152
+ self::$instance = new self();
153
+ }
154
+
155
+ return self::$instance;
156
+ }
157
+ }
admin/formatter/class-metabox-formatter.php CHANGED
@@ -45,6 +45,9 @@ class WPSEO_Metabox_Formatter {
45
  $analysis_seo = new WPSEO_Metabox_Analysis_SEO();
46
  $analysis_readability = new WPSEO_Metabox_Analysis_Readability();
47
 
 
 
 
48
  return array(
49
  'language' => WPSEO_Language_Utils::get_site_language_name(),
50
  'settings_link' => $this->get_settings_link(),
@@ -53,7 +56,6 @@ class WPSEO_Metabox_Formatter {
53
  'base_url' => '',
54
  'contentTab' => __( 'Readability', 'wordpress-seo' ),
55
  'keywordTab' => __( 'Keyword:', 'wordpress-seo' ),
56
- 'enterFocusKeyword' => __( 'Enter your focus keyword', 'wordpress-seo' ),
57
  'removeKeyword' => __( 'Remove keyword', 'wordpress-seo' ),
58
  'contentLocale' => get_locale(),
59
  'userLocale' => WPSEO_Utils::get_user_locale(),
@@ -63,9 +65,10 @@ class WPSEO_Metabox_Formatter {
63
  'metadesc_template' => '',
64
  'contentAnalysisActive' => $analysis_readability->is_enabled() ? 1 : 0,
65
  'keywordAnalysisActive' => $analysis_seo->is_enabled() ? 1 : 0,
 
66
  'intl' => $this->get_content_analysis_component_translations(),
67
  'isRtl' => is_rtl(),
68
- 'gutenbergSidebar' => defined( 'YOAST_FEATURE_GUTENBERG_SIDEBAR' ) && YOAST_FEATURE_GUTENBERG_SIDEBAR,
69
 
70
  /**
71
  * Filter to determine if the markers should be enabled or not.
@@ -172,6 +175,37 @@ class WPSEO_Metabox_Formatter {
172
  );
173
  }
174
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
  /**
176
  * Returns Jed compatible YoastSEO.js translations.
177
  *
45
  $analysis_seo = new WPSEO_Metabox_Analysis_SEO();
46
  $analysis_readability = new WPSEO_Metabox_Analysis_Readability();
47
 
48
+ $premium_benefits = new WPSEO_Premium_Benefits_List();
49
+ $premium_benefits->enqueue_translations();
50
+
51
  return array(
52
  'language' => WPSEO_Language_Utils::get_site_language_name(),
53
  'settings_link' => $this->get_settings_link(),
56
  'base_url' => '',
57
  'contentTab' => __( 'Readability', 'wordpress-seo' ),
58
  'keywordTab' => __( 'Keyword:', 'wordpress-seo' ),
 
59
  'removeKeyword' => __( 'Remove keyword', 'wordpress-seo' ),
60
  'contentLocale' => get_locale(),
61
  'userLocale' => WPSEO_Utils::get_user_locale(),
65
  'metadesc_template' => '',
66
  'contentAnalysisActive' => $analysis_readability->is_enabled() ? 1 : 0,
67
  'keywordAnalysisActive' => $analysis_seo->is_enabled() ? 1 : 0,
68
+ 'cornerstoneActive' => WPSEO_Options::get( 'enable_cornerstone_content', false ) ? 1 : 0,
69
  'intl' => $this->get_content_analysis_component_translations(),
70
  'isRtl' => is_rtl(),
71
+ 'addKeywordUpsell' => $this->get_add_keyword_upsell_translations(),
72
 
73
  /**
74
  * Filter to determine if the markers should be enabled or not.
175
  );
176
  }
177
 
178
+ /**
179
+ * Returns the translations for the Add Keyword modal.
180
+ *
181
+ * These strings are not escaped because they're meant to be used with React
182
+ * which already takes care of that. If used in PHP, they should be escaped.
183
+ *
184
+ * @return array Translated text strings for the Add Keyword modal.
185
+ */
186
+ public function get_add_keyword_upsell_translations() {
187
+ return array(
188
+ 'title' => __( 'Would you like to add more than one keyword?', 'wordpress-seo' ),
189
+ 'intro' => sprintf(
190
+ /* translators: %1$s expands to a 'Yoast SEO Premium' text linked to the yoast.com website. */
191
+ __( 'Great news: you can, with %1$s!', 'wordpress-seo' ),
192
+ '{{link}}Yoast SEO Premium{{/link}}'
193
+ ),
194
+ 'link' => WPSEO_Shortlinker::get( 'https://yoa.st/pe-premium-page' ),
195
+ 'other' => sprintf(
196
+ /* translators: %s expands to 'Yoast SEO Premium'. */
197
+ __( 'Other benefits of %s for you:', 'wordpress-seo' ), 'Yoast SEO Premium'
198
+ ),
199
+ 'buylink' => WPSEO_Shortlinker::get( 'https://yoa.st/add-keywords-popup' ),
200
+ 'buy' => sprintf(
201
+ /* translators: %s expands to 'Yoast SEO Premium'. */
202
+ __( 'Get %s now!', 'wordpress-seo' ), 'Yoast SEO Premium'
203
+ ),
204
+ 'small' => __( '1 year free updates and upgrades included!', 'wordpress-seo' ),
205
+ 'a11yNotice.opensInNewTab' => __( '(Opens in a new browser tab)', 'wordpress-seo' ),
206
+ );
207
+ }
208
+
209
  /**
210
  * Returns Jed compatible YoastSEO.js translations.
211
  *
admin/menu/class-admin-menu.php CHANGED
@@ -8,18 +8,7 @@
8
  /**
9
  * Registers the admin menu on the left of the admin area.
10
  */
11
- class WPSEO_Admin_Menu implements WPSEO_WordPress_Integration {
12
- /** @var WPSEO_Menu Menu */
13
- protected $menu;
14
-
15
- /**
16
- * Constructs the Admin Menu.
17
- *
18
- * @param WPSEO_Menu $menu Menu to use.
19
- */
20
- public function __construct( WPSEO_Menu $menu ) {
21
- $this->menu = $menu;
22
- }
23
 
24
  /**
25
  * Registers all hooks to WordPress.
@@ -35,7 +24,7 @@ class WPSEO_Admin_Menu implements WPSEO_WordPress_Integration {
35
  * Registers the menu item submenus.
36
  */
37
  public function register_settings_page() {
38
- $can_manage_options = WPSEO_Capability_Utils::current_user_can( $this->get_manage_capability() );
39
 
40
  if ( $can_manage_options ) {
41
  /*
@@ -48,13 +37,14 @@ class WPSEO_Admin_Menu implements WPSEO_WordPress_Integration {
48
  'Yoast SEO: ' . __( 'Dashboard', 'wordpress-seo' ),
49
  __( 'SEO', 'wordpress-seo' ) . ' ' . $this->get_notification_counter(),
50
  $this->get_manage_capability(),
51