All in One SEO Pack - Version 4.0.7

Version Description

This update adds major improvements and bugfixes.

Download this release

Release Info

Developer benjaminprojas
Plugin Icon 128x128 All in One SEO Pack
Version 4.0.7
Comparing to
See all releases

Code changes from version 3.7.1 to 4.0.7

Files changed (113) hide show
  1. admin/aioseop_module_class.php +0 -3365
  2. admin/aioseop_module_manager.php +0 -285
  3. admin/class-aioseop-helper.php +0 -1293
  4. admin/class-aioseop-notices.php +0 -1229
  5. admin/class-aioseop-usage.php +0 -215
  6. admin/display/aioseop-welcome.php +0 -146
  7. admin/display/dashboard_widget.php +0 -149
  8. admin/display/general-metaboxes.php +0 -219
  9. admin/display/index.php +0 -4
  10. admin/display/menu.php +0 -91
  11. admin/display/notice-aioseop.php +0 -80
  12. admin/display/notice-default.php +0 -80
  13. admin/display/notice-remote.php +0 -51
  14. admin/display/notices/blog-visibility-notice.php +0 -48
  15. admin/display/notices/check-php-version-notice.php +0 -37
  16. admin/display/notices/conflicting-plugin-notice.php +0 -45
  17. admin/display/notices/deprecated-additional-headers-settings-notice.php +0 -59
  18. admin/display/notices/deprecated-unprotect-post-meta-setting-notice.php +0 -38
  19. admin/display/notices/index.php +0 -4
  20. admin/display/notices/local-business-markup-notice.php +0 -34
  21. admin/display/notices/local-business-notice.php +0 -33
  22. admin/display/notices/local-business-organization-notice.php +0 -34
  23. admin/display/notices/news-sitemap-notice.php +0 -40
  24. admin/display/notices/review-plugin-cta-notice.php +0 -116
  25. admin/display/notices/sitemap-indexes-notice.php +0 -44
  26. admin/display/notices/wc-detected-notice.php +0 -41
  27. admin/display/welcome-content.php +0 -137
  28. admin/index.php +0 -4
  29. admin/meta_import.php +0 -635
  30. aioseop-init.php +0 -9
  31. aioseop_class.php +0 -5790
  32. all_in_one_seo_pack.php +66 -50
  33. app/AIOSEO.php +341 -0
  34. app/Common/Admin/Admin.php +1080 -0
  35. app/Common/Admin/AutoUpdates.php +226 -0
  36. app/Common/Admin/ConflictingPlugins.php +104 -0
  37. app/Common/Admin/Dashboard.php +123 -0
  38. app/Common/Admin/LocalBusinessSeo.php +128 -0
  39. app/Common/Admin/Notices/Import.php +63 -0
  40. app/Common/Admin/Notices/Migration.php +50 -0
  41. app/Common/Admin/Notices/Notices.php +579 -0
  42. app/Common/Admin/Notices/Review.php +203 -0
  43. app/Common/Admin/PostSettings.php +171 -0
  44. app/Common/Admin/SetupWizard.php +228 -0
  45. app/Common/Admin/SiteHealth.php +560 -0
  46. app/Common/Admin/Usage.php +217 -0
  47. app/Common/Api/Analyze.php +111 -0
  48. app/Common/Api/Api.php +217 -0
  49. app/Common/Api/Connect.php +96 -0
  50. app/Common/Api/Migration.php +61 -0
  51. app/Common/Api/Notifications.php +206 -0
  52. app/Common/Api/Ping.php +23 -0
  53. app/Common/Api/Plugins.php +115 -0
  54. app/Common/Api/PostsTerms.php +320 -0
  55. app/Common/Api/Settings.php +322 -0
  56. app/Common/Api/Sitemaps.php +113 -0
  57. app/Common/Api/Tags.php +23 -0
  58. app/Common/Api/Tools.php +251 -0
  59. app/Common/Api/Wizard.php +360 -0
  60. {public/js/vendor → app/Common/Assets/js}/autotrack.js +0 -0
  61. app/Common/Help/Help.php +69 -0
  62. app/Common/ImportExport/Helpers.php +81 -0
  63. app/Common/ImportExport/ImportExport.php +358 -0
  64. app/Common/ImportExport/Importer.php +40 -0
  65. app/Common/ImportExport/RankMath/GeneralSettings.php +85 -0
  66. app/Common/ImportExport/RankMath/Helpers.php +88 -0
  67. app/Common/ImportExport/RankMath/PostMeta.php +200 -0
  68. app/Common/ImportExport/RankMath/RankMath.php +65 -0
  69. app/Common/ImportExport/RankMath/Sitemap.php +185 -0
  70. app/Common/ImportExport/RankMath/TitleMeta.php +438 -0
  71. app/Common/ImportExport/SearchAppearance.php +56 -0
  72. app/Common/ImportExport/SeoPress/Helpers.php +33 -0
  73. app/Common/ImportExport/SeoPress/SearchAppearance.php +42 -0
  74. app/Common/ImportExport/SeoPress/SeoPress.php +29 -0
  75. app/Common/ImportExport/YoastSeo/GeneralSettings.php +53 -0
  76. app/Common/ImportExport/YoastSeo/Helpers.php +101 -0
  77. app/Common/ImportExport/YoastSeo/PostMeta.php +230 -0
  78. app/Common/ImportExport/YoastSeo/SearchAppearance.php +260 -0
  79. app/Common/ImportExport/YoastSeo/SocialMeta.php +112 -0
  80. app/Common/ImportExport/YoastSeo/YoastSeo.php +70 -0
  81. app/Common/Main/Activate.php +39 -0
  82. app/Common/Main/Filters.php +116 -0
  83. app/Common/Main/GoogleAnalytics.php +233 -0
  84. app/Common/Main/Head.php +162 -0
  85. app/Common/Main/Main.php +91 -0
  86. app/Common/Main/Media.php +56 -0
  87. app/Common/Main/Updates.php +273 -0
  88. app/Common/Meta/Amp.php +57 -0
  89. app/Common/Meta/Description.php +216 -0
  90. app/Common/Meta/Included.php +103 -0
  91. app/Common/Meta/Keywords.php +229 -0
  92. app/Common/Meta/Links.php +157 -0
  93. app/Common/Meta/Meta.php +44 -0
  94. app/Common/Meta/MetaData.php +119 -0
  95. app/Common/Meta/Robots.php +318 -0
  96. app/Common/Meta/SiteVerification.php +43 -0
  97. app/Common/Meta/Title.php +192 -0
  98. app/Common/Migration/BadRobots.php +59 -0
  99. app/Common/Migration/GeneralSettings.php +892 -0
  100. app/Common/Migration/Helpers.php +250 -0
  101. app/Common/Migration/Meta.php +396 -0
  102. app/Common/Migration/Migration.php +190 -0
  103. app/Common/Migration/OldOptions.php +265 -0
  104. app/Common/Migration/RobotsTxt.php +53 -0
  105. app/Common/Migration/Sitemap.php +397 -0
  106. app/Common/Migration/SocialMeta.php +485 -0
  107. app/Common/Migration/Wpml.php +124 -0
  108. app/Common/Models/Model.php +442 -0
  109. app/Common/Models/Notification.php +224 -0
  110. app/Common/Models/Post.php +319 -0
  111. app/Common/Rss.php +60 -0
  112. app/Common/Schema/Breadcrumb.php +312 -0
  113. app/Common/Schema/Context.php +64 -0
admin/aioseop_module_class.php DELETED
@@ -1,3365 +0,0 @@
1
- <?php
2
- /**
3
- * AIOSEOP Module Class
4
- *
5
- * @package All-in-One-SEO-Pack
6
- * @version 2.3.12.2
7
- */
8
-
9
- if ( ! class_exists( 'All_in_One_SEO_Pack_Module' ) ) {
10
-
11
- /**
12
- * The module base class; handles settings, options, menus, metaboxes, etc.
13
- */
14
- abstract class All_in_One_SEO_Pack_Module {
15
- /**
16
- * Instance
17
- *
18
- * @since ?
19
- *
20
- * @var null $instance
21
- */
22
- public static $instance = null;
23
-
24
- /**
25
- * Plugin Name
26
- *
27
- * @since ?
28
- *
29
- * @var string $plugin_name
30
- */
31
- protected $plugin_name;
32
-
33
- /**
34
- * Name
35
- *
36
- * @since ?
37
- *
38
- * @var string $name
39
- */
40
- protected $name;
41
-
42
- /**
43
- * Menu Name
44
- *
45
- * @since ?
46
- *
47
- * @var string $menu_name
48
- */
49
- protected $menu_name;
50
-
51
- /**
52
- * Module Prefix
53
- *
54
- * @since ?
55
- *
56
- * @var string $prefix
57
- */
58
- protected $prefix;
59
-
60
- /**
61
- * File
62
- *
63
- * @since ?
64
- *
65
- * @var string $file
66
- */
67
- protected $file;
68
-
69
- /**
70
- * Module Options
71
- *
72
- * @since ?
73
- *
74
- * @var array $options {
75
- * TODO Add details to show module database options. May need to use module classes instead.
76
- * }
77
- */
78
- protected $options;
79
-
80
- /**
81
- * Option Name
82
- *
83
- * @since ?
84
- *
85
- * @var string $option_name
86
- */
87
- protected $option_name;
88
-
89
- /**
90
- * Default Options
91
- *
92
- * @since ?
93
- *
94
- * @var array $default_options
95
- */
96
- protected $default_options;
97
-
98
- /**
99
- * Help Text
100
- *
101
- * @since ?
102
- * @deprecated
103
- *
104
- * @var array $help_text
105
- */
106
- protected $help_text = array();
107
-
108
- /**
109
- * Help Anchors
110
- *
111
- * @since ?
112
- * @deprecated
113
- *
114
- * @var array $help_anchors
115
- */
116
- protected $help_anchors = array();
117
-
118
- /**
119
- * Locations
120
- *
121
- * (Optional) Organize settings into settings pages with a menu items and/or metaboxes on post types edit screen.
122
- *
123
- * @since ?
124
- *
125
- * @var array $locations
126
- */
127
- protected $locations = null;
128
-
129
- /**
130
- * Layout
131
- *
132
- * (Optional) Organize settings on a settings page into multiple, separate metaboxes.
133
- *
134
- * @since ?
135
- *
136
- * @var array $layout
137
- */
138
- protected $layout = null;
139
-
140
- /**
141
- * Tabs
142
- *
143
- * (Optional) Organize layouts on a settings page into multiple.
144
- *
145
- * @since ?
146
- *
147
- * @var array $tabs
148
- */
149
- protected $tabs = null;
150
-
151
- /**
152
- * Current Tab
153
- *
154
- * @since ?
155
- *
156
- * @var string $current_tab
157
- */
158
- protected $current_tab = null;
159
-
160
- /**
161
- * Pagehook
162
- *
163
- * The current page hook.
164
- *
165
- * @since ?
166
- *
167
- * @var string $pagehook
168
- */
169
- protected $pagehook = null;
170
-
171
- /**
172
- * Store Option
173
- *
174
- * @since ?
175
- *
176
- * @var bool
177
- */
178
- protected $store_option = false;
179
-
180
- /**
181
- * Parent Option
182
- *
183
- * @since ?
184
- *
185
- * @var string $parent_option
186
- */
187
- protected $parent_option = 'aioseop_options';
188
-
189
- /**
190
- * Post Metaboxes
191
- *
192
- * @since ?
193
- *
194
- * @var array $post_metaboxes
195
- */
196
- protected $post_metaboxes = array();
197
-
198
- /**
199
- * Tabbed Metaboxes
200
- *
201
- * @since ?
202
- *
203
- * @var bool
204
- */
205
- protected $tabbed_metaboxes = true;
206
-
207
- /**
208
- * Credentials
209
- *
210
- * Used for WP Filesystem.
211
- *
212
- * @since ?
213
- *
214
- * @var bool
215
- */
216
- protected $credentials = false;
217
-
218
- /**
219
- * Script Data
220
- *
221
- * Used for passing data to JavaScript.
222
- *
223
- * @since ?
224
- *
225
- * @var array $script_data
226
- */
227
- protected $script_data = null;
228
-
229
- /**
230
- * Plugin Path
231
- *
232
- * @since ?
233
- *
234
- * @var array|null
235
- */
236
- protected $plugin_path = null;
237
-
238
- /**
239
- * Pointers
240
- *
241
- * @since ?
242
- *
243
- * @var array
244
- */
245
- protected $pointers = array();
246
-
247
- /**
248
- * Form
249
- *
250
- * @since ?
251
- *
252
- * @var string $form
253
- */
254
- protected $form = 'dofollow';
255
-
256
- /**
257
- * Handles calls to display_settings_page_{$location}, does error checking.
258
- *
259
- * @param $name
260
- * @param $arguments
261
- *
262
- * @throws Exception
263
- * @throws BadMethodCallException
264
- */
265
- function __call( $name, $arguments ) {
266
- if ( AIOSEOP_PHP_Functions::strpos( $name, 'display_settings_page_' ) === 0 ) {
267
- return $this->display_settings_page( AIOSEOP_PHP_Functions::substr( $name, 22 ) );
268
- }
269
- $error = sprintf( __( "Method %s doesn't exist", 'all-in-one-seo-pack' ), $name );
270
- if ( class_exists( 'BadMethodCallException' ) ) {
271
- throw new BadMethodCallException( $error );
272
- }
273
- throw new Exception( $error );
274
- }
275
-
276
- /**
277
- * All_in_One_SEO_Pack_Module constructor.
278
- */
279
- function __construct() {
280
- if ( empty( $this->file ) ) {
281
- $this->file = __FILE__;
282
- }
283
- $this->plugin_name = AIOSEOP_PLUGIN_NAME;
284
- $this->plugin_path = array();
285
- // $this->plugin_path['dir'] = plugin_dir_path( $this->file );
286
- $this->plugin_path['basename'] = plugin_basename( $this->file );
287
- $this->plugin_path['dirname'] = dirname( $this->plugin_path['basename'] );
288
- $this->plugin_path['url'] = plugin_dir_url( $this->file );
289
- $this->plugin_path['images_url'] = $this->plugin_path['url'] . 'images';
290
- $this->script_data['plugin_path'] = $this->plugin_path;
291
- }
292
-
293
- /**
294
- * Get options for module, stored individually or together.
295
- */
296
- function get_class_option() {
297
- $option_name = $this->get_option_name();
298
- if ( $this->store_option || $option_name == $this->parent_option ) {
299
- return get_option( $option_name );
300
- } else {
301
- $option = get_option( $this->parent_option );
302
- if ( isset( $option['modules'] ) && isset( $option['modules'][ $option_name ] ) ) {
303
- return $option['modules'][ $option_name ];
304
- }
305
- }
306
-
307
- return false;
308
- }
309
-
310
- /**
311
- * Update options for module, stored individually or together.
312
- *
313
- * @param $option_data
314
- * @param bool $option_name
315
- *
316
- * @return bool
317
- */
318
- function update_class_option( $option_data, $option_name = false ) {
319
- if ( false == $option_name ) {
320
- $option_name = $this->get_option_name();
321
- }
322
-
323
- // Delete rewrite rules when the XML Sitemap module is deactivated.
324
- if ( 'aiosp_feature_manager_options' === $option_name && 'on' !== $option_data['aiosp_feature_manager_enable_sitemap'] ) {
325
- aioseop_delete_rewrite_rules();
326
- }
327
-
328
- if ( $this->store_option || $option_name == $this->parent_option ) {
329
- return update_option( $option_name, $option_data );
330
- } else {
331
- $option = get_option( $this->parent_option );
332
- if ( ! isset( $option['modules'] ) ) {
333
- $option['modules'] = array();
334
- }
335
- $option['modules'][ $option_name ] = $option_data;
336
-
337
- return update_option( $this->parent_option, $option );
338
- }
339
- }
340
-
341
- /**
342
- * Delete options for module, stored individually or together.
343
- *
344
- * @param bool $delete
345
- *
346
- * @return bool
347
- */
348
- function delete_class_option( $delete = false ) {
349
- $option_name = $this->get_option_name();
350
- if ( $this->store_option || $delete ) {
351
- delete_option( $option_name );
352
- } else {
353
- $option = get_option( $this->parent_option );
354
- if ( isset( $option['modules'] ) && isset( $option['modules'][ $option_name ] ) ) {
355
- unset( $option['modules'][ $option_name ] );
356
-
357
- return update_option( $this->parent_option, $option );
358
- }
359
- }
360
-
361
- return false;
362
- }
363
-
364
- /**
365
- * Get the option name with prefix.
366
- */
367
- function get_option_name() {
368
- if ( ! isset( $this->option_name ) || empty( $this->option_name ) ) {
369
- $this->option_name = $this->prefix . 'options';
370
- }
371
-
372
- return $this->option_name;
373
- }
374
-
375
- /**
376
- * Convenience function to see if an option is set.
377
- *
378
- * @param string $option
379
- *
380
- * @param null $location
381
- *
382
- * @return bool
383
- */
384
- function option_isset( $option, $location = null ) {
385
- $prefix = $this->get_prefix( $location );
386
- $opt = $prefix . $option;
387
-
388
- return ( isset( $this->options[ $opt ] ) && $this->options[ $opt ] );
389
- }
390
-
391
- /**
392
- * Convert html string to php array - useful to get a serializable value.
393
- *
394
- * @param string $xmlstr
395
- *
396
- * @return array
397
- */
398
- function html_string_to_array( $htmlstr ) {
399
- if ( ! class_exists( 'DOMDocument' ) ) {
400
- return array();
401
- } else {
402
- $doc = new DOMDocument();
403
- $doc->loadXML( $htmlstr );
404
-
405
- // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
406
- return $this->domnode_to_array( $doc->documentElement );
407
- }
408
- }
409
-
410
- /**
411
- * DOM Node to Array
412
- *
413
- * @since ?
414
- *
415
- * @param DOMElement $node
416
- * @return array|string
417
- */
418
- function domnode_to_array( $node ) {
419
- // phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
420
- switch ( $node->nodeType ) {
421
- case XML_CDATA_SECTION_NODE:
422
- case XML_TEXT_NODE:
423
- return trim( $node->textContent );
424
- break;
425
- case XML_ELEMENT_NODE:
426
- $output = array();
427
- for ( $i = 0, $m = $node->childNodes->length; $i < $m; $i ++ ) {
428
- $child = $node->childNodes->item( $i );
429
- $v = $this->domnode_to_array( $child );
430
- if ( isset( $child->tagName ) ) {
431
- $t = $child->tagName;
432
- if ( ! isset( $output[ $t ] ) ) {
433
- $output[ $t ] = array();
434
- }
435
- if ( is_array( $output ) ) {
436
- $output[ $t ][] = $v;
437
- }
438
- } elseif ( $v || '0' === $v ) {
439
- $output = (string) $v;
440
- }
441
- }
442
- // Has attributes but isn't an array.
443
- if ( $node->attributes->length && ! is_array( $output ) ) {
444
- $output = array( '@content' => $output );
445
- } //Change output into an array.
446
- if ( is_array( $output ) ) {
447
- if ( $node->attributes->length ) {
448
- $a = array();
449
- foreach ( $node->attributes as $attr_name => $attr_node ) {
450
- $a[ $attr_name ] = (string) $attr_node->value;
451
- }
452
- $output['@attributes'] = $a;
453
- }
454
- foreach ( $output as $t => $v ) {
455
- if ( is_array( $v ) && 1 == count( $v ) && '@attributes' != $t ) {
456
- $output[ $t ] = $v[0];
457
- }
458
- }
459
- }
460
- default:
461
- return array();
462
- }
463
- // phpcs:enable
464
- if ( empty( $output ) ) {
465
- return '';
466
- }
467
-
468
- return $output;
469
- }
470
-
471
- /**
472
- * Apply Custom Fields
473
- *
474
- * Adds support for using %cf_(name of field)% for using
475
- * custom fields / Advanced Custom Fields in titles / descriptions etc. **
476
- *
477
- * @since ?
478
- *
479
- * @param $format
480
- * @return mixed
481
- */
482
- function apply_cf_fields( $format ) {
483
- return preg_replace_callback( '/%cf_([^%]*?)%/', array( $this, 'cf_field_replace' ), $format );
484
- }
485
-
486
- /**
487
- * (ACF) Custom Field Replace
488
- *
489
- * @since ?
490
- *
491
- * @param $matches
492
- * @return bool|mixed|string
493
- */
494
- function cf_field_replace( $matches ) {
495
- $result = '';
496
- if ( ! empty( $matches ) ) {
497
- if ( ! empty( $matches[1] ) ) {
498
- if ( function_exists( 'get_field' ) ) {
499
- $result = get_field( $matches[1] );
500
- }
501
- if ( empty( $result ) ) {
502
- global $post;
503
- if ( ! empty( $post ) ) {
504
- $result = get_post_meta( $post->ID, $matches[1], true );
505
- }
506
- }
507
- } else {
508
- $result = $matches[0];
509
- }
510
- }
511
- $result = strip_tags( $result );
512
-
513
- return $result;
514
- }
515
-
516
- /**
517
- * Returns child blogs of parent in a multisite.
518
- */
519
- function get_child_blogs() {
520
- global $wpdb, $blog_id;
521
- $site_id = $wpdb->siteid;
522
- if ( is_multisite() ) {
523
- if ( $site_id != $blog_id ) {
524
- return false;
525
- }
526
-
527
- // @codingStandardsIgnoreStart
528
- return $wpdb->get_col( $wpdb->prepare( "SELECT blog_id FROM {$wpdb->blogs} WHERE site_id = %d AND site_id != blog_id", $blog_id ) );
529
- // @codingStandardsIgnoreEnd
530
- }
531
-
532
- return false;
533
- }
534
-
535
- /**
536
- * Is AIOSEOP Active Blog
537
- *
538
- * Checks if the plugin is active on a given blog by blogid on a multisite.
539
- *
540
- * @since ?
541
- *
542
- * @param bool $bid
543
- * @return bool
544
- */
545
- function is_aioseop_active_on_blog( $bid = false ) {
546
- global $blog_id;
547
- if ( empty( $bid ) || ( $bid == $blog_id ) || ! is_multisite() ) {
548
- return true;
549
- }
550
- if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
551
- require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
552
- }
553
- if ( is_plugin_active_for_network( AIOSEOP_PLUGIN_BASENAME ) ) {
554
- return true;
555
- }
556
-
557
- return in_array( AIOSEOP_PLUGIN_BASENAME, (array) get_blog_option( $bid, 'active_plugins', array() ) );
558
- }
559
-
560
- /**
561
- * Quote List for Regex
562
- *
563
- * @since ?
564
- *
565
- * @param $list
566
- * @param string $quote
567
- * @return string
568
- */
569
- function quote_list_for_regex( $list, $quote = '/' ) {
570
- $regex = '';
571
- $cont = 0;
572
- foreach ( $list as $l ) {
573
- $trim_l = trim( $l );
574
- if ( ! empty( $trim_l ) ) {
575
- if ( $cont ) {
576
- $regex .= '|';
577
- }
578
- $cont = 1;
579
- $regex .= preg_quote( trim( $l ), $quote );
580
- }
581
- }
582
-
583
- return $regex;
584
- }
585
-
586
- /**
587
- * Is Good Bot
588
- *
589
- * @see Original code, thanks to Sean M. Brown.
590
- * @link http://smbrown.wordpress.com/2009/04/29/verify-googlebot-forward-reverse-dns/
591
- *
592
- * @return bool
593
- */
594
- function is_good_bot() {
595
- $botlist = array(
596
- 'Yahoo! Slurp' => 'crawl.yahoo.net',
597
- 'googlebot' => '.googlebot.com',
598
- 'msnbot' => 'search.msn.com',
599
- );
600
- $botlist = apply_filters( $this->prefix . 'botlist', $botlist );
601
- if ( ! empty( $botlist ) ) {
602
- if ( ! isset( $_SERVER['HTTP_USER_AGENT'] ) ) {
603
- return false;
604
- }
605
- $ua = $_SERVER['HTTP_USER_AGENT'];
606
- $uas = $this->quote_list_for_regex( $botlist );
607
- if ( preg_match( '/' . $uas . '/i', $ua ) ) {
608
- $ip = $_SERVER['REMOTE_ADDR'];
609
- $hostname = gethostbyaddr( $ip );
610
- $ip_by_hostname = gethostbyname( $hostname );
611
- if ( $ip_by_hostname == $ip ) {
612
- $hosts = array_values( $botlist );
613
- foreach ( $hosts as $k => $h ) {
614
- $hosts[ $k ] = preg_quote( $h ) . '$';
615
- }
616
- $hosts = join( '|', $hosts );
617
- if ( preg_match( '/' . $hosts . '/i', $hostname ) ) {
618
- return true;
619
- }
620
- }
621
- }
622
-
623
- return false;
624
- }
625
- }
626
-
627
- /**
628
- * Default Bad Bots
629
- *
630
- * @since ?
631
- *
632
- * @return array
633
- */
634
- function default_bad_bots() {
635
- $botlist = array(
636
- 'Abonti',
637
- 'aggregator',
638
- 'AhrefsBot',
639
- 'asterias',
640
- 'BDCbot',
641
- 'BLEXBot',
642
- 'BuiltBotTough',
643
- 'Bullseye',
644
- 'BunnySlippers',
645
- 'ca-crawler',
646
- 'CCBot',
647
- 'Cegbfeieh',
648
- 'CheeseBot',
649
- 'CherryPicker',
650
- 'CopyRightCheck',
651
- 'cosmos',
652
- 'Crescent',
653
- 'discobot',
654
- 'DittoSpyder',
655
- 'DotBot',
656
- 'Download Ninja',
657
- 'EasouSpider',
658
- 'EmailCollector',
659
- 'EmailSiphon',
660
- 'EmailWolf',
661
- 'EroCrawler',
662
- 'ExtractorPro',
663
- 'Fasterfox',
664
- 'FeedBooster',
665
- 'Foobot',
666
- 'Genieo',
667
- 'grub-client',
668
- 'Harvest',
669
- 'hloader',
670
- 'httplib',
671
- 'HTTrack',
672
- 'humanlinks',
673
- 'ieautodiscovery',
674
- 'InfoNaviRobot',
675
- 'IstellaBot',
676
- 'Java/1.',
677
- 'JennyBot',
678
- 'k2spider',
679
- 'Kenjin Spider',
680
- 'Keyword Density/0.9',
681
- 'larbin',
682
- 'LexiBot',
683
- 'libWeb',
684
- 'libwww',
685
- 'LinkextractorPro',
686
- 'linko',
687
- 'LinkScan/8.1a Unix',
688
- 'LinkWalker',
689
- 'LNSpiderguy',
690
- 'lwp-trivial',
691
- 'magpie',
692
- 'Mata Hari',
693
- 'MaxPointCrawler',
694
- 'MegaIndex',
695
- 'Microsoft URL Control',
696
- 'MIIxpc',
697
- 'Mippin',
698
- 'Missigua Locator',
699
- 'Mister PiX',
700
- 'MJ12bot',
701
- 'moget',
702
- 'MSIECrawler',
703
- 'NetAnts',
704
- 'NICErsPRO',
705
- 'Niki-Bot',
706
- 'NPBot',
707
- 'Nutch',
708
- 'Offline Explorer',
709
- 'Openfind',
710
- 'panscient.com',
711
- 'PHP/5.{',
712
- 'ProPowerBot/2.14',
713
- 'ProWebWalker',
714
- 'Python-urllib',
715
- 'QueryN Metasearch',
716
- 'RepoMonkey',
717
- 'SISTRIX',
718
- 'sitecheck.Internetseer.com',
719
- 'SiteSnagger',
720
- 'SnapPreviewBot',
721
- 'Sogou',
722
- 'SpankBot',
723
- 'spanner',
724
- 'spbot',
725
- 'Spinn3r',
726
- 'suzuran',
727
- 'Szukacz/1.4',
728
- 'Teleport',
729
- 'Telesoft',
730
- 'The Intraformant',
731
- 'TheNomad',
732
- 'TightTwatBot',
733
- 'Titan',
734
- 'toCrawl/UrlDispatcher',
735
- 'True_Robot',
736
- 'turingos',
737
- 'TurnitinBot',
738
- 'UbiCrawler',
739
- 'UnisterBot',
740
- 'URLy Warning',
741
- 'VCI',
742
- 'WBSearchBot',
743
- 'Web Downloader/6.9',
744
- 'Web Image Collector',
745
- 'WebAuto',
746
- 'WebBandit',
747
- 'WebCopier',
748
- 'WebEnhancer',
749
- 'WebmasterWorldForumBot',
750
- 'WebReaper',
751
- 'WebSauger',
752
- 'Website Quester',
753
- 'Webster Pro',
754
- 'WebStripper',
755
- 'WebZip',
756
- 'Wotbox',
757
- 'wsr-agent',
758
- 'WWW-Collector-E',
759
- 'Xenu',
760
- 'Zao',
761
- 'Zeus',
762
- 'ZyBORG',
763
- 'coccoc',
764
- 'Incutio',
765
- 'lmspider',
766
- 'memoryBot',
767
- 'serf',
768
- 'Unknown',
769
- 'uptime files',
770
- );
771
-
772
- return $botlist;
773
- }
774
-
775
- /**
776
- * Is Bad Bot
777
- *
778
- * @since ?
779
- *
780
- * @return bool
781
- */
782
- function is_bad_bot() {
783
- $botlist = $this->default_bad_bots();
784
- $botlist = apply_filters( $this->prefix . 'badbotlist', $botlist );
785
- if ( ! empty( $botlist ) ) {
786
- if ( ! isset( $_SERVER['HTTP_USER_AGENT'] ) ) {
787
- return false;
788
- }
789
- $ua = $_SERVER['HTTP_USER_AGENT'];
790
- $uas = $this->quote_list_for_regex( $botlist );
791
- if ( preg_match( '/' . $uas . '/i', $ua ) ) {
792
- return true;
793
- }
794
- }
795
-
796
- return false;
797
- }
798
-
799
- /**
800
- * Default Bad Referers
801
- *
802
- * @since ?
803
- *
804
- * @return array
805
- */
806
- function default_bad_referers() {
807
- $referlist = array(
808
- 'semalt.com',
809
- 'kambasoft.com',
810
- 'savetubevideo.com',
811
- 'buttons-for-website.com',
812
- 'sharebutton.net',
813
- 'soundfrost.org',
814
- 'srecorder.com',
815
- 'softomix.com',
816
- 'softomix.net',
817
- 'myprintscreen.com',
818
- 'joinandplay.me',
819
- 'fbfreegifts.com',
820
- 'openmediasoft.com',
821
- 'zazagames.org',
822
- 'extener.org',
823
- 'openfrost.com',
824
- 'openfrost.net',
825
- 'googlsucks.com',
826
- 'best-seo-offer.com',
827
- 'buttons-for-your-website.com',
828
- 'www.Get-Free-Traffic-Now.com',
829
- 'best-seo-solution.com',
830
- 'buy-cheap-online.info',
831
- 'site3.free-share-buttons.com',
832
- 'webmaster-traffic.com',
833
- );
834
-
835
- return $referlist;
836
- }
837
-
838
- /**
839
- * Is Bad Referer
840
- *
841
- * @since ?
842
- *
843
- * @return bool
844
- */
845
- function is_bad_referer() {
846
- $referlist = $this->default_bad_referers();
847
- $referlist = apply_filters( $this->prefix . 'badreferlist', $referlist );
848
-
849
- if ( ! empty( $referlist ) && ! empty( $_SERVER ) && ! empty( $_SERVER['HTTP_REFERER'] ) ) {
850
- $ref = $_SERVER['HTTP_REFERER'];
851
- $regex = $this->quote_list_for_regex( $referlist );
852
- if ( preg_match( '/' . $regex . '/i', $ref ) ) {
853
- return true;
854
- }
855
- }
856
-
857
- return false;
858
- }
859
-
860
- /**
861
- * Allow Bot
862
- *
863
- * @since ?
864
- *
865
- * @return mixed|void
866
- */
867
- function allow_bot() {
868
- $allow_bot = true;
869
- if ( ( ! $this->is_good_bot() ) && $this->is_bad_bot() && ! is_user_logged_in() ) {
870
- $allow_bot = false;
871
- }
872
-
873
- return apply_filters( $this->prefix . 'allow_bot', $allow_bot );
874
- }
875
-
876
- /**
877
- * Displays tabs for tabbed locations on a settings page.
878
- *
879
- * @since ?
880
- *
881
- * @param $location
882
- */
883
- function display_tabs( $location ) {
884
- if ( ( null != $location ) && isset( $locations[ $location ]['tabs'] ) ) {
885
- // TODO Fix undefined variable.
886
- $tabs = $locations['location']['tabs'];
887
- } else {
888
- $tabs = $this->tabs;
889
- }
890
- if ( ! empty( $tabs ) ) {
891
- ?>
892
- <div class="aioseop_tabs_div"><label class="aioseop_head_nav">
893
- <?php
894
- foreach ( $tabs as $k => $v ) {
895
- ?>
896
- <a class="aioseop_head_nav_tab aioseop_head_nav_
897
- <?php
898
- if ( $this->current_tab != $k ) {
899
- echo 'in';
900
- }
901
- ?>
902
- active"
903
- href="<?php echo esc_url( add_query_arg( 'tab', $k ) ); ?>"><?php echo $v['name']; ?></a>
904
- <?php
905
- }
906
- ?>
907
- </label></div>
908
- <?php
909
- }
910
- }
911
-
912
- /**
913
- * Get Object Labels
914
- *
915
- * @since ?
916
- *
917
- * @param $post_objs
918
- * @return array
919
- */
920
- function get_object_labels( $post_objs ) {
921
- $pt = array_keys( $post_objs );
922
- $post_types = array();
923
- foreach ( $pt as $p ) {
924
- if ( ! empty( $post_objs[ $p ]->label ) ) {
925
- $post_types[ $p ] = $post_objs[ $p ]->label;
926
- } else {
927
- $post_types[ $p ] = $p;
928
- }
929
- }
930
-
931
- return $post_types;
932
- }
933
-
934
- /**
935
- * Get Post Type Titles
936
- *
937
- * @since ?
938
- *
939
- * @param array $args
940
- * @return array
941
- */
942
- function get_post_type_titles( $args = array() ) {
943
- $object_labels = $this->get_object_labels( get_post_types( $args, 'objects' ) );
944
- if ( isset( $object_labels['attachment'] ) ) {
945
- $object_labels['attachment'] = __( 'Media / Attachments', 'all-in-one-seo-pack' );
946
- }
947
- return $object_labels;
948
- }
949
-
950
- /**
951
- * Get Taxonomy Titles
952
- *
953
- * @since ?
954
- *
955
- * @param array $args
956
- * @return array
957
- */
958
- function get_taxonomy_titles( $args = array() ) {
959
- return $this->get_object_labels( get_taxonomies( $args, 'objects' ) );
960
- }
961
-
962
- /**
963
- * Helper function for exporting settings on post data.
964
- *
965
- * @param string $prefix
966
- * @param array $query
967
- *
968
- * @return string
969
- */
970
- function post_data_export( $prefix = '_aioseop', $query = array( 'posts_per_page' => - 1 ) ) {
971
- $buf = '';
972
- $posts_query = new WP_Query( $query );
973
- while ( $posts_query->have_posts() ) {
974
- $posts_query->the_post();
975
- global $post;
976
- $guid = $post->guid;
977
- $type = $post->post_type;
978
- $title = $post->post_title;
979
- $date = $post->post_date;
980
- $data = '';
981
- $post_custom_fields = get_post_custom( $post->ID );
982
- $has_data = null;
983
-
984
- if ( is_array( $post_custom_fields ) ) {
985
- foreach ( $post_custom_fields as $field_name => $field ) {
986
- if ( ( AIOSEOP_PHP_Functions::strpos( $field_name, $prefix ) === 0 ) && $field[0] ) {
987
- $has_data = true;
988
- $data .= $field_name . " = '" . $field[0] . "'\n";
989
- }
990
- }
991
- }
992
- if ( ! empty( $data ) ) {
993
- $has_data = true;
994
- }
995
-
996
- if ( null != $has_data ) {
997
- $post_info = "\n[post_data]\n\n";
998
- $post_info .= "post_title = '" . $title . "'\n";
999
- $post_info .= "post_guid = '" . $guid . "'\n";
1000
- $post_info .= "post_date = '" . $date . "'\n";
1001
- $post_info .= "post_type = '" . $type . "'\n";
1002
- if ( $data ) {
1003
- $buf .= $post_info . $data . "\n";
1004
- }
1005
- }
1006
- }
1007
- wp_reset_postdata();
1008
-
1009
- return $buf;
1010
- }
1011
-
1012
- /**
1013
- * Handles exporting settings data for a module.
1014
- *
1015
- * @since 2.4.13 Fixed bug on empty options.
1016
- *
1017
- * @param $buf
1018
- *
1019
- * @return string
1020
- */
1021
- function settings_export( $buf ) {
1022
- global $aiosp;
1023
- $post_types = apply_filters( 'aioseop_export_settings_exporter_post_types', null );
1024
- $has_data = null;
1025
- $general_settings = null;
1026
- $exporter_choices = apply_filters( 'aioseop_export_settings_exporter_choices', '' );
1027
- if ( ! empty( $_REQUEST['aiosp_importer_exporter_export_choices'] ) ) {
1028
- $exporter_choices = $_REQUEST['aiosp_importer_exporter_export_choices'];
1029
- }
1030
-
1031
- $post_types = isset( $_REQUEST['aiosp_importer_exporter_export_post_types'] ) ? $_REQUEST['aiosp_importer_exporter_export_post_types'] : array();
1032
- if ( ! empty( $exporter_choices ) && is_array( $exporter_choices ) ) {
1033
- foreach ( $exporter_choices as $ex ) {
1034
- if ( 1 == $ex ) {
1035
- $general_settings = true;
1036
- }
1037
- }
1038
- }
1039
-
1040
- if ( ( null != $post_types ) && ( $this === $aiosp ) ) {
1041
- $buf .= $this->post_data_export(
1042
- '_aioseop',
1043
- array(
1044
- 'posts_per_page' => - 1,
1045
- 'post_type' => $post_types,
1046
- 'post_status' => array( 'publish', 'pending', 'draft', 'future', 'private', 'inherit' ),
1047
- )
1048
- );
1049
- }
1050
-
1051
- /* Add all active settings to settings file */
1052
- $name = $this->get_option_name();
1053
- $options = $this->get_class_option();
1054
- if ( ! empty( $options ) && null != $general_settings ) {
1055
- $buf .= "\n[$name]\n\n";
1056
- foreach ( $options as $key => $value ) {
1057
- if ( ( $name == $this->parent_option ) && ( 'modules' == $key ) ) {
1058
- continue;
1059
- } // don't re-export all module settings -- pdb
1060
- if ( is_array( $value ) ) {
1061
- $value = "'" . str_replace(
1062
- array( "'", "\n", "\r" ),
1063
- array(
1064
- "\'",
1065
- '\n',
1066
- '\r',
1067
- ),
1068
- trim( serialize( $value ) )
1069
- ) . "'";
1070
- } else {
1071
- $value = str_replace(
1072
- array( "\n", "\r" ),
1073
- array(
1074
- '\n',
1075
- '\r',
1076
- ),
1077
- trim( var_export( $value, true ) )
1078
- );
1079
- }
1080
- $buf .= "$key = $value\n";
1081
- }
1082
- }
1083
-
1084
- return $buf;
1085
- }
1086
-
1087
- /**
1088
- * Order for adding the menus for the aioseop_modules_add_menus hook.
1089
- */
1090
- function menu_order() {
1091
- return 10;
1092
- }
1093
-
1094
- /**
1095
- * Print a basic error message.
1096
- *
1097
- * @param $error
1098
- *
1099
- * @return bool
1100
- */
1101
- function output_error( $error ) {
1102
- $error = esc_html( $error );
1103
- echo "<div class='aioseop_module error'>$error</div>";
1104
-
1105
- return false;
1106
- }
1107
-
1108
- /**
1109
- *
1110
- * Backwards compatibility - see http://php.net/manual/en/function.str-getcsv.php
1111
- *
1112
- * @param $input
1113
- * @param string $delimiter
1114
- * @param string $enclosure
1115
- * @param string $escape
1116
- *
1117
- * @return array
1118
- */
1119
- function str_getcsv( $input, $delimiter = ',', $enclosure = '"', $escape = '\\' ) {
1120
- $fp = fopen( 'php://memory', 'r+' );
1121
- fputs( $fp, $input );
1122
- rewind( $fp );
1123
- $data = fgetcsv( $fp, null, $delimiter, $enclosure ); // $escape only got added in 5.3.0
1124
- fclose( $fp );
1125
-
1126
- return $data;
1127
- }
1128
-
1129
- /**
1130
- *
1131
- * Helper function to convert csv in key/value pair format to an associative array.
1132
- *
1133
- * @param $csv
1134
- *
1135
- * @return array
1136
- */
1137
- function csv_to_array( $csv ) {
1138
- $args = array();
1139
- if ( version_compare( PHP_VERSION, '5.3', '>=' ) ) {
1140
- $v = $this->str_getcsv( $csv );
1141
- } else {
1142
- $v = str_getcsv( $csv ); // phpcs:ignore PHPCompatibility.FunctionUse.NewFunctions.str_getcsvFound
1143
- }
1144
- $size = count( $v );
1145
- if ( is_array( $v ) && isset( $v[0] ) && $size >= 2 ) {
1146
- for ( $i = 0; $i < $size; $i += 2 ) {
1147
- $args[ $v[ $i ] ] = $v[ $i + 1 ];
1148
- }
1149
- }
1150
-
1151
- return $args;
1152
- }
1153
-
1154
- /** Allow modules to use WP Filesystem if available and desired, fall back to PHP filesystem access otherwise.
1155
- *
1156
- * @param string $method
1157
- * @param bool $form_fields
1158
- * @param string $url
1159
- * @param bool $error
1160
- *
1161
- * @return bool
1162
- */
1163
- function use_wp_filesystem( $method = '', $form_fields = false, $url = '', $error = false ) {
1164
- if ( empty( $method ) ) {
1165
- $this->credentials = request_filesystem_credentials( $url );
1166
- } else {
1167
- $this->credentials = request_filesystem_credentials( $url, $method, $error, false, $form_fields );
1168
- }
1169
-
1170
- return $this->credentials;
1171
- }
1172
-
1173
- /**
1174
- * Wrapper function to get filesystem object.
1175
- */
1176
- function get_filesystem_object() {
1177
- $cred = get_transient( 'aioseop_fs_credentials' );
1178
- if ( ! empty( $cred ) ) {
1179
- $this->credentials = $cred;
1180
- }
1181
-
1182
- if ( function_exists( 'WP_Filesystem' ) && WP_Filesystem( $this->credentials ) ) {
1183
- global $wp_filesystem;
1184
-
1185
- return $wp_filesystem;
1186
- } else {
1187
- require_once( ABSPATH . 'wp-admin/includes/template.php' );
1188
- require_once( ABSPATH . 'wp-admin/includes/screen.php' );
1189
- require_once( ABSPATH . 'wp-admin/includes/file.php' );
1190
-
1191
- if ( ! WP_Filesystem( $this->credentials ) ) {
1192
- $this->use_wp_filesystem();
1193
- }
1194
-
1195
- if ( ! empty( $this->credentials ) ) {
1196
- set_transient( 'aioseop_fs_credentials', $this->credentials, 10800 );
1197
- }
1198
- global $wp_filesystem;
1199
- if ( is_object( $wp_filesystem ) ) {
1200
- return $wp_filesystem;
1201
- }
1202
- }
1203
-
1204
- return false;
1205
- }
1206
-
1207
- /**
1208
- * See if a file exists using WP Filesystem.
1209
- *
1210
- * @param string $filename
1211
- *
1212
- * @return bool
1213
- */
1214
- function file_exists( $filename ) {
1215
- $wpfs = $this->get_filesystem_object();
1216
- if ( is_object( $wpfs ) ) {
1217
- return $wpfs->exists( $filename );
1218
- }
1219
-
1220
- return $wpfs;
1221
- }
1222
-
1223
- /**
1224
- * See if the directory entry is a file using WP Filesystem.
1225
- *
1226
- * @param $filename
1227
- *
1228
- * @return bool
1229
- */
1230
- function is_file( $filename ) {
1231
- $wpfs = $this->get_filesystem_object();
1232
- if ( is_object( $wpfs ) ) {
1233
- return $wpfs->is_file( $filename );
1234
- }
1235
-
1236
- return $wpfs;
1237
- }
1238
-
1239
- /**
1240
- * List files in a directory using WP Filesystem.
1241
- *
1242
- * @param $path
1243
- *
1244
- * @return array|bool
1245
- */
1246
- function scandir( $path ) {
1247
- $wpfs = $this->get_filesystem_object();
1248
- if ( is_object( $wpfs ) ) {
1249
- $dirlist = $wpfs->dirlist( $path );
1250
- if ( empty( $dirlist ) ) {
1251
- return $dirlist;
1252
- }
1253
-
1254
- return array_keys( $dirlist );
1255
- }
1256
-
1257
- return $wpfs;
1258
- }
1259
-
1260
- /**
1261
- * Load a file through WP Filesystem; implement basic support for offset and maxlen.
1262
- *
1263
- * @param $filename
1264
- * @param bool $use_include_path
1265
- * @param null $context
1266
- * @param int $offset
1267
- * @param int $maxlen
1268
- *
1269
- * @return bool|mixed
1270
- */
1271
- function load_file( $filename, $use_include_path = false, $context = null, $offset = - 1, $maxlen = - 1 ) {
1272
- $wpfs = $this->get_filesystem_object();
1273
- if ( is_object( $wpfs ) ) {
1274
- if ( ! $wpfs->exists( $filename ) ) {
1275
- return false;
1276
- }
1277
- if ( ( $offset > 0 ) || ( $maxlen >= 0 ) ) {
1278
- if ( 0 === $maxlen ) {
1279
- return '';
1280
- }
1281
- if ( 0 > $offset ) {
1282
- $offset = 0;
1283
- }
1284
- $file = $wpfs->get_contents( $filename );
1285
- if ( ! is_string( $file ) || empty( $file ) ) {
1286
- return $file;
1287
- }
1288
- if ( 0 > $maxlen ) {
1289
- return AIOSEOP_PHP_Functions::substr( $file, $offset );
1290
- } else {
1291
- return AIOSEOP_PHP_Functions::substr( $file, $offset, $maxlen );
1292
- }
1293
- } else {
1294
- return $wpfs->get_contents( $filename );
1295
- }
1296
- }
1297
-
1298
- return false;
1299
- }
1300
-
1301
- /**
1302
- * Save a file through WP Filesystem.
1303
- *
1304
- * @param string $filename
1305
- *
1306
- * @param $contents
1307
- *
1308
- * @return bool
1309
- */
1310
- function save_file( $filename, $contents ) {
1311
- /* translators: %s is a placeholder and will be replaced with the name of the relevant file. */
1312
- $failed_str = sprintf( __( 'Failed to write file %s!', 'all-in-one-seo-pack' ) . "\n", $filename );
1313
- /* translators: %s is a placeholder and will be replaced with the name of the relevant file. */
1314
- $readonly_str = sprintf( __( 'File %s isn\'t writable!', 'all-in-one-seo-pack' ) . "\n", $filename );
1315
-
1316
- $wpfs = $this->get_filesystem_object();
1317
- if ( is_object( $wpfs ) ) {
1318
- $file_exists = $wpfs->exists( $filename );
1319
- if ( ! $file_exists || $wpfs->is_writable( $filename ) ) {
1320
- if ( $wpfs->put_contents( $filename, $contents ) === false ) {
1321
- return $this->output_error( $failed_str );
1322
- }
1323
- } else {
1324
- return $this->output_error( $readonly_str );
1325
- }
1326
-
1327
- return true;
1328
- }
1329
-
1330
- return false;
1331
- }
1332
-
1333
- /**
1334
- * Delete a file through WP Filesystem.
1335
- *
1336
- * @param string $filename
1337
- *
1338
- * @return bool
1339
- */
1340
- function delete_file( $filename ) {
1341
- $wpfs = $this->get_filesystem_object();
1342
- if ( is_object( $wpfs ) ) {
1343
- if ( $wpfs->exists( $filename ) ) {
1344
- if ( $wpfs->delete( $filename ) === false ) {
1345
- /* translators: %s is a placeholder and will be replaced with the name of the relevant file. */
1346
- $this->output_error( sprintf( __( 'Failed to delete file %s!', 'all-in-one-seo-pack' ) . "\n", $filename ) );
1347
- } else {
1348
- return true;
1349
- }
1350
- } else {
1351
- /* translators: %s is a placeholder and will be replaced with the name of the relevant file. */
1352
- $this->output_error( sprintf( __( "File %s doesn't exist!", 'all-in-one-seo-pack' ) . "\n", $filename ) );
1353
- }
1354
- }
1355
-
1356
- return false;
1357
- }
1358
-
1359
- /**
1360
- * Rename a file through WP Filesystem.
1361
- *
1362
- * @param string $filename
1363
- * @param string $newname
1364
- *
1365
- * @return bool
1366
- */
1367
- function rename_file( $filename, $newname ) {
1368
- $wpfs = $this->get_filesystem_object();
1369
- if ( is_object( $wpfs ) ) {
1370
- $file_exists = $wpfs->exists( $filename );
1371
- $newfile_exists = $wpfs->exists( $newname );
1372
- if ( $file_exists && ! $newfile_exists ) {
1373
- if ( $wpfs->move( $filename, $newname ) === false ) {
1374
- /* translators: %s is a placeholder and will be replaced with the name of the relevant file. */
1375
- $this->output_error( sprintf( __( 'Failed to rename file %s!', 'all-in-one-seo-pack' ) . "\n", $filename ) );
1376
- } else {
1377
- return true;
1378
- }
1379
- } else {
1380
- if ( ! $file_exists ) {
1381
- /* translators: %s is a placeholder and will be replaced with the name of the relevant file. */
1382
- $this->output_error( sprintf( __( "File %s doesn't exist!", 'all-in-one-seo-pack' ) . "\n", $filename ) );
1383
- } elseif ( $newfile_exists ) {
1384
- /* translators: %s is a placeholder and will be replaced with the name of the relevant file. */
1385
- $this->output_error( sprintf( __( 'File %s already exists!', 'all-in-one-seo-pack' ) . "\n", $newname ) );
1386
- }
1387
- }
1388
- }
1389
-
1390
- return false;
1391
- }
1392
-
1393
- /**
1394
- * Load multiple files.
1395
- *
1396
- * @param $options
1397
- * @param $opts
1398
- * @param $prefix
1399
- *
1400
- * @return mixed
1401
- */
1402
- function load_files( $options, $opts, $prefix ) {
1403
- foreach ( $opts as $opt => $file ) {
1404
- $opt = $prefix . $opt;
1405
- $file = ABSPATH . $file;
1406
- $contents = $this->load_file( $file );
1407
- if ( false !== $contents ) {
1408
- $options[ $opt ] = $contents;
1409
- }
1410
- }
1411
-
1412
- return $options;
1413
- }
1414
-
1415
- /**
1416
- * Save multiple files.
1417
- *
1418
- * @param $opts
1419
- * @param $prefix
1420
- */
1421
- function save_files( $opts, $prefix ) {
1422
- foreach ( $opts as $opt => $file ) {
1423
- $opt = $prefix . $opt;
1424
- if ( isset( $_POST[ $opt ] ) ) {
1425
- $output = stripslashes_deep( $_POST[ $opt ] );
1426
- $file = ABSPATH . $file;
1427
- $this->save_file( $file, $output );
1428
- }
1429
- }
1430
- }
1431
-
1432
- /**
1433
- * Delete multiple files.
1434
- *
1435
- * @param $opts
1436
- */
1437
- function delete_files( $opts ) {
1438
- foreach ( $opts as $opt => $file ) {
1439
- $file = ABSPATH . $file;
1440
- $this->delete_file( $file );
1441
- }
1442
- }
1443
-
1444
- /**
1445
- * Returns available social seo images.
1446
- *
1447
- * @since 2.4 #1079 Fixes array_flip warning on opengraph module.
1448
- *
1449
- * @param array $options Plugin/module options.
1450
- * @param object $p Post.
1451
- *
1452
- * @return array
1453
- */
1454
- function get_all_images_by_type( $options = null, $p = null ) {
1455
- $img = array();
1456
- if ( empty( $img ) ) {
1457
- $size = apply_filters( 'post_thumbnail_size', 'large' );
1458
-
1459
- global $aioseop_options, $wp_query, $aioseop_opengraph;
1460
-
1461
- if ( null === $p ) {
1462
- global $post;
1463
- } else {
1464
- $post = $p;
1465
- }
1466
-
1467
- $count = 1;
1468
-
1469
- if ( ! empty( $post ) ) {
1470
- if ( ! is_object( $post ) ) {
1471
- $post = get_post( $post );
1472
- }
1473
- if ( is_object( $post ) && function_exists( 'get_post_thumbnail_id' ) ) {
1474
- if ( 'attachment' == $post->post_type ) {
1475
- $post_thumbnail_id = $post->ID;
1476
- } else {
1477
- $post_thumbnail_id = get_post_thumbnail_id( $post->ID );
1478
- }
1479
- if ( ! empty( $post_thumbnail_id ) ) {
1480
- $image = wp_get_attachment_image_src( $post_thumbnail_id, $size );
1481
- if ( is_array( $image ) ) {
1482
- $img[] = array(
1483
- 'type' => 'featured',
1484
- 'id' => $post_thumbnail_id,
1485
- 'link' => $image[0],
1486
- );
1487
- }
1488
- }
1489
- }
1490
-
1491
- $post_id = $post->ID;
1492
- $p = $post;
1493
- $w = $wp_query;
1494
-
1495
- $meta_key = '';
1496
- if ( is_array( $options ) && isset( $options['meta_key'] ) ) {
1497
- $meta_key = $options['meta_key'];
1498
- }
1499
-
1500
- if ( ! empty( $meta_key ) && ! empty( $post ) ) {
1501
- $image = $this->get_the_image_by_meta_key(
1502
- array(
1503
- 'post_id' => $post->ID,
1504
- 'meta_key' => explode( ',', $meta_key ),
1505
- )
1506
- );
1507
- if ( ! empty( $image ) ) {
1508
- $img[] = array(
1509
- 'type' => 'meta_key',
1510
- 'id' => $meta_key,
1511
- 'link' => $image,
1512
- );
1513
- }
1514
- }
1515
-
1516
- if ( '' != ! $post->post_modified_gmt ) {
1517
- $wp_query = new WP_Query(
1518
- array(
1519
- 'p' => $post_id,
1520
- 'post_type' => $post->post_type,
1521
- )
1522
- );
1523
- }
1524
- if ( 'page' == $post->post_type ) {
1525
- $wp_query->is_page = true;
1526
- } elseif ( 'attachment' == $post->post_type ) {
1527
- $wp_query->is_attachment = true;
1528
- } else {
1529
- $wp_query->is_single = true;
1530
- }
1531
- if ( 'page' == get_option( 'show_on_front' ) && get_option( 'page_for_posts' ) == $post->ID ) {
1532
- $wp_query->is_home = true;
1533
- }
1534
- $args['options']['type'] = 'html';
1535
- $args['options']['nowrap'] = false;
1536
- $args['options']['save'] = false;
1537
- $wp_query->queried_object = $post;
1538
-
1539
- $attachments = get_children(
1540
- array(
1541
- 'post_parent' => $post->ID,
1542
- 'post_status' => 'inherit',
1543
- 'post_type' => 'attachment',
1544
- 'post_mime_type' => 'image',
1545
- 'order' => 'ASC',
1546
- 'orderby' => 'menu_order ID',
1547
- )
1548
- );
1549
- if ( ! empty( $attachments ) ) {
1550
- foreach ( $attachments as $id => $attachment ) {
1551
- $image = wp_get_attachment_image_src( $id, $size );
1552
- if ( is_array( $image ) ) {
1553
- $img[] = array(
1554
- 'type' => 'attachment',
1555
- 'id' => $id,
1556
- 'link' => $image[0],
1557
- );
1558
- }
1559
- }
1560
- }
1561
- $matches = array();
1562
- preg_match_all( '|<img.*?src=[\'"](.*?)[\'"].*?>|i', get_post_field( 'post_content', $post->ID ), $matches );
1563
- if ( isset( $matches ) && ! empty( $matches[1] ) && ! empty( $matches[1][0] ) ) {
1564
- foreach ( $matches[1] as $i => $m ) {
1565
- $img[] = array(
1566
- 'type' => 'post_content',
1567
- 'id' => 'post' . $count ++,
1568
- 'link' => $m,
1569
- );
1570
- }
1571
- }
1572
- wp_reset_postdata();
1573
- $wp_query = $w;
1574
- $post = $p;
1575
- }
1576
- }
1577
-
1578
- return $img;
1579
- }
1580
-
1581
- /**
1582
- * Get All Images
1583
- *
1584
- * @since ?
1585
- *
1586
- * @param null $options
1587
- * @param null $p
1588
- * @return array
1589
- */
1590
- function get_all_images( $options = null, $p = null ) {
1591
- $img = $this->get_all_images_by_type( $options, $p );
1592
- $legacy = array();
1593
- foreach ( $img as $k => $v ) {
1594
- $v['link'] = set_url_scheme( $v['link'] );
1595
- if ( 'featured' == $v['type'] ) {
1596
- $legacy[ $v['link'] ] = 1;
1597
- } else {
1598
- $legacy[ $v['link'] ] = $v['id'];
1599
- }
1600
- }
1601
-
1602
- return $legacy;
1603
- }
1604
-
1605
- /**
1606
- * Thanks to Justin Tadlock for the original get-the-image code - http://themehybrid.com/plugins/get-the-image **
1607
- *
1608
- * @param null $options
1609
- * @param null $p
1610
- *
1611
- * @return bool|mixed|string
1612
- */
1613
- function get_the_image( $options = null, $p = null ) {
1614
-
1615
- if ( null === $p ) {
1616
- global $post;
1617
- } else {
1618
- $post = $p;
1619
- }
1620
-
1621
- $meta_key = '';
1622
- if ( is_array( $options ) && isset( $options['meta_key'] ) ) {
1623
- $meta_key = $options['meta_key'];
1624
- }
1625
-
1626
- if ( ! empty( $meta_key ) && ! empty( $post ) ) {
1627
- $meta_key = explode( ',', $meta_key );
1628
- $image = $this->get_the_image_by_meta_key(
1629
- array(
1630
- 'post_id' => $post->ID,
1631
- 'meta_key' => $meta_key,
1632
- )
1633
- );
1634
- }
1635
- if ( empty( $image ) ) {
1636
- $image = $this->get_the_image_by_post_thumbnail( $post );
1637
- }
1638
- if ( empty( $image ) ) {
1639
- $image = $this->get_the_image_by_attachment( $post );
1640
- }
1641
- if ( empty( $image ) ) {
1642
- $image = $this->get_the_image_by_scan( $post );
1643
- }
1644
- if ( empty( $image ) ) {
1645
- $image = $this->get_the_image_by_default( $post );
1646
- }
1647
-
1648
- return $image;
1649
- }
1650
-
1651
- /**
1652
- * Get the Image by Default
1653
- *
1654
- * @since ?
1655
- *
1656
- * @param null $p
1657
- * @return string
1658
- */
1659
- function get_the_image_by_default( $p = null ) {
1660
- return '';
1661
- }
1662
-
1663
- /**
1664
- * Get the Image by Meta Key
1665
- *
1666
- * @since ?
1667
- *
1668
- * @param array $args
1669
- * @return bool|mixed
1670
- */
1671
- function get_the_image_by_meta_key( $args = array() ) {
1672
-
1673
- /* If $meta_key is not an array. */
1674
- if ( ! is_array( $args['meta_key'] ) ) {
1675
- $args['meta_key'] = array( $args['meta_key'] );
1676
- }
1677
-
1678
- /* Loop through each of the given meta keys. */
1679
- foreach ( $args['meta_key'] as $meta_key ) {
1680
- /* Get the image URL by the current meta key in the loop. */
1681
- $image = get_post_meta( $args['post_id'], $meta_key, true );
1682
- /* If a custom key value has been given for one of the keys, return the image URL. */
1683
- if ( ! empty( $image ) ) {
1684
- return $image;
1685
- }
1686
- }
1687
-
1688
- return false;
1689
- }
1690
-
1691
- /**
1692
- * Get the Image by Post Thumbnail
1693
- *
1694
- * @since ?
1695
- * @since 2.4.13 Fixes when content is taxonomy.
1696
- *
1697
- * @param null $p
1698
- * @return bool
1699
- */
1700
- function get_the_image_by_post_thumbnail( $p = null ) {
1701
-
1702
- if ( null === $p ) {
1703
- global $post;
1704
- } else {
1705
- $post = $p;
1706
- }
1707
-
1708
- if ( is_category() || is_tag() || is_tax() ) {
1709
- return false;
1710
- }
1711
-
1712
- $post_thumbnail_id = null;
1713
- if ( function_exists( 'get_post_thumbnail_id' ) ) {
1714
- $post_thumbnail_id = get_post_thumbnail_id( $post->ID );
1715
- }
1716
-
1717
- if ( empty( $post_thumbnail_id ) ) {
1718
- return false;
1719
- }
1720
-
1721
- // Check if someone is using built-in WP filter.
1722
- $size = apply_filters( 'aioseop_thumbnail_size', apply_filters( 'post_thumbnail_size', 'large' ) );
1723
- $image = wp_get_attachment_image_src( $post_thumbnail_id, $size );
1724
-
1725
- return $image[0];
1726
- }
1727
-
1728
- /**
1729
- * Get the Image by Attachment
1730
- *
1731
- * @since ?
1732
- * @since 3.4 Change return variable type bool|string to just string.
1733
- *
1734
- * @param null|WP_Post $p WP Post object.
1735
- * @return string Image URL.
1736
- */
1737
- function get_the_image_by_attachment( $p = null ) {
1738
-
1739
- if ( null === $p ) {
1740
- global $post;
1741
- } else {
1742
- $post = $p;
1743
- }
1744
-
1745
- if ( empty( $post ) ) {
1746
- return '';
1747
- }
1748
-
1749
- $attachments = get_children(
1750
- array(
1751
- 'post_parent' => $post->ID,
1752
- 'post_status' => 'inherit',
1753
- 'post_type' => 'attachment',
1754
- 'post_mime_type' => 'image',
1755
- 'order' => 'ASC',
1756
- 'orderby' => 'menu_order ID',
1757
- )
1758
- );
1759
-
1760
- if ( empty( $attachments ) && 'attachment' == get_post_type( $post->ID ) ) {
1761
- $size = apply_filters( 'aioseop_attachment_size', 'large' );
1762
- $image = wp_get_attachment_image_src( $post->ID, $size );
1763
- }
1764
-
1765
- /* If no attachments or image is found, return false. */
1766
- if ( empty( $attachments ) && empty( $image ) ) {
1767
- return '';
1768
- }
1769
-
1770
- /* Set the default iterator to 0. */
1771
- $i = 0;
1772
-
1773
- /* Loop through each attachment. Once the $order_of_image (default is '1') is reached, break the loop. */
1774
- foreach ( $attachments as $id => $attachment ) {
1775
- if ( 1 == ++ $i ) {
1776
- $size = apply_filters( 'aioseop_attachment_size', 'large' );
1777
- $image = wp_get_attachment_image_src( $id, $size );
1778
- $alt = trim( strip_tags( get_post_field( 'post_excerpt', $id ) ) );
1779
- break;
1780
- }
1781
- }
1782
-
1783
- /* Return the image URL. */
1784
-
1785
- return $image[0];
1786
-
1787
- }
1788
-
1789
- /**
1790
- * Get the Image by Scan
1791
- *
1792
- * Scans a Post's content by (regex) capturing an <img> element's source for the image URL.
1793
- *
1794
- * @since ?
1795
- * @since 3.4 Change return variable type bool|string to just string.
1796
- *
1797
- * @param null|WP_Post $p WP Post object.
1798
- * @return string Image URL source.
1799
- */
1800
- function get_the_image_by_scan( $p = null ) {
1801
- if ( null === $p ) {
1802
- global $post;
1803
- } else {
1804
- $post = $p;
1805
- }
1806
-
1807
- if ( empty( $post ) ) {
1808
- return '';
1809
- }
1810
-
1811
- $rtn_url = '';
1812
-
1813
- /* Search the post's content for the <img /> tag and get its URL. */
1814
- preg_match_all( '|<img.*?src=[\'"](.*?)[\'"].*?>|i', get_post_field( 'post_content', $post->ID ), $matches );
1815
-
1816
- /* If there is a match for the image, return its URL. */
1817
- if ( isset( $matches ) && ! empty( $matches[1][0] ) ) {
1818
- $rtn_url = $matches[1][0];
1819
- }
1820
-
1821
- return $rtn_url;
1822
- }
1823
-
1824
- /**
1825
- * Load scripts and styles for metaboxes.
1826
- * edit-tags exists only for pre 4.5 support... remove when we drop 4.5 support.
1827
- * Also, that check and others should be pulled out into their own functions.
1828
- *
1829
- * @todo is it possible to migrate this to \All_in_One_SEO_Pack_Module::add_page_hooks? Or refactor? Both function about the same.
1830
- *
1831
- * @since 2.4.14 Added term as screen base.
1832
- */
1833
- function enqueue_metabox_scripts() {
1834
- $screen = '';
1835
- if ( function_exists( 'get_current_screen' ) ) {
1836
- $screen = get_current_screen();
1837
- }
1838
- $bail = false;
1839
- if ( empty( $screen ) ) {
1840
- $bail = true;
1841
- }
1842
- if ( true != $bail ) {
1843
- if ( ( 'post' != $screen->base ) && ( 'term' != $screen->base ) && ( 'edit-tags' != $screen->base ) && ( 'toplevel_page_shopp-products' != $screen->base ) ) {
1844
- $bail = true;
1845
- }
1846
- }
1847
- $prefix = $this->get_prefix();
1848
- $bail = apply_filters( $prefix . 'bail_on_enqueue', $bail, $screen );
1849
- if ( $bail ) {
1850
- return;
1851
- }
1852
- $this->form = 'post';
1853
- if ( 'term' == $screen->base || 'edit-tags' == $screen->base ) {
1854
- $this->form = 'edittag';
1855
- }
1856
- if ( 'toplevel_page_shopp-products' == $screen->base ) {
1857
- $this->form = 'product';
1858
- }
1859
- $this->form = apply_filters( $prefix . 'set_form_on_enqueue', $this->form, $screen );
1860
- foreach ( $this->locations as $k => $v ) {
1861
- if ( 'metabox' === $v['type'] && isset( $v['display'] ) && ! empty( $v['display'] ) ) {
1862
- $enqueue_scripts = false;
1863
- $enqueue_scripts =
1864
- (
1865
- ( 'toplevel_page_shopp-products' == $screen->base ) &&
1866
- in_array( 'shopp_product', $v['display'] )
1867
- ) ||
1868
- in_array( $screen->post_type, $v['display'] ) ||
1869
- 'edit-category' == $screen->base ||
1870
- 'edit-post_tag' == $screen->base ||
1871
- 'term' == $screen->base;
1872
- $enqueue_scripts = apply_filters( $prefix . 'enqueue_metabox_scripts', $enqueue_scripts, $screen, $v );
1873
- if ( $enqueue_scripts ) {
1874
- add_filter( 'aioseop_localize_script_data', array( $this, 'localize_script_data' ) );
1875
- add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ), 20 );
1876
- add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_styles' ), 20 );
1877
- }
1878
- }
1879
- }
1880
- }
1881
-
1882
- /**
1883
- * Load styles for module.
1884
- *
1885
- * Add hook in \All_in_One_SEO_Pack_Module::enqueue_metabox_scripts - Bails adding hook if not on target valid screen.
1886
- * Add hook in \All_in_One_SEO_Pack_Module::add_page_hooks - Function itself is hooked based on the screen_id/page.
1887
- *
1888
- * @since 2.9
1889
- * @since 3.0 Added jQuery UI CSS missing from WP. #1850
1890
- *
1891
- * @see 'admin_enqueue_scripts' hook
1892
- * @link https://developer.wordpress.org/reference/hooks/admin_enqueue_scripts/
1893
- * @uses wp_scripts() Gets the Instance of WP Scripts.
1894
- * @link https://developer.wordpress.org/reference/functions/wp_scripts/
1895
- *
1896
- * @param string $hook_suffix
1897
- */
1898
- function admin_enqueue_styles( $hook_suffix ) {
1899
- wp_enqueue_style( 'thickbox' );
1900
- if ( ! empty( $this->pointers ) ) {
1901
- wp_enqueue_style( 'wp-pointer' );
1902
- }
1903
- wp_enqueue_style( 'aioseop-module-style', AIOSEOP_PLUGIN_URL . 'css/modules/aioseop_module.css', array(), AIOSEOP_VERSION );
1904
- if ( function_exists( 'is_rtl' ) && is_rtl() ) {
1905
- wp_enqueue_style( 'aioseop-module-style-rtl', AIOSEOP_PLUGIN_URL . 'css/modules/aioseop_module-rtl.css', array( 'aioseop-module-style' ), AIOSEOP_VERSION );
1906
- }
1907
-
1908
- if ( ! wp_style_is( 'aioseop-jquery-ui', 'registered' ) && ! wp_style_is( 'aioseop-jquery-ui', 'enqueued' ) ) {
1909
- wp_enqueue_style(
1910
- 'aioseop-jquery-ui',
1911
- AIOSEOP_PLUGIN_URL . 'css/aioseop-jquery-ui.css',
1912
- array(),
1913
- AIOSEOP_VERSION
1914
- );
1915
- }
1916
- }
1917
-
1918
- /**
1919
- * Admin Enqueue Scripts
1920
- *
1921
- * Hook function to enqueue scripts and localize data to scripts.
1922
- *
1923
- * Add hook in \All_in_One_SEO_Pack_Module::enqueue_metabox_scripts - Bails adding hook if not on target valid screen.
1924
- * Add hook in \All_in_One_SEO_Pack_Module::add_page_hooks - Function itself is hooked based on the screen_id/page.
1925
- *
1926
- * @since ?
1927
- * @since 2.3.12.3 Add missing wp_enqueue_media.
1928
- * @since 2.9 Switch to admin_enqueue_scripts; both the hook and function name.
1929
- * @since 3.0 Add enqueue footer JS for jQuery UI Compatibility. #1850
1930
- *
1931
- * @see 'admin_enqueue_scripts' hook
1932
- * @link https://developer.wordpress.org/reference/hooks/admin_enqueue_scripts/
1933
- * @global WP_Post $post Used to set the post ID in wp_enqueue_media().
1934
- *
1935
- * @param string $hook_suffix
1936
- */
1937
- public function admin_enqueue_scripts( $hook_suffix ) {
1938
- wp_enqueue_script( 'sack' );
1939
- wp_enqueue_script( 'jquery' );
1940
- wp_enqueue_script( 'jquery-ui-tabs' );
1941
- wp_enqueue_script( 'media-upload' );
1942
- wp_enqueue_script( 'thickbox' );
1943
- wp_enqueue_script( 'common' );
1944
- wp_enqueue_script( 'wp-lists' );
1945
- wp_enqueue_script( 'postbox' );
1946
-
1947
- if ( ! empty( $this->pointers ) ) {
1948
- wp_enqueue_script(
1949
- 'wp-pointer',
1950
- false,
1951
- array( 'jquery' )
1952
- );
1953
- }
1954
-
1955
- global $post;
1956
- if ( ! empty( $post->ID ) ) {
1957
- wp_enqueue_media( array( 'post' => $post->ID ) );
1958
- } else {
1959
- wp_enqueue_media();
1960
- }
1961
-
1962
- $helper_dep = array(
1963
- 'jquery',
1964
- 'jquery-ui-core',
1965
- 'jquery-ui-widget',
1966
- 'jquery-ui-position',
1967
- 'jquery-ui-tooltip',
1968
- );
1969
-
1970
- // AIOSEOP Script enqueue.
1971
- wp_enqueue_script(
1972
- 'aioseop-module-script',
1973
- AIOSEOP_PLUGIN_URL . 'js/modules/aioseop_module.js',
1974
- array(),
1975
- AIOSEOP_VERSION
1976
- );
1977
- wp_enqueue_script(
1978
- 'aioseop-helper-js',
1979
- AIOSEOP_PLUGIN_URL . 'js/aioseop-helper.js',
1980
- $helper_dep,
1981
- AIOSEOP_VERSION,
1982
- true
1983
- );
1984
-
1985
- // Localize aiosp_data in JS.
1986
- if ( ! empty( $this->script_data ) ) {
1987
- aioseop_localize_script_data();
1988
- }
1989
- }
1990
-
1991
- /**
1992
- * Localize Script Data
1993
- *
1994
- * @since ?
1995
- *
1996
- * @param $data
1997
- * @return array
1998
- */
1999
- function localize_script_data( $data ) {
2000
- if ( ! is_array( $data ) ) {
2001
- $data = array( 0 => $data );
2002
- }
2003
- if ( empty( $this->script_data ) ) {
2004
- $this->script_data = array();
2005
- }
2006
- if ( ! empty( $this->pointers ) ) {
2007
- $this->script_data['pointers'] = $this->pointers;
2008
- }
2009
- if ( empty( $data[0]['condshow'] ) ) {
2010
- $data[0]['condshow'] = array();
2011
- }
2012
- if ( empty( $this->script_data['condshow'] ) ) {
2013
- $this->script_data['condshow'] = array();
2014
- }
2015
- $condshow = $this->script_data['condshow'];
2016
- $data[0]['condshow'] = array_merge( $data[0]['condshow'], $condshow );
2017
- unset( $this->script_data['condshow'] );
2018
- $data[0] = array_merge( $this->script_data, $data[0] );
2019
- $this->script_data['condshow'] = $condshow;
2020
-
2021
- return $data;
2022
- }
2023
-
2024
- /**
2025
- * Override this to run code at the beginning of the settings page.
2026
- */
2027
- function settings_page_init() {
2028
-
2029
- }
2030
-
2031
- /**
2032
- * Filter out admin pointers that have already been clicked.
2033
- */
2034
- function filter_pointers() {
2035
- if ( ! empty( $this->pointers ) ) {
2036
- $dismissed = explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) );
2037
- foreach ( $dismissed as $d ) {
2038
- if ( isset( $this->pointers[ $d ] ) ) {
2039
- unset( $this->pointers[ $d ] );
2040
- }
2041
- }
2042
- }
2043
- }
2044
-
2045
- /**
2046
- * Add basic hooks when on the module's page.
2047
- */
2048
- function add_page_hooks() {
2049
- $hookname = current_filter();
2050
- if ( AIOSEOP_PHP_Functions::strpos( $hookname, 'load-' ) === 0 ) {
2051
- $this->pagehook = AIOSEOP_PHP_Functions::substr( $hookname, 5 );
2052
- }
2053
- add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
2054
- add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_styles' ) );
2055
- add_filter( 'aioseop_localize_script_data', array( $this, 'localize_script_data' ) );
2056
- add_action( $this->prefix . 'settings_header', array( $this, 'display_tabs' ) );
2057
- }
2058
-
2059
- /**
2060
- * Get Admin Links
2061
- *
2062
- * @since ?
2063
- *
2064
- * @return array
2065
- */
2066
- function get_admin_links() {
2067
- if ( ! empty( $this->menu_name ) ) {
2068
- $name = $this->menu_name;
2069
- } else {
2070
- $name = $this->name;
2071
- }
2072
-
2073
- $hookname = plugin_basename( $this->file );
2074
-
2075
- $links = array();
2076
- $url = '';
2077
- if ( function_exists( 'menu_page_url' ) ) {
2078
- $url = menu_page_url( $hookname, 0 );
2079
- }
2080
- if ( empty( $url ) ) {
2081
- $url = esc_url( admin_url( 'admin.php?page=' . $hookname ) );
2082
- }
2083
-
2084
- $parent = is_admin() ? AIOSEOP_PLUGIN_DIRNAME : 'aioseop-settings';
2085
-
2086
- if ( null === $this->locations ) {
2087
- array_unshift(
2088
- $links,
2089
- array(
2090
- 'parent' => $parent,
2091
- 'title' => $name,
2092
- 'id' => $hookname,
2093
- 'href' => $url,
2094
- 'order' => $this->menu_order(),
2095
- )
2096
- );
2097
- } else {
2098
- foreach ( $this->locations as $k => $v ) {
2099
- if ( 'settings' === $v['type'] ) {
2100
- if ( 'default' === $k ) {
2101
- array_unshift(
2102
- $links,
2103
- array(
2104
- 'parent' => $parent,
2105
- 'title' => $name,
2106
- 'id' => $hookname,
2107
- 'href' => $url,
2108
- 'order' => $this->menu_order(),
2109
- )
2110
- );
2111
- } else {
2112
- if ( ! empty( $v['menu_name'] ) ) {
2113
- $name = $v['menu_name'];
2114
- } else {
2115
- $name = $v['name'];
2116
- }
2117
- array_unshift(
2118
- $links,
2119
- array(
2120
- 'parent' => $parent,
2121
- 'title' => $name,
2122
- 'id' => $this->get_prefix( $k ) . $k,
2123
- 'href' => esc_url( admin_url( 'admin.php?page=' . $this->get_prefix( $k ) . $k ) ),
2124
- 'order' => $this->menu_order(),
2125
- )
2126
- );
2127
- }
2128
- }
2129
- }
2130
- }
2131
-
2132
- return $links;
2133
- }
2134
-
2135
- function add_admin_bar_submenu() {
2136
- global $aioseop_admin_menu, $wp_admin_bar;
2137
-
2138
- if ( $aioseop_admin_menu ) {
2139
- $links = $this->get_admin_links();
2140
- if ( ! empty( $links ) ) {
2141
- foreach ( $links as $l ) {
2142
- $wp_admin_bar->add_menu( $l );
2143
- }
2144
- }
2145
- }
2146
- }
2147
-
2148
- /**
2149
- * Collect metabox data together for tabbed metaboxes.
2150
- *
2151
- * @param $args
2152
- *
2153
- * @return array
2154
- */
2155
- function filter_return_metaboxes( $args ) {
2156
- return array_merge( $args, $this->post_metaboxes );
2157
- }
2158
-
2159
- /** Add submenu for module, call page hooks, set up metaboxes.
2160
- *
2161
- * @param $parent_slug
2162
- *
2163
- * @return bool
2164
- */
2165
- function add_menu( $parent_slug ) {
2166
- if ( ! empty( $this->menu_name ) ) {
2167
- $name = $this->menu_name;
2168
- } else {
2169
- $name = $this->name;
2170
- }
2171
-
2172
- // Don't add unlicensed addons to admin menu.
2173
- if ( null === $name ) {
2174
- return;
2175
- }
2176
-
2177
- if ( null === $this->locations ) {
2178
- $hookname = add_submenu_page(
2179
- $parent_slug,
2180
- $name,
2181
- $name,
2182
- apply_filters( 'manage_aiosp', 'aiosp_manage_seo' ),
2183
- plugin_basename( $this->file ),
2184
- array(
2185
- $this,
2186
- 'display_settings_page',
2187
- )
2188
- );
2189
- add_action( "load-{$hookname}", array( $this, 'add_page_hooks' ) );
2190
-
2191
- return true;
2192
- }
2193
- foreach ( $this->locations as $k => $v ) {
2194
- if ( 'settings' === $v['type'] ) {
2195
- if ( 'default' === $k ) {
2196
- if ( ! empty( $this->menu_name ) ) {
2197
- $name = $this->menu_name;
2198
- } else {
2199
- $name = $this->name;
2200
- }
2201
- $hookname = add_submenu_page(
2202
- $parent_slug,
2203
- $name,
2204
- $name,
2205
- apply_filters( 'manage_aiosp', 'aiosp_manage_seo' ),
2206
- plugin_basename( $this->file ),
2207
- array(
2208
- $this,
2209
- 'display_settings_page',
2210
- )
2211
- );
2212
- } else {
2213
- if ( ! empty( $v['menu_name'] ) ) {
2214
- $name = $v['menu_name'];
2215
- } else {
2216
- $name = $v['name'];
2217
- }
2218
- $hookname = add_submenu_page(
2219
- $parent_slug,
2220
- $name,
2221
- $name,
2222
- apply_filters( 'manage_aiosp', 'aiosp_manage_seo' ),
2223
- $this->get_prefix( $k ) . $k,
2224
- array(
2225
- $this,
2226
- "display_settings_page_$k",
2227
- )
2228
- );
2229
- }
2230
- add_action( "load-{$hookname}", array( $this, 'add_page_hooks' ) );
2231
- } elseif ( 'metabox' === $v['type'] ) {
2232
- // hack -- make sure this runs anyhow, for now -- pdb.
2233
- $this->setting_options( $k );
2234
- $this->toggle_save_post_hooks( true );
2235
- if ( isset( $v['display'] ) && ! empty( $v['display'] ) ) {
2236
- add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_metabox_scripts' ), 5 );
2237
- if ( $this->tabbed_metaboxes ) {
2238
- add_filter( 'aioseop_add_post_metabox', array( $this, 'filter_return_metaboxes' ) );
2239
- }
2240
- foreach ( $v['display'] as $posttype ) {
2241
- $v['location'] = $k;
2242
- $v['posttype'] = $posttype;
2243
-
2244
- if ( post_type_exists( $posttype ) ) {
2245
- // Metabox priority/context on edit post screen.
2246
- $v['context'] = apply_filters( 'aioseop_post_metabox_context', 'normal' );
2247
- $v['priority'] = apply_filters( 'aioseop_post_metabox_priority', 'high' );
2248
- }
2249
- if ( false !== strpos( $posttype, 'edit-' ) ) {
2250
- // Metabox priority/context on edit taxonomy screen.
2251
- $v['context'] = 'advanced';
2252
- $v['priority'] = 'default';
2253
- }
2254
-
2255
- // Metabox priority for everything else.
2256
- if ( ! isset( $v['context'] ) ) {
2257
- $v['context'] = 'advanced';
2258
- }
2259
- if ( ! isset( $v['priority'] ) ) {
2260
- $v['priority'] = 'default';
2261
- }
2262
-
2263
- if ( $this->tabbed_metaboxes ) {
2264
- $this->post_metaboxes[] = array(
2265
- 'id' => $v['prefix'] . $k,
2266
- 'title' => $v['name'],
2267
- 'callback' => array( $this, 'display_metabox' ),
2268
- 'post_type' => $posttype,
2269
- 'context' => $v['context'],
2270
- 'priority' => $v['priority'],
2271
- 'callback_args' => $v,
2272
- );
2273
- } else {
2274
- $title = $v['name'];
2275
- if ( $title != $this->plugin_name ) {
2276
- $title = $this->plugin_name . ' - ' . $title;
2277
- }
2278
- if ( ! empty( $v['help_link'] ) ) {
2279
- $title .= "<a class='aioseop_help_text_link aioseop_meta_box_help' target='_blank' href='" . $lopts['help_link'] . "'><span>" .
2280
- /* translators: This string is used as an action link which users can click on to view the relevant documentation on our website. */
2281
- __( 'Help', 'all-in-one-seo-pack' ) . '</span></a>';
2282
- }
2283
- add_meta_box(
2284
- $v['prefix'] . $k,
2285
- $title,
2286
- array(
2287
- $this,
2288
- 'display_metabox',
2289
- ),
2290
- $posttype,
2291
- $v['context'],
2292
- $v['priority'],
2293
- $v
2294
- );
2295
- }
2296
- }
2297
- }
2298
- }
2299
- }
2300
- }
2301
-
2302
- /**
2303
- * Adds or removes hooks that could be called while editing a post.
2304
- *
2305
- * TODO: Review if all these hooks are really required (save_post should be enough vs. edit_post and publish_post).
2306
- */
2307
- private function toggle_save_post_hooks( $add ) {
2308
- if ( $add ) {
2309
- add_action( 'edit_post', array( $this, 'save_post_data' ) );
2310
- add_action( 'publish_post', array( $this, 'save_post_data' ) );
2311
- add_action( 'add_attachment', array( $this, 'save_post_data' ) );
2312
- add_action( 'edit_attachment', array( $this, 'save_post_data' ) );
2313
- add_action( 'save_post', array( $this, 'save_post_data' ) );
2314
- add_action( 'edit_page_form', array( $this, 'save_post_data' ) );
2315
- } else {
2316
- remove_action( 'edit_post', array( $this, 'save_post_data' ) );
2317
- remove_action( 'publish_post', array( $this, 'save_post_data' ) );
2318
- remove_action( 'add_attachment', array( $this, 'save_post_data' ) );
2319
- remove_action( 'edit_attachment', array( $this, 'save_post_data' ) );
2320
- remove_action( 'save_post', array( $this, 'save_post_data' ) );
2321
- remove_action( 'edit_page_form', array( $this, 'save_post_data' ) );
2322
- }
2323
- }
2324
-
2325
- /**
2326
- * Update postmeta for metabox.
2327
- *
2328
- * @param $post_id
2329
- */
2330
- function save_post_data( $post_id ) {
2331
- $this->toggle_save_post_hooks( false );
2332
- if ( null !== $this->locations ) {
2333
- foreach ( $this->locations as $k => $v ) {
2334
- if ( isset( $v['type'] ) && ( 'metabox' === $v['type'] ) ) {
2335
- $opts = $this->default_options( $k );
2336
- $options = array();
2337
- foreach ( $opts as $l => $o ) {
2338
- if ( isset( $_POST[ $l ] ) ) {
2339
- $options[ $l ] = stripslashes_deep( $_POST[ $l ] );
2340
- $options[ $l ] = esc_attr( $options[ $l ] );
2341
- }
2342
- }
2343
- $prefix = $this->get_prefix( $k );
2344
- $options = apply_filters( $prefix . 'filter_metabox_options', $options, $k, $post_id );
2345
- foreach ( $options as $option ) {
2346
- $option = aioseop_sanitize( $option );
2347
- }
2348
- update_post_meta( $post_id, '_' . $prefix . $k, $options );
2349
- }
2350
- }
2351
- }
2352
-
2353
- $this->toggle_save_post_hooks( true );
2354
- }
2355
-
2356
- /**
2357
- * Outputs radio buttons, checkboxes, selects, multiselects, handles groups.
2358
- *
2359
- * @param $args
2360
- *
2361
- * @return string
2362
- */
2363
- function do_multi_input( $args ) {
2364
- $options = $args['options'];
2365
- $value = $args['value'];
2366
- $name = $args['name'];
2367
- $attr = $args['attr'];
2368
-
2369
- $buf1 = '';
2370
- $type = $options['type'];
2371
-
2372
- $strings = array(
2373
- 'block' => "<select name='$name' $attr>%s\n</select>\n",
2374
- 'group' => "\t<optgroup label='%s'>\n%s\t</optgroup>\n",
2375
- 'item' => "\t<option %s value='%s'>%s</option>\n",
2376
- 'item_args' => array( 'sel', 'v', 'subopt' ),
2377
- 'selected' => 'selected ',
2378
- );
2379
-
2380
- if ( ( 'radio' === $type ) || ( 'checkbox' === $type ) ) {
2381
- $strings = array(
2382
- 'block' => "%s\n",
2383
- 'group' => "\t<b>%s</b><br>\n%s\n",
2384
- 'item' => "\t<label class='aioseop_option_setting_label'><input type='$type' %s name='%s' value='%s' %s> %s</label>\n",
2385
- 'item_args' => array( 'sel', 'name', 'v', 'attr', 'subopt' ),
2386
- 'selected' => 'checked ',
2387
- );
2388
- }
2389
-
2390
- $setsel = $strings['selected'];
2391
- if ( isset( $options['initial_options'] ) && is_array( $options['initial_options'] ) ) {
2392
- foreach ( $options['initial_options'] as $l => $option ) {
2393
- $option_check = strip_tags( is_array( $option ) ? implode( ' ', $option ) : $option );
2394
- if ( empty( $l ) && empty( $option_check ) ) {
2395
- continue;
2396
- }
2397
- $is_group = is_array( $option );
2398
- if ( ! $is_group ) {
2399
- $option = array( $l => $option );
2400
- }
2401
- $buf2 = '';
2402
- foreach ( $option as $v => $subopt ) {
2403
- $sel = '';
2404
- $is_arr = is_array( $value );
2405
- if ( is_string( $v ) || is_string( $value ) ) {
2406
- if ( is_string( $value ) ) {
2407
- $cmp = ! strcmp( $v, $value );
2408
- } else {
2409
- $cmp = ! strcmp( $v, '' );
2410
- }
2411
- // $cmp = !strcmp( (string)$v, (string)$value );
2412
- } else {
2413
- $cmp = ( $value == $v );
2414
- }
2415
- if ( ( ! $is_arr && $cmp ) || ( $is_arr && in_array( $v, $value ) ) ) {
2416
- $sel = $setsel;
2417
- }
2418
- $item_arr = array();
2419
- foreach ( $strings['item_args'] as $arg ) {
2420
- $item_arr[] = $$arg;
2421
- }
2422
- $buf2 .= vsprintf( $strings['item'], $item_arr );
2423
- }
2424
- if ( $is_group ) {
2425
- $buf1 .= sprintf( $strings['group'], $l, $buf2 );
2426
- } else {
2427
- $buf1 .= $buf2;
2428
- }
2429
- }
2430
- $buf1 = sprintf( $strings['block'], $buf1 );
2431
- }
2432
-
2433
- return $buf1;
2434
- }
2435
-
2436
- /**
2437
- * Get Option HTML
2438
- *
2439
- * Outputs a setting item for settings pages and metaboxes.
2440
- *
2441
- * @since ?
2442
- * @since 2.12 Add 'input' to allowed tags with 'html'. #2157
2443
- *
2444
- * @param array $args {
2445
- * Contains the admin option element values and attributes for rendering.
2446
- *
2447
- * @type string $attr The HTML element's attributes to render within the element.
2448
- * @type string $name THE HTML element's name attribute. Used with form input elements.
2449
- * @type string $prefix Optional. The AIOSEOP Module prefix.
2450
- * @type string $value The HTML element's value attribute.
2451
- * @type array $options {
2452
- * Arguments used for this function/method operations and rendering.
2453
- *
2454
- * @type string $class Optional. The HTML element's class attribute. This is used if
2455
- * `$options['count']` is not empty.
2456
- * @type int $cols Optional. Character count length of column.
2457
- * @type boolean $count Optional. Determines whether to add the character count for SEO.
2458
- * @type string $count_desc Optional. The description/help text to rend to the admin.
2459
- * @type string $name Optional. Used within the description/help text when it's for character count.
2460
- * @type boolean $required Optional. Determines whether to require a value in the input element.
2461
- * @type int $rows Optional. Number of rows to multiply with cols.
2462
- * @type string $type Which Switch Case (HTML element) to use.
2463
- * }
2464
- * }
2465
- * @return string
2466
- */
2467
- function get_option_html( $args ) {
2468
- static $n = 0;
2469
-
2470
- $options = $args['options'];
2471
- $value = $args['value'];
2472
- $name = $args['name'];
2473
- $attr = $args['attr'];
2474
- $prefix = isset( $args['prefix'] ) ? $args['prefix'] : '';
2475
-
2476
- if ( 'custom' == $options['type'] ) {
2477
- return apply_filters( "{$prefix}output_option", '', $args );
2478
- }
2479
- if ( in_array(
2480
- $options['type'],
2481
- array(
2482
- 'multiselect',
2483
- 'select',
2484
- 'multicheckbox',
2485
- 'radio',
2486
- 'checkbox',
2487
- 'textarea',
2488
- 'text',
2489
- 'submit',
2490
- 'hidden',
2491
- 'date',
2492
- )
2493
- ) && is_string( $value )
2494
- ) {
2495
- $value = esc_attr( $value );
2496
- }
2497
- $buf = '';
2498
- $onload = '';
2499
- if ( ! empty( $options['count'] ) ) {
2500
- $n ++;
2501
- $classes = isset( $options['class'] ) ? $options['class'] : '';
2502
- $classes .= ' aioseop_count_chars';
2503
- $attr .= " class='{$classes}' data-length-field='{$prefix}length$n'";
2504
- }
2505
- if ( isset( $opts['id'] ) ) {
2506
- $attr .= " id=\"{$opts['id']}\" ";
2507
- }
2508
- if ( isset( $options['required'] ) && true === $options['required'] ) {
2509
- $attr .= ' required';
2510
- }
2511
- switch ( $options['type'] ) {
2512
- case 'multiselect':
2513
- $attr .= ' MULTIPLE';
2514
- $args['attr'] = $attr;
2515
- $name = "{$name}[]";
2516
- $args['name'] = $name;
2517
- // fall through.
2518
- case 'select':
2519
- $buf .= $this->do_multi_input( $args );
2520
- break;
2521
- case 'multicheckbox':
2522
- $name = "{$name}[]";
2523
- $args['name'] = $name;
2524
- $args['options']['type'] = 'checkbox';
2525
- $options['type'] = 'checkbox';
2526
- // fall through.
2527
- case 'radio':
2528
- $buf .= $this->do_multi_input( $args );
2529
- break;
2530
- case 'checkbox':
2531
- if ( $value ) {
2532
- $attr .= ' CHECKED';
2533
- }
2534
- $buf .= "<input name='$name' type='{$options['type']}' $attr>\n";
2535
- break;
2536
- case 'textarea':
2537
- // #1363: prevent characters like ampersand in title and description (in social meta module) from getting changed to &amp;
2538
- if ( in_array( $name, array( 'aiosp_description', 'aiosp_opengraph_hometitle', 'aiosp_opengraph_description' ), true ) ) {
2539
- $value = htmlspecialchars_decode( $value, ENT_QUOTES );
2540
- }
2541
- $buf .= "<textarea name='$name' $attr>$value</textarea>";
2542
- break;
2543
- case 'address':
2544
- $address_defaults = array(
2545
- 'street_address' => '',
2546
- 'address_locality' => '',
2547
- 'address_region' => '',
2548
- 'postal_code' => '',
2549
- 'address_country' => '',
2550
- );
2551
- $value = wp_parse_args( $value, $address_defaults );
2552
-
2553
- $buf .= '
2554
- <label for="' . $name . '_street_address" class="aioseop_label_street_address">' . __( 'Street Address', 'all-in-one-seo-pack' ) . '</label>
2555
- <input name="' . $name . '_street_address" class="aioseop_input_street_address" type="text" ' . $attr . ' value="' . $value['street_address'] . '" />
2556
- <label for="' . $name . '_address_locality" class="aioseop_label_address_locality">' . __( 'City', 'all-in-one-seo-pack' ) . '</label>
2557
- <input name="' . $name . '_address_locality" class="aioseop_input_address_locality" type="text" ' . $attr . ' value="' . $value['address_locality'] . '" />
2558
- <label for="' . $name . '_address_region" class="aioseop_label_address_region">' . __( 'State', 'all-in-one-seo-pack' ) . '</label>
2559
- <input name="' . $name . '_address_region" class="aioseop_input_address_region" type="text" ' . $attr . ' value="' . $value['address_region'] . '" />
2560
- <label for="' . $name . '_postal_code" class="aioseop_label_postal_code">' . __( 'Zip code', 'all-in-one-seo-pack' ) . '</label>
2561
- <input name="' . $name . '_postal_code" class="aioseop_input_postal_code" type="text" ' . $attr . ' value="' . $value['postal_code'] . '" />
2562
- <label for="' . $name . '_address_country" class="aioseop_label_address_country">' . __( 'Country', 'all-in-one-seo-pack' ) . '</label>
2563
- <input name="' . $name . '_address_country" class="aioseop_input_address_country" type="text" ' . $attr . ' value="' . $value['address_country'] . '" />
2564
- ';
2565
- $buf = '<div class="aioseop_postal_address">' . $buf . '</div>';
2566
- break;
2567
- case 'image':
2568
- $buf .= '<input class="aioseop_upload_image_checker" type="hidden" name="' . $name . '_checker" value="0">' .
2569
- "<input class='aioseop_upload_image_button button-primary' type='button' value='";
2570
- $buf .= __( 'Upload Image', 'all-in-one-seo-pack' );
2571
- $buf .= "' />" .
2572
- "<input class='aioseop_upload_image_label' name='" . esc_attr( $name ) . "' type='text' " . esc_html( $attr ) . " value='" . esc_attr( $value ) . "' size=57 style='float:left;clear:left;'>\n";
2573
- break;
2574
- case 'html':
2575
- $allowed_tags = wp_kses_allowed_html( 'post' );
2576
- $allowed_tags['input'] = array(
2577
- 'name' => true,
2578
- 'type' => true,
2579
- 'value' => true,
2580
- 'class' => true,
2581
- 'placeholder' => true,
2582
- );
2583
-
2584
- $buf .= wp_kses( $value, $allowed_tags );
2585
- break;
2586
- case 'esc_html':
2587
- $buf .= '<pre>' . esc_html( $value ) . "</pre>\n";
2588
- break;
2589
- case 'date':
2590
- // firefox and IE < 11 do not have support for HTML5 date, so we will fall back to the datepicker.
2591
- wp_enqueue_script( 'jquery-ui-datepicker' );
2592
- // fall through.
2593
- default:
2594
- if ( 'number' === $options['type'] ) {
2595
- $value = intval( $value );
2596
- }
2597
- $buf .= "<input name='" . esc_attr( $name ) . "' type='" . esc_attr( $options['type'] ) . "' " . wp_kses( $attr, wp_kses_allowed_html( 'data' ) ) . " value='" . htmlspecialchars_decode( $value ) . "' autocomplete='aioseop-" . time() . "'>\n";
2598
- }
2599
-
2600
- // TODO Maybe Change/Add a function for SEO character count.
2601
- if ( ! empty( $options['count'] ) ) {
2602
- $size = 60;
2603
- if ( isset( $options['size'] ) ) {
2604
- $size = $options['size'];
2605
- } elseif ( isset( $options['rows'] ) && isset( $options['cols'] ) ) {
2606
- $size = $options['rows'] * $options['cols'];
2607
- }
2608
- if ( isset( $options['count_desc'] ) ) {
2609
- $count_desc = $options['count_desc'];
2610
- } else {
2611
- /* translators: %1$s and %2$s are placeholders and should not be translated. %1$s is replaced with a number, %2$s is replaced with the name of an meta tag field (e.g; "Title", "Description", etc.). */
2612
- $count_desc = __( ' characters. Most search engines use a maximum of %1$s chars for the %2$s.', 'all-in-one-seo-pack' );
2613
- }
2614
- $buf .= "<br /><input readonly tabindex='-1' type='text' name='{$prefix}length$n' size='3' maxlength='3' style='width:53px;height:23px;margin:0px;padding:0px 0px 0px 10px;' value='" . AIOSEOP_PHP_Functions::strlen( $value ) . "' />"
2615
- . sprintf( $count_desc, $size, trim( AIOSEOP_PHP_Functions::strtolower( $options['name'] ), ':' ) );
2616
- if ( ! empty( $onload ) ) {
2617
- $buf .= "<script>jQuery( document ).ready(function() { {$onload} });</script>";
2618
- }
2619
- }
2620
-
2621
- return $buf;
2622
- }
2623
-
2624
- /**
2625
- * Format a row for an option on a settings page.
2626
- *
2627
- * @since ?
2628
- * @since 3.0 Added Helper Class for jQuery Tooltips. #1850
2629
- *
2630
- * @param $name
2631
- * @param $opts
2632
- * @param $args
2633
- *
2634
- * @return string
2635
- */
2636
- function get_option_row( $name, $opts, $args ) {
2637
- $label_text = '';
2638
- $input_attr = '';
2639
- $id_attr = '';
2640
-
2641
- require_once( AIOSEOP_PLUGIN_DIR . 'admin/class-aioseop-helper.php' );
2642
- $info = new AIOSEOP_Helper( get_class( $this ) );
2643
-
2644
- $align = 'right';
2645
- if ( 'top' == $opts['label'] ) {
2646
- $align = 'left';
2647
- }
2648
- if ( isset( $opts['id'] ) ) {
2649
- $id_attr .= " id=\"{$opts['id']}_div\" ";
2650
- }
2651
- if ( 'none' != $opts['label'] ) {
2652
- $tmp_help_text = $info->get_help_text( $name );
2653
- if ( isset( $tmp_help_text ) && ! empty( $tmp_help_text ) ) {
2654
- $display_help = '<a tabindex="0" class="aioseop_help_text_link" style="cursor: help;" title="<h4 aria-hidden>%2$s:</h4> %1$s"></a><label class="aioseop_label textinput">%2$s</label>';
2655
- $help_text = sprintf( $display_help, $info->get_help_text( $name ), $opts['name'] );
2656
- } else {
2657
- $help_text = $opts['name'];
2658
- }
2659
-
2660
- // TODO Possible remove text align.
2661
- // Currently aligns to the right when everything is being aligned to the left; which is usually a workaround.
2662
- $display_label_format = '<span class="aioseop_option_label" style="text-align:%s;vertical-align:top;">%s</span>';
2663
- $label_text = sprintf( $display_label_format, $align, $help_text );
2664
- } else {
2665
- $input_attr .= ' aioseop_no_label ';
2666
- }
2667
- if ( 'top' == $opts['label'] ) {
2668
- $label_text .= "</div><div class='aioseop_input aioseop_top_label'>";
2669
- }
2670
- $input_attr .= " aioseop_{$opts['type']}_type";
2671
-
2672
- $display_row_template = '<div class="aioseop_wrapper%s" id="%s_wrapper"><div class="aioseop_input">%s<div class="aioseop_option_input"><div class="aioseop_option_div" %s>%s</div></div><p style="clear:left"></p></div></div>';
2673
- return sprintf( $display_row_template, $input_attr, $name, $label_text, $id_attr, $this->get_option_html( $args ) );
2674
- }
2675
-
2676
- /**
2677
- * Display options for settings pages and metaboxes, allows for filtering settings, custom display options.
2678
- *
2679
- * @param null $location
2680
- * @param null $meta_args
2681
- */
2682
- function display_options( $location = null, $meta_args = null ) {
2683
- static $location_settings = array();
2684
-
2685
- $defaults = null;
2686
- $prefix = $this->get_prefix( $location );
2687
- $help_link = '';
2688
-
2689
- if ( is_array( $meta_args['args'] ) && ! empty( $meta_args['args']['default_options'] ) ) {
2690
- $defaults = $meta_args['args']['default_options'];
2691
- }
2692
- if ( ! empty( $meta_args['callback_args'] ) && ! empty( $meta_args['callback_args']['help_link'] ) ) {
2693
- $help_link = $meta_args['callback_args']['help_link'];
2694
- }
2695
- if ( ! empty( $help_link ) ) {
2696
- echo "<a class='aioseop_help_text_link aioseop_meta_box_help' target='_blank' href='" . $help_link . "'><span>" . __( 'Help', 'all-in-one-seo-pack' ) . '</span></a>';
2697
- }
2698
-
2699
- if ( ! isset( $location_settings[ $prefix ] ) ) {
2700
- $current_options = apply_filters( "{$this->prefix}display_options", $this->get_current_options( array(), $location, $defaults ), $location );
2701
- $settings = apply_filters( "{$this->prefix}display_settings", $this->setting_options( $location, $defaults ), $location, $current_options );
2702
- $current_options = apply_filters( "{$this->prefix}override_options", $current_options, $location, $settings );
2703
- $location_settings[ $prefix ]['current_options'] = $current_options;
2704
- $location_settings[ $prefix ]['settings'] = $settings;
2705
- } else {
2706
- $current_options = $location_settings[ $prefix ]['current_options'];
2707
- $settings = $location_settings[ $prefix ]['settings'];
2708
- }
2709
- // $opts["snippet"]["default"] = sprintf( $opts["snippet"]["default"], "foo", "bar", "moby" );
2710
- $container = "<div class='aioseop aioseop_options {$this->prefix}settings'>";
2711
- if ( is_array( $meta_args['args'] ) && ! empty( $meta_args['args']['options'] ) ) {
2712
- $args = array();
2713
- $arg_keys = array();
2714
- foreach ( $meta_args['args']['options'] as $a ) {
2715
- if ( ! empty( $location ) ) {
2716
- $key = $prefix . $location . '_' . $a;
2717
- if ( ! isset( $settings[ $key ] ) ) {
2718
- $key = $a;
2719
- }
2720
- } else {
2721
- $key = $prefix . $a;
2722
- }
2723
- if ( isset( $settings[ $key ] ) ) {
2724
- $arg_keys[ $key ] = 1;
2725
- } elseif ( isset( $settings[ $a ] ) ) {
2726
- $arg_keys[ $a ] = 1;
2727
- }
2728
- }
2729
- $setting_keys = array_keys( $settings );
2730
- foreach ( $setting_keys as $s ) {
2731
- if ( ! empty( $arg_keys[ $s ] ) ) {
2732
- $args[ $s ] = $settings[ $s ];
2733
- }
2734
- }
2735
- } else {
2736
- $args = $settings;
2737
- }
2738
- foreach ( $args as $name => $opts ) {
2739
- // List of valid element attributes.
2740
- $attr_list = array( 'class', 'style', 'readonly', 'disabled', 'size', 'placeholder', 'autocomplete' );
2741
- if ( 'textarea' == $opts['type'] ) {
2742
- $attr_list = array_merge( $attr_list, array( 'rows', 'cols' ) );
2743
- }
2744
-
2745
- // Set element attribute values.
2746
- $attr = '';
2747
- foreach ( $attr_list as $a ) {
2748
- if ( isset( $opts[ $a ] ) ) {
2749
- $attr .= ' ' . $a . '="' . esc_attr( $opts[ $a ] ) . '" ';
2750
- }
2751
- }
2752
-
2753
- $opt = '';
2754
- if ( isset( $current_options[ $name ] ) ) {
2755
- $opt = $current_options[ $name ];
2756
- }
2757
- if ( 'none' == $opts['label'] && 'submit' == $opts['type'] && false == $opts['save'] ) {
2758
- $opt = $opts['name'];
2759
- }
2760
- if ( 'html' == $opts['type'] && empty( $opt ) && false == $opts['save'] ) {
2761
- $opt = $opts['default'];
2762
- }
2763
-
2764
- $newArgs = array(
2765
- 'name' => $name,
2766
- 'options' => $opts,
2767
- 'attr' => $attr,
2768
- 'value' => $opt,
2769
- 'prefix' => $prefix,
2770
- );
2771
- if ( ! empty( $opts['nowrap'] ) ) {
2772
- echo $this->get_option_html( $newArgs );
2773
- } else {
2774
- if ( $container ) {
2775
- echo $container;
2776
- $container = '';
2777
- }
2778
- echo $this->get_option_row( $name, $opts, $newArgs );
2779
- }
2780
- }
2781
- if ( ! $container ) {
2782
- echo '</div>';
2783
- }
2784
- }
2785
-
2786
- /**
2787
- * Sanitize Domain
2788
- *
2789
- * @since ?
2790
- *
2791
- * @param $domain
2792
- * @return mixed|string
2793
- */
2794
- function sanitize_domain( $domain ) {
2795
- $domain = trim( $domain );
2796
- $domain = AIOSEOP_PHP_Functions::strtolower( $domain );
2797
- if ( 0 === AIOSEOP_PHP_Functions::strpos( $domain, 'http://' ) ) {
2798
- $domain = AIOSEOP_PHP_Functions::substr( $domain, 7 );
2799
- } elseif ( 0 === AIOSEOP_PHP_Functions::strpos( $domain, 'https://' ) ) {
2800
- $domain = AIOSEOP_PHP_Functions::substr( $domain, 8 );
2801
- }
2802
- $domain = untrailingslashit( $domain );
2803
-
2804
- return $domain;
2805
- }
2806
-
2807
- /** Sanitize options
2808
- *
2809
- * @param null $location
2810
- */
2811
- function sanitize_options( $location = null ) {
2812
- foreach ( $this->setting_options( $location ) as $k => $v ) {
2813
- if ( isset( $this->options[ $k ] ) ) {
2814
- if ( ! empty( $v['sanitize'] ) ) {
2815
- $type = $v['sanitize'];
2816
- } else {
2817
- $type = $v['type'];
2818
- }
2819
- switch ( $type ) {
2820
- case 'multiselect':
2821
- // fall through.
2822
- case 'multicheckbox':
2823
- $this->options[ $k ] = urlencode_deep( $this->options[ $k ] );
2824
- break;
2825
- case 'textarea':
2826
- // #1363: prevent characters like ampersand in title and description (in social meta module) from getting changed to &amp;
2827
- if ( ! ( 'opengraph' === $location && in_array( $k, array( 'aiosp_opengraph_hometitle', 'aiosp_opengraph_description' ), true ) ) ) {
2828
- $this->options[ $k ] = wp_kses_post( $this->options[ $k ] );
2829
- }
2830
- $this->options[ $k ] = htmlspecialchars( $this->options[ $k ], ENT_QUOTES, 'UTF-8' );
2831
- break;
2832
- case 'filename':
2833
- $this->options[ $k ] = sanitize_file_name( $this->options[ $k ] );
2834
- break;
2835
- case 'address':
2836
- foreach ( $this->options[ $k ] as &$address_value ) {
2837
- $address_value = wp_kses_post( $address_value );
2838
- }
2839
- break;
2840
- case 'url':
2841
- // fall through.
2842
- case 'text':
2843
- $this->options[ $k ] = wp_kses_post( $this->options[ $k ] );
2844
- // fall through.
2845
- case 'checkbox':
2846
- // fall through.
2847
- case 'radio':
2848
- // fall through.
2849
- case 'select':
2850
- // fall through.
2851
- default:
2852
- if ( ! is_array( $this->options[ $k ] ) ) {
2853
- $this->options[ $k ] = esc_attr( $this->options[ $k ] );
2854
- }
2855
- }
2856
- }
2857
- }
2858
- }
2859
-
2860
- /**
2861
- * Display metaboxes with display_options()
2862
- *
2863
- * @param $post
2864
- * @param $metabox
2865
- */
2866
- function display_metabox( $post, $metabox ) {
2867
- $this->display_options( $metabox['args']['location'], $metabox );
2868
- }
2869
-
2870
- /**
2871
- * Handle resetting options to defaults.
2872
- *
2873
- * @param null $location
2874
- * @param bool $delete
2875
- */
2876
- function reset_options( $location = null, $delete = false ) {
2877
- if ( true === $delete ) {
2878
- $this->delete_class_option( $delete );
2879
- $this->options = array();
2880
- }
2881
- $default_options = $this->default_options( $location );
2882
- foreach ( $default_options as $k => $v ) {
2883
- $this->options[ $k ] = $v;
2884
- }
2885
- $this->update_class_option( $this->options );
2886
- }
2887
-
2888
- /**
2889
- * Handle Settings Updates
2890
- *
2891
- * Handle option resetting and updating.
2892
- *
2893
- * @since ?
2894
- *
2895
- * @param null $location
2896
- * @return mixed|string|void
2897
- */
2898
- function handle_settings_updates( $location = null ) {
2899
- $message = '';
2900
- if (
2901
- (
2902
- isset( $_POST['action'] ) &&
2903
- 'aiosp_update_module' == $_POST['action'] &&
2904
- (
2905
- isset( $_POST['Submit_Default'] ) ||
2906
- isset( $_POST['Submit_All_Default'] ) ||
2907
- ! empty( $_POST['Submit'] )
2908
- )
2909
- )
2910
- ) {
2911
- $nonce = $_POST['nonce-aioseop'];
2912
- if ( ! wp_verify_nonce( $nonce, 'aioseop-nonce' ) ) {
2913
- die( __( 'Security Check - If you receive this in error, log out and back in to WordPress', 'all-in-one-seo-pack' ) );
2914
- }
2915
- if ( isset( $_POST['Submit_Default'] ) || isset( $_POST['Submit_All_Default'] ) ) {
2916
- /* translators: This message confirms that the options have been reset. */
2917
- $message = __( 'Options Reset.', 'all-in-one-seo-pack' );
2918
- if ( isset( $_POST['Submit_All_Default'] ) ) {
2919
- $this->reset_options( $location, true );
2920
- do_action( 'aioseop_options_reset' );
2921
- } else {
2922
- $this->reset_options( $location );
2923
- }
2924
- }
2925
- if ( ! empty( $_POST['Submit'] ) ) {
2926
- /* translators: %s is a placeholder and will be replace with the name of the plugin. */
2927
- $message = sprintf( __( '%s Options Updated.', 'all-in-one-seo-pack' ), AIOSEOP_PLUGIN_NAME );
2928
- $default_options = $this->default_options( $location );
2929
- $prefix = $this->prefix;
2930
- foreach ( $this->default_options as $k => $option_arr ) {
2931
- if ( isset( $option_arr['type'] ) && 'address' === $option_arr['type'] ) {
2932
- $address_values = array(
2933
- 'street_address' => '',
2934
- 'address_locality' => '',
2935
- 'address_region' => '',
2936
- 'postal_code' => '',
2937
- 'address_country' => '',
2938
- );
2939
- foreach ( $address_values as $address_key => &$address_value ) {
2940
- if ( isset( $_POST[ $prefix . $k . '_' . $address_key ] ) ) {
2941
- $address_value = stripslashes_deep( $_POST[ $prefix . $k . '_' . $address_key ] );
2942
- }
2943
- }
2944
- $this->options[ $prefix . $k ] = $address_values;
2945
- } elseif ( isset( $_POST[ $prefix . $k ] ) ) {
2946
- $this->options[ $prefix . $k ] = stripslashes_deep( $_POST[ $prefix . $k ] );
2947
- } else {
2948
- $this->options[ $prefix . $k ] = '';
2949
- }
2950
- }
2951
- $this->sanitize_options( $location );
2952
- $this->options = apply_filters( $this->prefix . 'update_options', $this->options, $location );
2953
- $this->update_class_option( $this->options );
2954
- wp_cache_flush();
2955
- }
2956
- do_action( $this->prefix . 'settings_update', $this->options, $location );
2957
- }
2958
-
2959
- return $message;
2960
- }
2961
-
2962
- /** Update / reset settings, printing options, sanitizing, posting back
2963
- *
2964
- * @param null $location
2965
- */
2966
- function display_settings_page( $location = null ) {
2967
- if ( null != $location ) {
2968
- $location_info = $this->locations[ $location ];
2969
- }
2970
- $name = null;
2971
- if ( $location && isset( $location_info['name'] ) ) {
2972
- $name = $location_info['name'];
2973
- }
2974
- if ( ! $name ) {
2975
- $name = $this->name;
2976
- }
2977
- $message = $this->handle_settings_updates( $location );
2978
- $this->settings_page_init();
2979
- ?>
2980
- <div class="wrap <?php echo get_class( $this ); ?>">
2981
- <?php
2982
- ob_start();
2983
- do_action( $this->prefix . 'settings_header_errors', $location );
2984
- $errors = ob_get_clean();
2985
- echo $errors;
2986
- ?>
2987
- <div id="aioseop_settings_header">
2988
- <?php
2989
- if ( ! empty( $message ) && empty( $errors ) ) {
2990
- echo "<div id=\"message\" class=\"updated fade\"><p>$message</p></div>";
2991
- }
2992
- ?>
2993
- <div id="icon-aioseop" class="icon32"><br></div>
2994
- <h1><?php echo $name; ?></h1>
2995
- <div id="dropmessage" class="updated" style="display:none;"></div>
2996
- </div>
2997
- <?php
2998
- do_action( 'aioseop_global_settings_header', $location );
2999
- do_action( $this->prefix . 'settings_header', $location );
3000
- ?>
3001
- <form id="aiosp_settings_form" name="dofollow" enctype="multipart/form-data" action="#" method="post">
3002
- <div id="aioseop_top_button">
3003
- <div id="aiosp_ajax_settings_message"></div>
3004
- <?php
3005
-
3006
- $submit_options = array(
3007
- 'action' => array(
3008
- 'type' => 'hidden',
3009
- 'value' => 'aiosp_update_module',
3010
- ),
3011
- 'module' => array(
3012
- 'type' => 'hidden',
3013
- 'value' => get_class( $this ),
3014
- ),
3015
- 'location' => array(
3016
- 'type' => 'hidden',
3017
- 'value' => $location,
3018
- ),
3019
- 'nonce-aioseop' => array(
3020
- 'type' => 'hidden',
3021
- 'value' => wp_create_nonce( 'aioseop-nonce' ),
3022
- ),
3023
- 'page_options' => array(
3024
- 'type' => 'hidden',
3025
- 'value' => 'aiosp_home_description',
3026
- ),
3027
- 'Submit' => array(
3028
- 'type' => 'submit',
3029
- 'class' => 'aioseop_update_options_button button-primary',
3030
- 'value' => __( 'Update Options', 'all-in-one-seo-pack' ) . ' &raquo;',
3031
- ),
3032
- 'Submit_Default' => array(
3033
- 'type' => 'submit',
3034
- 'class' => 'aioseop_reset_settings_button button-secondary',
3035
- /* translators: This is a button users can click to reset the settings of a specific module to their default values. %s is a placeholder and will be replaced with the name of a settings menu (e.g. "Performance"). */
3036
- 'value' => sprintf( __( 'Reset %s Settings to Defaults', 'all-in-one-seo-pack' ), $name ) . ' &raquo;',
3037
- ),
3038
- );
3039
- $submit_options = apply_filters( "{$this->prefix}submit_options", $submit_options, $location );
3040
- foreach ( $submit_options as $k => $s ) {
3041
- if ( 'submit' == $s['type'] && 'Submit' != $k ) {
3042
- continue;
3043
- }
3044
- $class = '';
3045
- if ( isset( $s['class'] ) ) {
3046
- $class = " class='{$s['class']}' ";
3047
- }
3048
- echo $this->get_option_html(
3049
- array(
3050
- 'name' => $k,
3051
- 'options' => $s,
3052
- 'attr' => $class,
3053
- 'value' => $s['value'],
3054
- )
3055
- );
3056
- }
3057
- ?>
3058
- </div>
3059
- <div class="aioseop_options_wrapper aioseop_settings_left">
3060
- <?php
3061
- $opts = $this->get_class_option();
3062
- if ( false !== $opts ) {
3063
- $this->options = $opts;
3064
- }
3065
- if ( is_array( $this->layout ) ) {
3066
- foreach ( $this->layout as $l => $lopts ) {
3067
- if ( ! isset( $lopts['tab'] ) || ( $this->current_tab == $lopts['tab'] ) ) {
3068
- $title = $lopts['name'];
3069
- if ( ! empty( $lopts['help_link'] ) ) {
3070
- $title .= "<a class='aioseop_help_text_link aioseop_meta_box_help' target='_blank' href='" . $lopts['help_link'] . "'><span>" . __( 'Help', 'all-in-one-seo-pack' ) . '</span></a>';
3071
- }
3072
- add_meta_box(
3073
- $this->get_prefix( $location ) . $l . '_metabox',
3074
- $title,
3075
- array(
3076
- $this,
3077
- 'display_options',
3078
- ),
3079
- "{$this->prefix}settings",
3080
- 'advanced',
3081
- 'default',
3082
- $lopts
3083
- );
3084
- }
3085
- }
3086
- } else {
3087
- add_meta_box(
3088
- $this->get_prefix( $location ) . 'metabox',
3089
- $name,
3090
- array(
3091
- $this,
3092
- 'display_options',
3093
- ),
3094
- "{$this->prefix}settings",
3095
- 'advanced'
3096
- );
3097
- }
3098
- do_meta_boxes( "{$this->prefix}settings", 'advanced', $location );
3099
- ?>
3100
- <p class="submit" style="clear:both;">
3101
- <?php
3102
- foreach ( array( 'action', 'nonce-aioseop', 'page_options' ) as $submit_field ) {
3103
- if ( isset( $submit_field ) ) {
3104
- unset( $submit_field );
3105
- }
3106
- }
3107
- foreach ( $submit_options as $k => $s ) {
3108
- $class = '';
3109
- if ( isset( $s['class'] ) ) {
3110
- $class = " class='{$s['class']}' ";
3111
- }
3112
- echo $this->get_option_html(
3113
- array(
3114
- 'name' => $k,
3115
- 'options' => $s,
3116
- 'attr' => $class,
3117
- 'value' => $s['value'],
3118
- )
3119
- );
3120
- }
3121
- ?>
3122
- </p>
3123
- </div>
3124
- </form>
3125
- <?php
3126
- do_action( $this->prefix . 'settings_footer', $location );
3127
- do_action( 'aioseop_global_settings_footer', $location );
3128
- ?>
3129
- </div>
3130
- <?php
3131
- }
3132
-
3133
- /**
3134
- * Get the prefix used for a given location.
3135
- *
3136
- * @param null $location
3137
- *
3138
- * @return
3139
- */
3140
- function get_prefix( $location = null ) {
3141
- if ( ( null != $location ) && isset( $this->locations[ $location ]['prefix'] ) ) {
3142
- return $this->locations[ $location ]['prefix'];
3143
- }
3144
-
3145
- return $this->prefix;
3146
- }
3147
-
3148
- /** Sets up initial settings
3149
- *
3150
- * @param null $location
3151
- * @param null $defaults
3152
- *
3153
- * @return array
3154
- */
3155
- function setting_options( $location = null, $defaults = null ) {
3156
- if ( null === $defaults ) {
3157
- $defaults = $this->default_options;
3158
- }
3159
- $prefix = $this->get_prefix( $location );
3160
- $opts = array();
3161
- if ( null == $location || null === $this->locations[ $location ]['options'] ) {
3162
- $options = $defaults;
3163
- } else {
3164
- $options = array();
3165
- $prefix = "{$prefix}{$location}_";
3166
- if ( ! empty( $this->locations[ $location ]['default_options'] ) ) {
3167
- $options = $this->locations[ $location ]['default_options'];
3168
- }
3169
- foreach ( $this->locations[ $location ]['options'] as $opt ) {
3170
- if ( isset( $defaults[ $opt ] ) ) {
3171
- $options[ $opt ] = $defaults[ $opt ];
3172
- }
3173
- }
3174
- }
3175
- if ( ! $prefix ) {
3176
- $prefix = $this->prefix;
3177
- }
3178
- if ( ! empty( $options ) ) {
3179
- foreach ( $options as $k => $v ) {
3180
- if ( ! isset( $v['name'] ) ) {
3181
- $v['name'] = AIOSEOP_PHP_Functions::ucwords( strtr( $k, '_', ' ' ) );
3182
- }
3183
- if ( ! isset( $v['type'] ) ) {
3184
- $v['type'] = 'checkbox';
3185
- }
3186
- if ( ! isset( $v['default'] ) ) {
3187
- $v['default'] = null;
3188
- }
3189
- if ( ! isset( $v['initial_options'] ) ) {
3190
- $v['initial_options'] = $v['default'];
3191
- }
3192
- if ( 'custom' == $v['type'] && ( ! isset( $v['nowrap'] ) ) ) {
3193
- $v['nowrap'] = true;
3194
- } elseif ( ! isset( $v['nowrap'] ) ) {
3195
- $v['nowrap'] = null;
3196
- }
3197
- if ( isset( $v['condshow'] ) ) {
3198
- if ( ! is_array( $this->script_data ) ) {
3199
- $this->script_data = array();
3200
- }
3201
- if ( ! isset( $this->script_data['condshow'] ) ) {
3202
- $this->script_data['condshow'] = array();
3203
- }
3204
- $this->script_data['condshow'][ $prefix . $k ] = $v['condshow'];
3205
- }
3206
- if ( 'submit' == $v['type'] ) {
3207
- if ( ! isset( $v['save'] ) ) {
3208
- $v['save'] = false;
3209
- }
3210
- if ( ! isset( $v['label'] ) ) {
3211
- $v['label'] = 'none';
3212
- }
3213
- if ( ! isset( $v['prefix'] ) ) {
3214
- $v['prefix'] = false;
3215
- }
3216
- } else {
3217
- if ( ! isset( $v['label'] ) ) {
3218
- $v['label'] = null;
3219
- }
3220
- }
3221
- if ( 'hidden' == $v['type'] ) {
3222
- if ( ! isset( $v['label'] ) ) {
3223
- $v['label'] = 'none';
3224
- }
3225
- if ( ! isset( $v['prefix'] ) ) {
3226
- $v['prefix'] = false;
3227
- }
3228
- }
3229
- if ( ( 'text' == $v['type'] ) && ( ! isset( $v['size'] ) ) ) {
3230
- $v['size'] = 57;
3231
- }
3232
- if ( 'textarea' == $v['type'] ) {
3233
- if ( ! isset( $v['cols'] ) ) {
3234
- $v['cols'] = 57;
3235
- }
3236
- if ( ! isset( $v['rows'] ) ) {
3237
- $v['rows'] = 2;
3238
- }
3239
- }
3240
- if ( ! isset( $v['save'] ) ) {
3241
- $v['save'] = true;
3242
- }
3243
- if ( ! isset( $v['prefix'] ) ) {
3244
- $v['prefix'] = true;
3245
- }
3246
- if ( $v['prefix'] ) {
3247
- $opts[ $prefix . $k ] = $v;
3248
- } else {
3249
- $opts[ $k ] = $v;
3250
- }
3251
- }
3252
- }
3253
-
3254
- return $opts;
3255
- }
3256
-
3257
- /**
3258
- * Generates just the default option names and values
3259
- *
3260
- * @since 2.4.13 Applies filter before final return.
3261
- *
3262
- * @param null $location
3263
- * @param null $defaults
3264
- *
3265
- * @return array
3266
- */
3267
- function default_options( $location = null, $defaults = null ) {
3268
- $prefix = $this->get_prefix( $location );
3269
- $options = $this->setting_options( $location, $defaults );
3270
- $opts = array();
3271
- foreach ( $options as $k => $v ) {
3272
- if ( $v['save'] ) {
3273
- $opts[ $k ] = $v['default'];
3274
- }
3275
- }
3276
- return apply_filters( $prefix . 'default_options', $opts, $location );
3277
- }
3278
-
3279
- /**
3280
- * Gets the current options stored for a given location.
3281
- *
3282
- * @since 2.4.14 Added taxonomy options.
3283
- *
3284
- * @param array $opts
3285
- * @param null $location
3286
- * @param null $defaults
3287
- * @param null $post
3288
- *
3289
- * @return array
3290
- */
3291
- function get_current_options( $opts = array(), $location = null, $defaults = null, $post = null ) {
3292
- $prefix = $this->get_prefix( $location );
3293
- $get_opts = '';
3294
- if ( empty( $location ) ) {
3295
- $type = 'settings';
3296
- } else {
3297
- $type = $this->locations[ $location ]['type'];
3298
- }
3299
- if ( 'settings' === $type ) {
3300
- $get_opts = $this->get_class_option();
3301
- } elseif ( 'metabox' == $type ) {
3302
- if ( null == $post ) {
3303
- global $post;
3304
- }
3305
-
3306
- if (
3307
- (
3308
- isset( $_GET['taxonomy'] ) &&
3309
- isset( $_GET['tag_ID'] )
3310
- ) ||
3311
- is_category() ||
3312
- is_tag() ||
3313
- is_tax()
3314
- ) {
3315
- $term_id = isset( $_GET['tag_ID'] ) ? (int) $_GET['tag_ID'] : 0;
3316
- $term_id = $term_id ? $term_id : get_queried_object()->term_id;
3317
- if ( AIOSEOPPRO ) {
3318
- $get_opts = AIO_ProGeneral::getprotax( $get_opts );
3319
- $get_opts = get_term_meta( $term_id, '_' . $prefix . $location, true );
3320
- }
3321
- } elseif ( isset( $post ) ) {
3322
- $get_opts = get_post_meta( $post->ID, '_' . $prefix . $location, true );
3323
- }
3324
- }
3325
-
3326
- if ( is_home() && ! is_front_page() ) {
3327
- // If we're on the non-front page blog page, WP doesn't really know its post meta data so we need to get that manually for social meta.
3328
- $get_opts = get_post_meta( get_option( 'page_for_posts' ), '_' . $prefix . $location, true );
3329
- }
3330
-
3331
- $defs = $this->default_options( $location, $defaults );
3332
- if ( empty( $get_opts ) ) {
3333
- $get_opts = $defs;
3334
- } else {
3335
- $get_opts = wp_parse_args( $get_opts, $defs );
3336
- }
3337
- $opts = wp_parse_args( $opts, $get_opts );
3338
-
3339
- return $opts;
3340
- }
3341
-
3342
- /** Updates the options array in the module; loads saved settings with get_option() or uses defaults
3343
- *
3344
- * @param array $opts
3345
- * @param null $location
3346
- * @param null $defaults
3347
- */
3348
- function update_options( $opts = array(), $location = null, $defaults = null ) {
3349
- if ( null === $location ) {
3350
- $type = 'settings';
3351
- } else {
3352
- $type = $this->locations[ $location ][ $type ];
3353
- }
3354
- if ( 'settings' === $type ) {
3355
- $get_opts = $this->get_class_option();
3356
- }
3357
- if ( false === $get_opts ) {
3358
- $get_opts = $this->default_options( $location, $defaults );
3359
- } else {
3360
- $this->setting_options( $location, $defaults );
3361
- } // hack -- make sure this runs anyhow, for now -- pdb
3362
- $this->options = wp_parse_args( $opts, $get_opts );
3363
- }
3364
- }
3365
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/aioseop_module_manager.php DELETED
@@ -1,285 +0,0 @@
1
- <?php
2
- /**
3
- * The Module Manager.
4
- *
5
- * Mostly we're activating and deactivating modules/features.
6
- *
7
- * @package All-in-One-SEO-Pack
8
- * @since 2.0
9
- */
10
-
11
- if ( ! class_exists( 'All_in_One_SEO_Pack_Module_Manager' ) ) {
12
-
13
- /**
14
- * Class All_in_One_SEO_Pack_Module_Manager
15
- */
16
- class All_in_One_SEO_Pack_Module_Manager {
17
- /**
18
- * Modules
19
- *
20
- * @since ?
21
- *
22
- * @var array $modules
23
- */
24
- protected $modules = array();
25
-
26
- /**
27
- * Settings Update
28
- *
29
- * @since ?
30
- *
31
- * @var bool $settings_update
32
- */
33
- protected $settings_update = false;
34
-
35
- /**
36
- * Settings Reset
37
- *
38
- * @since ?
39
- *
40
- * @var bool $settings_reset
41
- */
42
- protected $settings_reset = false;
43
-
44
- /**
45
- * Settings Reset All
46
- *
47
- * @since ?
48
- *
49
- * @var bool $settings_reset_all
50
- */
51
- protected $settings_reset_all = false;
52
-
53
- /**
54
- * Module Settings Update
55
- *
56
- * @since ?
57
- *
58
- * @var bool $module_settings_update
59
- */
60
- protected $module_settings_update = false;
61
-
62
- /**
63
- * All_in_One_SEO_Pack_Module_Manager constructor.
64
- *
65
- * Initialize module list.
66
- *
67
- * @param $mod Modules.
68
- */
69
- function __construct( $mod ) {
70
-
71
- $this->modules['feature_manager'] = null;
72
- foreach ( $mod as $m ) {
73
- $this->modules[ $m ] = null;
74
- }
75
- $reset = false;
76
- $reset_all = ( isset( $_POST['Submit_All_Default'] ) && '' !== $_POST['Submit_All_Default'] );
77
- $reset = ( ( isset( $_POST['Submit_Default'] ) && '' !== $_POST['Submit_Default'] ) || $reset_all );
78
- $update = ( isset( $_POST['action'] ) && $_POST['action']
79
- && ( ( isset( $_POST['Submit'] ) && '' !== $_POST['Submit'] ) || $reset )
80
- );
81
- if ( $update ) {
82
- if ( $reset ) {
83
- $this->settings_reset = true;
84
- }
85
- if ( $reset_all ) {
86
- $this->settings_reset_all = true;
87
- }
88
- if ( 'aiosp_update' === $_POST['action'] ) {
89
- $this->settings_update = true;
90
- }
91
- if ( 'aiosp_update_module' === $_POST['action'] ) {
92
- $this->module_settings_update = true;
93
- }
94
- }
95
- $this->do_load_module( 'feature_manager', $mod );
96
- }
97
-
98
- /**
99
- * Return Module
100
- *
101
- * @since ?
102
- *
103
- * @param $class
104
- * @return $this|bool|mixed
105
- */
106
- function return_module( $class ) {
107
- global $aiosp;
108
- /* This is such a strange comparison! Don't know what the intent is. */
109
- if ( get_class( $aiosp ) === $class ) {
110
- return $aiosp;
111
- }
112
- if ( get_class( $aiosp ) === $class ) {
113
- return $this;
114
- }
115
- foreach ( $this->modules as $m ) {
116
- if ( is_object( $m ) && ( get_class( $m ) === $class ) ) {
117
- return $m;
118
- }
119
- }
120
-
121
- return false;
122
- }
123
-
124
- /**
125
- * Get Loaded Module List
126
- *
127
- * @since ?
128
- *
129
- * @return array
130
- */
131
- function get_loaded_module_list() {
132
- $module_list = array();
133
- if ( ! empty( $this->modules ) ) {
134
- foreach ( $this->modules as $k => $v ) {
135
- if ( ! empty( $v ) ) {
136
- $module_list[ $k ] = get_class( $v );
137
- }
138
- }
139
- }
140
-
141
- return $module_list;
142
- }
143
-
144
- /**
145
- * Do Load Module
146
- *
147
- * @since ?
148
- *
149
- * @param $mod Module.
150
- * @param null $args
151
- * @return bool
152
- */
153
- function do_load_module( $mod, $args = null ) {
154
- // Module name is used for these automatic settings:
155
- // The aiosp_enable_$module settings - whether each plugin is active or not.
156
- // The name of the .php file containing the module - aioseop_$module.php.
157
- // The name of the class - All_in_One_SEO_Pack_$Module.
158
- // The global $aioseop_$module.
159
- // $this->modules[$module].
160
- $mod_path = apply_filters( "aioseop_include_$mod", AIOSEOP_PLUGIN_DIR . "modules/aioseop_$mod.php" );
161
- if ( ! empty( $mod_path ) ) {
162
- require_once( $mod_path );
163
- }
164
- $ref = "aioseop_$mod";
165
- $classname = 'All_in_One_SEO_Pack_' . strtr( ucwords( strtr( $mod, '_', ' ' ) ), ' ', '_' );
166
- $classname = apply_filters( "aioseop_class_$mod", $classname );
167
- $module_class = new $classname( $args );
168
- global $$ref;
169
- $$ref = $module_class;
170
- $this->modules[ $mod ] = $module_class;
171
- if ( is_user_logged_in() && is_admin_bar_showing() && current_user_can( 'aiosp_manage_seo' ) ) {
172
- add_action(
173
- 'admin_bar_menu',
174
- array(
175
- $module_class,
176
- 'add_admin_bar_submenu',
177
- ),
178
- 1001 + $module_class->menu_order()
179
- );
180
- }
181
- if ( is_admin() ) {
182
- add_action(
183
- 'aioseop_modules_add_menus',
184
- array(
185
- $module_class,
186
- 'add_menu',
187
- ),
188
- $module_class->menu_order()
189
- );
190
- add_action( 'aiosoep_options_reset', array( $module_class, 'reset_options' ) );
191
- add_filter( 'aioseop_export_settings', array( $module_class, 'settings_export' ) );
192
- }
193
-
194
- return true;
195
- }
196
-
197
- /**
198
- * Load Module
199
- *
200
- * @since ?
201
- *
202
- * @param $mod
203
- * @return bool
204
- */
205
- function load_module( $mod ) {
206
- static $feature_options = null;
207
- static $feature_prefix = null;
208
- if ( ! is_array( $this->modules ) ) {
209
- return false;
210
- }
211
- $v = $this->modules[ $mod ];
212
- if ( null !== $v ) {
213
- return false;
214
- } // Already loaded.
215
- if ( 'performance' === $mod && ! is_super_admin() ) {
216
- return false;
217
- }
218
- if (
219
- ( 'file_editor' === $mod )
220
- && ( ( defined( 'DISALLOW_FILE_EDIT' ) && DISALLOW_FILE_EDIT )
221
- || ( defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS )
222
- || ! is_super_admin() )
223
- ) {
224
- return false;
225
- }
226
- $mod_enable = false;
227
-
228
- $is_module_page = isset( $_REQUEST['page'] ) && trailingslashit( AIOSEOP_PLUGIN_DIRNAME ) . 'modules/aioseop_feature_manager.php' === $_REQUEST['page'];
229
- if ( defined( 'AIOSEOP_UNIT_TESTING' ) ) {
230
- // using $_REQUEST does not work because even if the parameter is set in $_POST or $_GET, it does not percolate to $_REQUEST.
231
- $is_module_page = ( isset( $_GET['page'] ) && trailingslashit( AIOSEOP_PLUGIN_DIRNAME ) . 'modules/aioseop_feature_manager.php' === $_GET['page'] ) || ( isset( $_POST['page'] ) && trailingslashit( AIOSEOP_PLUGIN_DIRNAME ) . 'modules/aioseop_feature_manager.php' === $_POST['page'] );
232
- }
233
- $fm_page = $this->module_settings_update && wp_verify_nonce( $_POST['nonce-aioseop'], 'aioseop-nonce' ) && $is_module_page;
234
- if ( $fm_page && ! $this->settings_reset ) {
235
- if ( isset( $_POST[ "aiosp_feature_manager_enable_$mod" ] ) ) {
236
- $mod_enable = $_POST[ "aiosp_feature_manager_enable_$mod" ];
237
- } else {
238
- $mod_enable = false;
239
- }
240
- } else {
241
- if ( null === $feature_prefix ) {
242
- $feature_prefix = $this->modules['feature_manager']->get_prefix();
243
- }
244
- if ( $fm_page && $this->settings_reset ) {
245
- $feature_options = $this->modules['feature_manager']->default_options();
246
- }
247
- if ( null === $feature_options ) {
248
- if ( $this->module_settings_update && $this->settings_reset_all && wp_verify_nonce( $_POST['nonce-aioseop'], 'aioseop-nonce' ) ) {
249
- $feature_options = $this->modules['feature_manager']->default_options();
250
- } else {
251
- $feature_options = $this->modules['feature_manager']->get_current_options();
252
- }
253
- }
254
- if ( isset( $feature_options[ "{$feature_prefix}enable_$mod" ] ) ) {
255
- $mod_enable = $feature_options[ "{$feature_prefix}enable_$mod" ];
256
- }
257
- }
258
- if ( $mod_enable ) {
259
- if ( AIOSEOPPRO ) {
260
- return $this->do_load_module( $mod );
261
- }
262
-
263
- // Don't load Pro modules if Pro was previously installed.
264
- switch ( $mod ) {
265
- case 'schema_local_business':
266
- case 'video_sitemap':
267
- case 'image_seo':
268
- break;
269
- default:
270
- return $this->do_load_module( $mod );
271
- }
272
- }
273
-
274
- return false;
275
- }
276
-
277
- function load_modules() {
278
- if ( is_array( $this->modules ) ) {
279
- foreach ( $this->modules as $k => $v ) {
280
- $this->load_module( $k );
281
- }
282
- }
283
- }
284
- }
285
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/class-aioseop-helper.php DELETED
@@ -1,1293 +0,0 @@
1
- <?php
2
- /**
3
- * AIOSEOP Helper/Info Class
4
- *
5
- * Description. (use period)
6
- *
7
- * @link URL
8
- *
9
- * @package All-in-One-SEO-Pack
10
- * @since 2.4.2
11
- */
12
-
13
- /**
14
- * All in One SEO Plugin Helper
15
- *
16
- * @since 2.4.2
17
- */
18
- class AIOSEOP_Helper {
19
- /**
20
- * Help Text for jQuery UI Tooltips.
21
- *
22
- * @since 2.4.2
23
- * @var array $help_text {
24
- * @type string
25
- * }
26
- */
27
- private $help_text = array();
28
-
29
- /**
30
- * Constructor
31
- *
32
- * @since 2.4.2
33
- *
34
- * @param string $module Module/Class name.
35
- */
36
- public function __construct( $module = '' ) {
37
- if ( current_user_can( 'aiosp_manage_seo' ) ) {
38
- $this->_set_help_text( $module );
39
- }
40
- }
41
-
42
- /**
43
- * Set this Help Text
44
- *
45
- * Sets the Help Text according to the module/class in use, but if there is
46
- * no class name in $module, then this Help Text will add all module help texts.
47
- *
48
- * @ignore
49
- * @since 3.0
50
- * @access private
51
- *
52
- * @param string $module All_in_One_SEO_Pack module.
53
- */
54
- private function _set_help_text( $module ) {
55
-
56
- switch ( $module ) {
57
- case 'All_in_One_SEO_Pack':
58
- $this->help_text = $this->help_text_general();
59
- $this->help_text = array_merge( $this->help_text, $this->help_text_post_meta() );
60
- break;
61
- case 'All_in_One_SEO_Pack_Performance':
62
- $this->help_text = $this->help_text_performance();
63
- break;
64
- case 'All_in_One_SEO_Pack_Sitemap':
65
- case 'All_in_One_SEO_Pack_Sitemap_Pro':
66
- $this->help_text = $this->help_text_sitemap();
67
- break;
68
- case 'All_in_One_SEO_Pack_Opengraph':
69
- $this->help_text = $this->help_text_opengraph();
70
- break;
71
- case 'All_in_One_SEO_Pack_Robots':
72
- $this->help_text = $this->help_text_robots_generator();
73
- break;
74
- case 'All_in_One_SEO_Pack_File_Editor':
75
- $this->help_text = $this->help_text_file_editor();
76
- break;
77
- case 'All_in_One_SEO_Pack_Importer_Exporter':
78
- $this->help_text = $this->help_text_importer_exporter();
79
- break;
80
- case 'All_in_One_SEO_Pack_Bad_Robots':
81
- $this->help_text = $this->help_text_bad_robots();
82
- break;
83
- case 'All_in_One_SEO_Pack_Image_Seo':
84
- $this->help_text = $this->help_text_image_seo();
85
- break;
86
- default:
87
- break;
88
- }
89
-
90
- /**
91
- * Set Help Text
92
- *
93
- * @since 3.0
94
- *
95
- * @param array $this->help_text Contains an array of help text for each setting.
96
- * @param string $module Shows which class module is using the function.
97
- */
98
- $this->help_text = apply_filters( 'aioseop_helper_set_help_text', $this->help_text, $module );
99
- }
100
-
101
- /**
102
- * Help Text General Settings
103
- *
104
- * @ignore
105
- * @since 2.4.2
106
- * @access private
107
- *
108
- * @return array
109
- */
110
- private function help_text_general() {
111
- // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
112
- // phpcs:disable WordPress.WP.I18n.UnorderedPlaceholdersText
113
- $rtn_help_text = array(
114
- // General Settings.
115
- 'aiosp_can' => __( 'This option will automatically generate Canonical URLs for your entire WordPress installation. This will help to prevent duplicate content penalties by Google.', 'all-in-one-seo-pack' ),
116
- 'aiosp_no_paged_canonical_links' => __( 'Checking this option will set the Canonical URL for all paginated content to the first page.', 'all-in-one-seo-pack' ),
117
- 'aiosp_use_original_title' => __( 'Use wp_title to get the title used by the theme; this is disabled by default. If you use this option, set your title formats appropriately, as your theme might try to do its own title SEO as well.', 'all-in-one-seo-pack' ),
118
- 'aiosp_schema_markup' => __( 'This enables Schema.org structured data markup for rich snippets in search results.', 'all-in-one-seo-pack' ),
119
- /* translators: %s is a placeholder, which means that it should not be translated. It will be replaced with the name of the plugin, All in One SEO Pack. */
120
- 'aiosp_do_log' => sprintf( __( 'Check this and %s will create a log of important events (all-in-one-seo-pack.log) in the wp-content directory which might help debugging. Make sure this directory is writable.', 'all-in-one-seo-pack' ), AIOSEOP_PLUGIN_NAME ),
121
- 'aiosp_usage_tracking' => __( 'By allowing us to track usage data we can better help you because we know with which WordPress configurations, themes and plugins we should test.', 'all-in-one-seo-pack' ),
122
-
123
- // Home Page Settings.
124
- 'aiosp_home_title' => __( 'As the name implies, this will be the Meta Title of your homepage. This is independent of any other option. If not set, the default Site Title (found in WordPress under Settings, General, Site Title) will be used.', 'all-in-one-seo-pack' ),
125
- 'aiosp_home_description' => __( 'This will be the Meta Description for your homepage. This is independent of any other option. The default is no Meta Description at all if this is not set.', 'all-in-one-seo-pack' ),
126
- 'aiosp_home_keywords' => __( 'Enter a comma separated list of your most important keywords for your site that will be written as Meta Keywords on your homepage. Do not stuff everything in here.', 'all-in-one-seo-pack' ),
127
- 'aiosp_use_static_home_info' => __( 'Checking this option uses the title, description, and keywords set on your static Front Page.', 'all-in-one-seo-pack' ),
128
-
129
- // Title Settings.
130
- 'aiosp_home_page_title_format' =>
131
- __( 'This controls the format of the title tag for your Homepage.', 'all-in-one-seo-pack' ) . '<br />' .
132
- __( 'The following macros are supported:', 'all-in-one-seo-pack' ) .
133
- '<dl>' .
134
- '<dt>%site_title%</dt>' .
135
- '<dd>' . __( 'Your site title', 'all-in-one-seo-pack' ) . '</dd>' .
136
- '<dt>%site_description%</dt>' .
137
- '<dd>' . __( 'Your site description', 'all-in-one-seo-pack' ) . '</dd>' .
138
- '<dt>%page_title%</dt>' .
139
- /* translators: %s is replaced with a content type such as Post, Page, etc. */
140
- '<dd>' . sprintf( __( 'The original title of the %s', 'all-in-one-seo-pack' ), __( 'Homepage', 'all-in-one-seo-pack' ) ) . '</dd>' .
141
- '<dt>%page_author_login%</dt>' .
142
- /* translators: Example sentence: "The first name of the author of the Post" */
143
- '<dd>' . sprintf( __( 'The %1$s of the author of the %2$s', 'all-in-one-seo-pack' ), __( 'username', 'all-in-one-seo-pack' ), __( 'Homepage', 'all-in-one-seo-pack' ) ) . '</dd>' .
144
- '<dt>%page_author_nicename%</dt>' .
145
- '<dd>' . sprintf(
146
- __( 'The %1$s of the author of the %2$s', 'all-in-one-seo-pack' ),
147
- /* translators: The "nicename" is the sanitized version of a username. */
148
- __( 'nicename', 'all-in-one-seo-pack' ),
149
- __( 'Homepage', 'all-in-one-seo-pack' )
150
- ) . '</dd>' .
151
- '<dt>%page_author_firstname%</dt>' .
152
- '<dd>' . sprintf( __( 'The %1$s of the author of the %2$s', 'all-in-one-seo-pack' ), __( 'first name', 'all-in-one-seo-pack' ), __( 'Homepage', 'all-in-one-seo-pack' ) ) . '</dd>' .
153
- '<dt>%page_author_lastname%</dt>' .
154
- '<dd>' . sprintf( __( 'The %1$s of the author of the %2$s', 'all-in-one-seo-pack' ), __( 'last name', 'all-in-one-seo-pack' ), __( 'Homepage', 'all-in-one-seo-pack' ) ) . '</dd>' .
155
- '<dt>%current_date%</dt>' .
156
- /* translators: %s is replaced with a time related term such as Date, Year, Month, etc. */
157
- '<dd>' . sprintf( __( 'The current %s (localized)', 'all-in-one-seo-pack' ), __( 'date', 'all-in-one-seo-pack' ) ) . '</dd>' .
158
- '<dt>%current_year%</dt>' .
159
- /* translators: %s is replaced with a time related term such as Date, Year, Month, etc. */
160
- '<dd>' . sprintf( __( 'The current %s', 'all-in-one-seo-pack' ), __( 'year', 'all-in-one-seo-pack' ) ) . '</dd>' .
161
- '<dt>%current_month%</dt>' .
162
- '<dd>' . sprintf( __( 'The current %s', 'all-in-one-seo-pack' ), __( 'month', 'all-in-one-seo-pack' ) ) . '</dd>' .
163
- '<dt>%current_month_i18n%</dt>' .
164
- /* translators: %s is replaced with a time related term such as Date, Year, Month, etc. */
165
- '<dd>' . sprintf( __( 'The current %s (localized)', 'all-in-one-seo-pack' ), __( 'month', 'all-in-one-seo-pack' ) ) . '</dd>' .
166
- '<dt>%cf_fieldname%</dt>' .
167
- '<dd>' . __( 'The name of a custom field', 'all-in-one-seo-pack' ) . '</dd>' .
168
- '</dl>',
169
- 'aiosp_page_title_format' =>
170
- __( 'This controls the format of the title tag for Pages.', 'all-in-one-seo-pack' ) . '<br />' .
171
- __( 'The following macros are supported:', 'all-in-one-seo-pack' ) .
172
- '<dl>' .
173
- '<dt>%site_title%</dt>' .
174
- '<dd>' . __( 'Your site title', 'all-in-one-seo-pack' ) . '</dd>' .
175
- '<dt>%site_description%</dt>' .
176
- '<dd>' . __( 'Your site description', 'all-in-one-seo-pack' ) . '</dd>' .
177
- '<dt>%page_title%</dt>' .
178
- '<dd>' . sprintf( __( 'The original title of the %s', 'all-in-one-seo-pack' ), __( 'Page', 'all-in-one-seo-pack' ) ) . '</dd>' .
179
- '<dt>%page_author_login%</dt>' .
180
- '<dd>' . sprintf( __( 'The %1$s of the author of the %2$s', 'all-in-one-seo-pack' ), __( 'username', 'all-in-one-seo-pack' ), __( 'Page', 'all-in-one-seo-pack' ) ) . '</dd>' .
181
- '<dt>%page_author_nicename%</dt>' .
182
- '<dd>' . sprintf( __( 'The %1$s of the author of the %2$s', 'all-in-one-seo-pack' ), __( 'nicename', 'all-in-one-seo-pack' ), __( 'Page', 'all-in-one-seo-pack' ) ) . '</dd>' .
183
- '<dt>%page_author_firstname%</dt>' .
184
- '<dd>' . sprintf( __( 'The %1$s of the author of the %2$s', 'all-in-one-seo-pack' ), __( 'first name', 'all-in-one-seo-pack' ), __( 'Page', 'all-in-one-seo-pack' ) ) . '</dd>' .
185
- '<dt>%page_author_lastname%</dt>' .
186
- '<dd>' . sprintf( __( 'The %1$s of the author of the %2$s', 'all-in-one-seo-pack' ), __( 'last name', 'all-in-one-seo-pack' ), __( 'Page', 'all-in-one-seo-pack' ) ) . '</dd>' .
187
- '<dt>%current_date%</dt>' .
188
- /* translators: %s is replaced with a time related term such as Date, Year, Month, etc. */
189
- '<dd>' . sprintf( __( 'The current %s (localized)', 'all-in-one-seo-pack' ), __( 'date', 'all-in-one-seo-pack' ) ) . '</dd>' .
190
- '<dt>%current_year%</dt>' .
191
- '<dd>' . sprintf( __( 'The current %s', 'all-in-one-seo-pack' ), __( 'year', 'all-in-one-seo-pack' ) ) . '</dd>' .
192
- '<dt>%current_month%</dt>' .
193
- '<dd>' . sprintf( __( 'The current %s', 'all-in-one-seo-pack' ), __( 'month', 'all-in-one-seo-pack' ) ) . '</dd>' .
194
- '<dt>%current_month_i18n%</dt>' .
195
- '<dd>' . sprintf( __( 'The current %s (localized)', 'all-in-one-seo-pack' ), __( 'month', 'all-in-one-seo-pack' ) ) . '</dd>' .
196
- '<dt>%post_date%</dt>' .
197
- '<dd>' . sprintf( __( 'The %1$s when the %2$s was published (localized)', 'all-in-one-seo-pack' ), __( 'date', 'all-in-one-seo-pack' ), __( 'Page', 'all-in-one-seo-pack' ) ) . '</dd>' .
198
- '<dt>%post_year%</dt>' .
199
- '<dd>' . sprintf( __( 'The %1$s when the %2$s was published (localized)', 'all-in-one-seo-pack' ), __( 'year', 'all-in-one-seo-pack' ), __( 'Page', 'all-in-one-seo-pack' ) ) . '</dd>' .
200
- '<dt>%post_month%</dt>' .
201
- '<dd>' . sprintf( __( 'The %1$s when the %2$s was published (localized)', 'all-in-one-seo-pack' ), __( 'month', 'all-in-one-seo-pack' ), __( 'Page', 'all-in-one-seo-pack' ) ) . '</dd>' .
202
- '<dt>%cf_fieldname%</dt>' .
203
- '<dd>' . __( 'The name of a custom field', 'all-in-one-seo-pack' ) . '</dd>' .
204
- '</dl>',
205
- 'aiosp_post_title_format' =>
206
- __( 'This controls the format of the title tag for Posts.', 'all-in-one-seo-pack' ) . '<br />' .
207
- __( 'The following macros are supported:', 'all-in-one-seo-pack' ) .
208
- '<dl>' .
209
- '<dt>%site_title%</dt>' .
210
- '<dd>' . __( 'Your site title', 'all-in-one-seo-pack' ) . '</dd>' .
211
- '<dt>%site_description%</dt>' .
212
- '<dd>' . __( 'Your site description', 'all-in-one-seo-pack' ) . '</dd>' .
213
- '<dt>%post_title%</dt>' .
214
- '<dd>' . sprintf( __( 'The original title of the %s', 'all-in-one-seo-pack' ), __( 'Post', 'all-in-one-seo-pack' ) ) . '</dd>' .
215
- '<dt>%category_title%</dt>' .
216
- /* translators: %s is replaced with a content type such as Post, Page, etc. */
217
- '<dd>' . sprintf( __( 'The (main) Category of the %s', 'all-in-one-seo-pack' ), __( 'Post', 'all-in-one-seo-pack' ) ) . '</dd>' .
218
- '<dt>%page_author_login%</dt>' .
219
- '<dd>' . sprintf( __( 'The %1$s of the author of the %2$s', 'all-in-one-seo-pack' ), __( 'username', 'all-in-one-seo-pack' ), __( 'Post', 'all-in-one-seo-pack' ) ) . '</dd>' .
220
- '<dt>%page_author_nicename%</dt>' .
221
- '<dd>' . sprintf( __( 'The %1$s of the author of the %2$s', 'all-in-one-seo-pack' ), __( 'nicename', 'all-in-one-seo-pack' ), __( 'Post', 'all-in-one-seo-pack' ) ) . '</dd>' .
222
- '<dt>%page_author_firstname%</dt>' .
223
- '<dd>' . sprintf( __( 'The %1$s of the author of the %2$s', 'all-in-one-seo-pack' ), __( 'first name', 'all-in-one-seo-pack' ), __( 'Post', 'all-in-one-seo-pack' ) ) . '</dd>' .
224
- '<dt>%page_author_lastname%</dt>' .
225
- '<dd>' . sprintf( __( 'The %1$s of the author of the %2$s', 'all-in-one-seo-pack' ), __( 'last name', 'all-in-one-seo-pack' ), __( 'Post', 'all-in-one-seo-pack' ) ) . '</dd>' .
226
- '<dt>%current_date%</dt>' .
227
- '<dd>' . sprintf( __( 'The current %s (localized)', 'all-in-one-seo-pack' ), __( 'date', 'all-in-one-seo-pack' ) ) . '</dd>' .
228
- '<dt>%current_year%</dt>' .
229
- '<dd>' . sprintf( __( 'The current %s', 'all-in-one-seo-pack' ), __( 'year', 'all-in-one-seo-pack' ) ) . '</dd>' .
230
- '<dt>%current_month%</dt>' .
231
- '<dd>' . sprintf( __( 'The current %s', 'all-in-one-seo-pack' ), __( 'month', 'all-in-one-seo-pack' ) ) . '</dd>' .
232
- '<dt>%current_month_i18n%</dt>' .
233
- '<dd>' . sprintf( __( 'The current %s (localized)', 'all-in-one-seo-pack' ), __( 'month', 'all-in-one-seo-pack' ) ) . '</dd>' .
234
- '<dt>%post_date%</dt>' .
235
- '<dd>' . sprintf( __( 'The %1$s when the %2$s was published (localized)', 'all-in-one-seo-pack' ), __( 'date', 'all-in-one-seo-pack' ), __( 'Post', 'all-in-one-seo-pack' ) ) . '</dd>' .
236
- '<dt>%post_year%</dt>' .
237
- '<dd>' . sprintf( __( 'The %1$s when the %2$s was published (localized)', 'all-in-one-seo-pack' ), __( 'year', 'all-in-one-seo-pack' ), __( 'Post', 'all-in-one-seo-pack' ) ) . '</dd>' .
238
- '<dt>%post_month%</dt>' .
239
- '<dd>' . sprintf( __( 'The %1$s when the %2$s was published (localized)', 'all-in-one-seo-pack' ), __( 'month', 'all-in-one-seo-pack' ), __( 'Post', 'all-in-one-seo-pack' ) ) . '</dd>' .
240
- '<dt>%cf_fieldname%</dt>' .
241
- '<dd>' . __( 'The name of a custom field', 'all-in-one-seo-pack' ) . '</dd>' .
242
- '</dl>',
243
- 'aiosp_category_title_format' =>
244
- __( 'This controls the format of the title tag for Category Archives.', 'all-in-one-seo-pack' ) . '<br />' .
245
- __( 'The following macros are supported:', 'all-in-one-seo-pack' ) .
246
- '<dl>' .
247
- '<dt>%site_title%</dt>' .
248
- '<dd>' . __( 'Your site title', 'all-in-one-seo-pack' ) . '</dd>' .
249
- '<dt>%site_description%</dt>' .
250
- '<dd>' . __( 'Your site description', 'all-in-one-seo-pack' ) . '</dd>' .
251
- '<dt>%category_title%</dt>' .
252
- '<dd>' . sprintf( __( 'The original title of the %s', 'all-in-one-seo-pack' ), __( 'Category', 'all-in-one-seo-pack' ) ) . '</dd>' .
253
- '<dt>%category_description%</dt>' .
254
- /* translators: %s is replaced with a content type such as Post, Page, etc. */
255
- '<dd>' . sprintf( __( 'The description of the %s', 'all-in-one-seo-pack' ), __( 'Category', 'all-in-one-seo-pack' ) ) . '</dd>' .
256
- '<dt>%current_year%</dt>' .
257
- '<dd>' . sprintf( __( 'The current %s', 'all-in-one-seo-pack' ), __( 'year', 'all-in-one-seo-pack' ) ) . '</dd>' .
258
- '<dt>%current_month%</dt>' .
259
- '<dd>' . sprintf( __( 'The current %s', 'all-in-one-seo-pack' ), __( 'month', 'all-in-one-seo-pack' ) ) . '</dd>' .
260
- '<dt>%current_month_i18n%</dt>' .
261
- '<dd>' . sprintf( __( 'The current %s (localized)', 'all-in-one-seo-pack' ), __( 'month', 'all-in-one-seo-pack' ) ) . '</dd>' .
262
- '</dl>',
263
- 'aiosp_archive_title_format' =>
264
- __( 'This controls the format of the title tag for Custom Post Archives.', 'all-in-one-seo-pack' ) . '<br />' .
265
- __( 'The following macros are supported:', 'all-in-one-seo-pack' ) .
266
- '<dl>' .
267
- '<dt>%site_title%</dt>' .
268
- '<dd>' . __( 'Your site title', 'all-in-one-seo-pack' ) . '</dd>' .
269
- '<dt>%site_description%</dt>' .
270
- '<dd>' . __( 'Your site description', 'all-in-one-seo-pack' ) . '</dd>' .
271
- '<dt>%archive_title%</dt>' .
272
- '<dd>' . sprintf(
273
- __( 'The original title of the %s', 'all-in-one-seo-pack' ),
274
- /* translators: "Archive" is used in the context of a WordPress archive page. */
275
- __( 'Archive', 'all-in-one-seo-pack' )
276
- ) . '</dd>' .
277
- '</dl>',
278
- 'aiosp_date_title_format' =>
279
- __( 'This controls the format of the title tag for Date Archives.', 'all-in-one-seo-pack' ) . '<br />' .
280
- __( 'The following macros are supported:', 'all-in-one-seo-pack' ) .
281
- '<dl>' .
282
- '<dt>%site_title%</dt>' .
283
- '<dd>' . __( 'Your site title', 'all-in-one-seo-pack' ) . '</dd>' .
284
- '<dt>%site_description%</dt>' .
285
- '<dd>' . __( 'Your site description', 'all-in-one-seo-pack' ) . '</dd>' .
286
- '<dt>%date%</dt>' .
287
- '<dd>' . __( 'The original archive title (localized), e.g. "2019" or "2019 August"', 'all-in-one-seo-pack' ) . '</dd>' .
288
- '<dt>%day%</dt>' .
289
- '<dd>' . __( 'The original archive day, e.g. "17"', 'all-in-one-seo-pack' ) . '</dd>' .
290
- '<dt>%month%</dt>' .
291
- '<dd>' . __( 'The original archive month (localized), e.g. "August"', 'all-in-one-seo-pack' ) . '</dd>' .
292
- '<dt>%year%</dt>' .
293
- '<dd>' . __( 'The original archive year, e.g. "2019"', 'all-in-one-seo-pack' ) . '</dd>' .
294
- '</dl>',
295
- 'aiosp_author_title_format' =>
296
- __( 'This controls the format of the title tag for Author Archives.', 'all-in-one-seo-pack' ) . '<br />' .
297
- __( 'The following macros are supported:', 'all-in-one-seo-pack' ) .
298
- '<dl>' .
299
- '<dt>%site_title%</dt>' .
300
- '<dd>' . __( 'Your site title', 'all-in-one-seo-pack' ) . '</dd>' .
301
- '<dt>%site_description%</dt>' .
302
- '<dd>' . __( 'Your site description', 'all-in-one-seo-pack' ) . '</dd>' .
303
- '<dt>%author%</dt>' .
304
- '<dd>' . __( 'The original archive title, e.g. "Steve" or "John Smith"', 'all-in-one-seo-pack' ) . '</dd>' .
305
- '</dl>',
306
- 'aiosp_tag_title_format' =>
307
- __( 'This controls the format of the title tag for Tag Archives.', 'all-in-one-seo-pack' ) . '<br />' .
308
- __( 'The following macros are supported:', 'all-in-one-seo-pack' ) .
309
- '<dl>' .
310
- '<dt>%site_title%</dt>' .
311
- '<dd>' . __( 'Your site title', 'all-in-one-seo-pack' ) . '</dd>' .
312
- '<dt>%site_description%</dt>' .
313
- '<dd>' . __( 'Your site description', 'all-in-one-seo-pack' ) . '</dd>' .
314
- '<dt>%tag%</dt>' .
315
- '<dd>' . sprintf( __( 'The name of the %s', 'all-in-one-seo-pack' ), __( 'Tag', 'all-in-one-seo-pack' ) ) . '</dd>' .
316
- '</dl>',
317
- 'aiosp_search_title_format' =>
318
- __( 'This controls the format of the title tag for the Search page.', 'all-in-one-seo-pack' ) . '<br />' .
319
- __( 'The following macros are supported:', 'all-in-one-seo-pack' ) .
320
- '<dl>' .
321
- '<dt>%site_title%</dt>' .
322
- '<dd>' . __( 'Your site title', 'all-in-one-seo-pack' ) . '</dd>' .
323
- '<dt>%site_description%</dt>' .
324
- '<dd>' . __( 'Your site description', 'all-in-one-seo-pack' ) . '</dd>' .
325
- '<dt>%search%</dt>' .
326
- '<dd>' . __( 'The search term that was entered', 'all-in-one-seo-pack' ) . '</dd>' .
327
- '</dl>',
328
- 'aiosp_description_format' =>
329
- __( 'This controls the format of Meta Descriptions. The following macros are supported:', 'all-in-one-seo-pack' ) .
330
- '<dl>' .
331
- '<dt>%site_title%</dt>' .
332
- '<dd>' . __( 'Your site title', 'all-in-one-seo-pack' ) . '</dd>' .
333
- '<dt>%site_description%</dt>' .
334
- '<dd>' . __( 'Your site description', 'all-in-one-seo-pack' ) . '</dd>' .
335
- '<dt>%description%</dt>' .
336
- '<dd>' . __( 'This outputs the description you write for each page/post or the autogenerated description, if enabled. Auto-generated descriptions are generated from the excerpt or the first 160 characters of the content if there is no excerpt.', 'all-in-one-seo-pack' ) . '</dd>' .
337
- '<dt>%post_title%</dt>' .
338
- '<dd>' . sprintf( __( 'The original title of the %s', 'all-in-one-seo-pack' ), __( 'Post', 'all-in-one-seo-pack' ) ) . '</dd>' .
339
- '<dt>%wp_title%</dt>' .
340
- '<dd>' . __( 'The original WordPress title', 'all-in-one-seo-pack' ) . '</dd>' .
341
- '<dt>%current_date%</dt>' .
342
- '<dd>' . sprintf( __( 'The current %s (localized)', 'all-in-one-seo-pack' ), __( 'date', 'all-in-one-seo-pack' ) ) . '</dd>' .
343
- '<dt>%current_year%</dt>' .
344
- '<dd>' . sprintf( __( 'The current %s', 'all-in-one-seo-pack' ), __( 'year', 'all-in-one-seo-pack' ) ) . '</dd>' .
345
- '<dt>%current_month%</dt>' .
346
- '<dd>' . sprintf( __( 'The current %s', 'all-in-one-seo-pack' ), __( 'month', 'all-in-one-seo-pack' ) ) . '</dd>' .
347
- '<dt>%current_month_i18n%</dt>' .
348
- '<dd>' . sprintf( __( 'The current %s (localized)', 'all-in-one-seo-pack' ), __( 'month', 'all-in-one-seo-pack' ) ) . '</dd>' .
349
- '<dt>%post_date%</dt>' .
350
- '<dd>' . sprintf(
351
- __( 'The %1$s when the %2$s was published (localized)', 'all-in-one-seo-pack' ),
352
- __( 'date', 'all-in-one-seo-pack' ),
353
- /* translators: "Post/Page" are the two main content types in WordPress. */
354
- __( 'Post/Page', 'all-in-one-seo-pack' )
355
- ) . '</dd>' .
356
- '<dt>%post_year%</dt>' .
357
- '<dd>' . sprintf( __( 'The %1$s when the %2$s was published (localized)', 'all-in-one-seo-pack' ), __( 'year', 'all-in-one-seo-pack' ), __( 'Post/Page', 'all-in-one-seo-pack' ) ) . '</dd>' .
358
- '<dt>%post_month%</dt>' .
359
- '<dd>' . sprintf( __( 'The %1$s when the %2$s was published (localized)', 'all-in-one-seo-pack' ), __( 'month', 'all-in-one-seo-pack' ), __( 'Post/Page', 'all-in-one-seo-pack' ) ) . '</dd>' .
360
- '<dt>%cf_fieldname%</dt>' .
361
- '<dd>' . __( 'The name of a custom field', 'all-in-one-seo-pack' ) . '</dd>' .
362
- '</dl>',
363
- 'aiosp_404_title_format' =>
364
- __( 'This controls the format of the title tag for the 404 page.', 'all-in-one-seo-pack' ) . ' <br />' .
365
- __( 'The following macros are supported:', 'all-in-one-seo-pack' ) .
366
- '<dl>' .
367
- '<dt>%site_title%</dt>' .
368
- '<dd>' . __( 'Your site title', 'all-in-one-seo-pack' ) . '</dd>' .
369
- '<dt>%site_description%</dt>' .
370
- '<dd>' . __( 'Your site description', 'all-in-one-seo-pack' ) . '</dd>' .
371
- '<dt>%request_url%</dt>' .
372
- '<dd>' . __( 'The original URL path, like "/url-that-does-not-exist/"', 'all-in-one-seo-pack' ) . '</dd>' .
373
- '<dt>%request_words%</dt>' .
374
- '<dd>' . __( 'The URL path in human readable form, like "Url That Does Not Exist"', 'all-in-one-seo-pack' ) . '</dd>' .
375
- '<dt>%404_title%</dt>' .
376
- '<dd>' . __( 'Additional 404 title input', 'all-in-one-seo-pack' ) . '</dd>' .
377
- '</dl>',
378
- 'aiosp_paged_format' =>
379
- __( 'This string gets appended/prepended to titles of paged index pages (like home or archive pages).', 'all-in-one-seo-pack' ) .
380
- __( 'The following macros are supported:', 'all-in-one-seo-pack' ) .
381
- '<dl>' .
382
- '<dt>%page%</dt>' .
383
- '<dd>' . __( 'The page number', 'all-in-one-seo-pack' ) . '</dd>' .
384
- '</dl>',
385
- //phpcs:enable
386
-
387
- // Custom Post Type Settings.
388
- /* translators: %s is a placeholder, which means that it should not be translated. It will be replaced with the name of the plugin, All in One SEO Pack. */
389
- 'aiosp_cpostactive' => sprintf( __( 'Use these checkboxes to select which Content Types you want to use %s with.', 'all-in-one-seo-pack' ), AIOSEOP_PLUGIN_NAME ),
390
-
391
- // Display Settings.
392
- 'aiosp_posttypecolumns' => __( 'This lets you select which screens display the SEO Title, SEO Keywords and SEO Description columns.', 'all-in-one-seo-pack' ),
393
-
394
- // Webmaster Verification.
395
- 'aiosp_google_verify' => __( 'Enter your verification code here to verify your site with Google Search Console.', 'all-in-one-seo-pack' ),
396
- 'aiosp_bing_verify' => __( 'Enter your verification code here to verify your site with Bing Webmaster Tools.', 'all-in-one-seo-pack' ),
397
- 'aiosp_pinterest_verify' => __( 'Enter your verification code here to verify your site with Pinterest.', 'all-in-one-seo-pack' ),
398
- 'aiosp_yandex_verify' => __( 'Enter your verification code here to verify your site with Yandex Webmaster Tools.', 'all-in-one-seo-pack' ),
399
- 'aiosp_baidu_verify' => __( 'Enter your verification code here to verify your site with Baidu Webmaster Tools.', 'all-in-one-seo-pack' ),
400
-
401
- // Google Analytics.
402
-
403
- 'aiosp_google_analytics_id' => __( 'Enter your Google Analytics ID here to track visitor behavior on your site using Google Analytics.', 'all-in-one-seo-pack' ),
404
- 'aiosp_ga_advanced_options' => __( 'Check to use advanced Google Analytics options.', 'all-in-one-seo-pack' ),
405
- 'aiosp_ga_domain' => __( 'Enter your domain name without the http:// to set your cookie domain.', 'all-in-one-seo-pack' ),
406
- 'aiosp_ga_multi_domain' => __( 'Use this option to enable tracking of multiple or additional domains.', 'all-in-one-seo-pack' ),
407
- 'aiosp_ga_addl_domains' => __( 'Add a list of additional domains to track here. Enter one domain name per line without the http://.', 'all-in-one-seo-pack' ),
408
- 'aiosp_ga_anonymize_ip' => __( 'This enables support for IP Anonymization in Google Analytics.', 'all-in-one-seo-pack' ),
409
- 'aiosp_ga_display_advertising' => __( 'This enables support for the Display Advertiser Features in Google Analytics.', 'all-in-one-seo-pack' ),
410
- 'aiosp_ga_exclude_users' => __( 'Exclude logged-in users from Google Analytics tracking by role.', 'all-in-one-seo-pack' ),
411
- 'aiosp_ga_track_outbound_links' => __( 'Check this if you want to track outbound links with Google Analytics.', 'all-in-one-seo-pack' ),
412
- 'aiosp_ga_link_attribution' => __( 'This enables support for the Enhanced Link Attribution in Google Analytics.', 'all-in-one-seo-pack' ),
413
- 'aiosp_ga_enhanced_ecommerce' => __( 'This enables support for the Enhanced Ecommerce in Google Analytics.', 'all-in-one-seo-pack' ),
414
-
415
- // Schema Settings.
416
- 'aiosp_schema_search_results_page' => __( 'Select this to output markup that notifies Google to display the Sitelinks Search Box within certain search results.', 'all-in-one-seo-pack' ),
417
- 'aiosp_schema_social_profile_links' => __( 'Add the URLs for your website\'s social profiles here (Facebook, Twitter, Instagram, LinkedIn, etc.), one per line. These may be used in rich search results such as Google Knowledge Graph.', 'all-in-one-seo-pack' ),
418
- 'aiosp_schema_site_represents' => __( 'Select whether your website is primarily for a person or an organization.', 'all-in-one-seo-pack' ),
419
- 'aiosp_schema_organization_name' => __( 'Enter your organization or business name.', 'all-in-one-seo-pack' ),
420
- 'aiosp_schema_organization_logo' => __( 'Add a logo that represents your organization or business. The image must be in PNG, JPG or GIF format and a minimum size of 112px by 112px. If no image is selected, then the plugin will try to use the logo in the Customizer settings.', 'all-in-one-seo-pack' ),
421
- 'aiosp_schema_person_user' => __( 'Select the primary owner for your site from the list of users. Only users with the role of Author, Editor or Administrator will be listed here. Alternatively, you can choose Manually Enter to manually enter the site owner\'s name.', 'all-in-one-seo-pack' ),
422
- 'aiosp_schema_person_manual_name' => __( 'Enter the name of the site owner here.', 'all-in-one-seo-pack' ),
423
- 'aiosp_schema_person_manual_image' => __( 'Upload or enter the URL for the site owner\'s image or avatar.', 'all-in-one-seo-pack' ),
424
- 'aiosp_schema_phone_number' => __( 'Enter the primary phone number your organization or business. You must include the country code and the phone number must use the standard format for your country, for example: 1-888-888-8888.', 'all-in-one-seo-pack' ),
425
- 'aiosp_schema_contact_type' => __( 'Select the type of contact for the phone number you have entered.', 'all-in-one-seo-pack' ),
426
-
427
- // Noindex Settings.
428
- 'aiosp_cpostnoindex' => __( 'Set the default NOINDEX setting for each Post Type.', 'all-in-one-seo-pack' ),
429
- 'aiosp_cpostnofollow' => __( 'Set the default NOFOLLOW setting for each Post Type.', 'all-in-one-seo-pack' ),
430
- 'aiosp_category_noindex' => __( 'Check this to ask search engines not to index Category Archives. Useful for avoiding duplicate content.', 'all-in-one-seo-pack' ),
431
- 'aiosp_archive_date_noindex' => __( 'Check this to ask search engines not to index Date Archives. Useful for avoiding duplicate content.', 'all-in-one-seo-pack' ),
432
- 'aiosp_archive_author_noindex' => __( 'Check this to ask search engines not to index Author Archives. Useful for avoiding duplicate content.', 'all-in-one-seo-pack' ),
433
- 'aiosp_tags_noindex' => __( 'Check this to ask search engines not to index Tag Archives. Useful for avoiding duplicate content.', 'all-in-one-seo-pack' ),
434
- 'aiosp_search_noindex' => __( 'Check this to ask search engines not to index the Search page. Useful for avoiding duplicate content.', 'all-in-one-seo-pack' ),
435
- 'aiosp_404_noindex' => __( 'Check this to ask search engines not to index the 404 page.', 'all-in-one-seo-pack' ),
436
- 'aiosp_paginated_noindex' => __( 'Check this to ask search engines not to index paginated pages/posts. Useful for avoiding duplicate content.', 'all-in-one-seo-pack' ),
437
- 'aiosp_paginated_nofollow' => __( 'Check this to ask search engines not to follow links from paginated pages/posts. Useful for avoiding duplicate content.', 'all-in-one-seo-pack' ),
438
- 'aiosp_tax_noindex' => __( 'Check this to ask search engines not to index custom Taxonomy archive pages. Useful for avoiding duplicate content.', 'all-in-one-seo-pack' ),
439
-
440
- // Advanced Settings.
441
- 'aiosp_generate_descriptions' => __( 'Check this and your Meta Descriptions for any Post Type will be auto-generated using the Post Excerpt, or the first 160 characters of the post content if there is no Post Excerpt. You can overwrite any auto-generated Meta Description by editing the post or page.', 'all-in-one-seo-pack' ),
442
- 'aiosp_skip_excerpt' => __( 'This option will auto generate your meta descriptions from your post content instead of your post excerpt. This is useful if you want to use your content for your autogenerated meta descriptions instead of the excerpt. WooCommerce users should read the documentation regarding this setting.', 'all-in-one-seo-pack' ),
443
- 'aiosp_run_shortcodes' => __( 'Check this and shortcodes will get executed for descriptions auto-generated from content.', 'all-in-one-seo-pack' ),
444
- 'aiosp_hide_paginated_descriptions' => __( 'Check this and your Meta Descriptions will be removed from page 2 or later of paginated content.', 'all-in-one-seo-pack' ),
445
- 'aiosp_dont_truncate_descriptions' => __( 'Check this to prevent your Description from being truncated regardless of its length.', 'all-in-one-seo-pack' ),
446
- 'aiosp_redirect_attachement_parent' => __( 'Redirect attachment pages to post parent.', 'all-in-one-seo-pack' ),
447
- /* translators: %s is a placeholder, which means that it should not be translated. It will be replaced with the name of the plugin, All in One SEO Pack. */
448
- 'aiosp_ex_pages' => sprintf( __( 'Enter a comma separated list of pages here to be excluded by %s. This is helpful when using plugins which generate their own non-WordPress dynamic pages. Ex: <em>/forum/, /contact/</em><br />For instance, if you want to exclude the virtual pages generated by a forum plugin, all you have to do is add "forum" or "/forum" or "/forum/" or any URL with the word "forum" in it here, such as "http://example.com/forum" or "http://example.com/forum/someforumpage", and it will be excluded.', 'all-in-one-seo-pack' ), AIOSEOP_PLUGIN_NAME ),
449
-
450
- // RSS Content Settings.
451
- 'aiosp_rss_content_before' =>
452
- __( "This feature allows you to automatically add content <u>before</u> each post in your site's RSS feed.", 'all-in-one-seo-pack' ) . '<br /><br />' .
453
- __( 'The following macros are supported:', 'all-in-one-seo-pack' ) .
454
- '<dl>' .
455
- '<dt>%site_title%</dt>' .
456
- '<dd>' . __( 'Your site title', 'all-in-one-seo-pack' ) . '</dd>' .
457
- '<dt>%site_link%</dt>' .
458
- '<dd>' . __( 'A link to your homepage with your site title as anchor text', 'all-in-one-seo-pack' ) . '</dd>' .
459
- '<dt>%site_link_raw%</dt>' .
460
- '<dd>' . __( 'A link to your homepage with the link as anchor text.', 'all-in-one-seo-pack' ) . '</dd>' .
461
- '<dt>%post_title%</dt>' .
462
- '<dd>' . sprintf( __( 'The original title of the %s', 'all-in-one-seo-pack' ), __( 'Post', 'all-in-one-seo-pack' ) ) . '</dd>' .
463
- '<dt>%post_link%</dt>' .
464
- '<dd>' . __( 'A link to the post with the post name as anchor text', 'all-in-one-seo-pack' ) . '</dd>' .
465
- '<dt>%post_link_raw%</dt>' .
466
- '<dd>' . __( 'A link to the post with the link as anchor text', 'all-in-one-seo-pack' ) . '</dd>' .
467
- '<dt>%author_name%</dt>' .
468
- '<dd>' . __( 'The display name of the post author', 'all-in-one-seo-pack' ) . '</dd>' .
469
- '<dt>%author_link%</dt>' .
470
- '<dd>' . __( 'A link to the author archive of the post author with the author name as anchor text', 'all-in-one-seo-pack' ) . '</dd>' .
471
- '</dl>',
472
- 'aiosp_rss_content_after' =>
473
- __( "This feature allows you to automatically add content <u>after</u> each post in your site's RSS feed.", 'all-in-one-seo-pack' ) . '<br /><br />' .
474
- __( 'The following macros are supported:', 'all-in-one-seo-pack' ) .
475
- '<dl>' .
476
- '<dt>%site_title%</dt>' .
477
- '<dd>' . __( 'Your site title', 'all-in-one-seo-pack' ) . '</dd>' .
478
- '<dt>%site_link%</dt>' .
479
- '<dd>' . __( 'A link to your homepage with your site title as anchor text', 'all-in-one-seo-pack' ) . '</dd>' .
480
- '<dt>%site_link_raw%</dt>' .
481
- '<dd>' . __( 'A link to your homepage with the link as anchor text.', 'all-in-one-seo-pack' ) . '</dd>' .
482
- '<dt>%post_title%</dt>' .
483
- '<dd>' . sprintf( __( 'The original title of the %s', 'all-in-one-seo-pack' ), __( 'Post', 'all-in-one-seo-pack' ) ) . '</dd>' .
484
- '<dt>%post_link%</dt>' .
485
- '<dd>' . __( 'A link to the post with the post name as anchor text', 'all-in-one-seo-pack' ) . '</dd>' .
486
- '<dt>%post_link_raw%</dt>' .
487
- '<dd>' . __( 'A link to the post with the link as anchor text', 'all-in-one-seo-pack' ) . '</dd>' .
488
- '<dt>%author_name%</dt>' .
489
- '<dd>' . __( 'The display name of the post author', 'all-in-one-seo-pack' ) . '</dd>' .
490
- '<dt>%author_link%</dt>' .
491
- '<dd>' . __( 'A link to the author archive of the post author with the author name as anchor text', 'all-in-one-seo-pack' ) . '</dd>' .
492
- '</dl>',
493
- // Keyword Settings.
494
- 'aiosp_togglekeywords' => __( 'This option allows you to toggle the use of Meta Keywords throughout the whole of the site.', 'all-in-one-seo-pack' ),
495
- 'aiosp_use_categories' => __( 'Check this if you want your categories for a given post used as the Meta Keywords for this post (in addition to any keywords you specify on the Edit Post screen).', 'all-in-one-seo-pack' ),
496
- 'aiosp_use_tags_as_keywords' => __( 'Check this if you want your tags for a given post used as the Meta Keywords for this post (in addition to any keywords you specify on the Edit Post screen).', 'all-in-one-seo-pack' ),
497
- 'aiosp_dynamic_postspage_keywords' => __( 'Check this if you want your keywords on your Posts page (set in WordPress under Settings, Reading, Front Page Displays) and your archive pages to be dynamically generated from the keywords of the posts showing on that page. If unchecked, it will use the keywords set in the edit page screen for the posts page.', 'all-in-one-seo-pack' ),
498
-
499
- 'aiosp_license_key' => sprintf(
500
- '%s</br></br>%s</br>',
501
- sprintf(
502
- esc_html__( 'To unlock more features consider %s.', 'all-in-one-seo-pack' ),
503
- sprintf(
504
- '<a href="%1$s" title="%2$s">%3$s</a>',
505
- aioseop_get_utm_url( 'license-key-help-text' ),
506
- sprintf( esc_html__( 'Upgrade to %s', 'all-in-one-seo-pack' ), AIOSEOP_PLUGIN_NAME . '&nbsp;Pro' ),
507
- esc_html__( 'upgrading to PRO', 'all-in-one-seo-pack' )
508
- )
509
- ),
510
- sprintf(
511
- esc_html__( 'As a valued %1$s user you receive %2$s, automatically applied at checkout!', 'all-in-one-seo-pack' ),
512
- AIOSEOP_PLUGIN_NAME,
513
- sprintf( '<span class="aioseop-upsell-discount-amount">%s</span>', esc_html__( '30% off', 'all-in-one-seo-pack' ) )
514
- )
515
- ),
516
- );
517
-
518
- // phpcs:disable WordPress.WP.I18n.MissingTranslatorsComment
519
- $post_types = get_post_types( '', 'names' );
520
- foreach ( $post_types as $v1_pt ) {
521
- if ( ! isset( $rtn_help_text[ 'aiosp_' . $v1_pt . '_title_format' ] ) ) {
522
- $name = ucwords( preg_replace( '/-|\_/', ' ', get_post_type_object( $v1_pt )->labels->singular_name ) );
523
-
524
- $help_text_macros =
525
- '<dt>%site_title%</dt>' .
526
- '<dd>' . __( 'Your site title', 'all-in-one-seo-pack' ) . '</dd>' .
527
- '<dt>%site_description%</dt>' .
528
- '<dd>' . __( 'Your site description', 'all-in-one-seo-pack' ) . '</dd>' .
529
- '<dt>%post_title%</dt>' .
530
- '<dd>' . sprintf( __( 'The original title of the %s', 'all-in-one-seo-pack' ), $name ) . '</dd>';
531
-
532
- $pt_obj_taxes = get_object_taxonomies( $v1_pt, 'objects' );
533
- foreach ( $pt_obj_taxes as $k2_slug => $v2_tax_obj ) {
534
- if ( $v2_tax_obj->public ) {
535
- $help_text_macros .= sprintf(
536
- '<dt>%%tax_%1$s%%</dt><dd>' .
537
- /* translators: %2$s and %3$s are placeholders and should not be translated. These are replaced with the name of the taxonomy (%2$s) that is associated with (the name of) a custom post type (%2$s). */
538
- __( 'The title of the %2$s taxonomy that is associated to this %3$s', 'all-in-one-seo-pack' )
539
- . '</dd>',
540
- $k2_slug,
541
- ucwords( $v2_tax_obj->label ),
542
- $name
543
- );
544
- }
545
- }
546
-
547
- $help_text_macros .=
548
- '<dt>%page_author_login%</dt>' .
549
- '<dd>' . sprintf( __( 'The %1$s of the author of the %2$s', 'all-in-one-seo-pack' ), __( 'username', 'all-in-one-seo-pack' ), $name ) . '</dd>' .
550
- '<dt>%page_author_nicename%</dt>' .
551
- '<dd>' . sprintf( __( 'The %1$s of the author of the %2$s', 'all-in-one-seo-pack' ), __( 'nicename', 'all-in-one-seo-pack' ), $name ) . '</dd>' .
552
- '<dt>%page_author_firstname%</dt>' .
553
- '<dd>' . sprintf( __( 'The %1$s of the author of the %2$s', 'all-in-one-seo-pack' ), __( 'first name', 'all-in-one-seo-pack' ), $name ) . '</dd>' .
554
- '<dt>%page_author_lastname%</dt>' .
555
- '<dd>' . sprintf( __( 'The %1$s of the author of the %2$s', 'all-in-one-seo-pack' ), __( 'last name', 'all-in-one-seo-pack' ), $name ) . '</dd>' .
556
- '<dt>%current_date%</dt>' .
557
- '<dd>' . sprintf( __( 'The current %s (localized)', 'all-in-one-seo-pack' ), __( 'date', 'all-in-one-seo-pack' ) ) . '</dd>' .
558
- '<dt>%current_year%</dt>' .
559
- '<dd>' . sprintf( __( 'The current %s', 'all-in-one-seo-pack' ), __( 'year', 'all-in-one-seo-pack' ) ) . '</dd>' .
560
- '<dt>%current_month%</dt>' .
561
- '<dd>' . sprintf( __( 'The current %s', 'all-in-one-seo-pack' ), __( 'month', 'all-in-one-seo-pack' ) ) . '</dd>' .
562
- '<dt>%current_month_i18n%</dt>' .
563
- '<dd>' . sprintf( __( 'The current %s (localized)', 'all-in-one-seo-pack' ), __( 'month', 'all-in-one-seo-pack' ) ) . '</dd>' .
564
- '<dt>%post_date%</dt>' .
565
- '<dd>' . sprintf( __( 'The %1$s when the %2$s was published (localized)', 'all-in-one-seo-pack' ), __( 'date', 'all-in-one-seo-pack' ), $name ) . '</dd>' .
566
- '<dt>%post_year%</dt>' .
567
- '<dd>' . sprintf( __( 'The %1$s when the %2$s was published (localized)', 'all-in-one-seo-pack' ), __( 'year', 'all-in-one-seo-pack' ), $name ) . '</dd>' .
568
- '<dt>%post_month%</dt>' .
569
- '<dd>' . sprintf( __( 'The %1$s when the %2$s was published (localized)', 'all-in-one-seo-pack' ), __( 'month', 'all-in-one-seo-pack' ), $name ) . '</dd>';
570
-
571
- $rtn_help_text[ 'aiosp_' . $v1_pt . '_title_format' ] = __( 'The following macros are supported:', 'all-in-one-seo-pack' ) . '<dl>' . $help_text_macros . '</dl>' . '<br /><a href="https://semperplugins.com/documentation/custom-post-type-settings/#custom-titles" target="_blank">' . __( 'Click here for documentation on this setting', 'all-in-one-seo-pack' ) . '</a>';
572
- }
573
- }
574
- // phpcs:enable
575
-
576
- $help_doc_link = array(
577
- // General Settings.
578
- 'aiosp_can' => 'https://semperplugins.com/documentation/general-settings/#canonical-urls',
579
- 'aiosp_no_paged_canonical_links' => 'https://semperplugins.com/documentation/general-settings/#no-pagination-for-canonical-urls',
580
- 'aiosp_use_original_title' => 'https://semperplugins.com/documentation/general-settings/#use-original-title',
581
- 'aiosp_schema_markup' => 'https://semperplugins.com/documentation/schema-settings/#use-schema-markup',
582
- 'aiosp_do_log' => 'https://semperplugins.com/documentation/general-settings/#log-important-events',
583
- 'aiosp_usage_tracking' => 'https://semperplugins.com/documentation/usage-tracking/',
584
-
585
- // Home Page Settings.
586
- 'aiosp_home_title' => 'https://semperplugins.com/documentation/home-page-settings/#home-title',
587
- 'aiosp_home_description' => 'https://semperplugins.com/documentation/home-page-settings/#home-description',
588
- 'aiosp_home_keywords' => 'https://semperplugins.com/documentation/home-page-settings/#home-keywords',
589
- 'aiosp_use_static_home_info' => 'https://semperplugins.com/documentation/home-page-settings/#use-static-front-page-instead',
590
-
591
- // Title Settings.
592
- 'aiosp_home_page_title_format' => 'https://semperplugins.com/documentation/title-settings/#title-format-fields',
593
- 'aiosp_page_title_format' => 'https://semperplugins.com/documentation/title-settings/#title-format-fields',
594
- 'aiosp_post_title_format' => 'https://semperplugins.com/documentation/title-settings/#title-format-fields',
595
- 'aiosp_category_title_format' => 'https://semperplugins.com/documentation/title-settings/#title-format-fields',
596
- 'aiosp_archive_title_format' => 'https://semperplugins.com/documentation/title-settings/#title-format-fields',
597
- 'aiosp_date_title_format' => 'https://semperplugins.com/documentation/title-settings/#title-format-fields',
598
- 'aiosp_author_title_format' => 'https://semperplugins.com/documentation/title-settings/#title-format-fields',
599
- 'aiosp_tag_title_format' => 'https://semperplugins.com/documentation/title-settings/#title-format-fields',
600
- 'aiosp_search_title_format' => 'https://semperplugins.com/documentation/title-settings/#title-format-fields',
601
- 'aiosp_description_format' => 'https://semperplugins.com/documentation/title-settings/#title-format-fields',
602
- 'aiosp_404_title_format' => 'https://semperplugins.com/documentation/title-settings/#title-format-fields',
603
- 'aiosp_paged_format' => 'https://semperplugins.com/documentation/title-settings/#title-format-fields',
604
-
605
- // Custom Post Type Settings.
606
- 'aiosp_cpostactive' => 'https://semperplugins.com/documentation/custom-post-type-settings/#seo-on-only-these-post-types',
607
-
608
- // Display Settings.
609
- 'aiosp_posttypecolumns' => 'https://semperplugins.com/documentation/display-settings/#show-column-labels-for-custom-post-types',
610
-
611
- // Webmaster Verification.
612
- 'aiosp_google_verify' => 'https://semperplugins.com/documentation/google-search-console-verification/',
613
- 'aiosp_bing_verify' => 'https://semperplugins.com/documentation/bing-webmaster-verification/',
614
- 'aiosp_pinterest_verify' => 'https://semperplugins.com/documentation/pinterest-site-verification/',
615
- 'aiosp_yandex_verify' => 'https://semperplugins.com/documentation/yandex-webmaster-verification/',
616
- 'aiosp_baidu_verify' => 'https://semperplugins.com/documentation/baidu-webmaster-tools-verification/',
617
-
618
- // Google Analytics.
619
- 'aiosp_google_analytics_id' => 'https://semperplugins.com/documentation/setting-up-google-analytics/',
620
- 'aiosp_ga_advanced_options' => 'https://semperplugins.com/documentation/advanced-google-analytics-settings/',
621
- 'aiosp_ga_domain' => 'https://semperplugins.com/documentation/advanced-google-analytics-settings/#tracking-domain',
622
- 'aiosp_ga_multi_domain' => 'https://semperplugins.com/documentation/advanced-google-analytics-settings/#track-multiple-domains-additional-domains',
623
- 'aiosp_ga_addl_domains' => 'https://semperplugins.com/documentation/advanced-google-analytics-settings/#track-multiple-domains-additional-domains',
624
- 'aiosp_ga_anonymize_ip' => 'https://semperplugins.com/documentation/advanced-google-analytics-settings/#anonymize-ip-addresses',
625
- 'aiosp_ga_display_advertising' => 'https://semperplugins.com/documentation/advanced-google-analytics-settings/#display-advertiser-tracking',
626
- 'aiosp_ga_exclude_users' => 'https://semperplugins.com/documentation/advanced-google-analytics-settings/#exclude-users-from-tracking',
627
- 'aiosp_ga_track_outbound_links' => 'https://semperplugins.com/documentation/advanced-google-analytics-settings/#track-outbound-links',
628
- 'aiosp_ga_link_attribution' => 'https://semperplugins.com/documentation/advanced-google-analytics-settings/#enhanced-link-attribution',
629
- 'aiosp_ga_enhanced_ecommerce' => 'https://semperplugins.com/documentation/advanced-google-analytics-settings/#enhanced-ecommerce',
630
-
631
- // Schema Settings.
632
- 'aiosp_schema_search_results_page' => 'https://semperplugins.com/documentation/schema-settings/#display-sitelinks-search-box',
633
- 'aiosp_schema_social_profile_links' => 'https://semperplugins.com/documentation/schema-settings/#social-profile-links',
634
- 'aiosp_schema_site_represents' => 'https://semperplugins.com/documentation/schema-settings/#person-or-organization',
635
- 'aiosp_schema_organization_name' => 'https://semperplugins.com/documentation/schema-settings/#organization-name',
636
- 'aiosp_schema_organization_logo' => 'https://semperplugins.com/documentation/schema-settings/#organization-logo',
637
- 'aiosp_schema_person_user' => 'https://semperplugins.com/documentation/schema-settings/#persons-username',
638
- 'aiosp_schema_phone_number' => 'https://semperplugins.com/documentation/schema-settings/#phone-number',
639
- 'aiosp_schema_contact_type' => 'https://semperplugins.com/documentation/schema-settings/#type-of-contact',
640
-
641
- // Noindex Settings.
642
- 'aiosp_cpostnoindex' => 'https://semperplugins.com/documentation/noindex-settings/#noindex',
643
- 'aiosp_cpostnofollow' => 'https://semperplugins.com/documentation/noindex-settings/#nofollow',
644
- 'aiosp_category_noindex' => 'https://semperplugins.com/documentation/noindex-settings/#noindex-settings',
645
- 'aiosp_archive_date_noindex' => 'https://semperplugins.com/documentation/noindex-settings/#noindex-settings',
646
- 'aiosp_archive_author_noindex' => 'https://semperplugins.com/documentation/noindex-settings/#noindex-settings',
647
- 'aiosp_tags_noindex' => 'https://semperplugins.com/documentation/noindex-settings/#noindex-settings',
648
- 'aiosp_search_noindex' => 'https://semperplugins.com/documentation/noindex-settings/#use-noindex-for-the-search-page',
649
- 'aiosp_404_noindex' => 'https://semperplugins.com/documentation/noindex-settings/#use-noindex-for-the-404-page',
650
- 'aiosp_paginated_noindex' => 'https://semperplugins.com/documentation/noindex-settings/#use-noindex-for-paginated-pages-posts',
651
- 'aiosp_paginated_nofollow' => 'https://semperplugins.com/documentation/noindex-settings/#use-nofollow-for-paginated-pages-posts',
652
- 'aiosp_tax_noindex' => 'https://semperplugins.com/documentation/noindex-settings/#use-noindex-for-the-taxonomy-archives',
653
-
654
- // Advanced Settings.
655
- 'aiosp_generate_descriptions' => 'https://semperplugins.com/documentation/all-in-one-seo-pack-advanced-settings/#autogenerate-descriptions',
656
- 'aiosp_skip_excerpt' => 'https://semperplugins.com/documentation/all-in-one-seo-pack-advanced-settings/#remove-descriptions-for-paginated-pages',
657
- 'aiosp_run_shortcodes' => 'https://semperplugins.com/documentation/all-in-one-seo-pack-advanced-settings/#never-shorten-long-descriptions',
658
- 'aiosp_hide_paginated_descriptions' => 'https://semperplugins.com/documentation/all-in-one-seo-pack-advanced-settings/#unprotect-post-meta-fields',
659
- 'aiosp_dont_truncate_descriptions' => 'https://semperplugins.com/documentation/all-in-one-seo-pack-advanced-settings/#never-shorten-long-descriptions',
660
- 'aiosp_redirect_attachement_parent' => 'https://semperplugins.com/documentation/all-in-one-seo-pack-advanced-settings/#redirect-attachments-to-post-parent',
661
- 'aiosp_ex_pages' => 'https://semperplugins.com/documentation/all-in-one-seo-pack-advanced-settings/#exclude-pages',
662
-
663
- // RSS Content Settings.
664
- 'aiosp_rss_content_before' => 'https://semperplugins.com/documentation/rss-content-settings/',
665
- 'aiosp_rss_content_after' => 'https://semperplugins.com/documentation/rss-content-settings/',
666
-
667
- // Keyword Settings.
668
- 'aiosp_togglekeywords' => 'https://semperplugins.com/documentation/keyword-settings/#use-keywords',
669
- 'aiosp_use_categories' => 'https://semperplugins.com/documentation/keyword-settings/#use-categories-for-meta-keywords',
670
- 'aiosp_use_tags_as_keywords' => 'https://semperplugins.com/documentation/keyword-settings/#use-tags-for-meta-keywords',
671
- 'aiosp_dynamic_postspage_keywords' => 'https://semperplugins.com/documentation/keyword-settings/#dynamically-generate-keywords-for-posts-page',
672
-
673
- );
674
-
675
- foreach ( $help_doc_link as $k1_slug => $v1_url ) {
676
- // Any help text that ends with a ul or ol element will cause text to start at the next line.
677
- $tooltips_with_ul = array(
678
- 'aiosp_home_page_title_format',
679
- 'aiosp_page_title_format',
680
- 'aiosp_post_title_format',
681
- 'aiosp_category_title_format',
682
- 'aiosp_archive_title_format',
683
- 'aiosp_date_title_format',
684
- 'aiosp_author_title_format',
685
- 'aiosp_tag_title_format',
686
- 'aiosp_search_title_format',
687
- 'aiosp_description_format',
688
- 'aiosp_404_title_format',
689
- 'aiosp_paged_format',
690
- );
691
-
692
- $br = '<br /><br />';
693
- if ( in_array( $k1_slug, $tooltips_with_ul, true ) ) {
694
- $br = '<br />';
695
- }
696
-
697
- $rtn_help_text[ $k1_slug ] .= $br . '<a href="' . $v1_url . '" target="_blank">' . __( 'Click here for documentation on this setting.', 'all-in-one-seo-pack' ) . '</a>';
698
- }
699
-
700
- return $rtn_help_text;
701
- }
702
-
703
- /**
704
- * Help Text Performance Module
705
- *
706
- * @ignore
707
- * @since 2.4.2
708
- * @access private
709
- *
710
- * @return array
711
- */
712
- private function help_text_performance() {
713
- $rtn_help_text = array(
714
- 'aiosp_performance_memory_limit' => __( 'This setting allows you to raise your PHP memory limit to a reasonable value. Note: WordPress core and other WordPress plugins may also change the value of the memory limit.', 'all-in-one-seo-pack' ),
715
- 'aiosp_performance_execution_time' => __( 'This setting allows you to raise your PHP execution time to a reasonable value.', 'all-in-one-seo-pack' ),
716
- 'aiosp_performance_force_rewrites' => __( 'Use output buffering to ensure that the title gets rewritten. Enable this option if you run into issues with the title tag being set by your theme or another plugin.', 'all-in-one-seo-pack' ),
717
- );
718
-
719
- $help_doc_link = array(
720
- 'aiosp_performance_memory_limit' => 'https://semperplugins.com/documentation/performance-settings/#raise-memory-limit',
721
- 'aiosp_performance_execution_time' => 'https://semperplugins.com/documentation/performance-settings/#raise-execution-time',
722
- 'aiosp_performance_force_rewrites' => 'https://semperplugins.com/documentation/performance-settings/#force-rewrites',
723
- );
724
-
725
- return $this->merge_text_with_links( $rtn_help_text, $help_doc_link );
726
- }
727
-
728
- /**
729
- * Help Text Sitemap Module
730
- *
731
- * @ignore
732
- * @since 2.4.2
733
- * @access private
734
- *
735
- * @return array
736
- */
737
- private function help_text_sitemap() {
738
- $rtn_help_text = array(
739
- // XML Sitemap.
740
- 'aiosp_sitemap_daily_cron' => __( 'Notify search engines based on the selected schedule, and also update static sitemap daily if in use. (this uses WP-Cron, so make sure this is working properly on your server as well)', 'all-in-one-seo-pack' ),
741
- 'aiosp_sitemap_indexes' => __( 'Organize sitemap entries into distinct files in your sitemap. We recommend you enable this setting if your sitemap contains more than 1,000 URLs.', 'all-in-one-seo-pack' ),
742
- 'aiosp_sitemap_max_posts' => __( 'Allows you to specify the maximum number of posts in a sitemap (up to 50,000).', 'all-in-one-seo-pack' ),
743
- 'aiosp_sitemap_posttypes' => __( 'Select which Post Types appear in your sitemap.', 'all-in-one-seo-pack' ),
744
- 'aiosp_sitemap_taxonomies' => __( 'Select which taxonomy archives appear in your sitemap', 'all-in-one-seo-pack' ),
745
- 'aiosp_sitemap_archive' => __( 'Include Date Archives in your sitemap.', 'all-in-one-seo-pack' ),
746
- 'aiosp_sitemap_author' => __( 'Include Author Archives in your sitemap.', 'all-in-one-seo-pack' ),
747
- 'aiosp_sitemap_images' => __( 'Exclude Images in your sitemap.', 'all-in-one-seo-pack' ),
748
- 'aiosp_sitemap_robots' => __( 'Places a link to your Sitemap.xml into your virtual Robots.txt file.', 'all-in-one-seo-pack' ),
749
- 'aiosp_sitemap_rewrite' => __( 'Dynamically creates the XML sitemap instead of using a static file.', 'all-in-one-seo-pack' ),
750
- 'aiosp_sitemap_addl_url' => __( 'URL to the page. This field only accepts absolute URLs with the protocol specified.', 'all-in-one-seo-pack' ),
751
- 'aiosp_sitemap_addl_prio' => __( 'The priority of the page.', 'all-in-one-seo-pack' ),
752
- 'aiosp_sitemap_addl_freq' => __( 'The frequency of the page.', 'all-in-one-seo-pack' ),
753
- 'aiosp_sitemap_addl_mod' => __( 'Last modified date of the page.', 'all-in-one-seo-pack' ),
754
- 'aiosp_sitemap_excl_terms' => __( 'Exclude any category, tag or custom taxonomy from the XML sitemap. Start typing the name of a category, tag or taxonomy term in the field and a dropdown will populate with the matching terms for you to select from.<br/><br/>This will also exclude any content belonging to the specified term. For example, if you exclude the "Uncategorized" category then all posts in that category will also be excluded from the sitemap.', 'all-in-one-seo-pack' ),
755
- 'aiosp_sitemap_excl_pages' => __( 'Use page slugs or page IDs, separated by commas, to exclude pages from the sitemap.', 'all-in-one-seo-pack' ),
756
-
757
- // Additional Sitemaps
758
- 'aiosp_sitemap_posttypes_news' => __( 'Select which Post Types should appear in your Google News sitemap. This sitemap only includes posts that were published in the last 48 hours.', 'all-in-one-seo-pack' ),
759
- 'aiosp_sitemap_rss_sitemap' => __( 'Generate an RSS sitemap in addition to the regular XML Sitemap.', 'all-in-one-seo-pack' ),
760
- 'aiosp_sitemap_publication_name' => __( 'The publication name for your Google News sitemap. It must exactly match the name as it appears on your articles on news.google.com, except for anything in parentheses.', 'all-in-one-seo-pack' ),
761
-
762
- // Priorities.
763
- 'aiosp_sitemap_prio_homepage' => sprintf( __( 'Manually set the %1$s of your %2$s.', 'all-in-one-seo-pack' ), __( 'priority', 'all-in-one-seo-pack' ), __( 'Homepage', 'all-in-one-seo-pack' ) ),
764
- 'aiosp_sitemap_prio_post' => sprintf( __( 'Manually set the %1$s of your %2$s.', 'all-in-one-seo-pack' ), __( 'priority', 'all-in-one-seo-pack' ), __( 'Posts', 'all-in-one-seo-pack' ) ),
765
- 'aiosp_sitemap_prio_taxonomies' => sprintf( __( 'Manually set the %1$s of your %2$s.', 'all-in-one-seo-pack' ), __( 'priority', 'all-in-one-seo-pack' ), __( 'Taxonomies', 'all-in-one-seo-pack' ) ),
766
- 'aiosp_sitemap_prio_archive' => sprintf( __( 'Manually set the %1$s of your %2$s.', 'all-in-one-seo-pack' ), __( 'priority', 'all-in-one-seo-pack' ), __( 'Archive Pages', 'all-in-one-seo-pack' ) ),
767
- 'aiosp_sitemap_prio_author' => sprintf( __( 'Manually set the %1$s of your %2$s.', 'all-in-one-seo-pack' ), __( 'priority', 'all-in-one-seo-pack' ), __( 'Author Pages', 'all-in-one-seo-pack' ) ),
768
-
769
- // Frequencies.
770
- 'aiosp_sitemap_freq_homepage' => sprintf( __( 'Manually set the %1$s of your %2$s.', 'all-in-one-seo-pack' ), __( 'frequency', 'all-in-one-seo-pack' ), __( 'Homepage', 'all-in-one-seo-pack' ) ),
771
- 'aiosp_sitemap_freq_post' => sprintf( __( 'Manually set the %1$s of your %2$s.', 'all-in-one-seo-pack' ), __( 'frequency', 'all-in-one-seo-pack' ), __( 'Posts', 'all-in-one-seo-pack' ) ),
772
- 'aiosp_sitemap_freq_taxonomies' => sprintf( __( 'Manually set the %1$s of your %2$s.', 'all-in-one-seo-pack' ), __( 'frequency', 'all-in-one-seo-pack' ), __( 'Taxonomies', 'all-in-one-seo-pack' ) ),
773
- 'aiosp_sitemap_freq_archive' => sprintf( __( 'Manually set the %1$s of your %2$s.', 'all-in-one-seo-pack' ), __( 'frequency', 'all-in-one-seo-pack' ), __( 'Archive Pages', 'all-in-one-seo-pack' ) ),
774
- 'aiosp_sitemap_freq_author' => sprintf( __( 'Manually set the %1$s of your %2$s.', 'all-in-one-seo-pack' ), __( 'frequency', 'all-in-one-seo-pack' ), __( 'Author Pages', 'all-in-one-seo-pack' ) ),
775
-
776
- );
777
-
778
- $args = array(
779
- 'public' => true,
780
- );
781
-
782
- $post_types = get_post_types( $args, 'names' );
783
- foreach ( $post_types as $pt ) {
784
- $pt_obj = get_post_type_object( $pt );
785
- $rtn_help_text[ 'aiosp_sitemap_prio_post_' . $pt ] = sprintf( __( 'Manually set the %1$s of your %2$s.', 'all-in-one-seo-pack' ), __( 'priority', 'all-in-one-seo-pack' ), ucwords( $pt_obj->label ) );
786
- $rtn_help_text[ 'aiosp_sitemap_freq_post_' . $pt ] = sprintf( __( 'Manually set the %1$s of your %2$s.', 'all-in-one-seo-pack' ), __( 'frequency', 'all-in-one-seo-pack' ), ucwords( $pt_obj->label ) );
787
- $help_doc_link[ 'aiosp_sitemap_prio_post_' . $pt ] = 'https://semperplugins.com/documentation/xml-sitemaps-module/#priorities-and-frequencies';
788
- $help_doc_link[ 'aiosp_sitemap_freq_post_' . $pt ] = 'https://semperplugins.com/documentation/xml-sitemaps-module/#priorities-and-frequencies';
789
- }
790
-
791
- $taxonomies = get_taxonomies( $args, 'object' );
792
- foreach ( $taxonomies as $tax ) {
793
- $rtn_help_text[ 'aiosp_sitemap_prio_taxonomies_' . $tax->name ] = sprintf( __( 'Manually set the %1$s of your %2$s.', 'all-in-one-seo-pack' ), __( 'priority', 'all-in-one-seo-pack' ), ucwords( $tax->label ) );
794
- $rtn_help_text[ 'aiosp_sitemap_freq_taxonomies_' . $tax->name ] = sprintf( __( 'Manually set the %1$s of your %2$s.', 'all-in-one-seo-pack' ), __( 'frequency', 'all-in-one-seo-pack' ), ucwords( $tax->label ) );
795
- $help_doc_link[ 'aiosp_sitemap_prio_taxonomies_' . $tax->name ] = 'https://semperplugins.com/documentation/xml-sitemaps-module/#priorities-and-frequencies';
796
- $help_doc_link[ 'aiosp_sitemap_freq_taxonomies_' . $tax->name ] = 'https://semperplugins.com/documentation/xml-sitemaps-module/#priorities-and-frequencies';
797
- }
798
-
799
- $help_doc_link = array(
800
- // XML Sitemap.
801
- 'aiosp_sitemap_daily_cron' => 'https://semperplugins.com/documentation/xml-sitemaps-module/#schedule-updates',
802
- 'aiosp_sitemap_indexes' => 'https://semperplugins.com/documentation/xml-sitemaps-module/#enable-sitemap-indexes',
803
- 'aiosp_sitemap_max_posts' => 'https://semperplugins.com/documentation/xml-sitemaps-module/#enable-sitemap-indexes',
804
- 'aiosp_sitemap_posttypes' => 'https://semperplugins.com/documentation/xml-sitemaps-module/#post-types-and-taxonomies',
805
- 'aiosp_sitemap_taxonomies' => 'https://semperplugins.com/documentation/xml-sitemaps-module/#post-types-and-taxonomies',
806
- 'aiosp_sitemap_archive' => 'https://semperplugins.com/documentation/xml-sitemaps-module/#include-archive-pages',
807
- 'aiosp_sitemap_author' => 'https://semperplugins.com/documentation/xml-sitemaps-module/#include-archive-pages',
808
- 'aiosp_sitemap_images' => 'https://semperplugins.com/documentation/xml-sitemaps-module/#exclude-images',
809
- 'aiosp_sitemap_robots' => 'https://semperplugins.com/documentation/xml-sitemaps-module/#link-from-virtual-robots',
810
- 'aiosp_sitemap_rewrite' => 'https://semperplugins.com/documentation/xml-sitemaps-module/#dynamically-generate-sitemap',
811
-
812
- // Additional Sitemaps
813
- 'aiosp_sitemap_rss_sitemap' => 'https://semperplugins.com/documentation/xml-sitemaps-module/#create-rss_sitemap',
814
- 'aiosp_sitemap_posttypes_news' => 'https://semperplugins.com/documentation/google-news-sitemap/',
815
- 'aiosp_sitemap_publication_name' => 'https://semperplugins.com/documentation/google-news-sitemap/',
816
-
817
- // Additional Pages.
818
- 'aiosp_sitemap_addl_url' => 'https://semperplugins.com/documentation/xml-sitemaps-module/#additional-pages',
819
- 'aiosp_sitemap_addl_prio' => 'https://semperplugins.com/documentation/xml-sitemaps-module/#additional-pages',
820
- 'aiosp_sitemap_addl_freq' => 'https://semperplugins.com/documentation/xml-sitemaps-module/#additional-pages',
821
- 'aiosp_sitemap_addl_mod' => 'https://semperplugins.com/documentation/xml-sitemaps-module/#additional-pages',
822
-
823
- // Exclude Items.
824
- 'aiosp_sitemap_excl_terms' => 'https://semperplugins.com/documentation/xml-sitemaps-module/#excluded-items',
825
- 'aiosp_sitemap_excl_pages' => 'https://semperplugins.com/documentation/xml-sitemaps-module/#excluded-items',
826
-
827
- // Priorities.
828
- 'aiosp_sitemap_prio_homepage' => 'https://semperplugins.com/documentation/xml-sitemaps-module/#priorities-and-frequencies',
829
- 'aiosp_sitemap_prio_post' => 'https://semperplugins.com/documentation/xml-sitemaps-module/#priorities-and-frequencies',
830
- 'aiosp_sitemap_prio_taxonomies' => 'https://semperplugins.com/documentation/xml-sitemaps-module/#priorities-and-frequencies',
831
- 'aiosp_sitemap_prio_archive' => 'https://semperplugins.com/documentation/xml-sitemaps-module/#priorities-and-frequencies',
832
- 'aiosp_sitemap_prio_author' => 'https://semperplugins.com/documentation/xml-sitemaps-module/#priorities-and-frequencies',
833
-
834
- // Frequencies.
835
- 'aiosp_sitemap_freq_homepage' => 'https://semperplugins.com/documentation/xml-sitemaps-module/#priorities-and-frequencies',
836
- 'aiosp_sitemap_freq_post' => 'https://semperplugins.com/documentation/xml-sitemaps-module/#priorities-and-frequencies',
837
- 'aiosp_sitemap_freq_taxonomies' => 'https://semperplugins.com/documentation/xml-sitemaps-module/#priorities-and-frequencies',
838
- 'aiosp_sitemap_freq_archive' => 'https://semperplugins.com/documentation/xml-sitemaps-module/#priorities-and-frequencies',
839
- 'aiosp_sitemap_freq_author' => 'https://semperplugins.com/documentation/xml-sitemaps-module/#priorities-and-frequencies',
840
-
841
- );
842
-
843
- /*
844
- * Currently has no links, but may be added later.
845
- foreach ( $post_types as $pt ) {
846
- $help_doc_link[ 'aiosp_sitemap_prio_post_' . $pt ] = '';
847
- $help_doc_link[ 'aiosp_sitemap_freq_post_' . $pt ] = '';
848
- }
849
- */
850
-
851
- /*
852
- * Currently has no links, but may be added later.
853
- foreach ( $taxonomies as $tax ) {
854
- $help_doc_link[ 'aiosp_sitemap_prio_taxonomies_' . $tax->name ] = '';
855
- $help_doc_link[ 'aiosp_sitemap_freq_taxonomies_' . $tax->name ] = '';
856
- }
857
- */
858
-
859
- return $this->merge_text_with_links( $rtn_help_text, $help_doc_link );
860
- }
861
-
862
- /**
863
- * Help Text Opengraph Module
864
- *
865
- * @ignore
866
- * @since 2.4.2
867
- * @access private
868
- *
869
- * @return array
870
- */
871
- private function help_text_opengraph() {
872
- $rtn_help_text = array(
873
- // Home Page Settings.
874
- /* translators: %s is a placeholder, which means that it should not be translated. It will be replaced with the name of the plugin, All in One SEO Pack. */
875
- 'aiosp_opengraph_setmeta' => sprintf( __( 'Checking this box will use the Home Title and Home Description set in %s, General Settings as the Open Graph title and description for your home page.', 'all-in-one-seo-pack' ), AIOSEOP_PLUGIN_NAME ),
876
- 'aiosp_opengraph_sitename' => __( 'The Site Name is the name that is used to identify your website.', 'all-in-one-seo-pack' ),
877
- 'aiosp_opengraph_hometitle' => __( 'The Home Title is the Open Graph title for your home page.', 'all-in-one-seo-pack' ),
878
- 'aiosp_opengraph_description' => __( 'The Home Description is the Open Graph description for your home page.', 'all-in-one-seo-pack' ),
879
- 'aiosp_opengraph_homeimage' => __( 'The Home Image is the Open Graph image for your home page.', 'all-in-one-seo-pack' ),
880
-
881
- // Image Settings.
882
- 'aiosp_opengraph_defimg' => __( 'This option lets you choose which image will be displayed by default for the Open Graph image. You may override this on individual posts.', 'all-in-one-seo-pack' ),
883
- 'aiosp_opengraph_fallback' => __( 'This option lets you fall back to the default image if no image could be found above.', 'all-in-one-seo-pack' ),
884
- 'aiosp_opengraph_dimg' => __( 'This option sets a default image that can be used for the Open Graph image. You can upload an image, select an image from your Media Library or paste the URL of an image here.', 'all-in-one-seo-pack' ),
885
- 'aiosp_opengraph_dimgwidth' => __( 'This option lets you set a default width for your images, where unspecified.', 'all-in-one-seo-pack' ),
886
- 'aiosp_opengraph_dimgheight' => __( 'This option lets you set a default height for your images, where unspecified.', 'all-in-one-seo-pack' ),
887
- 'aiosp_opengraph_meta_key' => __( 'Enter the name of a custom field (or multiple field names separated by commas) to use that field to specify the Open Graph image on Pages or Posts.', 'all-in-one-seo-pack' ),
888
-
889
- // Facebook Settings.
890
- 'aiosp_opengraph_key' => __( 'Enter your Facebook Admin ID here. You can enter multiple IDs separated by a comma.', 'all-in-one-seo-pack' ),
891
- 'aiosp_opengraph_appid' => __( 'Enter your Facebook App ID here. Information about how to get your Facebook App ID can be found at https://developers.facebook.com/docs/apps/register', 'all-in-one-seo-pack' ),
892
- 'aiosp_opengraph_gen_tags' => __( 'Automatically generate article tags for Facebook type article when not provided.', 'all-in-one-seo-pack' ),
893
- 'aiosp_opengraph_gen_keywords' => __( 'Use keywords in generated article tags.', 'all-in-one-seo-pack' ),
894
- 'aiosp_opengraph_gen_categories' => __( 'Use categories in generated article tags.', 'all-in-one-seo-pack' ),
895
- 'aiosp_opengraph_gen_post_tags' => __( 'Use post tags in generated article tags.', 'all-in-one-seo-pack' ),
896
- 'aiosp_opengraph_types' => __( 'Select which Post Types you want to set Open Graph meta values for.', 'all-in-one-seo-pack' ),
897
- 'aiosp_opengraph_facebook_publisher' => __( 'Link articles to the Facebook page associated with your website.', 'all-in-one-seo-pack' ),
898
- 'aiosp_opengraph_facebook_author' => __( 'Allows your authors to be identified by their Facebook pages as content authors on the Opengraph meta for their articles.', 'all-in-one-seo-pack' ),
899
-
900
- // Twitter Settings.
901
- 'aiosp_opengraph_defcard' => __( 'Select the default type of Twitter Card to display.', 'all-in-one-seo-pack' ),
902
- 'aiosp_opengraph_twitter_site' => __( 'Enter the Twitter username associated with your website here.', 'all-in-one-seo-pack' ),
903
- 'aiosp_opengraph_twitter_creator' => __( 'Allows your authors to be identified by their Twitter usernames as content creators on the Twitter cards for their posts.', 'all-in-one-seo-pack' ),
904
- 'aiosp_opengraph_twitter_domain' => __( 'Enter the name of your website here.', 'all-in-one-seo-pack' ),
905
-
906
- // Advanced Settings.
907
- 'aiosp_opengraph_title_shortcodes' => __( 'Run shortcodes that appear in social title meta tags.', 'all-in-one-seo-pack' ),
908
- 'aiosp_opengraph_description_shortcodes' => __( 'Run shortcodes that appear in social description meta tags.', 'all-in-one-seo-pack' ),
909
- 'aiosp_opengraph_generate_descriptions' => __( 'This option will auto generate your Open Graph descriptions from your post content instead of your post excerpt. WooCommerce users should read the documentation regarding this setting.', 'all-in-one-seo-pack' ),
910
-
911
- // POST META.
912
- 'aioseop_opengraph_settings_title' => __( 'This is the Open Graph title of this Page or Post.', 'all-in-one-seo-pack' ),
913
- 'aioseop_opengraph_settings_desc' => __( 'This is the Open Graph description of this Page or Post.', 'all-in-one-seo-pack' ),
914
- 'aioseop_opengraph_settings_image' => __( 'This option lets you select the Open Graph image that will be used for this Page or Post, overriding the default settings.', 'all-in-one-seo-pack' ),
915
- 'aioseop_opengraph_settings_customimg' => __( 'This option lets you upload an image to use as the Open Graph image for this Page or Post.', 'all-in-one-seo-pack' ),
916
- 'aioseop_opengraph_settings_imagewidth' => __( 'Enter the width for your Open Graph image in pixels (i.e. 600).', 'all-in-one-seo-pack' ),
917
- 'aioseop_opengraph_settings_imageheight' => __( 'Enter the height for your Open Graph image in pixels (i.e. 600).', 'all-in-one-seo-pack' ),
918
- 'aioseop_opengraph_settings_video' => __( 'This option lets you specify a link to the Open Graph video used on this Page or Post.', 'all-in-one-seo-pack' ),
919
- 'aioseop_opengraph_settings_videowidth' => __( 'Enter the width for your Open Graph video in pixels (i.e. 600).', 'all-in-one-seo-pack' ),
920
- 'aioseop_opengraph_settings_videoheight' => __( 'Enter the height for your Open Graph video in pixels (i.e. 600).', 'all-in-one-seo-pack' ),
921
- 'aioseop_opengraph_settings_category' => __( 'Select the Open Graph type that best describes the content of this Page or Post.', 'all-in-one-seo-pack' ),
922
- 'aioseop_opengraph_settings_facebook_debug' => __( 'Press this button to have Facebook re-fetch and debug this page.', 'all-in-one-seo-pack' ),
923
- 'aioseop_opengraph_settings_section' => __( 'This Open Graph meta allows you to add a general section name that best describes this content.', 'all-in-one-seo-pack' ),
924
- 'aioseop_opengraph_settings_tag' => __( 'This Open Graph meta allows you to add a list of keywords that best describe this content.', 'all-in-one-seo-pack' ),
925
- 'aioseop_opengraph_settings_setcard' => __( 'Select the Twitter Card type to use for this Page or Post, overriding the default setting.', 'all-in-one-seo-pack' ),
926
- 'aioseop_opengraph_settings_customimg_twitter' => __( 'This option lets you upload an image to use as the Twitter image for this Page or Post.', 'all-in-one-seo-pack' ),
927
- );
928
-
929
- $args_1 = array(
930
- 'public' => true,
931
- );
932
- $args_2 = array(
933
- 'public' => false,
934
- );
935
-
936
- $post_types = array_merge( get_post_types( $args_1, 'names' ), get_post_types( $args_2, 'names' ) );
937
- foreach ( $post_types as $pt ) {
938
- $rtn_help_text[ 'aiosp_opengraph_' . $pt . '_fb_object_type' ] = __( 'Choose a default value that best describes the content of your post type.', 'all-in-one-seo-pack' );
939
- $rtn_help_text[ 'aiosp_opengraph_' . $pt . '_fb_object_type' ] .= '<br /><br /><a href="https://semperplugins.com/documentation/social-meta-module/#content-object-types" target="_blank">' . __( 'Click here for documentation on this setting.', 'all-in-one-seo-pack' ) . '</a>';
940
- }
941
-
942
- $help_doc_link = array(
943
- // Home Page Settings.
944
- 'aiosp_opengraph_setmeta' => 'https://semperplugins.com/documentation/social-meta-module/#use-aioseo-title-and-description',
945
- 'aiosp_opengraph_sitename' => 'https://semperplugins.com/documentation/social-meta-module/#site-name',
946
- 'aiosp_opengraph_hometitle' => 'https://semperplugins.com/documentation/social-meta-module/#home-title-and-description',
947
- 'aiosp_opengraph_description' => 'https://semperplugins.com/documentation/social-meta-module/#home-title-and-description',
948
- 'aiosp_opengraph_homeimage' => 'https://semperplugins.com/documentation/social-meta-module/#home-image',
949
-
950
- // Image Settings.
951
- 'aiosp_opengraph_defimg' => 'https://semperplugins.com/documentation/social-meta-module/#select-og-image-source',
952
- 'aiosp_opengraph_fallback' => 'https://semperplugins.com/documentation/social-meta-module/#use-default-if-no-image-found',
953
- 'aiosp_opengraph_dimg' => 'https://semperplugins.com/documentation/social-meta-module/#default-og-image',
954
- 'aiosp_opengraph_dimgwidth' => 'https://semperplugins.com/documentation/social-meta-module/#default-image-width',
955
- 'aiosp_opengraph_dimgheight' => 'https://semperplugins.com/documentation/social-meta-module/#default-image-height',
956
- 'aiosp_opengraph_meta_key' => 'https://semperplugins.com/documentation/social-meta-module/#use-custom-field-for-image',
957
-
958
- // Facebook Settings.
959
- 'aiosp_opengraph_key' => 'https://semperplugins.com/documentation/social-meta-module/#facebook-admin-id',
960
- 'aiosp_opengraph_appid' => 'https://semperplugins.com/documentation/social-meta-module/#facebook-app-id',
961
- 'aiosp_opengraph_gen_tags' => 'https://semperplugins.com/documentation/social-meta-module/#automatically-generate-article-tags',
962
- 'aiosp_opengraph_gen_keywords' => 'https://semperplugins.com/documentation/social-meta-module/#use-keywords-in-article-tags',
963
- 'aiosp_opengraph_gen_categories' => 'https://semperplugins.com/documentation/social-meta-module/#use-categories-in-article-tags',
964
- 'aiosp_opengraph_gen_post_tags' => 'https://semperplugins.com/documentation/social-meta-module/#use-post-tags-in-article-tags',
965
- 'aiosp_opengraph_types' => 'https://semperplugins.com/documentation/social-meta-module/#enable-facebook-meta-for',
966
- 'aiosp_opengraph_facebook_publisher' => 'https://semperplugins.com/documentation/social-meta-module/#show-facebook-publisher-on-articles',
967
- 'aiosp_opengraph_facebook_author' => 'https://semperplugins.com/documentation/social-meta-module/#show-facebook-author-on-articles',
968
-
969
- // Twitter Settings.
970
- 'aiosp_opengraph_defcard' => 'https://semperplugins.com/documentation/social-meta-module/#default-twitter-card',
971
- 'aiosp_opengraph_twitter_site' => 'https://semperplugins.com/documentation/social-meta-module/#twitter-site',
972
- 'aiosp_opengraph_twitter_creator' => 'https://semperplugins.com/documentation/social-meta-module/#show-twitter-author',
973
- 'aiosp_opengraph_twitter_domain' => 'https://semperplugins.com/documentation/social-meta-module/#twitter-domain',
974
-
975
- // Advanced Settings.
976
- 'aiosp_opengraph_title_shortcodes' => 'https://semperplugins.com/documentation/social-meta-module/#run-shortcodes-in-title',
977
- 'aiosp_opengraph_description_shortcodes' => 'https://semperplugins.com/documentation/social-meta-module/#run-shortcodes-in-description',
978
- 'aiosp_opengraph_generate_descriptions' => 'https://semperplugins.com/documentation/social-meta-module/#auto-generate-og-descriptions',
979
-
980
- // POST META.
981
- 'aioseop_opengraph_settings_title' => 'https://semperplugins.com/documentation/social-meta-settings-individual-pagepost-settings/#title',
982
- 'aioseop_opengraph_settings_desc' => 'https://semperplugins.com/documentation/social-meta-settings-individual-pagepost-settings/#description',
983
- 'aioseop_opengraph_settings_image' => 'https://semperplugins.com/documentation/social-meta-settings-individual-pagepost-settings/#image',
984
- 'aioseop_opengraph_settings_customimg' => 'https://semperplugins.com/documentation/social-meta-settings-individual-pagepost-settings/#custom-image',
985
- 'aioseop_opengraph_settings_imagewidth' => 'https://semperplugins.com/documentation/social-meta-settings-individual-pagepost-settings/#specify-image-width-height',
986
- 'aioseop_opengraph_settings_imageheight' => 'https://semperplugins.com/documentation/social-meta-settings-individual-pagepost-settings/#specify-image-width-height',
987
- 'aioseop_opengraph_settings_video' => 'https://semperplugins.com/documentation/social-meta-settings-individual-pagepost-settings/#custom-video',
988
- 'aioseop_opengraph_settings_videowidth' => 'https://semperplugins.com/documentation/social-meta-settings-individual-pagepost-settings/#specify-video-width-height',
989
- 'aioseop_opengraph_settings_videoheight' => 'https://semperplugins.com/documentation/social-meta-settings-individual-pagepost-settings/#specify-video-width-height',
990
- 'aioseop_opengraph_settings_category' => 'https://semperplugins.com/documentation/social-meta-settings-individual-pagepost-settings/#facebook-object-type',
991
- 'aioseop_opengraph_settings_facebook_debug' => 'https://semperplugins.com/documentation/social-meta-settings-individual-pagepost-settings/#facebook-debug',
992
- 'aioseop_opengraph_settings_section' => 'https://semperplugins.com/documentation/social-meta-settings-individual-pagepost-settings/#article-section',
993
- 'aioseop_opengraph_settings_tag' => 'https://semperplugins.com/documentation/social-meta-settings-individual-pagepost-settings/#article-tags',
994
- 'aioseop_opengraph_settings_setcard' => 'https://semperplugins.com/documentation/social-meta-settings-individual-pagepost-settings/#twitter-card-type',
995
- 'aioseop_opengraph_settings_customimg_twitter' => 'https://semperplugins.com/documentation/social-meta-settings-individual-pagepost-settings/#custom-twitter-image',
996
- );
997
-
998
- return $this->merge_text_with_links( $rtn_help_text, $help_doc_link );
999
- }
1000
-
1001
- /**
1002
- * Help Text Robots Generator Module
1003
- *
1004
- * @ignore
1005
- * @since 2.4.2
1006
- * @access private
1007
- *
1008
- * @return array
1009
- */
1010
- private function help_text_robots_generator() {
1011
- $rtn_help_text = array(
1012
- 'aiosp_robots_type' => __( 'Use the dropdown to select whether you want to allow or block access to the specified directory or file.', 'all-in-one-seo-pack' ),
1013
- 'aiosp_robots_agent' => __( 'Enter the name of a User Agent here. You can use the wildcard * to allow or block all robots. A list of User Agents can be found <a target="_blank" rel="noopener noreferrer" href="http://www.robotstxt.org/db.html">here</a>.', 'all-in-one-seo-pack' ),
1014
- 'aiosp_robots_path' => __( 'Enter a valid path to a directory or file, for example: /wp-admin/ or /wp-admin/admin-ajax.php', 'all-in-one-seo-pack' ),
1015
- );
1016
-
1017
- return $rtn_help_text;
1018
- }
1019
-
1020
- /**
1021
- * Help Text File Editor Module
1022
- *
1023
- * @ignore
1024
- * @since 2.4.2
1025
- * @access private
1026
- *
1027
- * @return array
1028
- */
1029
- private function help_text_file_editor() {
1030
- return array(
1031
- 'aiosp_file_editor_htaccfile' => __( '.htaccess editor', 'all-in-one-seo-pack' ),
1032
- );
1033
- }
1034
-
1035
- /**
1036
- * Help Text Importer Exporter Module
1037
- *
1038
- * @ignore
1039
- * @since 2.4.2
1040
- * @access private
1041
- *
1042
- * @return array
1043
- */
1044
- private function help_text_importer_exporter() {
1045
- $rtn_help_text = array(
1046
- // Possible HTML link concept IF links become usable inside jQuery UI Tooltips.
1047
- /* translators: %1$s and 12$s are placeholders, which means these should not be translated. These will be replaced with the name of the plugin, All in One SEO Pack. */
1048
- 'aiosp_importer_exporter_import_submit' => sprintf( __( 'Choose a valid %1$s .ini file and click &quot;Import&quot; to import options from a previous state or install of %2$s.', 'all-in-one-seo-pack' ), AIOSEOP_PLUGIN_NAME, AIOSEOP_PLUGIN_NAME ),
1049
- 'aiosp_importer_exporter_export_choices' => __( 'You may choose to export settings from active modules, and content from post data.', 'all-in-one-seo-pack' ),
1050
- /* translators: %s is a placeholder, which means that it should not be translated. It will be replaced with the name of the plugin, All in One SEO Pack. */
1051
- 'aiosp_importer_exporter_export_post_types' => sprintf( __( 'Select which Post Types you want to export your %s meta data for.', 'all-in-one-seo-pack' ), AIOSEOP_PLUGIN_NAME ),
1052
- );
1053
-
1054
- $help_doc_link = array(
1055
- 'aiosp_importer_exporter_import_submit' => 'https://semperplugins.com/documentation/importer-exporter-module/',
1056
- 'aiosp_importer_exporter_export_choices' => 'https://semperplugins.com/documentation/importer-exporter-module/',
1057
- 'aiosp_importer_exporter_export_post_types' => 'https://semperplugins.com/documentation/importer-exporter-module/',
1058
- );
1059
-
1060
- return $this->merge_text_with_links( $rtn_help_text, $help_doc_link );
1061
- }
1062
-
1063
- /**
1064
- * Help Text Bad Robots Module
1065
- *
1066
- * @ignore
1067
- * @since 2.4.2
1068
- * @access private
1069
- *
1070
- * @return array
1071
- */
1072
- private function help_text_bad_robots() {
1073
- return array(
1074
- 'aiosp_bad_robots_block_bots' => __( 'Block requests from user agents that are known to misbehave with 503.', 'all-in-one-seo-pack' ),
1075
- 'aiosp_bad_robots_block_refer' => __( 'Block Referral Spam using HTTP.', 'all-in-one-seo-pack' ),
1076
- 'aiosp_bad_robots_track_blocks' => __( 'Log and show recent requests from blocked bots.', 'all-in-one-seo-pack' ),
1077
- 'aiosp_bad_robots_edit_blocks' => __( 'Check this to edit the list of disallowed user agents for blocking bad bots.', 'all-in-one-seo-pack' ),
1078
- 'aiosp_bad_robots_blocklist' => __( 'This is the list of disallowed user agents used for blocking bad bots.', 'all-in-one-seo-pack' ),
1079
- 'aiosp_bad_robots_referlist' => __( 'This is the list of disallowed referers used for blocking bad bots.', 'all-in-one-seo-pack' ),
1080
- 'aiosp_bad_robots_blocked_log' => __( 'Shows log of most recent requests from blocked bots. Note: this will not track any bots that were already blocked at the web server / .htaccess level.', 'all-in-one-seo-pack' ),
1081
- );
1082
- }
1083
-
1084
- /**
1085
- * Returns the tooltip help text for the Image SEO module screen.
1086
- *
1087
- * @ignore
1088
- * @since 3.4.0
1089
- *
1090
- * @return array
1091
- */
1092
- private function help_text_image_seo() {
1093
- $rtn_help_text = array(
1094
- 'aiosp_image_seo_title_format' =>
1095
- __( 'This controls the format of the title attribute of your images.', 'all-in-one-seo-pack' ) . '<br />' .
1096
- __( 'The following macros are supported:', 'all-in-one-seo-pack' ) .
1097
- '<dl>' .
1098
- '<dt>%image_title%</dt>' .
1099
- '<dd>' . __( 'Your image title', 'all-in-one-seo-pack' ) . '</dd>' .
1100
- '<dt>%site_title%</dt>' .
1101
- '<dd>' . __( 'Your site title', 'all-in-one-seo-pack' ) . '</dd>' .
1102
- '<dt>%site_description%</dt>' .
1103
- '<dd>' . __( 'Your site description', 'all-in-one-seo-pack' ) . '</dd>' .
1104
- '<dt>%image_seo_title%</dt>' .
1105
- '<dd>' . __( 'Your image SEO title. This is the title you enter in our metabox', 'all-in-one-seo-pack' ) . '</dd>' .
1106
- '<dt>%image_seo_description%</dt>' .
1107
- '<dd>' . __( 'Your image SEO description. This is the meta description you enter in our metabox', 'all-in-one-seo-pack' ) . '</dd>' .
1108
- '<dt>%post_seo_title%</dt>' .
1109
- '<dd>' . __( 'The SEO title set for the post or page', 'all-in-one-seo-pack' ) . '</dd>' .
1110
- '<dt>%post_seo_description%</dt>' .
1111
- '<dd>' . __( 'The SEO description set for the post or page', 'all-in-one-seo-pack' ) . '</dd>' .
1112
- '<dt>%alt_tag%</dt>' .
1113
- '<dd>' . __( "Your image's alt tag attribute", 'all-in-one-seo-pack' ) . '</dd>' .
1114
- '<dt>%post_title%</dt>' .
1115
- '<dd>' . __( 'The original title of the post or page', 'all-in-one-seo-pack' ) . '</dd>' .
1116
- '<dt>%category_title%</dt>' .
1117
- '<dd>' . __( 'The title of the category or taxonomy', 'all-in-one-seo-pack' ) . '</dd>' .
1118
- '<dt>%post_date%</dt>' .
1119
- '<dd>' . sprintf(
1120
- __( 'The %1$s when the %2$s was published (localized)', 'all-in-one-seo-pack' ),
1121
- __( 'date', 'all-in-one-seo-pack' ),
1122
- __( 'image', 'all-in-one-seo-pack' )
1123
- ) . '</dd>' .
1124
- '<dt>%post_year%</dt>' .
1125
- '<dd>' . sprintf(
1126
- __( 'The %1$s when the %2$s was published (localized)', 'all-in-one-seo-pack' ),
1127
- __( 'year', 'all-in-one-seo-pack' ),
1128
- __( 'image', 'all-in-one-seo-pack' )
1129
- ) . '</dd>' .
1130
- '<dt>%post_month%</dt>' .
1131
- '<dd>' . sprintf(
1132
- __( 'The %1$s when the %2$s was published (localized)', 'all-in-one-seo-pack' ),
1133
- __( 'month', 'all-in-one-seo-pack' ),
1134
- __( 'image', 'all-in-one-seo-pack' )
1135
- ) . '</dd>' .
1136
- '<dt>%tax_product_cat%</dt>' .
1137
- '<dd>' . __( 'The title of the first WooCommerce Product Category the Product is assigned to', 'all-in-one-seo-pack' ) . '</dd>' .
1138
- '<dt>%tax_product_tag%</dt>' .
1139
- '<dd>' . __( 'The title of the first WooCommerce Product Tag the Product is assigned to', 'all-in-one-seo-pack' ) . '</dd>' .
1140
- '</dl>',
1141
- 'aiosp_image_seo_title_strip_punc' => __( "Enable this setting to strip punctuation characters for your images' title attribute.", 'all-in-one-seo-pack' ),
1142
- 'aiosp_image_seo_alt_format' =>
1143
- __( 'This controls the format of the alt tag attribute of your images.', 'all-in-one-seo-pack' ) . '<br />' .
1144
- __( 'The following macros are supported:', 'all-in-one-seo-pack' ) .
1145
- '<dl>' .
1146
- '<dt>%image_title%</dt>' .
1147
- '<dd>' . __( 'Your image title', 'all-in-one-seo-pack' ) . '</dd>' .
1148
- '<dt>%site_title%</dt>' .
1149
- '<dd>' . __( 'Your site title', 'all-in-one-seo-pack' ) . '</dd>' .
1150
- '<dt>%site_description%</dt>' .
1151
- '<dd>' . __( 'Your site description', 'all-in-one-seo-pack' ) . '</dd>' .
1152
- '<dt>%image_seo_title%</dt>' .
1153
- '<dd>' . __( 'Your image SEO title. This is the title you enter in our metabox', 'all-in-one-seo-pack' ) . '</dd>' .
1154
- '<dt>%image_seo_description%</dt>' .
1155
- '<dd>' . __( 'Your image SEO description. This is the meta description you enter in our metabox', 'all-in-one-seo-pack' ) . '</dd>' .
1156
- '<dt>%post_seo_title%</dt>' .
1157
- '<dd>' . __( 'The SEO title set for the post or page', 'all-in-one-seo-pack' ) . '</dd>' .
1158
- '<dt>%post_seo_description%</dt>' .
1159
- '<dd>' . __( 'The SEO description set for the post or page', 'all-in-one-seo-pack' ) . '</dd>' .
1160
- '<dt>%alt_tag%</dt>' .
1161
- '<dd>' . __( "Your image's alt tag attribute", 'all-in-one-seo-pack' ) . '</dd>' .
1162
- '<dt>%post_title%</dt>' .
1163
- '<dd>' . __( 'The original title of the post or page', 'all-in-one-seo-pack' ) . '</dd>' .
1164
- '<dt>%category_title%</dt>' .
1165
- '<dd>' . __( 'The title of the category or taxonomy', 'all-in-one-seo-pack' ) . '</dd>' .
1166
- '<dt>%post_date%</dt>' .
1167
- '<dd>' . sprintf(
1168
- __( 'The %1$s when the %2$s was published (localized)', 'all-in-one-seo-pack' ),
1169
- __( 'date', 'all-in-one-seo-pack' ),
1170
- __( 'image', 'all-in-one-seo-pack' )
1171
- ) . '</dd>' .
1172
- '<dt>%post_year%</dt>' .
1173
- '<dd>' . sprintf(
1174
- __( 'The %1$s when the %2$s was published (localized)', 'all-in-one-seo-pack' ),
1175
- __( 'year', 'all-in-one-seo-pack' ),
1176
- __( 'image', 'all-in-one-seo-pack' )
1177
- ) . '</dd>' .
1178
- '<dt>%post_month%</dt>' .
1179
- '<dd>' . sprintf(
1180
- __( 'The %1$s when the %2$s was published (localized)', 'all-in-one-seo-pack' ),
1181
- __( 'month', 'all-in-one-seo-pack' ),
1182
- __( 'image', 'all-in-one-seo-pack' )
1183
- ) . '</dd>' .
1184
- '</dl>',
1185
- 'aiosp_image_seo_alt_strip_punc' => __( "Enable this setting to strip punctuation characters for your images' alt tag attribute.", 'all-in-one-seo-pack' ),
1186
- );
1187
-
1188
- $help_doc_link = array(
1189
- 'aiosp_image_seo_title_format' => 'https://semperplugins.com/documentation/image-seo-module/#title-attribute-format',
1190
- 'aiosp_image_seo_title_strip_punc' => 'https://semperplugins.com/documentation/image-seo-module/#strip-punctuation-for-title-attributes',
1191
- 'aiosp_image_seo_alt_format' => 'https://semperplugins.com/documentation/image-seo-module/#alt-tag-attribute-format',
1192
- 'aiosp_image_seo_alt_strip_punc' => 'https://semperplugins.com/documentation/image-seo-module/#strip-punctuation-for-alt-tag-attributes',
1193
- );
1194
-
1195
- return $this->merge_text_with_links( $rtn_help_text, $help_doc_link );
1196
- }
1197
-
1198
- /**
1199
- * Help Text Post Meta (Core Module)
1200
- *
1201
- * @ignore
1202
- * @since 2.4.2
1203
- * @access private
1204
- *
1205
- * @see self::_help_text_opengraph() Also adds Post Meta info.
1206
- *
1207
- * @return array
1208
- */
1209
- private function help_text_post_meta() {
1210
- $rtn_help_text = array(
1211
- 'aiosp_snippet' => __( 'A preview of what this page might look like in search engine results.', 'all-in-one-seo-pack' ),
1212
- 'aiosp_title' => __( 'A custom title that shows up in the title tag for this page.', 'all-in-one-seo-pack' ),
1213
- 'aiosp_description' => __( 'The META description for this page. This will override any autogenerated descriptions.', 'all-in-one-seo-pack' ),
1214
- 'aiosp_keywords' => __( 'A comma separated list of your most important keywords for this page that will be written as META keywords.', 'all-in-one-seo-pack' ),
1215
- 'aiosp_custom_link' => __( 'Override the canonical URLs for this post.', 'all-in-one-seo-pack' ),
1216
- 'aiosp_noindex' => __( 'Check this box to ask search engines not to index this page.', 'all-in-one-seo-pack' ),
1217
- 'aiosp_nofollow' => __( 'Check this box to ask search engines not to follow links from this page.', 'all-in-one-seo-pack' ),
1218
- 'aiosp_sitemap_exclude' => __( 'Don\'t display this page in the sitemap.', 'all-in-one-seo-pack' ),
1219
- 'aiosp_sitemap_priority' => __( 'Override the default sitemap priority for this post.', 'all-in-one-seo-pack' ),
1220
- 'aiosp_sitemap_frequency' => __( 'Override the default sitemap frequency for this post.', 'all-in-one-seo-pack' ),
1221
- 'aiosp_disable' => __( 'Disable SEO on this page.', 'all-in-one-seo-pack' ),
1222
- 'aiosp_disable_analytics' => __( 'Disable Google Analytics on this page.', 'all-in-one-seo-pack' ),
1223
- );
1224
-
1225
- $help_doc_link = array(
1226
- 'aiosp_snippet' => 'https://semperplugins.com/documentation/post-settings/#preview-snippet',
1227
- 'aiosp_title' => 'https://semperplugins.com/documentation/post-settings/#title',
1228
- 'aiosp_description' => 'https://semperplugins.com/documentation/post-settings/#description',
1229
- 'aiosp_keywords' => 'https://semperplugins.com/documentation/post-settings/#keywords',
1230
- 'aiosp_custom_link' => 'https://semperplugins.com/documentation/post-settings/#custom-canonical-url',
1231
- 'aiosp_noindex' => 'https://semperplugins.com/documentation/post-settings/#robots-meta-noindex',
1232
- 'aiosp_nofollow' => 'https://semperplugins.com/documentation/post-settings/#robots-meta-nofollow',
1233
- 'aiosp_sitemap_exclude' => 'https://semperplugins.com/documentation/post-settings/#exclude-from-sitemap',
1234
- 'aiosp_sitemap_priority' => 'https://semperplugins.com/documentation/post-settings/#sitemap-priority',
1235
- 'aiosp_sitemap_frequency' => 'https://semperplugins.com/documentation/post-settings/#sitemap-frequency',
1236
- 'aiosp_disable' => 'https://semperplugins.com/documentation/post-settings/#disable-on-this-post',
1237
- 'aiosp_disable_analytics' => 'https://semperplugins.com/documentation/post-settings/#disable-google-analytics',
1238
- );
1239
-
1240
- foreach ( $help_doc_link as $k1_slug => $v1_url ) {
1241
- $link_text = __( 'Click here for documentation on this setting.', 'all-in-one-seo-pack' );
1242
- $link_url = $v1_url;
1243
-
1244
- if ( ! AIOSEOPPRO &&
1245
- ( 'aiosp_sitemap_priority' === $k1_slug || 'aiosp_sitemap_frequency' === $k1_slug )
1246
- ) {
1247
- $link_text = sprintf( __( 'Upgrade to %s to unlock this feature.', 'all-in-one-seo-pack' ), AIOSEOP_PLUGIN_NAME . '&nbsp;Pro' );
1248
- $link_url = "https://semperplugins.com/all-in-one-seo-pack-pro-version/?utm_source=WordPress&utm_campaign=liteplugin&utm_medium=$k1_slug";
1249
- }
1250
-
1251
- $rtn_help_text[ $k1_slug ] .= '<br /><br /><a href="' . $link_url . '" target="_blank">' . $link_text . '</a>';
1252
- }
1253
-
1254
- return $rtn_help_text;
1255
- }
1256
-
1257
- /**
1258
- * Get Help Text
1259
- *
1260
- * Gets an individual help text if it exists, otherwise an error is returned
1261
- * to notify the AIOSEOP Devs.
1262
- * NOTE: Returning an empty string causes issues with the UI.
1263
- *
1264
- * @since 2.4.2
1265
- *
1266
- * @param string $slug Module option slug.
1267
- * @return string
1268
- */
1269
- public function get_help_text( $slug ) {
1270
- if ( isset( $this->help_text[ $slug ] ) ) {
1271
- return esc_html( $this->help_text[ $slug ] );
1272
- }
1273
- return 'DEV: Missing Help Text: ' . $slug;
1274
- }
1275
-
1276
- /**
1277
- * Returns the tooltip help text with their respective documentation links.
1278
- *
1279
- * @since 3.4.0
1280
- *
1281
- * @param array $doc_text The tooltip strings.
1282
- * @param array $doc_links The links to the docs on our website.
1283
- *
1284
- * @return array $tooltip_content The tooltip strings paired with their respective documentation links.
1285
- */
1286
- private function merge_text_with_links( $doc_text, $doc_links ) {
1287
-
1288
- foreach ( $doc_links as $setting_slug => $url ) {
1289
- $doc_text[ $setting_slug ] .= sprintf( "<br /><br /><a href='%s' target='_blank'>%s</a>", $url, __( 'Click here for documentation on this setting.', 'all-in-one-seo-pack' ) );
1290
- }
1291
- return $doc_text;
1292
- }
1293
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/class-aioseop-notices.php DELETED
@@ -1,1229 +0,0 @@
1
- <?php
2
- /**
3
- * AIOSEOP Notice API: AIOSEOP Notice Class
4
- *
5
- * Handles adding, updating, and removing notices. Then handles activating or
6
- * deactivating those notices site-wide or user based.
7
- *
8
- * @link https://wordpress.org/plugins/all-in-one-seo-pack/
9
- *
10
- * @package All_in_One_SEO_Pack
11
- * @since 3.0
12
- */
13
-
14
- if ( ! class_exists( 'AIOSEOP_Notices' ) ) {
15
- /**
16
- * AIOSEOP Notice.
17
- *
18
- * Admin notices for AIOSEOP.
19
- *
20
- * @since 3.0
21
- */
22
- class AIOSEOP_Notices {
23
- /**
24
- * Collection of notices to display.
25
- *
26
- * @since 3.0
27
- * @access public
28
- *
29
- * @var array $notices {
30
- * @type array $slug {
31
- * -- Server Variables --
32
- * @type string $slug Required. Notice unique ID.
33
- * @type int $time_start The time the notice was added to the object.
34
- * @type int $time_set Set when AJAX/Action_Option was last used to delay time. Primarily for PHPUnit tests.
35
- *
36
- * -- Filter Function Variables --
37
- * @type int $delay_time Amount of time to begin showing message.
38
- * @type string $message Content message to display in the container.
39
- * @type array $action_option {
40
- * Show options for users to click on. Default: See self::action_option_defaults().
41
- * @type array {
42
- * @type int $time Optional. The amount of time to delay. Zero immediately displays Default: 0.
43
- * @type string $text Optional. Button/Link HTML text to display. Default: ''.
44
- * @type string $class Optional. Class names to add to the link/button for styling. Default: ''.
45
- * @type string $link Optional. The elements href source/link. Default: '#'.
46
- * @type boolean $dismiss Optional. Variable for AJAX to dismiss showing a notice.
47
- * }
48
- * }
49
- * @type string $class The class notice used by WP, or a custom CSS class.
50
- * Ex. notice-error, notice-warning, notice-success, notice-info.
51
- * @type string $target Shows based on site-wide or user notice data.
52
- * @todo string $perms Displays based on user-role/permissions.
53
- * @type array $screens Which screens to exclusively display the notice on. Default: array().
54
- * array() = all,
55
- * array('aioseop') = $this->aioseop_screens,
56
- * array('CUSTOM') = specific screen(s).
57
- * }
58
- * }
59
- */
60
- public $notices = array();
61
-
62
- /**
63
- * Collection of remote notices.
64
- *
65
- * @since 3.6.0
66
- *
67
- * @var array $remote_notices => {
68
- * @type array $id => {
69
- * @type int $id Notice ID
70
- * @type string $title The notification title.
71
- * @type string $content Content of notification.
72
- * @type array $type License levels/type.
73
- * @type array $btns Notice buttons.
74
- * @type string $start Time/Date to show notification.
75
- * @type string $end Time/Date to hide notification.
76
- * @type string $notification_type Notification type class.
77
- * }
78
- * }
79
- */
80
- public $remote_notices = array();
81
-
82
- /**
83
- * Source of remote notifications.
84
- *
85
- * @since 3.6.0
86
- *
87
- * @var string
88
- */
89
- public $remote_url = 'https://plugin-cdn.aioseo.com/wp-content/notifications.json';
90
-
91
- /**
92
- * List of notice slugs that are currently active.
93
- * NOTE: Amount is reduced by 1 second in order to display at exactly X amount of time.
94
- *
95
- * @todo Change name to $display_times for consistancy both conceptually and with usermeta structure.
96
- *
97
- * @since 3.0
98
- * @access public
99
- *
100
- * @var array $active_notices {
101
- * @type string|int $slug => $display_time Contains the current active notices
102
- * that are scheduled to be displayed.
103
- * }
104
- */
105
- public $active_notices = array();
106
-
107
- /**
108
- * Dismissed Notices
109
- *
110
- * Stores notices that have been dismissed sitewide. Users are stored in usermeta data 'aioseop_notice_dismissed_{$slug}'.
111
- *
112
- * @since 3.0
113
- *
114
- * @var array $dismissed_notices {
115
- * @type boolean $notice_slug => $is_dismissed True if dismissed.
116
- * }
117
- */
118
- public $dismissed_notices = array();
119
-
120
- /**
121
- * The default dismiss time. An anti-nag setting.
122
- *
123
- * @var int $default_dismiss_delay
124
- */
125
- private $default_dismiss_delay = 315569260; // 10 years
126
-
127
- /**
128
- * List of Screens used in AIOSEOP.
129
- *
130
- * @since 3.0
131
- *
132
- * @var array $aioseop_screens {
133
- * @type string Screen ID.
134
- * }
135
- */
136
- private $aioseop_screens = array();
137
-
138
- /**
139
- * List of screens that should be excluded.
140
- *
141
- * @var array
142
- *
143
- * @since 3.4.0
144
- */
145
- private $excluded_screens = array(
146
- 'About Us' => 'all-in-one-seo_page_aioseop-about',
147
- );
148
-
149
- /**
150
- * __constructor.
151
- *
152
- * @since 3.0
153
- */
154
- public function __construct() {
155
- $this->_requires();
156
- $this->obj_load_options();
157
-
158
- if ( current_user_can( 'aiosp_manage_seo' ) ) {
159
- $this->aioseop_screens = aioseop_get_admin_screens();
160
-
161
- add_action( 'admin_init', array( $this, 'init' ) );
162
- add_action( 'current_screen', array( $this, 'admin_screen' ) );
163
- }
164
-
165
- add_action( 'aioseop_cron_check_remote_notices', array( $this, 'cron_check_remote_notices' ) );
166
- }
167
-
168
- /**
169
- * _Requires
170
- *
171
- * Internal use only. Additional files required.
172
- *
173
- * @since 3.0
174
- */
175
- private function _requires() {
176
- $this->autoload_notice_files();
177
- }
178
-
179
- /**
180
- * Autoload Notice Files
181
- *
182
- * @since 3.0
183
- *
184
- * @see DirectoryIterator class
185
- * @link https://php.net/manual/en/class.directoryiterator.php
186
- * @see StackOverflow for getting all filenamess in a directory.
187
- * @link https://stackoverflow.com/a/25988433/1376780
188
- */
189
- private function autoload_notice_files() {
190
- foreach ( new DirectoryIterator( AIOSEOP_PLUGIN_DIR . 'admin/display/notices/' ) as $file ) {
191
- $extension = pathinfo( $file->getFilename(), PATHINFO_EXTENSION );
192
- if ( $file->isFile() && 'php' === $extension ) {
193
- $filename = $file->getFilename();
194
-
195
- // Qualified file pattern; "*-notice.php".
196
- // Prevents any malicious files that may have spreaded.
197
- if ( array_search( 'notice', explode( '-', str_replace( '.php', '', $filename ) ), true ) ) {
198
- include_once AIOSEOP_PLUGIN_DIR . 'admin/display/notices/' . $filename;
199
- }
200
- }
201
- }
202
- }
203
-
204
- /**
205
- * Early operations required by the plugin.
206
- *
207
- * AJAX requires being added early before screens have been loaded.
208
- *
209
- * @since 3.0
210
- */
211
- public function init() {
212
- add_action( 'wp_ajax_aioseop_notice', array( $this, 'ajax_notice_action' ) );
213
- add_action( 'wp_ajax_aioseop_remote_notice', array( $this, 'ajax_remote_notice_action' ) );
214
-
215
- if ( ! wp_next_scheduled( 'aioseop_cron_check_remote_notices' ) ) {
216
- wp_schedule_event( time(), 'daily', 'aioseop_cron_check_remote_notices' );
217
- }
218
- }
219
-
220
- /**
221
- * Check Remote Notices.
222
- *
223
- * @since 3.6.0
224
- */
225
- public function cron_check_remote_notices() {
226
- $remote_notices = $this->get_remote_notices();
227
-
228
- // Replace existing notices in case they were updated.
229
- for ( $i = 0; $i < count( $this->remote_notices ); $i++ ) {
230
- foreach ( $remote_notices as $remote_notice ) {
231
- if ( $this->remote_notices[ $i ]['id'] === $remote_notice['id'] ) {
232
- unset( $this->remote_notices[ $i ] );
233
- }
234
- }
235
- }
236
-
237
- $this->remote_notices = array_merge( $this->remote_notices, $remote_notices );
238
-
239
- $this->obj_update_options();
240
- }
241
-
242
- /**
243
- * Get Remote URL.
244
- *
245
- * Checks is a constant is defined (dev purposes), if not, then use URL in `$this->remote_url`.
246
- *
247
- * @since 3.6.0
248
- *
249
- * @return string
250
- */
251
- public function get_remote_url() {
252
- if ( defined( 'AIOSEO_NOTIFICATIONS_URL' ) ) {
253
- return AIOSEO_NOTIFICATIONS_URL;
254
- }
255
-
256
- return $this->remote_url;
257
- }
258
-
259
- /**
260
- * Get Remote Notices.
261
- *
262
- * @since 3.6.0
263
- *
264
- * @return array
265
- */
266
- private function get_remote_notices() {
267
- $res = wp_remote_get( $this->get_remote_url() );
268
-
269
- if ( is_wp_error( $res ) ) {
270
- return array();
271
- }
272
-
273
- $body = wp_remote_retrieve_body( $res );
274
-
275
- if ( empty( $body ) ) {
276
- return array();
277
- }
278
-
279
- return $this->validate_remote_notices( json_decode( $body, true ) );
280
- }
281
-
282
- /**
283
- * Validate Remote Notices.
284
- *
285
- * @since 3.6.0
286
- *
287
- * @param $remote_notices
288
- * @return array
289
- */
290
- private function validate_remote_notices( $remote_notices ) {
291
- if ( ! is_array( $remote_notices ) || empty( $remote_notices ) ) {
292
- return array();
293
- }
294
-
295
- $data = array();
296
- $obj_options = $this->obj_get_options();
297
- foreach ( $remote_notices as $remote_notice ) {
298
- // Invalid notice if required variables have no value.
299
- if (
300
- empty( $remote_notice['content'] ) ||
301
- empty( $remote_notice['type'] )
302
- ) {
303
- continue;
304
- }
305
-
306
- // If no 'start' time exists, set start to current time.
307
- if ( ! isset( $remote_notice['start'] ) || empty( $remote_notice['start'] ) ) {
308
- $remote_notice['start'] = date( 'Y-m-d H:i:s' );
309
- }
310
- // Skip if already expired.
311
- if ( ! empty( $remote_notice['end'] ) && time() > strtotime( $remote_notice['end'] ) ) {
312
- continue;
313
- }
314
-
315
- // Skip if already dismissed.
316
- if ( in_array( 'remote_' . $remote_notice['id'], array_keys( $obj_options['dismissed_notices'] ) ) ) {
317
- continue;
318
- }
319
-
320
- // Store notice if version matches.
321
- if ( $this->version_match( AIOSEOP_VERSION, $remote_notice['type'] ) ) {
322
- $data[ 'remote_' . $remote_notice['id'] ] = $remote_notice;
323
- continue;
324
- }
325
-
326
- // Store notice if plan matches.
327
- if ( AIOSEOPPRO ) {
328
- if (
329
- 'pro' === $remote_notice['type'] ||
330
- in_array( $this->get_license_plan(), $remote_notice['type'], true )
331
- ) {
332
- $data[ 'remote_' . $remote_notice['id'] ] = $remote_notice;
333
- }
334
- } else {
335
- if ( 'lite' === $remote_notice['type'] ) {
336
- $data[ 'remote_' . $remote_notice['id'] ] = $remote_notice;
337
- }
338
- }
339
- }
340
- return $data;
341
- }
342
-
343
- /**
344
- * Get License Level.
345
- *
346
- * @since 3.6.0
347
- *
348
- * @return string Type of license level being used.
349
- */
350
- public function get_license_plan() {
351
- global $aioseop_options;
352
-
353
- if ( ! isset( $aioseop_options['plan'] ) || empty( $aioseop_options['plan'] ) ) {
354
- return AIOSEOPPRO ? 'unlicensed' : 'lite';
355
- }
356
- return $aioseop_options['plan'];
357
- }
358
-
359
- /**
360
- * Version Compare.
361
- *
362
- * @since 3.6.0
363
- *
364
- * @param string $current_version The current version being used.
365
- * @param string|array $compare_version The version to compare with.
366
- * @return bool
367
- */
368
- public function version_match( $current_version, $compare_version ) {
369
- if ( is_array( $compare_version ) ) {
370
- foreach ( $compare_version as $compare_single ) {
371
- $recursive_result = $this->version_match( $current_version, $compare_single );
372
- if ( $recursive_result ) {
373
- return true;
374
- }
375
- }
376
-
377
- return false;
378
- }
379
-
380
- $current_parse = explode( '.', $current_version );
381
-
382
- if ( strpos( $compare_version, '-' ) ) {
383
- $compare_parse = explode( '-', $compare_version );
384
- } elseif ( strpos( $compare_version, '.' ) ) {
385
- $compare_parse = explode( '.', $compare_version );
386
- } else {
387
- return false;
388
- }
389
-
390
- $current_count = count( $current_parse );
391
- $compare_count = count( $compare_parse );
392
- for ( $i = 0; $i < $current_count || $i < $compare_count; $i++ ) {
393
- if ( isset( $compare_parse[ $i ] ) && 'x' === strtolower( $compare_parse[ $i ] ) ) {
394
- unset( $compare_parse[ $i ] );
395
- }
396
-
397
- if ( ! isset( $current_parse[ $i ] ) ) {
398
- unset( $compare_parse[ $i ] );
399
- } elseif ( ! isset( $compare_parse[ $i ] ) ) {
400
- unset( $current_parse[ $i ] );
401
- }
402
- }
403
-
404
- foreach ( $compare_parse as $index => $sub_number ) {
405
- if ( $current_parse[ $index ] !== $sub_number ) {
406
- return false;
407
- }
408
- }
409
-
410
- return true;
411
- }
412
-
413
- /**
414
- * Setup/Init Admin Screen
415
- *
416
- * Adds the initial actions to WP based on the Admin Screen being loaded.
417
- * The AIOSEOP and Other Screens have separate methods that are used, and
418
- * additional screens can be made exclusive/unique.
419
- *
420
- * @since 3.0
421
- *
422
- * @param WP_Screen $current_screen The current screen object being loaded.
423
- */
424
- public function admin_screen( $current_screen ) {
425
- $this->deregister_scripts();
426
- if ( isset( $current_screen->id ) && in_array( $current_screen->id, $this->aioseop_screens, true ) ) {
427
- // AIOSEO Notice Content.
428
- add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
429
- add_action( 'all_admin_notices', array( $this, 'display_notice_aioseop' ) );
430
- add_action( 'all_admin_notices', array( $this, 'display_remote_notice' ) );
431
- } elseif ( isset( $current_screen->id ) ) {
432
- // Default WP Notice.
433
- add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
434
- add_action( 'all_admin_notices', array( $this, 'display_notice_default' ) );
435
- }
436
- }
437
-
438
- /**
439
- * Load AIOSEOP_Notice Options
440
- *
441
- * Gets the options for AIOSEOP_Notice to set its variables to.
442
- *
443
- * @since 3.0
444
- * @access private
445
- *
446
- * @see self::notices
447
- * @see self::active_notices
448
- */
449
- private function obj_load_options() {
450
- $notices_options = $this->obj_get_options();
451
-
452
- $this->notices = $notices_options['notices'];
453
- $this->remote_notices = $this->validate_remote_notices( $notices_options['remote_notices'] );
454
- $this->active_notices = $notices_options['active_notices'];
455
- $this->dismissed_notices = $notices_options['dismissed_notices'];
456
- }
457
-
458
- /**
459
- * Get AIOSEOP_Notice Options
460
- *
461
- * @since 3.0
462
- * @access private
463
- *
464
- * @return array
465
- */
466
- private function obj_get_options() {
467
- $defaults = array(
468
- 'notices' => array(),
469
- 'remote_notices' => array(),
470
- 'active_notices' => array(),
471
- 'dismissed_notices' => array(),
472
- );
473
-
474
- // Prevent old data from being loaded instead.
475
- // Some notices are instant notifications.
476
- wp_cache_delete( 'aioseop_notices', 'options' );
477
- $notices_options = get_option( 'aioseop_notices' );
478
- if ( false === $notices_options ) {
479
- return $defaults;
480
- }
481
-
482
- return wp_parse_args( $notices_options, $defaults );
483
- }
484
-
485
- /**
486
- * Update Notice Options
487
- *
488
- * @since 3.0
489
- * @access private
490
- *
491
- * @return boolean True if successful, using update_option() return value.
492
- */
493
- private function obj_update_options() {
494
- $notices_options = array(
495
- 'notices' => $this->notices,
496
- 'remote_notices' => $this->remote_notices,
497
- 'active_notices' => $this->active_notices,
498
- 'dismissed_notices' => $this->dismissed_notices,
499
- );
500
- $old_notices_options = $this->obj_get_options();
501
- $notices_options = wp_parse_args( $notices_options, $old_notices_options );
502
-
503
- // Prevent old data from being loaded instead.
504
- // Some notices are instant notifications.
505
- wp_cache_delete( 'aioseop_notices', 'options' );
506
- return update_option( 'aioseop_notices', $notices_options, false );
507
- }
508
-
509
- /**
510
- * Notice Default Values
511
- *
512
- * Returns the default value for a variable to be used in self::notices[].
513
- *
514
- * @since 3.0
515
- *
516
- * @see AIOSEOP_Notices::notices Array variable that stores the collection of notices.
517
- *
518
- * @return array Notice variable in self::notices.
519
- */
520
- public function notice_defaults() {
521
- return array_merge(
522
- $this->notice_defaults_server(),
523
- $this->notice_defaults_file()
524
- );
525
- }
526
-
527
- /**
528
- * Notice Defaults Server
529
- *
530
- * @since 3.0
531
- *
532
- * @return array
533
- */
534
- public function notice_defaults_server() {
535
- return array(
536
- 'slug' => '',
537
- 'time_start' => time(),
538
- 'time_set' => time(),
539
- );
540
- }
541
-
542
- /**
543
- * Notice Defaults File
544
- *
545
- * @since 3.0
546
- *
547
- * @return array
548
- */
549
- public function notice_defaults_file() {
550
- return array(
551
- 'slug' => '',
552
- 'delay_time' => 0,
553
- 'message' => '',
554
- 'action_options' => array(),
555
- 'class' => 'notice-info',
556
- 'target' => 'site',
557
- 'screens' => array(),
558
- );
559
- }
560
-
561
- /**
562
- * Action Options Default Values
563
- *
564
- * Returns the default value for action_options in self::notices[$slug]['action_options'].
565
- *
566
- * @since 3.0
567
- *
568
- * @return array Action_Options variable in self::notices[$slug]['action_options'].
569
- */
570
- public function action_options_defaults() {
571
- return array(
572
- 'time' => 0,
573
- 'text' => __( 'Dismiss', 'all-in-one-seo-pack' ),
574
- 'link' => '#',
575
- 'new_tab' => true,
576
- 'dismiss' => true,
577
- 'class' => '',
578
- );
579
- }
580
-
581
- /**
582
- * Add Notice
583
- *
584
- * Takes notice and adds it to object and saves to database.
585
- *
586
- * @since 3.0
587
- *
588
- * @param array $notice See self::notices for more info.
589
- * @return boolean True on success.
590
- */
591
- public function add_notice( $notice = array() ) {
592
- if ( empty( $notice['slug'] ) || isset( $this->notices[ $notice['slug'] ] ) ) {
593
- return false;
594
- }
595
-
596
- $this->notices[ $notice['slug'] ] = $this->prepare_notice( $notice );
597
-
598
- return true;
599
- }
600
-
601
- /**
602
- * Prepare Insert/Undate Notice
603
- *
604
- * @since 3.0
605
- *
606
- * @param array $notice The notice to prepare with the database.
607
- * @return array
608
- */
609
- public function prepare_notice( $notice = array() ) {
610
- $notice_default = $this->notice_defaults_server();
611
- $notice = wp_parse_args( $notice, $notice_default );
612
-
613
- $new_notice = array();
614
- foreach ( $notice_default as $key => $value ) {
615
- $new_notice[ $key ] = $notice[ $key ];
616
- }
617
-
618
- return $new_notice;
619
- }
620
-
621
- /**
622
- * Used strictly for any notices that are deprecated/obsolete. To stop notices,
623
- * use notice_deactivate().
624
- *
625
- * @since 3.0
626
- *
627
- * @param string $slug Unique notice slug.
628
- * @return boolean True if successfully removed.
629
- */
630
- public function remove_notice( $slug ) {
631
- if ( isset( $this->notices[ $slug ] ) ) {
632
- $this->deactivate_notice( $slug );
633
- unset( $this->notices[ $slug ] );
634
- $this->obj_update_options();
635
- return true;
636
- }
637
-
638
- return false;
639
- }
640
-
641
- /**
642
- * Activate Notice
643
- *
644
- * Activates a notice, or Re-activates with a new display time. Used after
645
- * updating a notice that requires a hard reset.
646
- *
647
- * @since 3.0
648
- *
649
- * @param string $slug Notice slug.
650
- * @return boolean
651
- */
652
- public function activate_notice( $slug ) {
653
- if ( empty( $slug ) ) {
654
- return false;
655
- }
656
- $notice = $this->get_notice( $slug );
657
- if ( 'site' === $notice['target'] && isset( $this->active_notices[ $slug ] ) ) {
658
- return true;
659
- } elseif ( 'user' === $notice['target'] && get_user_meta( get_current_user_id(), 'aioseop_notice_display_time_' . $slug, true ) ) {
660
- return true;
661
- }
662
-
663
- if ( ! isset( $this->notices[ $slug ] ) ) {
664
- $this->add_notice( $notice );
665
- }
666
-
667
- $this->set_notice_delay( $slug, $notice['delay_time'] );
668
-
669
- $this->obj_update_options();
670
-
671
- return true;
672
- }
673
-
674
- /**
675
- * Deactivate Notice
676
- *
677
- * Deactivates a notice set as active and completely removes it from the
678
- * list of active notices. Used to prevent conflicting notices that may be
679
- * active at any given point in time.
680
- *
681
- * @since 3.0
682
- *
683
- * @param string $slug Notice slug.
684
- * @return boolean
685
- */
686
- public function deactivate_notice( $slug ) {
687
- if ( ! isset( $this->active_notices[ $slug ] ) ) {
688
- return false;
689
- } elseif ( ! isset( $this->notices[ $slug ] ) ) {
690
- return false;
691
- }
692
-
693
- delete_metadata(
694
- 'user',
695
- 0,
696
- 'aioseop_notice_display_time_' . $slug,
697
- '',
698
- true
699
- );
700
- unset( $this->active_notices[ $slug ] );
701
- $this->obj_update_options();
702
-
703
- return true;
704
- }
705
-
706
- /**
707
- * Reset Notice
708
- *
709
- * @since 3.0
710
- *
711
- * @param string $slug The notice's slug.
712
- * @return bool
713
- */
714
- public function reset_notice( $slug ) {
715
- if (
716
- empty( $slug ) ||
717
- (
718
- ! isset( $this->notices[ $slug ] ) &&
719
- ! get_user_meta( get_current_user_id(), 'aioseop_notice_display_time_' . $slug, true ) &&
720
- ! get_user_meta( get_current_user_id(), 'aioseop_notice_dismissed_' . $slug, true )
721
- )
722
- ) {
723
- return false;
724
- }
725
-
726
- $notice = $this->get_notice( $slug );
727
-
728
- unset( $this->active_notices[ $slug ] );
729
- unset( $this->dismissed_notices[ $slug ] );
730
- delete_metadata(
731
- 'user',
732
- 0,
733
- 'aioseop_notice_time_set_' . $slug,
734
- '',
735
- true
736
- );
737
- delete_metadata(
738
- 'user',
739
- 0,
740
- 'aioseop_notice_display_time_' . $slug,
741
- '',
742
- true
743
- );
744
- delete_metadata(
745
- 'user',
746
- 0,
747
- 'aioseop_notice_dismissed_' . $slug,
748
- '',
749
- true
750
- );
751
-
752
- $this->set_notice_delay( $slug, $notice['delay_time'] );
753
-
754
- $this->obj_update_options();
755
-
756
- return true;
757
- }
758
-
759
- /**
760
- * Set Notice Delay
761
- *
762
- * @since 3.0
763
- *
764
- * @param string $slug The notice's slug.
765
- * @param int $delay_time Amount of time to delay.
766
- * @return boolean
767
- */
768
- public function set_notice_delay( $slug, $delay_time ) {
769
- if ( empty( $slug ) ) {
770
- return false;
771
- }
772
- $time_set = time();
773
-
774
- // Display at exactly X time, not (X + 1) time.
775
- $display_time = $time_set + $delay_time - 1;
776
- $notice = $this->get_notice( $slug );
777
- if ( 'user' === $notice['target'] ) {
778
- $current_user_id = get_current_user_id();
779
-
780
- update_user_meta( $current_user_id, 'aioseop_notice_time_set_' . $slug, $time_set );
781
- update_user_meta( $current_user_id, 'aioseop_notice_display_time_' . $slug, $display_time );
782
- }
783
-
784
- $this->notices[ $slug ]['time_set'] = $time_set;
785
- $this->notices[ $slug ]['time_start'] = $display_time;
786
- $this->active_notices[ $slug ] = $display_time;
787
-
788
- return true;
789
- }
790
-
791
- /**
792
- * Set Notice Dismiss
793
- *
794
- * @since 3.0
795
- *
796
- * @param string $slug The notice's slug.
797
- * @param boolean $dismiss Sets to dismiss a notice.
798
- */
799
- public function set_notice_dismiss( $slug, $dismiss ) {
800
- $notice = $this->get_notice( $slug );
801
- if ( 'site' === $notice['target'] ) {
802
- $this->dismissed_notices[ $slug ] = $dismiss;
803
- } elseif ( 'user' === $notice['target'] ) {
804
- $current_user_id = get_current_user_id();
805
-
806
- update_user_meta( $current_user_id, 'aioseop_notice_dismissed_' . $slug, $dismiss );
807
- }
808
- }
809
-
810
- /**
811
- * Get Notice
812
- *
813
- * @since 3.0
814
- *
815
- * @param string $slug The notice's slug.
816
- * @return array
817
- */
818
- public function get_notice( $slug ) {
819
- // Set defaults for notice.
820
- $rtn_notice = $this->notice_defaults();
821
-
822
- if ( isset( $this->notices[ $slug ] ) ) {
823
- // Get minimized (database) data.
824
- $rtn_notice = array_merge( $rtn_notice, $this->notices[ $slug ] );
825
- }
826
-
827
- /**
828
- * Admin Notice {$slug}
829
- *
830
- * Applies the notice data values for a given notice slug.
831
- * `aioseop_admin_notice-{$slug}` with the slug being the individual notice.
832
- *
833
- * @since 3.0
834
- *
835
- * @params array $notice_data See `\AIOSEOP_Notices::$notices` for structural documentation.
836
- */
837
- $notice_data = apply_filters( 'aioseop_admin_notice-' . $slug, array() ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
838
-
839
- if ( ! empty( $notice_data ) ) {
840
- $rtn_notice = array_merge( $rtn_notice, $notice_data );
841
-
842
- foreach ( $rtn_notice['action_options'] as &$action_option ) {
843
- // Set defaults for `$notice['action_options']`.
844
- $action_option = array_merge( $this->action_options_defaults(), $action_option );
845
- }
846
- }
847
-
848
- return $rtn_notice;
849
- }
850
-
851
- /*** DISPLAY Methods **************************************************/
852
- /**
853
- * Deregister Scripts
854
- *
855
- * Initial Admin Screen action to remove aioseop script(s) from all screens;
856
- * which will be registered if executed on screen.
857
- * NOTE: As of 3.0, most of it is default layout, styling, & scripting
858
- * that is loaded on all pages. Which can later be different.
859
- *
860
- * @since 3.0
861
- * @access private
862
- *
863
- * @see self::admin_screen()
864
- */
865
- private function deregister_scripts() {
866
- wp_deregister_script( 'aioseop-admin-notice-js' );
867
- wp_deregister_style( 'aioseop-admin-notice-css' );
868
- }
869
-
870
- /**
871
- * (Register) Enqueue Scripts
872
- *
873
- * Used to register, enqueue, and localize any JS data. Styles can later be added.
874
- *
875
- * @since 3.0
876
- */
877
- public function admin_enqueue_scripts() {
878
- // Register.
879
- wp_register_script(
880
- 'aioseop-admin-notice-js',
881
- AIOSEOP_PLUGIN_URL . 'js/admin-notice.js',
882
- array( 'jquery' ),
883
- AIOSEOP_VERSION,
884
- true
885
- );
886
-
887
- // Localization.
888
- $notice_actions = array();
889
- foreach ( $this->active_notices as $notice_slug => $notice_display_time ) {
890
- $notice = $this->get_notice( $notice_slug );
891
- foreach ( $notice['action_options'] as $action_index => $action_arr ) {
892
- $notice_actions[ $notice_slug ][] = $action_index;
893
- }
894
- }
895
-
896
- $loc_remote_notices = array();
897
- foreach ( $this->remote_notices as $remote_notice ) {
898
- if ( ! isset( $remote_notice['btns'] ) ) {
899
- continue;
900
- }
901
- array_push( $loc_remote_notices, $remote_notice['id'] );
902
- }
903
-
904
- $admin_notice_localize = array(
905
- 'notice_nonce' => wp_create_nonce( 'aioseop_ajax_notice' ),
906
- 'notice_actions' => $notice_actions,
907
- 'remote_notices' => $loc_remote_notices,
908
- );
909
- wp_localize_script( 'aioseop-admin-notice-js', 'aioseop_notice_data', $admin_notice_localize );
910
-
911
- // Enqueue.
912
- wp_enqueue_script( 'aioseop-admin-notice-js' );
913
-
914
- wp_enqueue_style(
915
- 'aioseop-admin-notice-css',
916
- AIOSEOP_PLUGIN_URL . 'css/admin-notice.css',
917
- false,
918
- AIOSEOP_VERSION,
919
- false
920
- );
921
- }
922
-
923
- /**
924
- * Display Notice as Default
925
- *
926
- * Method for default WP Admin notices.
927
- * NOTE: As of 3.0, display_notice_default() & display_notice_aioseop()
928
- * have the same functionality, but serves as a future development concept.
929
- *
930
- * @since 3.0
931
- *
932
- * @uses AIOSEOP_PLUGIN_DIR . 'admin/display/notice-default.php' Template for default notices.
933
- *
934
- * @return void
935
- */
936
- public function display_notice_default() {
937
- $this->display_notice( 'default' );
938
- }
939
-
940
- /**
941
- * Display Notice as AIOSEOP Screens
942
- *
943
- * Method for Admin notices exclusive to AIOSEOP screens.
944
- * NOTE: As of 3.0, display_notice_default() & display_notice_aioseop()
945
- * have the same functionality, but serves as a future development concept.
946
- *
947
- * @since 3.0
948
- *
949
- * @uses AIOSEOP_PLUGIN_DIR . 'admin/display/notice-aioseop.php' Template for notices.
950
- *
951
- * @return void
952
- */
953
- public function display_notice_aioseop() {
954
- $this->display_notice( 'aioseop' );
955
- }
956
-
957
- /**
958
- * Display Notice
959
- *
960
- * @since 2.8
961
- *
962
- * @param string $template Slug name for template.
963
- */
964
- public function display_notice( $template ) {
965
- if ( ! wp_script_is( 'aioseop-admin-notice-js', 'enqueued' ) || ! wp_style_is( 'aioseop-admin-notice-css', 'enqueued' ) ) {
966
- return;
967
- } elseif ( 'default' !== $template && 'aioseop' !== $template ) {
968
- return;
969
- } elseif ( ! current_user_can( 'aiosp_manage_seo' ) ) {
970
- return;
971
- }
972
-
973
- $current_screen = get_current_screen();
974
- $current_user_id = get_current_user_id();
975
- foreach ( $this->active_notices as $a_notice_slug => $a_notice_time_display ) {
976
- $notice_show = true;
977
- $notice = $this->get_notice( $a_notice_slug );
978
-
979
- // If we have no message or static HTML, this is a bad notice.
980
- if ( empty( $notice['message'] ) && empty( $notice['html'] ) ) {
981
- $this->remove_notice( $a_notice_slug );
982
- continue;
983
- }
984
-
985
- // Screen Restriction.
986
- if ( ! empty( $notice['screens'] ) ) {
987
-
988
- if ( in_array( 'aioseop', $notice['screens'], true ) ) {
989
- unset( $notice['screens']['aiosoep'] );
990
- $notice['screens'] = array_merge( $notice['screens'], aioseop_get_admin_screens() );
991
- }
992
-
993
- if ( ! in_array( $current_screen->id, $notice['screens'], true ) ) {
994
- continue;
995
- }
996
- }
997
-
998
- if ( in_array( $current_screen->id, $this->excluded_screens, true ) ) {
999
- continue;
1000
- }
1001
-
1002
- if ( isset( $this->dismissed_notices[ $a_notice_slug ] ) && $this->dismissed_notices[ $a_notice_slug ] ) {
1003
- $notice_show = false;
1004
- }
1005
-
1006
- // User Settings.
1007
- if ( 'user' === $notice['target'] ) {
1008
- $user_dismissed = get_user_meta( $current_user_id, 'aioseop_notice_dismissed_' . $a_notice_slug, true );
1009
- if ( ! $user_dismissed ) {
1010
- $user_notice_time_display = get_user_meta( $current_user_id, 'aioseop_notice_display_time_' . $a_notice_slug, true );
1011
- if ( ! empty( $user_notice_time_display ) ) {
1012
- $a_notice_time_display = intval( $user_notice_time_display );
1013
- }
1014
- } else {
1015
- $notice_show = false;
1016
- }
1017
- }
1018
-
1019
- // Display/Render.
1020
- $important_admin_notices = array(
1021
- 'notice-error',
1022
- 'notice-warning',
1023
- 'notice-do-nag',
1024
- );
1025
- if ( defined( 'DISABLE_NAG_NOTICES' ) && true === DISABLE_NAG_NOTICES && ( ! in_array( $notice['class'], $important_admin_notices, true ) ) ) {
1026
- // Skip if `DISABLE_NAG_NOTICES` is implemented (as true).
1027
- // Important notices, WP's CSS `notice-error` & `notice-warning`, are still rendered.
1028
- continue;
1029
- } elseif ( time() > $a_notice_time_display && $notice_show ) {
1030
- include AIOSEOP_PLUGIN_DIR . 'admin/display/notice-' . $template . '.php';
1031
- }
1032
- }
1033
- }
1034
-
1035
- /**
1036
- * Display Remote Notices
1037
- *
1038
- * @since 3.6.0
1039
- */
1040
- public function display_remote_notice() {
1041
- if ( ! current_user_can( 'aiosp_manage_seo' ) ) {
1042
- return;
1043
- }
1044
-
1045
- $current_screen = get_current_screen();
1046
- if ( in_array( $current_screen->id, $this->excluded_screens, true ) ) {
1047
- return;
1048
- }
1049
-
1050
- foreach ( $this->remote_notices as $remote_notice_slug => $remote_notice ) {
1051
- if (
1052
- time() < strtotime( $remote_notice['start'] ) ||
1053
- ( ! empty( $remote_notice['end'] ) && time() > strtotime( $remote_notice['end'] ) )
1054
- ) {
1055
- continue;
1056
- }
1057
- if ( isset( $this->dismissed_notices[ $remote_notice_slug ] ) && $this->dismissed_notices[ $remote_notice_slug ] ) {
1058
- continue;
1059
- }
1060
-
1061
- if ( isset( $remote_notice['btns'] ) ) {
1062
- foreach ( $remote_notice['btns'] as $btn_slug => $btn ) {
1063
- $remote_notice['btns'][ $btn_slug ]['url'] = $this->format_url( $btn['url'] );
1064
- }
1065
- }
1066
-
1067
- // Display/Render.
1068
- $important_admin_notices = array(
1069
- 'error',
1070
- 'warning',
1071
- 'do-nag',
1072
- );
1073
- if ( defined( 'DISABLE_NAG_NOTICES' ) && true === DISABLE_NAG_NOTICES && ( ! in_array( $remote_notice['notification_type'], $important_admin_notices, true ) ) ) {
1074
- continue;
1075
- } else {
1076
- include AIOSEOP_PLUGIN_DIR . 'admin/display/notice-remote.php';
1077
- }
1078
- }
1079
- }
1080
-
1081
- /**
1082
- * Format URL
1083
- *
1084
- * @since 3.6.0
1085
- *
1086
- * @param string $url
1087
- * @return string
1088
- */
1089
- public function format_url( $url ) {
1090
- $replace = array(
1091
- '(http://dismiss)' => '#dismiss',
1092
- '(http://route)' => admin_url() . 'admin.php',
1093
- '(#aioseo-settings)' => '?page=' . AIOSEOP_PLUGIN_DIRNAME . '/aioseop_class.php',
1094
- '(#aioseo-general-settings)' => '?page=' . AIOSEOP_PLUGIN_DIRNAME . '/aioseop_class.php',
1095
- '(#aioseo-performance)' => '?page=' . AIOSEOP_PLUGIN_DIRNAME . '/modules/aioseop_performance.php',
1096
- '(#aioseo-sitemap)' => '?page=' . AIOSEOP_PLUGIN_DIRNAME . '/modules/aioseop_sitemap.php',
1097
- '(#aioseo-opengraph)' => '?page=aiosp_opengraph',
1098
- '(#aioseo-robots)' => '?page=' . AIOSEOP_PLUGIN_DIRNAME . '/modules/aioseop_robots.php',
1099
- '(#aioseo-file-editor)' => '?page=' . AIOSEOP_PLUGIN_DIRNAME . '/modules/aioseop_file_editor.php',
1100
- '(#aioseo-importer-exporter)' => '?page=' . AIOSEOP_PLUGIN_DIRNAME . '/modules/aioseop_importer_exporter.php',
1101
- '(#aioseo-bad-robots)' => '?page=' . AIOSEOP_PLUGIN_DIRNAME . '/modules/aioseop_bad_robots.php',
1102
- '(#aioseo-feature-manager)' => '?page=' . AIOSEOP_PLUGIN_DIRNAME . '/modules/aioseop_feature_manager.php',
1103
- '(#aioseo-about)' => '?page=aioseop-about',
1104
- '(#aioseo-video-sitemap)' => '#',
1105
- '(#aioseo-image-seo)' => '#',
1106
- '(#aioseo-local-business)' => '#',
1107
- '(:[0-9a-zA-Z-_]+)' => '#',
1108
- );
1109
- if ( AIOSEOPPRO ) {
1110
- $replace['(#aioseo-sitemap)'] = '?page=' . AIOSEOP_PLUGIN_DIRNAME . '/pro/class-aioseop-pro-sitemap.php';
1111
- $replace['(#aioseo-video-sitemap)'] = '?page=' . AIOSEOP_PLUGIN_DIRNAME . '/pro/video_sitemap.php';
1112
- if ( aioseop_is_addon_allowed( 'image_seo' ) ) {
1113
- $replace['(#aioseo-image-seo)'] = '?page=aiosp_image_seo';
1114
- }
1115
- if ( aioseop_is_addon_allowed( 'schema_local_business' ) ) {
1116
- $replace['(#aioseo-local-business)'] = '?page=' . AIOSEOP_PLUGIN_DIRNAME . '/pro/modules/class-aioseop-schema-local-business.php';
1117
- }
1118
- }
1119
- $search = array_keys( $replace );
1120
-
1121
- $tmp_url = preg_replace( $search, $replace, $url );
1122
- if ( ! empty( $tmp_url ) ) {
1123
- $url = $tmp_url;
1124
- }
1125
-
1126
- return $url;
1127
- }
1128
-
1129
- /**
1130
- * AJAX Notice Action
1131
- *
1132
- * Fires when a Action_Option is clicked and sent via AJAX. Also includes
1133
- * WP Default Dismiss (rendered as a clickable button on upper-right).
1134
- *
1135
- * @since 3.0
1136
- *
1137
- * @see AIOSEOP_PLUGIN_DIR . 'js/admin-notice.js'
1138
- */
1139
- public function ajax_notice_action() {
1140
- check_ajax_referer( 'aioseop_ajax_notice' );
1141
- if ( ! current_user_can( 'aiosp_manage_seo' ) ) {
1142
- wp_send_json_error( __( "User doesn't have `aiosp_manage_seo` capabilities.", 'all-in-one-seo-pack' ) );
1143
- }
1144
- // Notice (Slug) => (Action_Options) Index.
1145
- $notice_slug = null;
1146
- $action_index = null;
1147
- if ( isset( $_POST['notice_slug'] ) ) {
1148
- $notice_slug = filter_input( INPUT_POST, 'notice_slug', FILTER_SANITIZE_STRING );
1149
-
1150
- // When PHPUnit is unable to use filter_input.
1151
- if ( defined( 'AIOSEOP_UNIT_TESTING' ) && null === $notice_slug && ! empty( $_POST['notice_slug'] ) ) {
1152
- $notice_slug = $_POST['notice_slug'];
1153
- }
1154
- }
1155
- if ( isset( $_POST['action_index'] ) ) {
1156
- $action_index = filter_input( INPUT_POST, 'action_index', FILTER_SANITIZE_STRING );
1157
-
1158
- // When PHPUnit is unable to use filter_input.
1159
- if ( defined( 'AIOSEOP_UNIT_TESTING' ) && null === $action_index && ( ! empty( $_POST['action_index'] ) || 0 === $_POST['action_index'] ) ) {
1160
- $action_index = $_POST['action_index'];
1161
- }
1162
- }
1163
- if ( empty( $notice_slug ) ) {
1164
- /* Translators: Displays the hardcoded slug that is missing. */
1165
- wp_send_json_error( sprintf( __( 'Missing values from `%s`.', 'all-in-one-seo-pack' ), 'notice_slug' ) );
1166
- } elseif ( empty( $action_index ) && 0 !== (int) $action_index ) {
1167
- /* Translators: Displays the hardcoded action index that is missing. */
1168
- wp_send_json_error( sprintf( __( 'Missing values from `%s`.', 'all-in-one-seo-pack' ), 'action_index' ) );
1169
- }
1170
-
1171
- $action_options = $this->action_options_defaults();
1172
- $action_options['time'] = $this->default_dismiss_delay;
1173
- $action_options['dismiss'] = false;
1174
-
1175
- $notice = $this->get_notice( $notice_slug );
1176
-
1177
- if ( isset( $notice['action_options'][ $action_index ] ) ) {
1178
- $action_options = array_merge( $action_options, $notice['action_options'][ $action_index ] );
1179
- }
1180
-
1181
- if ( $action_options['time'] ) {
1182
- $this->set_notice_delay( $notice_slug, $action_options['time'] );
1183
- }
1184
- if ( $action_options['dismiss'] ) {
1185
- $this->set_notice_dismiss( $notice_slug, $action_options['dismiss'] );
1186
- }
1187
-
1188
- $this->obj_update_options();
1189
- wp_send_json_success( __( 'Notice updated successfully.', 'all-in-one-seo-pack' ) );
1190
- }
1191
-
1192
- /**
1193
- * AJAX Remote Notice
1194
- *
1195
- * @since 3.6.0
1196
- */
1197
- public function ajax_remote_notice_action() {
1198
- check_ajax_referer( 'aioseop_ajax_notice' );
1199
- if ( ! current_user_can( 'aiosp_manage_seo' ) ) {
1200
- wp_send_json_error( __( "User doesn't have `aiosp_manage_seo` capabilities.", 'all-in-one-seo-pack' ) );
1201
- }
1202
-
1203
- $remote_notice_id = null;
1204
- if ( isset( $_POST['remote_notice_id'] ) ) {
1205
- $remote_notice_id = filter_input( INPUT_POST, 'remote_notice_id', FILTER_SANITIZE_STRING );
1206
-
1207
- // When PHPUnit is unable to use filter_input.
1208
- if ( defined( 'AIOSEOP_UNIT_TESTING' ) && null === $remote_notice_id && ! empty( $_POST['remote_notice_id'] ) ) {
1209
- $remote_notice_id = $_POST['remote_notice_id'];
1210
- }
1211
- }
1212
- if ( empty( $remote_notice_id ) ) {
1213
- /* Translators: Displays the hardcoded ID that is missing. */
1214
- wp_send_json_error( sprintf( __( 'Missing values from `%s`.', 'all-in-one-seo-pack' ), 'remote_notice_id' ) );
1215
- }
1216
-
1217
- $this->dismissed_notices[ 'remote_' . $remote_notice_id ] = time();
1218
-
1219
- $this->obj_update_options();
1220
- wp_send_json_success( __( 'Notice updated successfully.', 'all-in-one-seo-pack' ) );
1221
- }
1222
-
1223
- }
1224
-
1225
- // CLASS INITIALIZATION.
1226
- // Should this be a singleton class instead of a global?
1227
- global $aioseop_notices;
1228
- $aioseop_notices = new AIOSEOP_Notices();
1229
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/class-aioseop-usage.php DELETED
@@ -1,215 +0,0 @@
1
- <?php
2
- /**
3
- * AIOSEOP Usage Tracking Class
4
- *
5
- * @package All-in-One-SEO-Pack
6
- * @since 3.7
7
- */
8
-
9
- /**
10
- * All in One SEO Usage Tracking
11
- *
12
- * @since 3.7
13
- */
14
- class AIOSEOP_Usage {
15
- /**
16
- * Class Constructor.
17
- *
18
- * @since 3.7
19
- */
20
- public function __construct() {
21
- add_action( 'init', array( $this, 'schedule_send' ) );
22
- add_filter( 'cron_schedules', array( $this, 'add_schedules' ) );
23
- add_action( 'aioseop_usage_tracking_cron', array( $this, 'send_checkin' ) );
24
- }
25
-
26
- /**
27
- * Get the data.
28
- *
29
- * @since 3.7
30
- *
31
- * @return array An array of data.
32
- */
33
- private function get_data() {
34
- global $wpdb, $aioseop_options;
35
- $themeData = wp_get_theme();
36
-
37
- return array(
38
- // Generic data (environment).
39
- 'url' => home_url(),
40
- 'php_version' => PHP_VERSION,
41
- 'wp_version' => get_bloginfo( 'version' ),
42
- 'mysql_version' => $wpdb->db_version(),
43
- 'server_version' => isset( $_SERVER['SERVER_SOFTWARE'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) ) : '',
44
- 'is_ssl' => is_ssl(),
45
- 'is_multisite' => is_multisite(),
46
- 'sites_count' => function_exists( 'get_blog_count' ) ? (int) get_blog_count() : 1,
47
- 'active_plugins' => $this->get_active_plugins(),
48
- 'theme_name' => $themeData->name,
49
- 'theme_version' => $themeData->version,
50
- 'user_count' => function_exists( 'get_user_count' ) ? get_user_count() : null,
51
- 'locale' => get_locale(),
52
- 'timezone_offset' => date('P'),
53
- 'email' => get_bloginfo( 'admin_email' ),
54
- // AIOSEO specific data.
55
- 'aioseo_version' => AIOSEOP_VERSION,
56
- 'aioseo_license_key' => AIOSEOPPRO && isset( $aioseop_options['aiosp_license_key'] ) ? $aioseop_options['aiosp_license_key'] : null,
57
- 'aioseo_license_type' => AIOSEOPPRO && isset( $aioseop_options['plan'] ) ? $aioseop_options['plan'] : null,
58
- 'aioseo_is_pro' => AIOSEOPPRO,
59
- 'aioseo_settings' => $aioseop_options,
60
- 'aioseo_usagetracking' => get_option( 'aioseop_usage_tracking_config', false ),
61
- );
62
- }
63
-
64
- /**
65
- * Get the active plugins.
66
- *
67
- * @since 3.7
68
- *
69
- * @return array An array of active plugins.
70
- */
71
- private function get_active_plugins() {
72
- if ( ! function_exists( 'get_plugins' ) ) {
73
- include ABSPATH . '/wp-admin/includes/plugin.php';
74
- }
75
- $active = get_option( 'active_plugins', array() );
76
- $plugins = array_intersect_key( get_plugins(), array_flip( $active ) );
77
-
78
- return array_map( array( $this, 'getPluginVersion' ), $plugins );
79
- }
80
-
81
- /**
82
- * Get the plugin version.
83
- *
84
- * @since 3.7
85
- *
86
- * @return string The plugin version.
87
- */
88
- private function getPluginVersion( $plugin ) {
89
- if ( isset( $plugin['Version'] ) ) {
90
- return $plugin['Version'];
91
- }
92
-
93
- return 'Not Set';
94
- }
95
-
96
- /**
97
- * Send the checkin.
98
- *
99
- * @since 3.7
100
- *
101
- * @return boolean Whether or not it worked.
102
- */
103
- public function send_checkin( $override = false, $ignore_last_checkin = false ) {
104
- $home_url = trailingslashit( home_url() );
105
- if ( strpos( $home_url, 'aioseo.com' ) !== false ) {
106
- return false;
107
- }
108
-
109
- if ( ! $this->tracking_allowed() && ! $override ) {
110
- return false;
111
- }
112
-
113
- // Send a maximum of once per week
114
- $last_send = get_option( 'aioseop_usage_tracking_last_checkin' );
115
- if ( is_numeric( $last_send ) && $last_send > strtotime( '-1 week' ) && ! $ignore_last_checkin ) {
116
- return false;
117
- }
118
-
119
- $request = wp_remote_post( $this->get_url(), array(
120
- 'timeout' => 5,
121
- 'redirection' => 5,
122
- 'httpversion' => '1.1',
123
- 'blocking' => false,
124
- 'headers' => array( 'Content-Type' => 'application/json; charset=utf-8' ),
125
- 'body' => wp_json_encode( $this->get_data() ),
126
- 'user-agent' => 'AIOSEO/' . AIOSEOP_VERSION . '; ' . get_bloginfo( 'url' ),
127
- ) );
128
-
129
- // If we have completed successfully, recheck in 1 week
130
- if ( ! $ignore_last_checkin ) {
131
- update_option( 'aioseop_usage_tracking_last_checkin', time() );
132
- }
133
- return true;
134
- }
135
-
136
- /**
137
- * Checks if we are allowed to track.
138
- *
139
- * @since 3.7
140
- *
141
- * @return boolean Whether or not we are allowed to track.
142
- */
143
- private function tracking_allowed() {
144
- global $aioseop_options;
145
- return ! empty( $aioseop_options['aiosp_usage_tracking'] ) || AIOSEOPPRO;
146
- }
147
-
148
- /**
149
- * Get the url.
150
- *
151
- * @since 3.7
152
- *
153
- * @return string The url.
154
- */
155
- private function get_url() {
156
- $url = 'https://aiousage.com/v1/track';
157
- if ( defined( 'AIOSEO_USAGE_TRACKING_URL' ) ) {
158
- $url = AIOSEO_USAGE_TRACKING_URL;
159
- }
160
-
161
- return $url;
162
- }
163
-
164
- /**
165
- * Schedules sending.
166
- *
167
- * @since 3.7
168
- *
169
- * @return void
170
- */
171
- public function schedule_send() {
172
- $scheduled = wp_next_scheduled( 'aioseop_usage_tracking_cron' );
173
- if ( ! $this->tracking_allowed() ) {
174
- if ( $scheduled ) {
175
- wp_unschedule_event( $scheduled, 'aioseop_usage_tracking_cron' );
176
- }
177
- return;
178
- }
179
-
180
- if ( ! $scheduled ) {
181
- $tracking = array();
182
- $tracking['day'] = rand( 0, 6 );
183
- $tracking['hour'] = rand( 0, 23 );
184
- $tracking['minute'] = rand( 0, 59 );
185
- $tracking['second'] = rand( 0, 59 );
186
- $tracking['offset'] = ( $tracking['day'] * DAY_IN_SECONDS ) +
187
- ( $tracking['hour'] * HOUR_IN_SECONDS ) +
188
- ( $tracking['minute'] * MINUTE_IN_SECONDS ) +
189
- $tracking['second'];
190
- $tracking['initsend'] = strtotime("next sunday") + $tracking['offset'];
191
-
192
- wp_schedule_event( $tracking['initsend'], 'weekly', 'aioseop_usage_tracking_cron' );
193
- update_option( 'aioseop_usage_tracking_config', $tracking );
194
-
195
- // Send immediately.
196
- $this->send_checkin( true, true );
197
- }
198
- }
199
-
200
- /**
201
- * Create a weekly schedule.
202
- *
203
- * @since 3.7
204
- *
205
- * @return array an Array of schedules.
206
- */
207
- public function add_schedules( $schedules = array() ) {
208
- // Adds once weekly to the existing schedules.
209
- $schedules['weekly'] = array(
210
- 'interval' => 604800,
211
- 'display' => __( 'Once Weekly', 'all-in-one-seo-pack' ),
212
- );
213
- return $schedules;
214
- }
215
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/display/aioseop-welcome.php DELETED
@@ -1,146 +0,0 @@
1
- <?php
2
-
3
- if ( ! class_exists( 'AIOSEOP_Welcome' ) ) {
4
-
5
- /**
6
- * Handles the Welcome page.
7
- *
8
- * @since 3.6.0
9
- */
10
- class AIOSEOP_Welcome {
11
-
12
- /**
13
- * Registers our hooks.
14
- *
15
- * @since 3.6.0
16
- *
17
- * @return void
18
- */
19
- public static function hooks() {
20
- if ( AIOSEOPPRO ) {
21
- return;
22
- }
23
- add_action( 'admin_menu', array( 'AIOSEOP_Welcome', 'registerPage' ) );
24
- add_action( 'admin_enqueue_scripts', array( 'AIOSEOP_Welcome', 'loadAssets' ) );
25
- }
26
-
27
- /**
28
- * Registers our the Welcome page and removes it from our menu.
29
- *
30
- * @since 3.6.0
31
- *
32
- * @return void
33
- */
34
- public static function registerPage() {
35
- /* translators: %s is a placeholder, which means that it should not be translated. It will be replaced with the name of the plugin, All in One SEO Pack. */
36
- $welcome_text = sprintf( __( 'Welcome to %s', 'all-in-one-seo-pack' ), AIOSEOP_PLUGIN_NAME );
37
- add_dashboard_page(
38
- $welcome_text,
39
- $welcome_text,
40
- 'manage_options',
41
- 'aioseop-welcome',
42
- array( 'AIOSEOP_Welcome', 'output' )
43
- );
44
- remove_submenu_page( 'index.php', 'aioseop-welcome' );
45
- }
46
-
47
- /**
48
- * Loads the required assets for the welcome page.
49
- *
50
- * @since 3.6.0
51
- *
52
- * @param string $page The page ID.
53
- * @return void
54
- */
55
- public static function loadAssets( $page ) {
56
- if ( 'dashboard_page_aioseop-welcome' === $page ) {
57
- wp_enqueue_style( 'aioseop-welcome', AIOSEOP_PLUGIN_URL . 'css/aioseop-welcome.css', array(), AIOSEOP_VERSION );
58
- if ( function_exists( 'is_rtl' ) && is_rtl() ) {
59
- wp_enqueue_style( 'aioseop-welcome-rtl', AIOSEOP_PLUGIN_URL . 'css/aioseop-welcome-rtl.css', array( 'aioseop-welcome' ), AIOSEOP_VERSION );
60
- }
61
- }
62
- }
63
-
64
- /**
65
- * Shows the Welcome page if all conditions are met.
66
- *
67
- * @since 3.6.0
68
- *
69
- * @return void
70
- */
71
- public function showPage() {
72
- if (
73
- AIOSEOPPRO ||
74
- ! is_admin() ||
75
- is_network_admin() ||
76
- isset( $_GET['activate-multi'] ) ||
77
- ! current_user_can( 'manage_options' )
78
- ) {
79
- return;
80
- }
81
-
82
- delete_transient( '_aioseop_activation_redirect' );
83
-
84
- // Compare major versions so we don't show the welcome screen for minor versions.
85
- $lastSeenVersion = get_user_meta( get_current_user_id(), 'aioseop_seen_about_page', true );
86
- if (
87
- $lastSeenVersion &&
88
- ( get_major_version( AIOSEOP_VERSION ) === get_major_version( $lastSeenVersion ) )
89
- ) {
90
- return;
91
- }
92
-
93
- wp_safe_redirect(
94
- add_query_arg(
95
- array( 'page' => 'aioseop-welcome' ), admin_url( 'index.php' )
96
- )
97
- );
98
- exit;
99
- }
100
-
101
- /**
102
- * Outputs the Welcome page.
103
- *
104
- * @since 3.6.0
105
- *
106
- * @return void
107
- */
108
- public static function output() {
109
- // Update user meta once page has been shown.
110
- update_user_meta( get_current_user_id(), 'aioseop_seen_about_page', AIOSEOP_VERSION );
111
- ?>
112
-
113
- <div class="wrap about-wrap">
114
- <div class="aioseop-welcome-logo">
115
- <?php echo aioseop_get_logo( 180, 180, '#44619A' ); ?>
116
- </div>
117
- <h1>
118
- <?php
119
- /* translators: %1$s and %2$s are placeholders, which means that these should not be translated. These will be replaced with the name of the plugin, All in One SEO Pack, and the current version number. */
120
- printf( esc_html__( 'Welcome to %1$s %2$s', 'all-in-one-seo-pack' ), AIOSEOP_PLUGIN_NAME, AIOSEOP_VERSION );
121
- ?>
122
- </h1>
123
- <div class="about-text">
124
- <?php
125
- /* translators: %1$s and %2$s are placeholders, which means that these should not be translated. These will be replaced with the name of the plugin, All in One SEO Pack, and the current version number. */
126
- printf( esc_html__( '%1$s %2$s contains new features, bug fixes, increased security, and tons of under the hood performance improvements.', 'all-in-one-seo-pack' ), AIOSEOP_PLUGIN_NAME, AIOSEOP_VERSION );
127
- ?>
128
- </div>
129
-
130
- <h2 class="nav-tab-wrapper">
131
- <a
132
- class="nav-tab nav-tab-active" id="aioseop-welcome"
133
- href="<?php echo esc_url( admin_url( add_query_arg( array( 'page' => 'aioseop-welcome' ), 'index.php' ) ) ); ?>">
134
- <?php esc_html_e( 'What&#8217;s New', 'all-in-one-seo-pack' ); ?>
135
- </a>
136
- </h2>
137
-
138
- <div id='sections'>
139
- <section><?php include_once( AIOSEOP_PLUGIN_DIR . 'admin/display/welcome-content.php' ); ?></section>
140
- </div>
141
-
142
- </div>
143
- <?php
144
- }
145
- }
146
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/display/dashboard_widget.php DELETED
@@ -1,149 +0,0 @@
1
- <?php
2
- /**
3
- * Dashboard Widget
4
- *
5
- * @package All_in_One_SEO_Pack
6
- * @since 2.3.10
7
- */
8
-
9
- if ( ! class_exists( 'aioseop_dashboard_widget' ) ) {
10
-
11
- /**
12
- * Class aioseop_dashboard_widget
13
- *
14
- * @since 2.3.10
15
- */
16
- // @codingStandardsIgnoreStart
17
- class aioseop_dashboard_widget {
18
- // @codingStandardsIgnoreEnd
19
-
20
- /**
21
- * Constructor
22
- *
23
- * Add the action to the constructor.
24
- *
25
- * @since 2.3.10
26
- */
27
- function __construct() {
28
- add_action( 'wp_dashboard_setup', array( $this, 'add_dashboard_widget' ) );
29
- }
30
-
31
- /**
32
- * Add Dashboard Widget
33
- *
34
- * @since 2.3.10
35
- */
36
- function add_dashboard_widget() {
37
- if ( current_user_can( 'install_plugins' ) && false !== $this->show_widget() ) {
38
- wp_add_dashboard_widget(
39
- 'semperplugins-rss-feed',
40
- __( 'SEO News', 'all-in-one-seo-pack' ),
41
- array(
42
- $this,
43
- 'display_rss_dashboard_widget',
44
- )
45
- );
46
- }
47
-
48
- }
49
-
50
- /**
51
- * Show Widget
52
- *
53
- * @since 2.3.10.2
54
- */
55
- function show_widget() {
56
-
57
- $show = true;
58
-
59
- if ( apply_filters( 'aioseo_show_seo_news', true ) === false ) {
60
- // API filter hook to disable showing SEO News dashboard widget.
61
- return false;
62
- }
63
-
64
- global $aioseop_options;
65
-
66
- if ( AIOSEOPPRO && isset( $aioseop_options['aiosp_showseonews'] ) && ! $aioseop_options['aiosp_showseonews'] ) {
67
- return false;
68
- }
69
-
70
- return $show;
71
- }
72
-
73
- /**
74
- * Display RSS Dashboard Widget
75
- *
76
- * @since 2.3.10
77
- */
78
- function display_rss_dashboard_widget() {
79
- // check if the user has chosen not to display this widget through screen options.
80
- $current_screen = get_current_screen();
81
- $hidden_widgets = get_user_meta( get_current_user_id(), 'metaboxhidden_' . $current_screen->id );
82
- if ( $hidden_widgets && count( $hidden_widgets ) > 0 && is_array( $hidden_widgets[0] ) && in_array( 'semperplugins-rss-feed', $hidden_widgets[0], true ) ) {
83
- return;
84
- }
85
-
86
- include_once( ABSPATH . WPINC . '/feed.php' );
87
-
88
- $rss_items = get_transient( 'aioseop_feed' );
89
- if ( false === $rss_items ) {
90
-
91
- $rss = fetch_feed( 'https://www.semperplugins.com/feed/' );
92
- if ( is_wp_error( $rss ) ) {
93
- echo __( '{Temporarily unable to load feed.}', 'all-in-one-seo-pack' );
94
-
95
- return;
96
- }
97
- $rss_items = $rss->get_items( 0, 4 ); // Show four items.
98
-
99
- $cached = array();
100
- foreach ( $rss_items as $item ) {
101
- $cached[] = array(
102
- 'url' => $item->get_permalink(),
103
- 'title' => $item->get_title(),
104
- 'date' => $item->get_date( 'M jS Y' ),
105
- 'content' => substr( strip_tags( $item->get_content() ), 0, 128 ) . '...',
106
- );
107
- }
108
- $rss_items = $cached;
109
-
110
- set_transient( 'aioseop_feed', $cached, 12 * HOUR_IN_SECONDS );
111
-
112
- }
113
-
114
- ?>
115
-
116
- <ul>
117
- <?php
118
- if ( false === $rss_items ) {
119
- echo '<li>No items</li>';
120
-
121
- return;
122
- }
123
-
124
- foreach ( $rss_items as $item ) {
125
- ?>
126
- <li>
127
- <a target="_blank" href="<?php echo esc_url( $item['url'] ); ?>">
128
- <?php echo esc_html( $item['title'] ); ?>
129
- </a>
130
- <span class="aioseop-rss-date"><?php echo $item['date']; ?></span>
131
- <div class="aioseop_news">
132
- <?php echo strip_tags( $item['content'] ) . '...'; ?>
133
- </div>
134
- </li>
135
- <?php
136
- }
137
-
138
- ?>
139
- </ul>
140
-
141
- <?php
142
-
143
- }
144
- }
145
-
146
- new aioseop_dashboard_widget();
147
- }
148
-
149
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/display/general-metaboxes.php DELETED
@@ -1,219 +0,0 @@
1
- <?php
2
- /**
3
- * General Metaboxes
4
- *
5
- * @package All_in_One_SEO_Pack
6
- * @since 2.3.3
7
- */
8
-
9
- // @codingStandardsIgnoreStart
10
- class aiosp_metaboxes {
11
- // @codingStandardsIgnoreEnd
12
-
13
- /**
14
- * Constructor
15
- *
16
- * AIOSEOP metaboxes constructor.
17
- *
18
- * @since 2.3.3
19
- */
20
- function __construct() {
21
- // construct.
22
- }
23
-
24
- /**
25
- * Display Metaboxes
26
- *
27
- * @since 2.3.3
28
- *
29
- * @param $add
30
- * @param $meta
31
- */
32
- static function display_extra_metaboxes( $add, $meta ) {
33
- echo "<div class='aioseop_metabox_wrapper' >";
34
- switch ( $meta['id'] ) :
35
- case 'aioseop-about':
36
- ?>
37
- <div class="aioseop_metabox_text">
38
- <?php
39
- global $current_user;
40
- $user_id = $current_user->ID;
41
- $ignore = get_user_meta( $user_id, 'aioseop_ignore_notice' );
42
- if ( ! empty( $ignore ) ) {
43
- $qa = array();
44
- wp_parse_str( $_SERVER['QUERY_STRING'], $qa );
45
- $qa['aioseop_reset_notices'] = 1;
46
- $url = '?' . build_query( $qa );
47
- echo '<p><a href="' . $url . '">' . __( 'Reset Dismissed Notices', 'all-in-one-seo-pack' ) . '</a></p>';
48
- }
49
- ?>
50
- <?php if ( ! AIOSEOPPRO ) : ?>
51
- <p>
52
- <strong>
53
- <?php
54
- _e( 'Upgrade to our premium version and unlock:', 'all-in-one-seo-pack' );
55
- ?>
56
- </strong>
57
- </p>
58
- <?php endif; ?>
59
- </div>
60
- <?php
61
- // Fall-through.
62
- case 'aioseop-donate':
63
- ?>
64
- <div>
65
- <?php if ( ! AIOSEOPPRO ) : ?>
66
- <div class="aioseop_metabox_text">
67
- <?php self::pro_meta_content(); ?>
68
- </div>
69
- <?php
70
- endif;
71
- $aiosp_trans = new AIOSEOP_Translations();
72
- // Eventually if nothing is returned we should just remove this section.
73
- if ( get_locale() != 'en_US' ) :
74
- ?>
75
- <div class="aioseop_translations">
76
- <strong>
77
- <?php
78
- if ( $aiosp_trans->percent_translated < 100 ) {
79
- if ( ! empty( $aiosp_trans->native_name ) ) {
80
- $maybe_native_name = $aiosp_trans->native_name;
81
- } else {
82
- $maybe_native_name = $aiosp_trans->name;
83
- }
84
-
85
- /* translators: %1$s, %2$s, etc. are placeholders and shouldn't be translated. %1$s expands to the number of languages All in One SEO Pack has been translated into, %2$s to the name of the plugin, $3%s to the percentage translated of the current language, $4%s to the language name, %5$s and %6$s to anchor tags with link to the translation page at translate.wordpress.org */
86
- printf(
87
- __( '%1$s has been translated into %2$s languages, but currently the %3$s translation is only %4$s percent complete. %5$sClick here%6$s to help get it to 100 percent.', 'all-in-one-seo-pack' ),
88
- AIOSEOP_PLUGIN_NAME,
89
- $aiosp_trans->translated_count,
90
- $maybe_native_name,
91
- $aiosp_trans->percent_translated,
92
- "<a href=\"$aiosp_trans->translation_url\" target=\"_BLANK\">",
93
- '</a>'
94
- );
95
- }
96
-
97
- ?>
98
- </strong>
99
- </div>
100
- <?php endif; ?>
101
- </div>
102
- <?php break; ?>
103
- <?php
104
- case 'aioseop-list':
105
- ?>
106
- <div class="aioseop_metabox_text">
107
- <form
108
- <?php if ( AIOSEOPPRO ) : ?>
109
- action="https://semperplugins.us1.list-manage.com/subscribe/post?u=794674d3d54fdd912f961ef14&amp;id=b786958a9a"
110
- <?php else : ?>
111
- action="https://semperplugins.us1.list-manage.com/subscribe/post?u=794674d3d54fdd912f961ef14&amp;id=af0a96d3d9"
112
- <?php endif; ?>
113
- method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate"
114
- target="_blank">
115
- <h2><?php _e( 'Join our mailing list for tips, tricks, and WordPress secrets.', 'all-in-one-seo-pack' ); ?></h2>
116
- <p>
117
- <i><?php _e( 'Sign up today and receive a free copy of the e-book 5 SEO Tips for WordPress ($39 value).', 'all-in-one-seo-pack' ); ?></i>
118
- </p>
119
- <p>
120
- <input
121
- type="text" value="" name="EMAIL" class="required email" id="mce-EMAIL"
122
- placeholder="<?php _e( 'Email Address', 'all-in-one-seo-pack' ); ?>" aria-label="<?php _e( 'Enter your email address', 'all-in-one-seo-pack' ); ?>">
123
- <input
124
- type="submit" value="<?php _e( 'Subscribe', 'all-in-one-seo-pack' ); ?>" name="subscribe" id="mc-embedded-subscribe"
125
- class="button-primary" aria-label="<?php _e( 'Subscribe to our mailing list', 'all-in-one-seo-pack' ); ?>">
126
- </p>
127
- </form>
128
- </div>
129
- <?php break; ?>
130
- <?php
131
- case 'aioseop-support':
132
- ?>
133
- <div class="aioseop_metabox_text">
134
- <ul>
135
- <li>
136
- <div class="aioseop_icon aioseop-icon-file"></div>
137
- <a target="_blank" rel="noopener noreferrer"
138
- href="<?php echo aioseop_add_url_utm( 'https://semperplugins.com/documentation/' , array( 'utm_campaign' => 'support-box', 'utm_content' => 'documentation' ) ); ?>">
139
- <?php
140
- /* translators: %s is a placeholder, which means that it should not be translated. It will be replaced with the name of the plugin, All in One SEO Pack. */
141
- printf( __( 'Read the %s user guide', 'all-in-one-seo-pack' ), AIOSEOP_PLUGIN_NAME );
142
- ?>
143
- </a>
144
- </li>
145
- <li>
146
- <div class="aioseop_icon aioseop-icon-support"></div>
147
- <a target="_blank" rel="noopener noreferrer"
148
- title="<?php _e( 'All in One SEO Pro Plugin Support Forum', 'all-in-one-seo-pack' ); ?>"
149
- href="<?php echo aioseop_add_url_utm( 'https://semperplugins.com/support/', array( 'utm_campaign' => 'support-box', 'utm_content' => 'support' ) ); ?>"><?php _e( 'Access our Premium Support', 'all-in-one-seo-pack' ); ?></a>
150
- </li>
151
- <li>
152
- <div class="aioseop_icon aioseop-icon-cog"></div>
153
- <a target="_blank" rel="noopener noreferrer" title="<?php _e( 'All in One SEO Pro Plugin Changelog', 'all-in-one-seo-pack' ); ?>"
154
- href="<?php echo aioseop_add_url_utm( 'https://semperplugins.com/all-in-one-seo-pack-changelog/', array( 'utm_campaign' => 'support-box', 'utm_content' => 'changelog', ) ); ?>"><?php _e( 'View the Changelog', 'all-in-one-seo-pack' ); ?></a>
155
- </li>
156
- <li>
157
- <div class="aioseop_icon aioseop-icon-youtube"></div>
158
- <a target="_blank" rel="noopener noreferrer"
159
- href="<?php echo aioseop_add_url_utm( 'https://semperplugins.com/doc-type/video/', array( 'utm_campaign' => 'support-box', 'utm_content' => 'video') ); ?>"><?php _e( 'Watch video tutorials', 'all-in-one-seo-pack' ); ?></a>
160
- </li>
161
- <li>
162
- <div class="aioseop_icon aioseop-icon-book"></div>
163
- <a target="_blank" rel="noopener noreferrer"
164
- href="<?php echo aioseop_add_url_utm( 'https://semperplugins.com/documentation/quick-start-guide/', array( 'utm_campaign' => 'support-box', 'utm_content' => 'quick-start' ) ); ?>"><?php _e( 'Getting started? Read the Beginners Guide', 'all-in-one-seo-pack' ); ?></a>
165
- </li>
166
- </ul>
167
- </div>
168
- <?php break;
169
- default:
170
- break;
171
- ?>
172
- <?php endswitch; ?>
173
- </div>
174
- <?php
175
- }
176
-
177
- /**
178
- * Pro Meta Content
179
- *
180
- * @since 2.3.11
181
- */
182
- static function pro_meta_content() {
183
-
184
- echo '<ul>';
185
-
186
- echo '<li>' . __( 'SEO for Categories, Tags and Custom Taxonomies', 'all-in-one-seo-pack' ) . '</li>';
187
- echo '<li>' . __( 'Social Meta for Categories, Tags and Custom Taxonomies', 'all-in-one-seo-pack' ) . '</li>';
188
-
189
- if ( class_exists( 'WooCommerce' ) ) {
190
- echo '<li>' . __( 'Advanced support for WooCommerce', 'all-in-one-seo-pack' ) . '</li>';
191
- } else {
192
- echo '<li>' . __( 'Advanced support for eCommerce', 'all-in-one-seo-pack' ) . '</li>';
193
- }
194
-
195
- echo '<li>' . __( 'Video SEO Module', 'all-in-one-seo-pack' ) . '</li>';
196
- echo '<li>' . __( 'Image SEO Module', 'all-in-one-seo-pack' ) . '</li>';
197
- echo '<li>' . __( 'Advanced Google Analytics tracking', 'all-in-one-seo-pack' ) . '</li>';
198
- echo '<li>' . __( 'Support for Google Tag Manager', 'all-in-one-seo-pack' ) . '</li>';
199
- // echo '<li>' . __( 'Support for Local Business Schema', 'all-in-one-seo-pack' ) . '</li>'.
200
- echo '<li>' . __( 'Greater control over display settings', 'all-in-one-seo-pack' ) . '</li>';
201
- echo '<li>' . __( 'Access to Video Screencasts', 'all-in-one-seo-pack' ) . '</li>';
202
- echo '<li>' . __( 'Access to Premium Support', 'all-in-one-seo-pack' ) . '</li>';
203
- echo '<li>' . __( 'Access to Knowledge Center', 'all-in-one-seo-pack' ) . '</li>';
204
-
205
- echo '</ul>';
206
-
207
- /* translators: %s: "All in One SEO Pack Pro" */
208
- $text = sprintf( esc_html__( 'Get %s Now', 'all-in-one-seo-pack' ), AIOSEOP_PLUGIN_NAME . '&nbsp;Pro' );
209
-
210
- $link = sprintf(
211
- '<a href="%s" class="button button-primary button-hero button-pro-cta" target="_blank">%s</a>',
212
- aioseop_get_utm_url( 'sidebar-cta-button' ),
213
- $text
214
- );
215
-
216
- echo $link;
217
- }
218
-
219
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/display/index.php DELETED
@@ -1,4 +0,0 @@
1
- <?php
2
- /**
3
- * Silence is golden.
4
- */
 
 
 
 
admin/display/menu.php DELETED
@@ -1,91 +0,0 @@
1
- <?php
2
- /**
3
- * Menu
4
- *
5
- * @package All_in_One_SEO_Pack
6
- * @since ?
7
- */
8
-
9
- /**
10
- * Class AIOSEOPAdminMenus
11
- *
12
- * @since 2.3.11.5
13
- */
14
- class AIOSEOPAdminMenus {
15
-
16
- /**
17
- * Constructor to add the actions.
18
- */
19
- function __construct() {
20
-
21
- add_action( 'network_admin_menu', array( $this, 'remove_menus' ), 15 );
22
-
23
- if ( is_multisite() ) {
24
- return;
25
- }
26
-
27
- if ( ( current_user_can( 'manage_options' ) || current_user_can( 'aiosp_manage_seo' ) ) ) {
28
- add_action( 'admin_menu', array( $this, 'add_submenu_pages' ), 11 );
29
- add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
30
- }
31
- }
32
-
33
- function remove_menus() {
34
- remove_menu_page( AIOSEOP_PLUGIN_DIRNAME . '/aioseop_class.php' ); // Remove AIOSEOP menu from the network admin.
35
- }
36
-
37
- /**
38
- * Adds the submenu pages for Lite users.
39
- *
40
- * @since 2.3.11.5
41
- * @since 3.4.0 Added About page to admin menu.
42
- */
43
- function add_submenu_pages() {
44
- global $submenu;
45
-
46
- $plugin_base_dir = explode( '/', plugin_basename( __FILE__ ) );
47
- $menu_slug = $plugin_base_dir[0] . '/aioseop_class.php';
48
-
49
- add_submenu_page(
50
- $menu_slug,
51
- sprintf( __( 'About %s', 'all-in-one-seo-pack' ), AIOSEOP_PLUGIN_NAME ),
52
- __( 'About Us', 'all-in-one-seo-pack' ),
53
- apply_filters( 'manage_aiosp', 'aiosp_manage_seo' ),
54
- 'aioseop-about',
55
- array( 'AIOSEOP_About', 'init' ),
56
- null
57
- );
58
-
59
- if ( AIOSEOPPRO ) {
60
- return;
61
- }
62
-
63
- $url = aioseop_get_utm_url( 'admin-menu' );
64
- $upgrade_text = __( 'Upgrade to Pro', 'all-in-one-seo-pack' );
65
- $submenu[ AIOSEOP_PLUGIN_DIRNAME . '/aioseop_class.php' ][] = array(
66
- "<span class='upgrade_menu_link'>$upgrade_text</span>",
67
- 'manage_options',
68
- $url,
69
- );
70
- }
71
-
72
- /*
73
- * Opens Upgrade to Pro links in WP Admin as new tab.
74
- *
75
- * Enqueued here because All_in_One_SEO_Pack_Module::admin_enqueue_scripts does not work.
76
- *
77
- * @param string $hook
78
- *
79
- * @since 3.0
80
- */
81
- function admin_enqueue_scripts( $hook ) {
82
- wp_enqueue_script( 'aioseop_menu_js', AIOSEOP_PLUGIN_URL . 'js/aioseop-menu.js', array( 'jquery' ), AIOSEOP_VERSION, true );
83
-
84
- if ( 'plugins.php' === $hook ) {
85
- wp_enqueue_script( 'aioseop_plugins_menu_js', AIOSEOP_PLUGIN_URL . 'js/plugins-menu.js', array( 'jquery' ), AIOSEOP_VERSION, true );
86
- }
87
- }
88
- }
89
-
90
- new AIOSEOPAdminMenus();
91
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/display/notice-aioseop.php DELETED
@@ -1,80 +0,0 @@
1
- <?php
2
- /**
3
- * AIOSEOP Screen Notice Template.
4
- *
5
- * @since 3.0
6
- *
7
- * @see AIOSEOP_Notice::display_notice_aioseop();
8
- * @uses $notice in AIOSEOP_Notice::notices
9
- * @package All_in_One_SEO_Pack
10
- * @subpackage AIOSEOP_Notices
11
- */
12
-
13
- // $notice = $this->get_notice[ $a_notice_slug ];
14
- $notice_class = 'notice-info';
15
- if ( isset( $notice['class'] ) && ! empty( $notice['class'] ) ) {
16
- $notice_class = $notice['class'];
17
- }
18
-
19
- add_filter( 'safe_style_css', 'aioseop_filter_styles' );
20
-
21
- $dismissible = ! isset( $notice['dismissible'] ) || $notice['dismissible'] ? ' is-dismissible' : '';
22
-
23
- ?>
24
- <div class="notice <?php echo esc_attr( $notice_class ); ?><?php echo $dismissible; ?> aioseop-notice-container aioseop-notice-<?php echo esc_attr( $notice['slug'] ); ?>">
25
- <?php if ( ! empty( $notice['html'] ) ) : ?>
26
- <?php
27
- echo wp_kses(
28
- $notice['html'],
29
- array(
30
- 'br' => array(),
31
- 'div' => array(
32
- 'class' => true,
33
- 'style' => true,
34
- ),
35
- 'p' => array(),
36
- 'strong' => array(),
37
- 'a' => array(
38
- 'href' => true,
39
- 'class' => true,
40
- 'data-*' => true,
41
- 'target' => true,
42
- 'rel' => true,
43
- ),
44
- 'style' => array(),
45
- 'script' => array(
46
- 'type' => true,
47
- ),
48
- 'ul' => array(
49
- 'class' => true,
50
- ),
51
- 'li' => array(),
52
- )
53
- );
54
- ?>
55
- <?php else : ?>
56
- <p><?php echo esc_html( $notice['message'] ); ?></p>
57
- <?php endif; ?>
58
- <p class="aioseo-action-buttons">
59
- <?php foreach ( $notice['action_options'] as $key => $action_option ) : ?>
60
- <?php
61
- $link = $action_option['link'];
62
- $id = 'aioseop-notice-delay-' . $notice['slug'] . '-' . $key;
63
- $class = '';
64
- $class .= 'aioseop-delay-' . $key;
65
- $class .= ' ' . $action_option['class'];
66
- ?>
67
- <a
68
- href="<?php echo esc_url( $link ); ?>"
69
- id="<?php echo esc_attr( $id ); ?>"
70
- class="aioseop-notice-delay <?php echo esc_attr( $class ); ?>"
71
- <?php
72
- if ( $action_option['new_tab'] ) {
73
- echo 'target="_blank" rel="noopener"';}
74
- ?>
75
- >
76
- <?php echo esc_textarea( $action_option['text'] ); ?>
77
- </a>
78
- <?php endforeach; ?>
79
- </p>
80
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/display/notice-default.php DELETED
@@ -1,80 +0,0 @@
1
- <?php
2
- /**
3
- * Default Notice Template.
4
- *
5
- * @since 3.0
6
- *
7
- * @see AIOSEOP_Notice::display_notice_default();
8
- * @uses $notice in AIOSEOP_Notice::notices
9
- * @package All-in-One-SEO-Pack
10
- * @subpackage AIOSEOP_Notices
11
- */
12
-
13
- // $notice = $this->notices[ $a_notice_slug ];
14
- $notice_class = 'notice-info';
15
- if ( isset( $notice['class'] ) && ! empty( $notice['class'] ) ) {
16
- $notice_class = $notice['class'];
17
- }
18
-
19
- add_filter( 'safe_style_css', 'aioseop_filter_styles' );
20
-
21
- $dismissible = ! isset( $notice['dismissible'] ) || $notice['dismissible'] ? ' is-dismissible' : '';
22
-
23
- ?>
24
- <div class="notice <?php echo esc_attr( $notice_class ); ?><?php echo $dismissible; ?> aioseop-notice-container aioseop-notice-<?php echo esc_attr( $notice['slug'] ); ?>">
25
- <?php if ( ! empty( $notice['html'] ) ) : ?>
26
- <?php
27
- echo wp_kses(
28
- $notice['html'],
29
- array(
30
- 'br' => array(),
31
- 'div' => array(
32
- 'class' => true,
33
- 'style' => true,
34
- ),
35
- 'p' => array(),
36
- 'strong' => array(),
37
- 'a' => array(
38
- 'href' => true,
39
- 'class' => true,
40
- 'data-*' => true,
41
- 'target' => true,
42
- 'rel' => true,
43
- ),
44
- 'style' => array(),
45
- 'script' => array(
46
- 'type' => true,
47
- ),
48
- 'ul' => array(
49
- 'class' => true,
50
- ),
51
- 'li' => array(),
52
- )
53
- );
54
- ?>
55
- <?php else : ?>
56
- <p><?php echo esc_html( $notice['message'] ); ?></p>
57
- <?php endif; ?>
58
- <p class="aioseo-action-buttons">
59
- <?php foreach ( $notice['action_options'] as $key => $action_option ) : ?>
60
- <?php
61
- $link = $action_option['link'];
62
- $id = 'aioseop-notice-delay-' . $notice['slug'] . '-' . $key;
63
- $class = '';
64
- $class .= 'aioseop-delay-' . $key;
65
- $class .= ' ' . $action_option['class'];
66
- ?>
67
- <a
68
- href="<?php echo esc_url( $link ); ?>"
69
- id="<?php echo esc_attr( $id ); ?>"
70
- class="aioseop-notice-delay <?php echo esc_attr( $class ); ?>"
71
- <?php
72
- if ( isset( $action_option['new_tab'] ) && $action_option['new_tab'] ) {
73
- echo 'target="_blank" rel="noopener"';}
74
- ?>
75
- >
76
- <?php echo esc_textarea( $action_option['text'] ); ?>
77
- </a>
78
- <?php endforeach; ?>
79
- </p>
80
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/display/notice-remote.php DELETED
@@ -1,51 +0,0 @@
1
- <?php
2
- /**
3
- * Remote Notice Template.
4
- *
5
- * @since 3.6.0
6
- *
7
- * @see AIOSEOP_Notice::display_remote_notice();
8
- * @uses $remote_notice in AIOSEOP_Notice::remote_notices
9
- * @package All-in-One-SEO-Pack
10
- * @subpackage AIOSEOP_Notices
11
- */
12
-
13
- $notice_class = 'info';
14
- if ( isset( $remote_notice['notification_type'] ) && ! empty( $remote_notice['notification_type'] ) ) {
15
- $notice_class = $remote_notice['notification_type'];
16
- }
17
-
18
- add_filter( 'safe_style_css', 'aioseop_filter_styles' );
19
-
20
- $dismissible = ! isset( $remote_notice['dismissible'] ) || $remote_notice['dismissible'] ? ' is-dismissible' : '';
21
-
22
- ?>
23
- <div class="notice notice-<?php echo esc_attr( $notice_class ); ?><?php echo $dismissible; ?> aioseop-notice-container aioseop-remote-notice-<?php echo esc_attr( $remote_notice['id'] ); ?>">
24
- <h3><?php echo esc_html( $remote_notice['title'] ); ?></h3>
25
- <p><?php echo wp_kses_post( $remote_notice['content'] ); ?></p>
26
- <?php if ( isset( $remote_notice['btns'] ) ) : ?>
27
- <p class="aioseo-action-buttons">
28
- <?php foreach ( $remote_notice['btns'] as $btn_slug => $btn ) : ?>
29
- <?php
30
- $link = $btn['url'];
31
- $id = 'aioseop-remote-notice-btn-' . $remote_notice['id'] . '-' . $btn_slug;
32
- $btn_class = ( 'main' === $btn_slug ) ? 'button-primary' : 'button-secondary';
33
- $class = "aioseop-remote-notice-btn {$btn_class}";
34
-
35
- $homeUrl = home_url();
36
- preg_match( "#$homeUrl.*#", $link, $ownDomain );
37
- preg_match( "#.*?doNotDismiss=true.*#", $link, $noDismiss );
38
- ?>
39
- <a
40
- href="<?php echo esc_url( $link ); ?>"
41
- id="<?php echo esc_attr( $id ); ?>"
42
- class="aioseop-notice-delay <?php echo esc_attr( $class ); ?>"
43
- <?php if ( $noDismiss ) echo 'data-dismiss="false"'?>
44
- <?php if ( ! $ownDomain ) echo 'target="_blank"'; ?>
45
- >
46
- <?php echo esc_textarea( $btn['text'] ); ?>
47
- </a>
48
- <?php endforeach; ?>
49
- </p>
50
- <?php endif; ?>
51
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/display/notices/blog-visibility-notice.php DELETED
@@ -1,48 +0,0 @@
1
- <?php
2
- /**
3
- * Blog Visibility Notice
4
- *
5
- * @since 3.0
6
- * @package All-in-One-SEO-Pack
7
- * @subpackage AIOSEOP_Notices
8
- */
9
-
10
- /**
11
- * Notice - Blog Visibility
12
- *
13
- * Displays when blog disables search engines from indexing.
14
- *
15
- * @since 3.0
16
- *
17
- * @return array Notice configuration.
18
- */
19
- function aioseop_notice_blog_visibility() {
20
- $text_link = '<a href="' . admin_url( 'options-reading.php' ) . '">' . __( 'Reading Settings', 'all-in-one-seo-pack' ) . '</a>';
21
-
22
- return array(
23
- 'slug' => 'blog_public_disabled',
24
- 'delay_time' => 0,
25
- /* translators: %s is a placeholder, which means that it should not be translated. It will be replaced with the name of the plugin, All in One SEO Pack. "Settings > Reading" refers to the "Reading" submenu in WordPress Core. */
26
- 'message' => sprintf( __( 'Warning: %s has detected that you are blocking access to search engines. You can change this in Settings > Reading if this was unintended.', 'all-in-one-seo-pack' ), AIOSEOP_PLUGIN_NAME ),
27
- 'class' => 'notice-error',
28
- 'target' => 'site',
29
- 'screens' => array(),
30
- 'action_options' => array(
31
- array(
32
- 'time' => 0,
33
- 'text' => __( 'Update Reading Settings', 'all-in-one-seo-pack' ),
34
- 'link' => admin_url( 'options-reading.php' ),
35
- 'dismiss' => false,
36
- 'class' => 'button-primary',
37
- ),
38
- array(
39
- 'time' => 604800,
40
- 'text' => __( 'Remind me later', 'all-in-one-seo-pack' ),
41
- 'link' => '',
42
- 'dismiss' => false,
43
- 'class' => 'button-secondary',
44
- ),
45
- ),
46
- );
47
- }
48
- add_filter( 'aioseop_admin_notice-blog_public_disabled', 'aioseop_notice_blog_visibility' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/display/notices/check-php-version-notice.php DELETED
@@ -1,37 +0,0 @@
1
- <?php
2
- /**
3
- * Check PHP Version Notice.
4
- *
5
- * @since 3.4
6
- *
7
- * @package All-in-One-SEO-Pack
8
- */
9
-
10
- /**
11
- * Notice - Check PHP Version
12
- *
13
- * @since 3.4
14
- *
15
- * @return array Notice configuration.
16
- */
17
- function aioseop_notice_check_php_version() {
18
- $medium = ( AIOSEOPPRO ) ? 'proplugin' : 'liteplugin';
19
- return array(
20
- 'slug' => 'check_php_version',
21
- 'delay_time' => 0,
22
- 'target' => 'user',
23
- 'screens' => array(),
24
- 'class' => 'notice-error',
25
- 'dismissible' => false,
26
- /* translators: %1$s is a placeholder, which means that it should not be translated. It will be replaced with the name of the plugin, All in One SEO Pack. */
27
- 'html' => '
28
- <p>' . sprintf( __( 'Your site is running an outdated version of PHP that is no longer supported and may cause issues with %1$s. <a href="%2$s" target="_blank" rel="noopener noreferrer">Read more</a> for additional information.', 'all-in-one-seo-pack' ), AIOSEOP_PLUGIN_NAME, 'https://semperplugins.com/documentation/supported-php-version/?utm_source=WordPress&utm_medium=' . $medium . '&utm_campaign=outdated-php-notice' ) . '</p>
29
- <style>
30
- .aioseop-notice-check_php_version .aioseo-action-buttons {
31
- display: none;
32
- }
33
- </style>
34
- ',
35
- );
36
- }
37
- add_filter( 'aioseop_admin_notice-check_php_version', 'aioseop_notice_check_php_version' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/display/notices/conflicting-plugin-notice.php DELETED
@@ -1,45 +0,0 @@
1
- <?php
2
- /**
3
- * Conflicting Plugin Notice
4
- *
5
- * @since 3.4.0
6
- *
7
- * @package All-in-One-SEO-Pack
8
- * @subpackage AIOSEOP_Notices
9
- */
10
-
11
- /**
12
- * Returns the default values for our conflicting plugin notice.
13
- *
14
- * @since 3.4.0 Added UTM link and removed dismiss button.
15
- *
16
- * @return array
17
- */
18
- function aioseop_conflicting_plugin_notice() {
19
- return array(
20
- 'slug' => 'conflicting_plugin',
21
- 'delay_time' => 0,
22
- 'message' => '',
23
- 'target' => 'user',
24
- 'screens' => array(),
25
- 'class' => 'notice-error',
26
- 'action_options' => array(
27
- array(
28
- 'time' => 0,
29
- 'link' => '#',
30
- 'new_tab' => false,
31
- 'text' => __( 'Deactivate plugins', 'all-in-one-seo-pack' ),
32
- 'dismiss' => false,
33
- 'class' => 'button-primary',
34
- ),
35
- array(
36
- 'time' => 172800, // 48H
37
- 'text' => 'Remind me later',
38
- 'link' => '',
39
- 'dismiss' => false,
40
- 'class' => 'button-secondary',
41
- ),
42
- ),
43
- );
44
- }
45
- add_filter( 'aioseop_admin_notice-conflicting_plugin', 'aioseop_conflicting_plugin_notice' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/display/notices/deprecated-additional-headers-settings-notice.php DELETED
@@ -1,59 +0,0 @@
1
- <?php
2
- /**
3
- * Configures the Deprecated Additional Headers Settings notice.
4
- *
5
- * Appears when the user was previously using the Additional Headers settings. These have been removed in 3.7.1.
6
- *
7
- * @since 3.7.1
8
- *
9
- * @return array The notice data.
10
- */
11
- function aioseoDeprecatedAdditionalHeadersSettings() {
12
- global $aioseop_options;
13
- $settings = array_filter( array(
14
- __( 'Additional Post Headers', 'all-in-one-seo-pack' ) => $aioseop_options['aiosp_post_meta_tags'],
15
- __( 'Additional Page Headers', 'all-in-one-seo-pack' ) => $aioseop_options['aiosp_page_meta_tags'],
16
- __( 'Additional Front Page Headers', 'all-in-one-seo-pack' ) => $aioseop_options['aiosp_front_meta_tags'],
17
- __( 'Additional Posts Page Headers', 'all-in-one-seo-pack' ) => $aioseop_options['aiosp_home_meta_tags']
18
- ));
19
-
20
- if ( ! count( $settings ) ) {
21
- return array();
22
- }
23
-
24
- $message = sprintf( __( 'The Additional Headers settings have been deprecated and removed from %1$s.
25
- You are seeing this message because you may have been using these settings. We recommend you use the Insert Headers and Footers plugin from WPBeginner instead.
26
- Below you can find details of the settings that you were using:', 'all-in-one-seo-pack' ), AIOSEOP_PLUGIN_NAME );
27
-
28
- $content = "<p>$message<p/><ul>";
29
- foreach ( $settings as $name => $value ) {
30
- $content = $content ."<li>$name - $value</li>";
31
- }
32
- $content = $content . '</ul><style>
33
- .aioseop-notice-deprecated_additional_headers_settings ul {
34
- list-style-type: disc;
35
- margin-top: -5px;
36
- margin-left: 40px;
37
- }
38
- </style>';
39
-
40
- return array(
41
- 'slug' => 'deprecated_additional_headers_settings',
42
- 'delay_time' => 0,
43
- 'html' => $content,
44
- 'class' => 'notice-warning',
45
- 'target' => 'site',
46
- 'screens' => array(),
47
- 'action_options' => array(
48
- array(
49
- 'time' => 0,
50
- 'text' => __( 'Install Headers and Footers plugin', 'all-in-one-seo-pack' ),
51
- 'link' => admin_url( "plugin-install.php?s=insert+headers+and+footers&tab=search&type=term" ),
52
- 'new_tab' => false,
53
- 'dismiss' => false,
54
- 'class' => 'button-primary',
55
- )
56
- ),
57
- );
58
- }
59
- add_filter( 'aioseop_admin_notice-deprecated_additional_headers_settings', 'aioseoDeprecatedAdditionalHeadersSettings' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/display/notices/deprecated-unprotect-post-meta-setting-notice.php DELETED
@@ -1,38 +0,0 @@
1
- <?php
2
- /**
3
- * Configures the Deprecated Unprotect Post Meta Setting notice.
4
- *
5
- * Appears when the user was previously using the Unprotect Post Meta setting. It has been removed in 3.7.1.
6
- *
7
- * @since 3.7.1
8
- *
9
- * @return array The notice data.
10
- */
11
- function aioseoDeprecatedUnprotectPostMetaSetting() {
12
- $anchor = sprintf( '<a href="https://semperplugins.com/documentation/unprotecting-aioseops-post-meta/" target="_blank">%1$s</a>', __( 'this filter hook', 'all-in-one-seo-pack' ) );
13
- $message = sprintf( __( 'The Unprotect Post Meta setting in the General Settings menu has been deprecated and removed from %1$s.
14
- You are seeing this message only because you had it enabled. If you would like to retain this functionality, then you can do so by using %2$s.', 'all-in-one-seo-pack' ), AIOSEOP_PLUGIN_NAME, $anchor );
15
-
16
- return array(
17
- 'slug' => 'deprecated_unprotect_post_meta_setting',
18
- 'delay_time' => 0,
19
- 'html' => "<p>$message</p><style>
20
- .aioseop-notice-deprecated_unprotect_post_meta_setting .aioseo-action-buttons {
21
- display: none;
22
- }
23
- </style>",
24
- 'class' => 'notice-warning',
25
- 'target' => 'site',
26
- 'screens' => array(),
27
- 'action_options' => array(
28
- array(
29
- 'time' => 0,
30
- 'text' => '',
31
- 'link' => '',
32
- 'dismiss' => true,
33
- 'class' => 'aioseo-dismiss-review-notice-button',
34
- ),
35
- ),
36
- );
37
- }
38
- add_filter( 'aioseop_admin_notice-deprecated_unprotect_post_meta_setting', 'aioseoDeprecatedUnprotectPostMetaSetting' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/display/notices/index.php DELETED
@@ -1,4 +0,0 @@
1
- <?php
2
- /**
3
- * Silence is golden.
4
- */
 
 
 
 
admin/display/notices/local-business-markup-notice.php DELETED
@@ -1,34 +0,0 @@
1
- <?php
2
- /**
3
- * Configures the Local Business Markup notice.
4
- *
5
- * Appears when the user has enabled the Local Business SEO module, but has disabled Schema markup as a whole in the General Settings menu.
6
- *
7
- * @since 3.7.0
8
- *
9
- * @return array The notice data.
10
- */
11
- function aioseoLocalBusinessMarkupNotice() {
12
- $dirname = dirname( plugin_basename( AIOSEO_PLUGIN_FILE ) );
13
- $menuPath = admin_url( "admin.php?page=$dirname/aioseop_class.php" );
14
-
15
- return array(
16
- 'slug' => 'local_business_markup',
17
- 'delay_time' => 0,
18
- 'message' => __( "You've enabled the Local Business SEO module, but all Schema markup (including Local Business schema) is currently disabled.", 'all-in-one-seo-pack' ),
19
- 'class' => 'notice-warning',
20
- 'target' => 'site',
21
- 'screens' => aioseop_get_admin_screens(),
22
- 'action_options' => array(
23
- array(
24
- 'time' => 0,
25
- 'text' => __( 'Go to General Settings menu', 'all-in-one-seo-pack' ),
26
- 'link' => $menuPath,
27
- 'new_tab' => false,
28
- 'dismiss' => false,
29
- 'class' => 'button-primary',
30
- )
31
- ),
32
- );
33
- }
34
- add_filter( 'aioseop_admin_notice-local_business_markup', 'aioseoLocalBusinessMarkupNotice' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/display/notices/local-business-notice.php DELETED
@@ -1,33 +0,0 @@
1
- <?php
2
- /**
3
- * Configures the Local Business schema notice.
4
- *
5
- * Appears when the user's schema markup isn't set to Organization in the General Settings menu.
6
- *
7
- * @since 3.6.0
8
- *
9
- * @return array The notice data.
10
- */
11
- function aioseoLocalBusinessNotice() {
12
- $dirname = dirname( plugin_basename( AIOSEO_PLUGIN_FILE ) );
13
- $menuPath = admin_url( "admin.php?page=$dirname/aioseop_class.php" );
14
-
15
- return array(
16
- 'slug' => 'local_business',
17
- 'delay_time' => 0,
18
- 'message' => __( 'Your site is currently set to represent a Person. In order to use Local Business schema, you must set your site to represent an Organization.', 'all-in-one-seo-pack' ),
19
- 'class' => 'notice-error',
20
- 'target' => 'site',
21
- 'screens' => array(),
22
- 'action_options' => array(
23
- array(
24
- 'time' => 0,
25
- 'text' => __( 'Go to General Settings menu', 'all-in-one-seo-pack' ),
26
- 'link' => $menuPath,
27
- 'dismiss' => false,
28
- 'class' => 'button-primary',
29
- )
30
- ),
31
- );
32
- }
33
- add_filter( 'aioseop_admin_notice-local_business', 'aioseoLocalBusinessNotice' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/display/notices/local-business-organization-notice.php DELETED
@@ -1,34 +0,0 @@
1
- <?php
2
- /**
3
- * Configures the Local Business Organization notice.
4
- *
5
- * Appears when the user's schema markup isn't set to Organization in the General Settings menu.
6
- *
7
- * @since 3.6.0
8
- *
9
- * @return array The notice data.
10
- */
11
- function aioseoLocalBusinessOrganizationNotice() {
12
- $dirname = dirname( plugin_basename( AIOSEO_PLUGIN_FILE ) );
13
- $menuPath = admin_url( "admin.php?page=$dirname/aioseop_class.php" );
14
-
15
- return array(
16
- 'slug' => 'local_business_organization',
17
- 'delay_time' => 0,
18
- 'message' => __( 'Your site is currently set to represent a Person. In order to use Local Business schema, you must set your site to represent an Organization.', 'all-in-one-seo-pack' ),
19
- 'class' => 'notice-warning',
20
- 'target' => 'site',
21
- 'screens' => aioseop_get_admin_screens(),
22
- 'action_options' => array(
23
- array(
24
- 'time' => 0,
25
- 'text' => __( 'Go to General Settings menu', 'all-in-one-seo-pack' ),
26
- 'link' => $menuPath,
27
- 'new_tab' => false,
28
- 'dismiss' => false,
29
- 'class' => 'button-primary',
30
- )
31
- ),
32
- );
33
- }
34
- add_filter( 'aioseop_admin_notice-local_business_organization', 'aioseoLocalBusinessOrganizationNotice' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/display/notices/news-sitemap-notice.php DELETED
@@ -1,40 +0,0 @@
1
- <?php
2
- /**
3
- * Contains all data for the Google News sitemap notice.
4
- *
5
- * @since 3.6.0
6
- *
7
- * @return void
8
- */
9
- function aioseop_notice_news_sitemap() {
10
- $dirname = dirname( plugin_basename( AIOSEO_PLUGIN_FILE ) );
11
- $menu_path = admin_url( "admin.php?page=$dirname/pro/class-aioseop-pro-sitemap.php" );
12
- return array(
13
- 'slug' => 'news_sitemap',
14
- 'delay_time' => 0,
15
- 'target' => 'site',
16
- 'screens' => array(),
17
- 'class' => 'notice-error',
18
- 'dismissible' => false,
19
- 'message' => sprintf( __( 'You have not set the Google News Publication Name or the Site Title. %s requires at least one of these for the Google News sitemap to be valid.', 'all-in-one-seo-pack' ), AIOSEOP_PLUGIN_NAME ),
20
- 'action_options' => array(
21
- array(
22
- 'time' => 0,
23
- 'link' => $menu_path,
24
- 'new_tab' => false,
25
- 'text' => __( 'Go to XML Sitemap settings', 'all-in-one-seo-pack' ),
26
- 'dismiss' => false,
27
- 'class' => 'button-primary',
28
- ),
29
- array(
30
- 'time' => 0,
31
- 'link' => admin_url( 'options-general.php' ),
32
- 'new_tab' => false,
33
- 'text' => __( 'Go to Settings > General', 'all-in-one-seo-pack' ),
34
- 'dismiss' => false,
35
- 'class' => 'button-secondary',
36
- ),
37
- ),
38
- );
39
- }
40
- add_filter( 'aioseop_admin_notice-news_sitemap', 'aioseop_notice_news_sitemap' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/display/notices/review-plugin-cta-notice.php DELETED
@@ -1,116 +0,0 @@
1
- <?php
2
- /**
3
- * Review Plugin Notice
4
- *
5
- * @since 3.4
6
- *
7
- * @package All-in-One-SEO-Pack
8
- */
9
-
10
- /**
11
- * Notice - Review Plugin
12
- *
13
- * @since 3.4
14
- *
15
- * @return array Notice configuration.
16
- */
17
- function aioseop_notice_review_plugin_cta() {
18
- global $aioseop_options;
19
- $feedback_url = add_query_arg(
20
- array(
21
- 'wpf7528_24' => untrailingslashit( home_url() ),
22
- 'wpf7528_26' => ! empty( $aioseop_options['aiosp_license_key'] ) ? $aioseop_options['aiosp_license_key'] : null,
23
- 'wpf7528_27' => AIOSEOPPRO ? 'pro' : 'lite',
24
- 'wpf7528_28' => AIOSEOP_VERSION,
25
- 'utm_source' => AIOSEOPPRO ? 'proplugin' : 'liteplugin',
26
- 'utm_medium' => 'review-notice',
27
- 'utm_campaign' => 'feedback',
28
- 'utm_content' => AIOSEOP_VERSION,
29
- ),
30
- 'https://semperplugins.com/plugin-feedback/'
31
- );
32
-
33
- return array(
34
- 'slug' => 'review_plugin_cta',
35
- 'delay_time' => WEEK_IN_SECONDS * 2,
36
- 'target' => 'user',
37
- 'screens' => array(),
38
- 'class' => 'notice-info',
39
- 'html' => '
40
- <div class="aioseo-review-plugin-cta">
41
- <div class="step-1">' .
42
- /* translators: %1$s is a placeholder, which means that it should not be translated. It will be replaced with the name of the plugin, All in One SEO Pack. */
43
- '<p>' . sprintf( __( 'Are you enjoying %1$s?', 'all-in-one-seo-pack' ), AIOSEOP_PLUGIN_NAME ) . '</p>
44
- <p>
45
- <a href="#" class="aioseo-review-switch-step-3" data-step="3">' . __( 'Yes I love it', 'all-in-one-seo-pack' ) . '</a> 🙂 |
46
- <a href="#" class="aioseo-review-switch-step-2" data-step="2">' . __( 'Not Really...', 'all-in-one-seo-pack' ) . '</a>
47
- </p>
48
- </div>
49
- <div class="step-2" style="display:none;">' .
50
- /* translators: %1$s is a placeholder, which means that it should not be translated. It will be replaced with the name of the plugin, All in One SEO Pack. */
51
- '<p>' . sprintf( __( 'We\'re sorry to hear you aren\'t enjoying %1$s. We would love a chance to improve. Could you take a minute and let us know what we can do better?', 'all-in-one-seo-pack' ), AIOSEOP_PLUGIN_NAME ) . '</p>
52
- <p>
53
- <a href="' . $feedback_url . '" class="aioseo-dismiss-review-notice" target="_blank" rel="noopener noreferrer">' . __( 'Give feedback', 'all-in-one-seo-pack' ) . '</a>&nbsp;&nbsp;
54
- <a href="#" class="aioseo-dismiss-review-notice" target="_blank" rel="noopener noreferrer">' . __( 'No thanks', 'all-in-one-seo-pack' ) . '</a>
55
- </p>
56
- </div>
57
- <div class="step-3" style="display:none;">
58
- <p>' . __( 'That\'s awesome! Could you please do me a BIG favor and give it a 5-star rating on WordPress to help us spread the word and boost our motivation?', 'all-in-one-seo-pack' ) . '</p>' .
59
- /* translators: %1$s is a placeholder, which means that it should not be translated. It will be replaced with the name of the plugin, All in One SEO Pack. */
60
- '<p><strong>~ Syed Balkhi<br>' . sprintf( __( 'President of %1$s', 'all-in-one-seo-pack' ), AIOSEOP_PLUGIN_NAME ) . '</strong></p>
61
- <p>
62
- <a href="https://wordpress.org/support/plugin/all-in-one-seo-pack/reviews/?filter=5#new-post" class="aioseo-dismiss-review-notice" target="_blank" rel="noopener noreferrer">' . __( 'Ok, you deserve it', 'all-in-one-seo-pack' ) . '</a>&nbsp;&nbsp;
63
- <a href="#" class="aioseo-dismiss-review-notice-delay" target="_blank" rel="noopener noreferrer">' . __( 'Nope, maybe later', 'all-in-one-seo-pack' ) . '</a>&nbsp;&nbsp;
64
- <a href="#" class="aioseo-dismiss-review-notice" target="_blank" rel="noopener noreferrer">' . __( 'I already did', 'all-in-one-seo-pack' ) . '</a>
65
- </p>
66
- </div>
67
- </div>
68
- <style>
69
- .aioseop-notice-review_plugin_cta .aioseo-action-buttons {
70
- display: none;
71
- }
72
- </style>
73
- <script type="text/javascript">
74
- jQuery(document).on("click", ".aioseo-review-plugin-cta .aioseo-review-switch-step-3", function(event) {
75
- event.preventDefault();
76
- jQuery(".aioseo-review-plugin-cta .step-1, .aioseo-review-plugin-cta .step-2").hide();
77
- jQuery(".aioseo-review-plugin-cta .step-3").show();
78
- });
79
- jQuery(document).on("click", ".aioseo-review-plugin-cta .aioseo-review-switch-step-2", function(event) {
80
- event.preventDefault();
81
- jQuery(".aioseo-review-plugin-cta .step-1, .aioseo-review-plugin-cta .step-3").hide();
82
- jQuery(".aioseo-review-plugin-cta .step-2").show();
83
- });
84
- jQuery(document).on("click", ".aioseo-review-plugin-cta .aioseo-dismiss-review-notice-delay", function(event) {
85
- event.preventDefault();
86
- var element = jQuery(".aioseop-notice-review_plugin_cta .aioseo-action-buttons .aioseo-dismiss-review-notice-delay-button");
87
- element.click();
88
- });
89
- jQuery(document).on("click", ".aioseo-review-plugin-cta .aioseo-dismiss-review-notice", function(event) {
90
- if ("#" === jQuery(this).attr("href")) {
91
- event.preventDefault();
92
- }
93
- var element = jQuery(".aioseop-notice-review_plugin_cta .aioseo-action-buttons .aioseo-dismiss-review-notice-button");
94
- element.click();
95
- });
96
- </script>
97
- ',
98
- 'action_options' => array(
99
- array(
100
- 'time' => 0,
101
- 'text' => '',
102
- 'link' => '',
103
- 'dismiss' => true,
104
- 'class' => 'aioseo-dismiss-review-notice-button',
105
- ),
106
- array(
107
- 'time' => WEEK_IN_SECONDS,
108
- 'text' => '',
109
- 'link' => '',
110
- 'dismiss' => false,
111
- 'class' => 'aioseo-dismiss-review-notice-delay-button',
112
- ),
113
- ),
114
- );
115
- }
116
- add_filter( 'aioseop_admin_notice-review_plugin_cta', 'aioseop_notice_review_plugin_cta' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/display/notices/sitemap-indexes-notice.php DELETED
@@ -1,44 +0,0 @@
1
- <?php
2
- /**
3
- * Sitemap Index Notice
4
- *
5
- * @since 3.0
6
- * @package All-in-One-SEO-Pack
7
- * @subpackage AIOSEOP_Notices
8
- */
9
-
10
- /**
11
- * Notice - Sitemap Indexes
12
- *
13
- * @since 3.0
14
- *
15
- * @return array
16
- */
17
- function aioseop_notice_sitemap_indexes() {
18
- return array(
19
- 'slug' => 'sitemap_max_warning',
20
- 'delay_time' => 0,
21
- 'message' => __( 'Notice: To avoid problems with your XML Sitemap, we strongly recommend you set the Maximum Posts per Sitemap Page to 1,000.', 'all-in-one-seo-pack' ),
22
- 'class' => 'notice-warning',
23
- 'target' => 'user',
24
- 'screens' => array(),
25
- 'action_options' => array(
26
- array(
27
- 'time' => 0,
28
- 'text' => __( 'Update Sitemap Settings', 'all-in-one-seo-pack' ),
29
- 'link' => esc_url( get_admin_url( null, 'admin.php?page=' . AIOSEOP_PLUGIN_DIRNAME . '/modules/aioseop_sitemap.php' ) ),
30
- 'dismiss' => false,
31
- 'class' => 'button-primary',
32
- ),
33
- array(
34
- 'time' => 86400, // 24 hours.
35
- 'text' => __( 'Remind me later', 'all-in-one-seo-pack' ),
36
- 'link' => '',
37
- 'dismiss' => false,
38
- 'class' => 'button-secondary',
39
- ),
40
-
41
- ),
42
- );
43
- }
44
- add_filter( 'aioseop_admin_notice-sitemap_max_warning', 'aioseop_notice_sitemap_indexes' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/display/notices/wc-detected-notice.php DELETED
@@ -1,41 +0,0 @@
1
- <?php
2
- /**
3
- * WooCommerce Detected Notice
4
- *
5
- * @since 3.0.0
6
- *
7
- * @package All-in-One-SEO-Pack
8
- * @subpackage AIOSEOP_Notices
9
- */
10
-
11
- /**
12
- * Returns the default values for our WooCommerce upsell notice.
13
- *
14
- * @since 3.0.0
15
- * @since 3.4.0 Added UTM link and removed dismiss button.
16
- *
17
- * @return array
18
- */
19
- function aioseop_notice_pro_promo_woocommerce() {
20
- return array(
21
- 'slug' => 'woocommerce_detected',
22
- 'delay_time' => 0,
23
- /* translators: %s is a placeholder, which means that it should not be translated. It will be replaced with the name of the premium version of the plugin, All in One SEO Pack Pro. */
24
- 'message' => sprintf( __( 'We have detected you are running WooCommerce. Upgrade to %s to unlock our advanced eCommerce SEO features, including SEO for Product Categories and more.', 'all-in-one-seo-pack' ), 'All in One SEO Pack Pro' ),
25
-
26
- 'class' => 'notice-info',
27
- 'target' => 'site',
28
- 'screens' => array( 'aioseop', 'product', 'edit-product' ),
29
- 'action_options' => array(
30
- array(
31
- 'time' => 0,
32
- 'text' => __( 'Upgrade to Pro', 'all-in-one-seo-pack' ),
33
- 'link' => aioseop_get_utm_url( 'woocommerce-upsell-notice' ),
34
- 'dismiss' => false,
35
- 'class' => 'button-primary button-orange',
36
- ),
37
- ),
38
- );
39
- }
40
-
41
- add_filter( 'aioseop_admin_notice-woocommerce_detected', 'aioseop_notice_pro_promo_woocommerce' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/display/welcome-content.php DELETED
@@ -1,137 +0,0 @@
1
- <?php
2
- /**
3
- * Welcome Content
4
- *
5
- * @package All_in_One_SEO_Pack
6
- * @since ?
7
- */
8
-
9
- ?>
10
- <div class="welcome-panel">
11
- <div class="welcome-panel-content">
12
- <div class="welcome-panel-column-container">
13
- <div>
14
- <h3><a href="https://semperplugins.com/new-local-business-schema/" target="_blank"><?php echo esc_html( __( "Check out what's new in our latest release post!", 'all-in-one-seo-pack' ) ); ?></a></h3>
15
- </div>
16
- <div class="welcome-panel-column">
17
- <h3>
18
- <?php
19
- /* translators: %s is a placeholder, which means that it should not be translated. It will be replaced with the name of the plugin, All in One SEO Pack. */
20
- echo esc_html( sprintf( __( 'Support %s', 'all-in-one-seo-pack' ), AIOSEOP_PLUGIN_NAME ) );
21
- ?>
22
- </h3>
23
- <p class="message welcome-icon welcome-edit-page">
24
- <?php
25
- /* translators: %s is a placeholder, which means that it should not be translated. It will be replaced with the name of the plugin, All in One SEO Pack. */
26
- echo esc_html( sprintf( __( 'There are many ways you can help support %s.', 'all-in-one-seo-pack' ), AIOSEOP_PLUGIN_NAME ) );
27
- ?>
28
- </p>
29
- <p class="message aioseop-message welcome-icon welcome-edit-page">
30
- <?php
31
- /* translators: %s is a placeholder, which means that it should not be translated. It will be replaced with the name of the premium version of the plugin, All in One SEO Pack Pro. */
32
- echo esc_html( sprintf( __( 'Upgrade to %s to access priority support and premium features.', 'all-in-one-seo-pack' ), 'All in One SEO Pack Pro' ) );
33
- ?>
34
- </p>
35
- <p class="call-to-action">
36
- <a
37
- href="https://semperplugins.com/all-in-one-seo-pack-pro-version/?loc=aio_welcome"
38
- target="_blank"
39
- class="button button-primary button-orange"><?php echo __( 'Upgrade', 'all-in-one-seo-pack' ); ?></a>
40
- </p>
41
- <p class="message aioseop-message welcome-icon welcome-edit-page">
42
- <?php
43
- /* translators: %s is a placeholder, which means that it should not be translated. It will be replaced with the name of the plugin, All in One SEO Pack. */
44
- echo esc_html( sprintf( __( 'Help translate %s into your language.', 'all-in-one-seo-pack' ), AIOSEOP_PLUGIN_NAME ) );
45
- ?>
46
- </p>
47
- <p class="call-to-action">
48
- <a
49
- href="https://translate.wordpress.org/projects/wp-plugins/all-in-one-seo-pack"
50
- class="button button-primary"
51
- target="_blank"><?php echo __( 'Translate', 'all-in-one-seo-pack' ); ?></a></p>
52
- </div>
53
-
54
- <div class="welcome-panel-column">
55
- <h3><?php echo esc_html( __( 'Get Started', 'all-in-one-seo-pack' ) ); ?></h3>
56
- <ul>
57
- <li>
58
- <a
59
- href="https://semperplugins.com/documentation/quick-start-guide/"
60
- target="_blank"
61
- class="welcome-icon welcome-add-page">
62
- <?php
63
- /* translators: %s is a placeholder, which means that it should not be translated. It will be replaced with the name of the plugin, All in One SEO Pack. */
64
- echo sprintf( __( 'Beginners Guide for %s', 'all-in-one-seo-pack' ), AIOSEOP_PLUGIN_NAME );
65
- ?>
66
- </a>
67
-
68
- </li>
69
- <li>
70
- <a
71
- href="https://semperplugins.com/documentation/beginners-guide-to-xml-sitemaps/"
72
- target="_blank"
73
- class="welcome-icon welcome-add-page"><?php echo __( 'Beginners Guide for XML Sitemap module', 'all-in-one-seo-pack' ); ?></a>
74
- </li>
75
- <li>
76
- <a
77
- href="https://semperplugins.com/documentation/beginners-guide-to-social-meta/"
78
- target="_blank"
79
- class="welcome-icon welcome-add-page"><?php echo __( 'Beginners Guide for Social Meta module', 'all-in-one-seo-pack' ); ?></a>
80
- </li>
81
- <li>
82
- <a
83
- href="https://semperplugins.com/documentation/top-tips-for-good-on-page-seo/"
84
- target="_blank"
85
- class="welcome-icon welcome-add-page"><?php echo __( 'Tips for good on-page SEO', 'all-in-one-seo-pack' ); ?></a>
86
- </li>
87
- <li>
88
- <a
89
- href="https://semperplugins.com/documentation/quality-guidelines-for-seo-titles-and-descriptions/"
90
- target="_blank"
91
- class="welcome-icon welcome-add-page"><?php echo __( 'Quality guidelines for SEO titles and descriptions', 'all-in-one-seo-pack' ); ?></a>
92
- </li>
93
- <li>
94
- <a
95
- href="https://semperplugins.com/documentation/submitting-an-xml-sitemap-to-google/"
96
- target="_blank"
97
- class="welcome-icon welcome-add-page"><?php echo __( 'Submit an XML Sitemap to Google', 'all-in-one-seo-pack' ); ?></a>
98
- </li>
99
- <li>
100
- <a
101
- href="https://semperplugins.com/documentation/setting-up-google-analytics/"
102
- target="_blank"
103
- class="welcome-icon welcome-add-page"><?php echo __( 'Set up Google Analytics', 'all-in-one-seo-pack' ); ?></a>
104
- </li>
105
- </ul>
106
- </div>
107
-
108
- <div class="welcome-panel-column">
109
- <h3><?php echo esc_html( __( 'Did You Know?', 'all-in-one-seo-pack' ) ); ?></h3>
110
- <ul>
111
- <li>
112
- <a
113
- href="https://semperplugins.com/documentation/"
114
- target="_blank"
115
- class="welcome-icon welcome-learn-more"><?php echo __( 'We have complete documentation on every setting and feature', 'all-in-one-seo-pack' ); ?></a>
116
-
117
- </li>
118
- <li>
119
- <a
120
- href="https://semperplugins.com/videos/"
121
- target="_blank"
122
- class="welcome-icon welcome-learn-more"><?php echo __( 'Access to video tutorials about SEO with the Pro version', 'all-in-one-seo-pack' ); ?></a>
123
- </li>
124
- <li>
125
- <a
126
- href="https://semperplugins.com/all-in-one-seo-pack-pro-version/?loc=aio_welcome"
127
- target="_blank"
128
- class="welcome-icon welcome-learn-more"><?php echo __( 'Control SEO on categories, tags and custom taxonomies with the Pro version', 'all-in-one-seo-pack' ); ?></a>
129
- </li>
130
- </ul>
131
- </div>
132
- </div>
133
- </div>
134
- <p>
135
- <a href=" <?php echo get_admin_url( null, 'admin.php?page=' . AIOSEOP_PLUGIN_DIRNAME . '/aioseop_class.php' ); ?> "><?php _e( 'Continue to the General Settings', 'all-in-one-seo-pack' ); ?></a> &raquo;
136
- </p>
137
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/index.php DELETED
@@ -1,4 +0,0 @@
1
- <?php
2
- /**
3
- * Silence is golden.
4
- */
 
 
 
 
admin/meta_import.php DELETED
@@ -1,635 +0,0 @@
1
- <?php
2
- /**
3
- * Meta Import
4
- *
5
- * @package All_in_One_SEO_Pack
6
- * @since ?
7
- */
8
-
9
- if ( class_exists( 'WPSEO_Import_Hooks' ) ) {
10
-
11
- /**
12
- * Class WPSEO_Import_AIOSEO_Hooks
13
- *
14
- * @TODO Move this elsewhere.
15
- */
16
- class WPSEO_Import_AIOSEO_Hooks extends WPSEO_Import_Hooks {
17
-
18
- /**
19
- * Plugin File
20
- *
21
- * @since ?
22
- *
23
- * @var string $plugin_file
24
- */
25
- protected $plugin_file = 'all-in-one-seo-pack/all_in_one_seo_pack.php';
26
-
27
- /**
28
- * Deactivate Listener
29
- *
30
- * @since ?
31
- *
32
- * @var string $deactivation_listener
33
- */
34
- protected $deactivation_listener = 'deactivate_aioseo';
35
-
36
- /**
37
- * Show notice the old plugin is installed and offer to import its data.
38
- */
39
- public function show_import_settings_notice() {
40
-
41
- $yoasturl = add_query_arg( array( '_wpnonce' => wp_create_nonce( 'wpseo-import' ) ), admin_url( 'admin.php?page=wpseo_tools&tool=import-export&import=1&importaioseo=1#top#import-seo' ) );
42
- $aiourl = add_query_arg( array( '_wpnonce' => wp_create_nonce( 'aiosp-import' ) ), admin_url( 'tools.php?page=aiosp_import' ) );
43
-
44
- $aioseop_yst_detected_notice_dismissed = get_user_meta( get_current_user_id(), 'aioseop_yst_detected_notice_dismissed', true );
45
-
46
- if ( empty( $aioseop_yst_detected_notice_dismissed ) ) {
47
-
48
- /* translators: %1$s, %2$s and %3$s are placeholders, which means these shouldn't be translated. The first two placeholders are used to add a link to anchor text and the third is replaced with the name of the plugin, All in One SEO Pack. */
49
- echo '<div class="notice notice-warning row-title is-dismissible yst_notice"><p>', sprintf( esc_html__( 'The plugin Yoast SEO has been detected. Do you want to %1$simport its settings%2$s into %3$s', 'all-in-one-seo-pack' ), sprintf( '<a href="%s">', esc_url( $aiourl ) ), '</a>', AIOSEOP_PLUGIN_NAME ), '</p></div>';
50
-
51
- }
52
- // phpcs:disable WordPress.WP.I18n
53
- echo '<div class="error"><p>', sprintf( esc_html__( 'The plugin All-In-One-SEO has been detected. Do you want to %1$simport its settings%2$s?', 'wordpress-seo' ), sprintf( '<a href="%s">', esc_url( $yoasturl ) ), '</a>' ), '</p></div>';
54
- // phpcs:enable
55
- }
56
-
57
- public function show_deactivate_notice() {
58
- echo '<div class="updated"><p>', esc_html__( 'All in One SEO has been deactivated', 'all-in-one-seo-pack' ), '</p></div>';
59
- }
60
- }
61
- } else {
62
- if ( is_admin() ) {
63
- add_action( 'init', 'mi_aioseop_yst_detected_notice_dismissed' );
64
- }
65
- }
66
-
67
- /**
68
- * Deletes the stored dismissal of the notice.
69
- *
70
- * This should only happen after reactivating after being deactivated.
71
- */
72
- function mi_aioseop_yst_detected_notice_dismissed() {
73
- delete_user_meta( get_current_user_id(), 'aioseop_yst_detected_notice_dismissed' );
74
- }
75
-
76
- /**
77
- * Init for settings import class.
78
- *
79
- * At the moment we just register the admin menu page.
80
- */
81
- function aiosp_seometa_settings_init() {
82
- global $_aiosp_seometa_admin_pagehook;
83
-
84
- // TODO Put this in with the rest of the import/export stuff.
85
- $_aiosp_seometa_admin_pagehook = add_submenu_page( 'tools.php', __( 'Import SEO Data', 'all-in-one-seo-pack' ), __( 'SEO Data Import', 'all-in-one-seo-pack' ), 'manage_options', 'aiosp_import', 'aiosp_seometa_admin' );
86
- }
87
- add_action( 'admin_menu', 'aiosp_seometa_settings_init' );
88
-
89
-
90
- /**
91
- * Intercept POST data from the form submission.
92
- *
93
- * Use the intercepted data to convert values in the postmeta table from one platform to another and display feedback to the user about compatible conversion
94
- * elements and the conversion process.
95
- */
96
- function aiosp_seometa_action() {
97
-
98
- if ( empty( $_REQUEST['_wpnonce'] ) ) {
99
- return;
100
- }
101
-
102
- if ( empty( $_REQUEST['platform_old'] ) ) {
103
- printf( '<div class="error"><p>%s</p></div>', __( 'Sorry, you can\'t do that. Please choose a platform and then click Analyze or Convert.', 'all-in-one-seo-pack' ) );
104
-
105
- return;
106
- }
107
-
108
- if ( 'All in One SEO Pack' === $_REQUEST['platform_old'] ) {
109
- printf( '<div class="error"><p>%s</p></div>', __( 'Sorry, you can\'t do that. Please choose a platform and then click Analyze or Convert.', 'all-in-one-seo-pack' ) );
110
-
111
- return;
112
- }
113
-
114
- check_admin_referer( 'aiosp_nonce' ); // Verify nonce. TODO We should make this better.
115
-
116
- if ( ! empty( $_REQUEST['analyze'] ) ) {
117
-
118
- printf( '<h3>%s</h3>', __( 'Analysis Results', 'all-in-one-seo-pack' ) );
119
-
120
- $response = aiosp_seometa_post_meta_analyze( $_REQUEST['platform_old'], 'All in One SEO Pack' );
121
- if ( is_wp_error( $response ) ) {
122
- printf( '<div class="error"><p>%s</p></div>', __( 'Sorry, something went wrong. Please try again', 'all-in-one-seo-pack' ) );
123
-
124
- return;
125
- }
126
-
127
- printf( __( '<p>Analyzing records in a %1$s to %2$s conversion&hellip;', 'all-in-one-seo-pack' ), esc_html( $_POST['platform_old'] ), 'All in One SEO Pack' );
128
- printf( '<p><b>%d</b> Compatible Records were identified</p>', $response->update );
129
- // phpcs:ignore Squiz.Commenting.InlineComment.InvalidEndChar
130
- // printf( '<p>%d Compatible Records will be ignored</p>', $response->ignore );
131
- printf( '<p><b>%s</b></p>', __( 'Compatible data:', 'all-in-one-seo-pack' ) );
132
- echo '<ol>';
133
- foreach ( (array) $response->elements as $element ) {
134
- printf( '<li>%s</li>', $element );
135
- }
136
- echo '</ol>';
137
-
138
- return;
139
- }
140
-
141
- printf( '<h3>%s</h3>', __( 'Conversion Results', 'all-in-one-seo-pack' ) );
142
-
143
- $result = aiosp_seometa_post_meta_convert( stripslashes( $_REQUEST['platform_old'] ), 'All in One SEO Pack' );
144
- if ( is_wp_error( $result ) ) {
145
- printf( '<p>%s</p>', __( 'Sorry, something went wrong. Please try again', 'all-in-one-seo-pack' ) );
146
-
147
- return;
148
- }
149
-
150
- printf( '<p><b>%d</b> Records were updated</p>', isset( $result->updated ) ? $result->updated : 0 );
151
- printf( '<p><b>%d</b> Records were ignored</p>', isset( $result->ignored ) ? $result->ignored : 0 );
152
-
153
- }
154
-
155
- /**
156
- * The admin page output
157
- */
158
- function aiosp_seometa_admin() {
159
- global $_aiosp_seometa_themes, $_aiosp_seometa_plugins, $_aiosp_seometa_platforms;
160
- ?>
161
-
162
- <div class="wrap">
163
-
164
-
165
- <h1><?php _e( 'Import SEO Settings', 'all-in-one-seo-pack' ); ?></h1>
166
-
167
- <p><span
168
- class="description"><?php printf( __( 'Use the drop down below to choose which plugin or theme you wish to import SEO data from.', 'all-in-one-seo-pack' ) ); ?></span>
169
- </p>
170
-
171
- <p><span
172
- class="description">
173
- <?php
174
- /* translators: %s is a placeholder, which means that it should not be translated. It will be replaced with the name of the plugin, All in One SEO Pack. */
175
- printf( sprintf( __( 'Click "Analyze" for a list of SEO data that can be imported into %s, along with the number of records that will be imported.', 'all-in-one-seo-pack' ), AIOSEOP_PLUGIN_NAME ) );
176
- ?>
177
- </span>
178
- </p>
179
-
180
- <p>
181
- <span class="description">
182
- <strong><?php printf( __( 'Please Note: ', 'all-in-one-seo-pack' ) ); ?></strong>
183
- <?php
184
- /* translators: %s is a placeholder, which means that it should not be translated. It will be replaced with the name of the plugin, All in One SEO Pack. */
185
- printf(
186
- sprintf(
187
- __( 'Some plugins and themes do not share similar data, or they store data in a non-standard way. If we cannot import this data, it will remain unchanged in your database. Any compatible SEO data will be displayed for you to review. If a post or page already has SEO data in %s, we will not import data from another plugin/theme.', 'all-in-one-seo-pack' ),
188
- AIOSEOP_PLUGIN_NAME
189
- )
190
- );
191
- ?>
192
- </span>
193
- </p>
194
-
195
- <p><span
196
- class="description"><?php printf( __( 'Click "Convert" to perform the import. After the import has completed, you will be alerted to how many records were imported, and how many records had to be ignored, based on the criteria above.', 'all-in-one-seo-pack' ) ); ?></span>
197
- </p>
198
-
199
- <p><span
200
- class="row-title"><?php printf( esc_html__( 'Before performing an import, we strongly recommend that you make a backup of your site. We use and recommend %1$s VaultPress by Jetpack %2$s for backups.', 'all-in-one-seo-pack' ), sprintf( '<a target="_blank" href="%s">', esc_url( 'https://www.wpbeginner.com/refer/jetpack/' ) ), '</a>' ); ?></span>
201
- </p>
202
-
203
-
204
- <form action="<?php echo admin_url( 'tools.php?page=aiosp_import' ); ?>" method="post">
205
- <?php
206
- wp_nonce_field( 'aiosp_nonce' );
207
-
208
- $platform_old = ( ! isset( $_POST['platform_old'] ) ) ? '' : $_POST['platform_old'];
209
-
210
- _e( 'Import SEO data from:', 'all-in-one-seo-pack' );
211
- echo '<select name="platform_old" aria-label="' . __( 'Choose the platform you want to import SEO data from', 'all-in-one-seo-pack' ) . '">';
212
- printf( '<option value="">%s</option>', __( 'Choose platform:', 'all-in-one-seo-pack' ) );
213
-
214
- printf( '<optgroup label="%s">', __( 'Plugins', 'all-in-one-seo-pack' ) );
215
- foreach ( $_aiosp_seometa_plugins as $platform => $data ) {
216
- if ( 'All in One SEO Pack' !== $platform ) {
217
- printf( '<option value="%s" %s>%s</option>', $platform, selected( $platform, $platform_old, 0 ), $platform );
218
- }
219
- }
220
- printf( '</optgroup>' );
221
-
222
- printf( '<optgroup label="%s">', __( 'Themes', 'all-in-one-seo-pack' ) );
223
- foreach ( $_aiosp_seometa_themes as $platform => $data ) {
224
- printf( '<option value="%s" %s>%s</option>', $platform, selected( $platform, $platform_old, 0 ), $platform );
225
- }
226
- printf( '</optgroup>' );
227
-
228
- echo '</select>' . "\n\n";
229
-
230
- ?>
231
-
232
- <input
233
- type="submit"
234
- class="button-secondary"
235
- name="analyze"
236
- value="<?php _e( 'Analyze', 'all-in-one-seo-pack' ); ?>"
237
- aria-label="Analyze"/>
238
- <input
239
- type="submit"
240
- class="button-primary"
241
- value="<?php _e( 'Convert', 'all-in-one-seo-pack' ); ?>"
242
- aria-label="Convert"/>
243
-
244
- </form>
245
-
246
- <?php aiosp_seometa_action(); ?>
247
-
248
- </div>
249
-
250
- <?php
251
- }
252
-
253
- /**
254
- * Convert old meta_key entries in the post meta table into new entries.
255
- *
256
- * First check to see what records for $new already exist, storing the corresponding post_id values in an array.
257
- * When the conversion happens, ignore rows that contain a post_id, to avoid duplicate entries.
258
- *
259
- * @param string $old Old meta_key entries.
260
- * @param string $new New meta_key entries.
261
- * @param bool $delete_old Whether to delete the old entries.
262
- *
263
- * @return stdClass Object for error detection, and the number of affected rows.
264
- */
265
- function aiosp_seometa_meta_key_convert( $old = '', $new = '', $delete_old = false ) {
266
-
267
- do_action( 'pre_aiosp_seometa_meta_key_convert_before', $old, $new, $delete_old );
268
-
269
- global $wpdb;
270
-
271
- $output = new stdClass;
272
-
273
- if ( ! $old || ! $new ) {
274
- // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
275
- $output->WP_Error = 1;
276
-
277
- return $output;
278
- }
279
-
280
- // See which records we need to ignore, if any.
281
- $exclude = $wpdb->get_results( $wpdb->prepare( "SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = %s", $new ) );
282
-
283
- // If no records to ignore, we'll do a basic UPDATE and DELETE.
284
- if ( ! $exclude ) {
285
-
286
- $output->updated = $wpdb->update( $wpdb->postmeta, array( 'meta_key' => $new ), array( 'meta_key' => $old ) );
287
- $output->deleted = $delete_old ? $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->postmeta} WHERE meta_key = %s", $old ) ) : 0;
288
- $output->ignored = 0;
289
-
290
- } else {
291
- // Else, do a more complex UPDATE and DELETE.
292
- foreach ( (array) $exclude as $key => $value ) {
293
- $not_in[] = $value->post_id;
294
- }
295
- $not_in = implode( ', ', (array) $not_in );
296
-
297
- // @codingStandardsIgnoreStart
298
- $output->updated = $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_key = %s WHERE meta_key = %s AND post_id NOT IN (%s)", $new, $old, $not_in ) );
299
- $output->deleted = $delete_old ? $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->postmeta} WHERE meta_key = %s", $old ) ) : 0;
300
- // @codingStandardsIgnoreEnd
301
- $output->ignored = count( $exclude );
302
-
303
- }
304
-
305
- do_action( 'aiosp_seometa_meta_key_convert', $output, $old, $new, $delete_old );
306
-
307
- return $output;
308
-
309
- }
310
-
311
- /**
312
- * Convert old to new postmeta.
313
- *
314
- * Cycle through all compatible SEO entries of two platforms and aiosp_seometa_meta_key_convert conversion for each key.
315
- *
316
- * @param string $old_platform
317
- * @param string $new_platform
318
- * @param bool $delete_old
319
- *
320
- * @return stdClass Results object.
321
- */
322
- function aiosp_seometa_post_meta_convert( $old_platform = '', $new_platform = 'All in One SEO Pack', $delete_old = false ) {
323
-
324
- do_action( 'pre_aiosp_seometa_post_meta_convert', $old_platform, $new_platform, $delete_old );
325
-
326
- global $_aiosp_seometa_platforms;
327
-
328
- $output = new stdClass;
329
-
330
- if ( empty( $_aiosp_seometa_platforms[ $old_platform ] ) || empty( $_aiosp_seometa_platforms[ $new_platform ] ) ) {
331
- // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
332
- $output->WP_Error = 1;
333
-
334
- return $output;
335
- }
336
-
337
- $output->updated = 0;
338
- $output->deleted = 0;
339
- $output->ignored = 0;
340
-
341
- foreach ( (array) $_aiosp_seometa_platforms[ $old_platform ] as $label => $meta_key ) {
342
-
343
- // Skip iterations where no $new analog exists.
344
- if ( empty( $_aiosp_seometa_platforms[ $new_platform ][ $label ] ) ) {
345
- continue;
346
- }
347
-
348
- // Set $old and $new meta_key values.
349
- $old = $_aiosp_seometa_platforms[ $old_platform ][ $label ];
350
- $new = $_aiosp_seometa_platforms[ $new_platform ][ $label ];
351
-
352
- // Convert.
353
- $result = aiosp_seometa_meta_key_convert( $old, $new, $delete_old );
354
-
355
- // Error check.
356
- if ( is_wp_error( $result ) ) {
357
- continue;
358
- }
359
-
360
- // Update total updated/ignored count.
361
- $output->updated += (int) $result->updated;
362
- $output->ignored += (int) $result->ignored;
363
-
364
- }
365
-
366
- do_action( 'aiosp_seometa_post_meta_convert', $output, $old_platform, $new_platform, $delete_old );
367
-
368
- return $output;
369
-
370
- }
371
-
372
- /**
373
- * Analyze two platforms to find shared and compatible elements.
374
- *
375
- * See what data can be converted from one to the other.
376
- *
377
- * @param string $old_platform
378
- * @param string $new_platform
379
- *
380
- * @return stdClass
381
- */
382
- function aiosp_seometa_post_meta_analyze( $old_platform = '', $new_platform = 'All in One SEO Pack' ) {
383
- // TODO Figure out which elements to ignore.
384
- do_action( 'pre_aiosp_seometa_post_meta_analyze', $old_platform, $new_platform );
385
-
386
- global $wpdb, $_aiosp_seometa_platforms;
387
-
388
- $output = new stdClass;
389
-
390
- if ( empty( $_aiosp_seometa_platforms[ $old_platform ] ) || empty( $_aiosp_seometa_platforms[ $new_platform ] ) ) {
391
- // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
392
- $output->WP_Error = 1;
393
-
394
- return $output;
395
- }
396
-
397
- $output->update = 0;
398
- $output->ignore = 0;
399
- $output->elements = '';
400
-
401
- foreach ( (array) $_aiosp_seometa_platforms[ $old_platform ] as $label => $meta_key ) {
402
-
403
- // Skip iterations where no $new analog exists.
404
- if ( empty( $_aiosp_seometa_platforms[ $new_platform ][ $label ] ) ) {
405
- continue;
406
- }
407
-
408
- $elements[] = $label;
409
-
410
- // See which records to ignore, if any.
411
- $ignore = 0;
412
- // $ignore = $wpdb->get_results( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = %s", $meta_key ) );
413
- // See which records to update, if any.
414
- $update = $wpdb->get_results( $wpdb->prepare( "SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key = %s", $meta_key ) );
415
-
416
- // Count items in returned arrays.
417
- // phpcs:ignore Squiz.Commenting.InlineComment.InvalidEndChar
418
- // $ignore = count( (array)$ignore );
419
- $update = count( (array) $update );
420
-
421
- // Calculate update/ignore by comparison.
422
- // phpcs:ignore Squiz.Commenting.InlineComment.InvalidEndChar
423
- // $update = ( (int)$update > (int)$ignore ) ? ( (int)$update - (int)$ignore ) : 0;
424
- // update output numbers.
425
- $output->update += (int) $update;
426
- $output->ignore += (int) $ignore;
427
-
428
- }
429
-
430
- $output->elements = $elements;
431
-
432
- do_action( 'aiosp_seometa_post_meta_analyze', $output, $old_platform, $new_platform );
433
-
434
- return $output;
435
-
436
- }
437
-
438
- // phpcs:disable Squiz.Commenting.InlineComment.InvalidEndChar
439
- // define('aiosp_seometa_PLUGIN_DIR', dirname(__FILE__));
440
- // add_action( 'plugins_loaded', 'aiosp_seometa_import' );
441
- // phpcs:enable
442
- /**
443
- * Initialize the SEO Data Transporter plugin
444
- */
445
- function aiosp_seometa_import() {
446
-
447
- global $_aiosp_seometa_themes, $_aiosp_seometa_plugins, $_aiosp_seometa_platforms;
448
-
449
- /**
450
- * The associative array of supported themes.
451
- */
452
- $_aiosp_seometa_themes = array(
453
- // alphabatized.
454
- 'Builder' => array(
455
- 'Custom Doctitle' => '_builder_seo_title',
456
- 'META Description' => '_builder_seo_description',
457
- 'META Keywords' => '_builder_seo_keywords',
458
- ),
459
- 'Catalyst' => array(
460
- 'Custom Doctitle' => '_catalyst_title',
461
- 'META Description' => '_catalyst_description',
462
- 'META Keywords' => '_catalyst_keywords',
463
- 'noindex' => '_catalyst_noindex',
464
- 'nofollow' => '_catalyst_nofollow',
465
- 'noarchive' => '_catalyst_noarchive',
466
- ),
467
- 'Frugal' => array(
468
- 'Custom Doctitle' => '_title',
469
- 'META Description' => '_description',
470
- 'META Keywords' => '_keywords',
471
- 'noindex' => '_noindex',
472
- 'nofollow' => '_nofollow',
473
- ),
474
- 'Genesis' => array(
475
- 'Custom Doctitle' => '_genesis_title',
476
- 'META Description' => '_genesis_description',
477
- 'META Keywords' => '_genesis_keywords',
478
- 'noindex' => '_genesis_noindex',
479
- 'nofollow' => '_genesis_nofollow',
480
- 'noarchive' => '_genesis_noarchive',
481
- 'Canonical URI' => '_genesis_canonical_uri',
482
- 'Custom Scripts' => '_genesis_scripts',
483
- 'Redirect URI' => 'redirect',
484
- ),
485
- 'Headway' => array(
486
- 'Custom Doctitle' => '_title',
487
- 'META Description' => '_description',
488
- 'META Keywords' => '_keywords',
489
- ),
490
- 'Hybrid' => array(
491
- 'Custom Doctitle' => 'Title',
492
- 'META Description' => 'Description',
493
- 'META Keywords' => 'Keywords',
494
- ),
495
- 'Thesis 1.x' => array(
496
- 'Custom Doctitle' => 'thesis_title',
497
- 'META Description' => 'thesis_description',
498
- 'META Keywords' => 'thesis_keywords',
499
- 'Custom Scripts' => 'thesis_javascript_scripts',
500
- 'Redirect URI' => 'thesis_redirect',
501
- ),
502
-
503
- /*
504
- 'Thesis 2.x' => array(
505
- 'Custom Doctitle' => '_thesis_title_tag',
506
- 'META Description' => '_thesis_meta_description',
507
- 'META Keywords' => '_thesis_meta_keywords',
508
- 'Custom Scripts' => '_thesis_javascript_scripts',
509
- 'Canonical URI' => '_thesis_canonical_link',
510
- 'Redirect URI' => '_thesis_redirect',
511
- ),
512
- */
513
-
514
- 'WooFramework' => array(
515
- 'Custom Doctitle' => 'seo_title',
516
- 'META Description' => 'seo_description',
517
- 'META Keywords' => 'seo_keywords',
518
- ),
519
- );
520
-
521
- /**
522
- * The associative array of supported plugins.
523
- */
524
- $_aiosp_seometa_plugins = array(
525
- // alphabatized.
526
- 'Add Meta Tags' => array(
527
- 'Custom Doctitle' => '_amt_title',
528
- 'META Description' => '_amt_description',
529
- 'META Keywords' => '_amt_keywords',
530
- ),
531
- 'All in One SEO Pack' => array(
532
- 'Custom Doctitle' => '_aioseop_title',
533
- 'META Description' => '_aioseop_description',
534
- 'META Keywords' => '_aioseop_keywords',
535
- 'Canonical URI' => '_aioseop_custom_link',
536
- ),
537
- 'Greg\'s High Performance SEO' => array(
538
- 'Custom Doctitle' => '_ghpseo_secondary_title',
539
- 'META Description' => '_ghpseo_alternative_description',
540
- 'META Keywords' => '_ghpseo_keywords',
541
- ),
542
- 'Headspace2' => array(
543
- 'Custom Doctitle' => '_headspace_page_title',
544
- 'META Description' => '_headspace_description',
545
- 'META Keywords' => '_headspace_keywords',
546
- 'Custom Scripts' => '_headspace_scripts',
547
- ),
548
- 'Infinite SEO' => array(
549
- 'Custom Doctitle' => '_wds_title',
550
- 'META Description' => '_wds_metadesc',
551
- 'META Keywords' => '_wds_keywords',
552
- 'noindex' => '_wds_meta-robots-noindex',
553
- 'nofollow' => '_wds_meta-robots-nofollow',
554
- 'Canonical URI' => '_wds_canonical',
555
- 'Redirect URI' => '_wds_redirect',
556
- ),
557
- 'Jetpack' => array(
558
- 'META Description' => 'advanced_seo_description',
559
- ),
560
- 'Meta SEO Pack' => array(
561
- 'META Description' => '_msp_description',
562
- 'META Keywords' => '_msp_keywords',
563
- ),
564
- 'Platinum SEO' => array(
565
- 'Custom Doctitle' => 'title',
566
- 'META Description' => 'description',
567
- 'META Keywords' => 'keywords',
568
- ),
569
- 'Rank Math' => array(
570
- 'Custom Doctitle' => 'rank_math_title',
571
- 'META Description' => 'rank_math_description',
572
- 'Canonical URI' => 'rank_math_canonical_url',
573
- ),
574
- 'SEOpressor' => array(
575
- 'Custom Doctitle' => '_seopressor_meta_title',
576
- 'META Description' => '_seopressor_meta_description',
577
- ),
578
- 'SEO Title Tag' => array(
579
- 'Custom Doctitle' => 'title_tag',
580
- 'META Description' => 'meta_description',
581
- ),
582
- 'SEO Ultimate' => array(
583
- 'Custom Doctitle' => '_su_title',
584
- 'META Description' => '_su_description',
585
- 'META Keywords' => '_su_keywords',
586
- 'noindex' => '_su_meta_robots_noindex',
587
- 'nofollow' => '_su_meta_robots_nofollow',
588
- ),
589
- 'Yoast SEO' => array(
590
- 'Custom Doctitle' => '_yoast_wpseo_title',
591
- 'META Description' => '_yoast_wpseo_metadesc',
592
- 'META Keywords' => '_yoast_wpseo_metakeywords',
593
- 'noindex' => '_yoast_wpseo_meta-robots-noindex',
594
- 'nofollow' => '_yoast_wpseo_meta-robots-nofollow',
595
- 'Canonical URI' => '_yoast_wpseo_canonical',
596
- 'Redirect URI' => '_yoast_wpseo_redirect',
597
- ),
598
- );
599
-
600
- /**
601
- * The combined array of supported platforms.
602
- */
603
- $_aiosp_seometa_platforms = array_merge( $_aiosp_seometa_themes, $_aiosp_seometa_plugins );
604
-
605
- /**
606
- * Include the other elements of the plugin.
607
- */
608
- // phpcs:disable Squiz.Commenting.InlineComment.InvalidEndChar
609
- // require_once( aiosp_seometa_PLUGIN_DIR . '/admin.php' );
610
- // require_once( aiosp_seometa_PLUGIN_DIR . '/functions.php' );
611
- // phpcs:enable
612
- /**
613
- * Init hook.
614
- *
615
- * Hook fires after plugin functions are loaded.
616
- *
617
- * @since 0.9.10
618
- */
619
- do_action( 'aiosp_seometa_import' );
620
-
621
- }
622
-
623
- /**
624
- * Activation Hook
625
- *
626
- * @since 0.9.4
627
- */
628
- register_activation_hook( __FILE__, 'aiosp_seometa_activation_hook' );
629
- function aiosp_seometa_activation_hook() {
630
- // phpcs:ignore Squiz.Commenting.InlineComment.InvalidEndChar
631
- // require_once( aiosp_seometa_PLUGIN_DIR . '/functions.php' );
632
- aiosp_seometa_meta_key_convert( '_yoast_seo_title', 'yoast_wpseo_title', true );
633
- aiosp_seometa_meta_key_convert( '_yoast_seo_metadesc', 'yoast_wpseo_metadesc', true );
634
-
635
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
aioseop-init.php DELETED
@@ -1,9 +0,0 @@
1
- <?php
2
- /**
3
- * We'll eventually put stuff in here from the main plugin file.
4
- *
5
- * phpcs:disable Squiz.Commenting.FileComment.SpacingAfterComment
6
- *
7
- * @package All_in_One_SEO_Pack
8
- * @since 2.3.6
9
- */
 
 
 
 
 
 
 
 
 
aioseop_class.php DELETED
@@ -1,5790 +0,0 @@
1
- <?php
2
- /**
3
- * All in One SEO Pack Main Class file
4
- *
5
- * Main class file, to be broken up later.
6
- *
7
- * @package All_in_One_SEO_Pack
8
- * @since ?
9
- */
10
-
11
- /**
12
- * Module Base Class
13
- */
14
- require_once( AIOSEOP_PLUGIN_DIR . 'admin/aioseop_module_class.php' ); // Include the module base class.
15
- require_once( AIOSEOP_PLUGIN_DIR . 'inc/general/aioseop-robots-meta.php' ); // Include the module base class.
16
-
17
- /**
18
- * Class All_in_One_SEO_Pack
19
- *
20
- * The main class.
21
- */
22
- class All_in_One_SEO_Pack extends All_in_One_SEO_Pack_Module {
23
-
24
- /**
25
- * Plugin Version
26
- *
27
- * Current version of the plugin.
28
- *
29
- * @since ?
30
- *
31
- * @var string $version
32
- */
33
- var $version = AIOSEOP_VERSION;
34
-
35
- /**
36
- * Max Description Length
37
- *
38
- * Max numbers of chars in auto-generated description.
39
- *
40
- * @since ?
41
- *
42
- * @var int $maximum_description_length
43
- */
44
- var $maximum_description_length = 160;
45
-
46
- /**
47
- * Min Description Length
48
- *
49
- * Minimum number of chars an excerpt should be so that it can be used as description.
50
- *
51
- * @since ?
52
- *
53
- * @var int $minimum_description_length
54
- */
55
- var $minimum_description_length = 1;
56
-
57
- /**
58
- * OB Start Detected
59
- *
60
- * Whether output buffering is already being used during forced title rewrites.
61
- *
62
- * @since ?
63
- *
64
- * @var bool $ob_start_detected
65
- */
66
- var $ob_start_detected = false;
67
-
68
- /**
69
- * Title Start
70
- *
71
- * The start of the title text in the head section for forced title rewrites.
72
- *
73
- * @since ?
74
- *
75
- * @var int $title_start
76
- */
77
- var $title_start = - 1;
78
-
79
- /**
80
- * Title End
81
- *
82
- * The end of the title text in the head section for forced title rewrites.
83
- *
84
- * @since ?
85
- *
86
- * @var int $title_end
87
- */
88
- var $title_end = - 1;
89
-
90
- /**
91
- * Original Title
92
- *
93
- * The title before rewriting.
94
- *
95
- * @since ?
96
- *
97
- * @var string $orig_title
98
- */
99
- var $orig_title = '';
100
-
101
- /**
102
- * Log File
103
- *
104
- * Filename of log file.
105
- *
106
- * @since ?
107
- *
108
- * @var string $log_file
109
- */
110
- var $log_file;
111
-
112
- /**
113
- * Do Log
114
- *
115
- * Flag whether there should be logging.
116
- *
117
- * @since ?
118
- *
119
- * @var bool $do_log
120
- */
121
- var $do_log;
122
-
123
- /**
124
- * Usage Tracking
125
- *
126
- * Flag whether there should be usage tracking.
127
- *
128
- * @since 3.7
129
- *
130
- * @var bool $usage_tracking
131
- */
132
- var $usage_tracking;
133
-
134
- /**
135
- * Token
136
- *
137
- * @since ?
138
- * @deprecated
139
- *
140
- * @var null $token
141
- */
142
- var $token;
143
-
144
- /**
145
- * Secret
146
- *
147
- * @since ?
148
- * @deprecated
149
- *
150
- * @var null $secret
151
- */
152
- var $secret;
153
-
154
- /**
155
- * Access Token
156
- *
157
- * @since ?
158
- * @deprecated
159
- *
160
- * @var null $access_token
161
- */
162
- var $access_token;
163
-
164
- /**
165
- * GA Token
166
- *
167
- * @since ?
168
- * @deprecated
169
- *
170
- * @var null $ga_token
171
- */
172
- var $ga_token;
173
-
174
- /**
175
- * Account Cache
176
- *
177
- * @since ?
178
- * @deprecated
179
- *
180
- * @var null $account_cache
181
- */
182
- var $account_cache;
183
-
184
- /**
185
- * Profile ID
186
- *
187
- * @since ?
188
- * @deprecated
189
- *
190
- * @var null $profile_id
191
- */
192
- var $profile_id;
193
-
194
- /**
195
- * Meta Opts
196
- *
197
- * @since ?
198
- *
199
- * @var bool $meta_opts
200
- */
201
- var $meta_opts = false;
202
-
203
- /**
204
- * Is Front Page
205
- *
206
- * @since ?
207
- *
208
- * @var bool|null $is_front_page
209
- */
210
- var $is_front_page = null;
211
-
212
- /**
213
- * Constructor
214
- *
215
- * All_in_One_SEO_Pack constructor.
216
- *
217
- * @since ?
218
- * @since 2.3.14 #921 More google analytics options added.
219
- * @since 2.4.0 #1395 Longer Meta Descriptions.
220
- * @since 2.6.1 #1694 Back to shorter meta descriptions.
221
- */
222
- function __construct() {
223
- global $aioseop_options;
224
- $this->log_file = WP_CONTENT_DIR . '/all-in-one-seo-pack.log'; // PHP <5.3 compatibility, once we drop support we can use __DIR___.
225
-
226
- if ( ! empty( $aioseop_options ) && isset( $aioseop_options['aiosp_do_log'] ) && $aioseop_options['aiosp_do_log'] ) {
227
- $this->do_log = true;
228
- } else {
229
- $this->do_log = false;
230
- }
231
-
232
- if ( ! empty( $aioseop_options ) && isset( $aioseop_options['aiosp_usage_tracking'] ) && $aioseop_options['aiosp_usage_tracking'] ) {
233
- $this->usage_tracking = true;
234
- } else {
235
- $this->usage_tracking = false;
236
- }
237
-
238
- /* translators: This is a header for the General Settings menu. %s is a placeholder and is replaced with the name of the plugin. */
239
- $this->name = sprintf( __( '%s Plugin Options', 'all-in-one-seo-pack' ), AIOSEOP_PLUGIN_NAME );
240
- /* translators: This is the main menu of the plugin. */
241
- $this->menu_name = __( 'General Settings', 'all-in-one-seo-pack' );
242
-
243
- $this->prefix = 'aiosp_'; // Option prefix.
244
- $this->option_name = 'aioseop_options';
245
- $this->store_option = true;
246
- $this->file = __FILE__; // The current file.
247
- $blog_name = esc_attr( get_bloginfo( 'name' ) );
248
- parent::__construct();
249
-
250
- $this->default_options = array(
251
- 'license_key' => array(
252
- /* translators: This is a setting where users can enter their license code for All in One SEO Pack Pro. */
253
- 'name' => __( 'License Key', 'all-in-one-seo-pack' ),
254
- 'type' => 'text',
255
- ),
256
- 'home_title' => array(
257
- /* translators: This is a setting where users can enter the title for their homepage. */
258
- 'name' => __( 'Home Title', 'all-in-one-seo-pack' ),
259
- 'default' => null,
260
- 'type' => 'text',
261
- 'sanitize' => 'text',
262
- 'count' => true,
263
- 'rows' => 1,
264
- 'cols' => 60,
265
- 'condshow' => array( 'aiosp_use_static_home_info' => 0 ),
266
- ),
267
- 'home_description' => array(
268
- /* translators: This is a setting where users can enter the description for their homepage. */
269
- 'name' => __( 'Home Description', 'all-in-one-seo-pack' ),
270
- 'default' => '',
271
- 'type' => 'textarea',
272
- 'sanitize' => 'text',
273
- 'count' => true,
274
- 'cols' => 80,
275
- 'rows' => 2,
276
- 'condshow' => array( 'aiosp_use_static_home_info' => 0 ),
277
- ),
278
- 'togglekeywords' => array(
279
- /* translators: This is a setting where users can enable the use of meta keywords for their website. */
280
- 'name' => __( 'Use Keywords', 'all-in-one-seo-pack' ),
281
- 'default' => 1,
282
- 'type' => 'radio',
283
- 'initial_options' => array(
284
- /* translators: Some settings are either 'Enabled' or 'Disabled'. 'Activated' and 'Deactivated' mean the same. */
285
- 0 => __( 'Enabled', 'all-in-one-seo-pack' ),
286
- /* translators: Some settings are either 'Enabled' or 'Disabled'. 'Activated' and 'Deactivated' mean the same. */
287
- 1 => __( 'Disabled', 'all-in-one-seo-pack' ),
288
- ),
289
- ),
290
- 'home_keywords' => array(
291
- /* translators: This is a setting where users can enter meta keywords for their homepage. */
292
- 'name' => __( 'Home Keywords (comma separated)', 'all-in-one-seo-pack' ),
293
- 'default' => null,
294
- 'type' => 'textarea',
295
- 'sanitize' => 'text',
296
- 'condshow' => array(
297
- 'aiosp_togglekeywords' => 0,
298
- 'aiosp_use_static_home_info' => 0,
299
- ),
300
- ),
301
- 'use_static_home_info' => array(
302
- /* translators: This is a setting where users can indicate that they are using a static page for their homepage. */
303
- 'name' => __( 'Use Static Front Page Instead', 'all-in-one-seo-pack' ),
304
- 'default' => 0,
305
- 'type' => 'radio',
306
- 'initial_options' => array(
307
- 1 => __( 'Enabled', 'all-in-one-seo-pack' ),
308
- 0 => __( 'Disabled', 'all-in-one-seo-pack' ),
309
- ),
310
- ),
311
- 'can' => array(
312
- /* translators: This is the name of a setting. Canonical URLs help users prevent duplicate content issues - https://en.wikipedia.org/wiki/Canonical_link_element. Leave "Canonical" in English if there is no such term in your language. */
313
- 'name' => __( 'Canonical URLs', 'all-in-one-seo-pack' ),
314
- 'default' => 1,
315
- ),
316
- 'no_paged_canonical_links' => array(
317
- /* translators: This is the name of a setting. Canonical URLs help users prevent duplicate content issues - https://en.wikipedia.org/wiki/Canonical_link_element. Leave "Canonical" in English if there is no such term in your language. Enabling this setting means the plugin will use the URL of the first page as the canonical URL for all subsequent paginated pages. */
318
- 'name' => __( 'No Pagination for Canonical URLs', 'all-in-one-seo-pack' ),
319
- 'default' => 0,
320
- 'condshow' => array( 'aiosp_can' => 'on' ),
321
- ),
322
- 'force_rewrites' => array(
323
- /* translators: This is the name of a setting. Enabling this option forces the plugin to use output buffering to ensure that the title tag will be rewritten. */
324
- 'name' => __( 'Force Rewrites', 'all-in-one-seo-pack' ),
325
- 'default' => 1,
326
- 'type' => 'hidden',
327
- 'prefix' => $this->prefix,
328
- 'initial_options' => array(
329
- 1 => __( 'Enabled', 'all-in-one-seo-pack' ),
330
- 0 => __( 'Disabled', 'all-in-one-seo-pack' ),
331
- ),
332
- ),
333
- 'use_original_title' => array(
334
- /* translators: This is the name of a setting. Enabling this option forces the plugin to use the wp_title() function to fetch the title tag. */
335
- 'name' => __( 'Use Original Title', 'all-in-one-seo-pack' ),
336
- 'type' => 'radio',
337
- 'default' => 0,
338
- 'initial_options' => array(
339
- 1 => __( 'Enabled', 'all-in-one-seo-pack' ),
340
- 0 => __( 'Disabled', 'all-in-one-seo-pack' ),
341
- ),
342
- ),
343
- 'home_page_title_format' => array(
344
- /* translators: This is a setting where users can enter the title format for the homepage. The title format is the format All in One SEO Pack uses to rewrite the title tag. */
345
- 'name' => __( 'Home Page Title Format', 'all-in-one-seo-pack' ),
346
- 'type' => 'text',
347
- 'default' => '%page_title%',
348
- ),
349
- 'page_title_format' => array(
350
-
351
- /* translators: This is a setting where users can enter the title format for Pages. The title format is the format All in One SEO Pack uses to rewrite the title tag. */
352
- 'name' => __( 'Page Title Format', 'all-in-one-seo-pack' ),
353
- 'type' => 'text',
354
- 'default' => '%page_title% | %site_title%',
355
- ),
356
- 'post_title_format' => array(
357
- /* translators: This is a setting where users can enter the title format for Posts. The title format is the format All in One SEO Pack uses to rewrite the title tag. */
358
- 'name' => __( 'Post Title Format', 'all-in-one-seo-pack' ),
359
- 'type' => 'text',
360
- 'default' => '%post_title% | %site_title%',
361
- ),
362
- 'category_title_format' => array(
363
- /* translators: This is a setting where users can enter the title format for categories. The title format is the format All in One SEO Pack uses to rewrite the title tag. */
364
- 'name' => __( 'Category Title Format', 'all-in-one-seo-pack' ),
365
- 'type' => 'text',
366
- 'default' => '%category_title% | %site_title%',
367
- ),
368
- 'archive_title_format' => array(
369
- /* translators: This is a setting where users can enter the title format for archive pages. The title format is the format All in One SEO Pack uses to rewrite the title tag. */
370
- 'name' => __( 'Archive Title Format', 'all-in-one-seo-pack' ),
371
- 'type' => 'text',
372
- 'default' => '%archive_title% | %site_title%',
373
- ),
374
- 'date_title_format' => array(
375
- /* translators: This is a setting where users can enter the title format for date archive pages. The title format is the format All in One SEO Pack uses to rewrite the title tag. */
376
- 'name' => __( 'Date Archive Title Format', 'all-in-one-seo-pack' ),
377
- 'type' => 'text',
378
- 'default' => '%date% | %site_title%',
379
- ),
380
- 'author_title_format' => array(
381
- /* translators: This is a setting where users can enter the title format for author archive pages. The title format is the format All in One SEO Pack uses to rewrite the title tag. */
382
- 'name' => __( 'Author Archive Title Format', 'all-in-one-seo-pack' ),
383
- 'type' => 'text',
384
- 'default' => '%author% | %site_title%',
385
- ),
386
- 'tag_title_format' => array(
387
- /* translators: This is a setting where users can enter the title format for tag archive pages. The title format is the format All in One SEO Pack uses to rewrite the title tag. */
388
- 'name' => __( 'Tag Title Format', 'all-in-one-seo-pack' ),
389
- 'type' => 'text',
390
- 'default' => '%tag% | %site_title%',
391
- ),
392
- 'search_title_format' => array(
393
- /* translators: This is a setting where users can enter the title format for the search page. The title format is the format All in One SEO Pack uses to rewrite the title tag. */
394
- 'name' => __( 'Search Title Format', 'all-in-one-seo-pack' ),
395
- 'type' => 'text',
396
- 'default' => '%search% | %site_title%',
397
- ),
398
- 'description_format' => array(
399
- /* translators: This is a setting where users can enter the description format. The description format is the format All in One SEO Pack uses to rewrite the meta description tag. */
400
- 'name' => __( 'Description Format', 'all-in-one-seo-pack' ),
401
- 'type' => 'text',
402
- 'default' => '%description%',
403
- ),
404
- '404_title_format' => array(
405
- /* translators: This is a setting where users can enter the title format for the 404 page. The title format is the format All in One SEO Pack uses to rewrite the title tag. */
406
- 'name' => __( '404 Title Format', 'all-in-one-seo-pack' ),
407
- 'type' => 'text',
408
- 'default' => __( 'Nothing found for %request_words%', 'all-in-one-seo-pack' ),
409
- ),
410
- 'paged_format' => array(
411
- /* translators: This is a setting where users can enter the title format for paginated pages. The title format is the format All in One SEO Pack uses to rewrite the title tag. */
412
- 'name' => __( 'Paged Format', 'all-in-one-seo-pack' ),
413
- 'type' => 'text',
414
- 'default' => sprintf( ' - %s %%page%%', __( 'Part', 'all-in-one-seo-pack' ) ),
415
- ),
416
- 'cpostactive' => array(
417
- /* translators: This is a setting where users can indicate which post types they want to use All in One SEO Pack with. */
418
- 'name' => __( 'SEO on only these Content Types', 'all-in-one-seo-pack' ),
419
- 'type' => 'multicheckbox',
420
- 'default' => array( 'post', 'page', 'product' ),
421
- ),
422
- 'taxactive' => array(
423
- /* translators: This is a setting where users can indicate which taxonomies they want to use All in One SEO Pack with. */
424
- 'name' => __( 'SEO on only these taxonomies', 'all-in-one-seo-pack' ),
425
- 'type' => 'multicheckbox',
426
- 'default' => array( 'category', 'post_tag', 'product_cat', 'product_tag' ),
427
- ),
428
- 'cpostnoindex' => array(
429
- /* translators: This is a setting where users can indicate which post types they want to NOINDEX by default. NOINDEX is a value of the HTML robots meta tag that asks search engines not to index the page. */
430
- 'name' => __( 'Default to NOINDEX', 'all-in-one-seo-pack' ),
431
- 'type' => 'multicheckbox',
432
- 'default' => array(),
433
- ),
434
- 'cpostnofollow' => array(
435
- /* translators: This is a setting where users can indicate which post types they want to NOFOLLOW by default. NOFOLLOW is a value of the HTML robots meta tag that asks search engines not to follow any links on the page. */
436
- 'name' => __( 'Default to NOFOLLOW', 'all-in-one-seo-pack' ),
437
- 'type' => 'multicheckbox',
438
- 'default' => array(),
439
- ),
440
- 'posttypecolumns' => array(
441
- /* translators: This is a setting where users can indicate for which post types they want to enable columns. Columns are added to the All Posts, All Pages, etc. list pages and allow users to quick-edit their title and description - https://semperplugins.com/documentation/display-settings/#show-column-labels-for-custom-post-types. */
442
- 'name' => __( 'Show Column Labels for Custom Post Types', 'all-in-one-seo-pack' ),
443
- 'type' => 'multicheckbox',
444
- 'default' => array( 'post', 'page' ),
445
- ),
446
- 'google_verify' => array(
447
- 'name' => 'Google Search Console',
448
- 'default' => '',
449
- 'type' => 'text',
450
- ),
451
- 'bing_verify' => array(
452
- 'name' => 'Bing Webmaster Tools',
453
- 'default' => '',
454
- 'type' => 'text',
455
- ),
456
- 'pinterest_verify' => array(
457
- /* translators: This is a setting where users can add their Pinterest website verification code. */
458
- 'name' => __( 'Pinterest Site Verification', 'all-in-one-seo-pack' ),
459
- 'default' => '',
460
- 'type' => 'text',
461
- ),
462
- 'yandex_verify' => array(
463
- 'name' => 'Yandex Webmaster Tools',
464
- 'default' => '',
465
- 'type' => 'text',
466
- ),
467
- 'baidu_verify' => array(
468
- 'name' => 'Baidu Webmaster Tools',
469
- 'default' => '',
470
- 'type' => 'text',
471
- ),
472
- 'google_analytics_id' => array(
473
- /* translators: This is a setting where users can add their Google Analytics verification code. Leave this in English if there is no translation for "Google Analytics". */
474
- 'name' => __( 'Google Analytics ID', 'all-in-one-seo-pack' ),
475
- 'default' => null,
476
- 'type' => 'text',
477
- 'placeholder' => 'UA-########-#',
478
- ),
479
- 'ga_advanced_options' => array(
480
- /* translators: This is a setting users can enable to display more advanced options for Google Analytics. */
481
- 'name' => __( 'Advanced Analytics Options', 'all-in-one-seo-pack' ),
482
- 'default' => 'on',
483
- 'type' => 'radio',
484
- 'initial_options' => array(
485
- 'on' => __( 'Enabled', 'all-in-one-seo-pack' ),
486
- 0 => __( 'Disabled', 'all-in-one-seo-pack' ),
487
- ),
488
- 'condshow' => array(
489
- 'aiosp_google_analytics_id' => array(
490
- 'lhs' => 'aiosp_google_analytics_id',
491
- 'op' => '!=',
492
- 'rhs' => '',
493
- ),
494
- ),
495
- ),
496
- 'ga_domain' => array(
497
- /* translators: This is a setting which allows users to set the cookie domain for their Google Analytics tracking code. */
498
- 'name' => __( 'Tracking Domain', 'all-in-one-seo-pack' ),
499
- 'type' => 'text',
500
- 'condshow' => array(
501
- 'aiosp_google_analytics_id' => array(
502
- 'lhs' => 'aiosp_google_analytics_id',
503
- 'op' => '!=',
504
- 'rhs' => '',
505
- ),
506
- 'aiosp_ga_advanced_options' => 'on',
507
- ),
508
- ),
509
- 'ga_multi_domain' => array(
510
- /* translators: This is a setting which allows users to enable Google Analytics tracking for multiple domain names. */
511
- 'name' => __( 'Track Multiple Domains', 'all-in-one-seo-pack' ),
512
- 'default' => 0,
513
- 'condshow' => array(
514
- 'aiosp_google_analytics_id' => array(
515
- 'lhs' => 'aiosp_google_analytics_id',
516
- 'op' => '!=',
517
- 'rhs' => '',
518
- ),
519
- 'aiosp_ga_advanced_options' => 'on',
520
- ),
521
- ),
522
- 'ga_addl_domains' => array(
523
- /* translators: This is a setting which allows users to enter additional domain names used for Google Analytics cross-domain tracking - https://support.google.com/analytics/answer/1034342?hl=en.*/
524
- 'name' => __( 'Additional Domains', 'all-in-one-seo-pack' ),
525
- 'type' => 'textarea',
526
- 'condshow' => array(
527
- 'aiosp_google_analytics_id' => array(
528
- 'lhs' => 'aiosp_google_analytics_id',
529
- 'op' => '!=',
530
- 'rhs' => '',
531
- ),
532
- 'aiosp_ga_advanced_options' => 'on',
533
- 'aiosp_ga_multi_domain' => 'on',
534
- ),
535
- ),
536
- 'ga_anonymize_ip' => array(
537
- /* translators: This is a setting which tells Google Analytics not to track and store the IP addresses of website visitors. This is required to be compliant with the GDPR for example. */
538
- 'name' => __( 'Anonymize IP Addresses', 'all-in-one-seo-pack' ),
539
- 'type' => 'checkbox',
540
- 'condshow' => array(
541
- 'aiosp_google_analytics_id' => array(
542
- 'lhs' => 'aiosp_google_analytics_id',
543
- 'op' => '!=',
544
- 'rhs' => '',
545
- ),
546
- 'aiosp_ga_advanced_options' => 'on',
547
- ),
548
- ),
549
- 'ga_display_advertising' => array(
550
- /* translators: This is a setting that enables a collection of Google Analytics features so you can, for example, create segments based on demographic and interest data. */
551
- 'name' => __( 'Display Advertiser Tracking', 'all-in-one-seo-pack' ),
552
- 'type' => 'checkbox',
553
- 'condshow' => array(
554
- 'aiosp_google_analytics_id' => array(
555
- 'lhs' => 'aiosp_google_analytics_id',
556
- 'op' => '!=',
557
- 'rhs' => '',
558
- ),
559
- 'aiosp_ga_advanced_options' => 'on',
560
- ),
561
- ),
562
- 'ga_exclude_users' => array(
563
- /* translators: This is a setting that allows you to exclude certain WordPress user roles, e.g. Administrators, from Google Analytics tracking. */
564
- 'name' => __( 'Exclude Users From Tracking', 'all-in-one-seo-pack' ),
565
- 'type' => 'multicheckbox',
566
- 'condshow' => array(
567
- 'aiosp_google_analytics_id' => array(
568
- 'lhs' => 'aiosp_google_analytics_id',
569
- 'op' => '!=',
570
- 'rhs' => '',
571
- ),
572
- 'aiosp_ga_advanced_options' => 'on',
573
- ),
574
- ),
575
- 'ga_track_outbound_links' => array(
576
- /* translators: This is a setting that enables tracking of outbound/external links by Google Analytics. */
577
- 'name' => __( 'Track Outbound Links', 'all-in-one-seo-pack' ),
578
- 'default' => 0,
579
- 'condshow' => array(
580
- 'aiosp_google_analytics_id' => array(
581
- 'lhs' => 'aiosp_google_analytics_id',
582
- 'op' => '!=',
583
- 'rhs' => '',
584
- ),
585
- 'aiosp_ga_advanced_options' => 'on',
586
- ),
587
- ),
588
- 'ga_link_attribution' => array(
589
- /* translators: This is a setting for Google Analytics that allows you to tag your pages to implement enhanced link-tracking. */
590
- 'name' => __( 'Enhanced Link Attribution', 'all-in-one-seo-pack' ),
591
- 'default' => 0,
592
- 'condshow' => array(
593
- 'aiosp_google_analytics_id' => array(
594
- 'lhs' => 'aiosp_google_analytics_id',
595
- 'op' => '!=',
596
- 'rhs' => '',
597
- ),
598
- 'aiosp_ga_advanced_options' => 'on',
599
- ),
600
- ),
601
- 'ga_enhanced_ecommerce' => array(
602
- /* translators: This is a setting which tells Google Analytics to track your customers' path to purchase on your e-commerce website. */
603
- 'name' => __( 'Enhanced Ecommerce', 'all-in-one-seo-pack' ),
604
- 'default' => 0,
605
- 'condshow' => array(
606
- 'aiosp_google_analytics_id' => array(
607
- 'lhs' => 'aiosp_google_analytics_id',
608
- 'op' => '!=',
609
- 'rhs' => '',
610
- ),
611
- 'aiosp_ga_advanced_options' => 'on',
612
- ),
613
- ),
614
- 'schema_markup' => array(
615
- /* translators: This is a setting that outputs basic Schema.org markup, also known as structured data, into the source code of each page. */
616
- 'name' => __( 'Use Schema.org Markup', 'all-in-one-seo-pack' ),
617
- 'type' => 'radio',
618
- 'default' => 1,
619
- 'initial_options' => array(
620
- 1 => __( 'Enabled', 'all-in-one-seo-pack' ),
621
- 0 => __( 'Disabled', 'all-in-one-seo-pack' ),
622
- ),
623
- ),
624
- // TODO Change `schema_search_results_page` to `schema_add_search_results_page`. Requires modifying double arrow alignment.
625
- 'schema_search_results_page' => array(
626
- /* translators: This is a setting users can enable to add the basic markup code to their source code that is needed for Google to generate a Sitelinks Search Box - https://developers.google.com/search/docs/data-types/sitelinks-searchbox.*/
627
- 'name' => __( 'Display Sitelinks Search Box', 'all-in-one-seo-pack' ),
628
- 'condshow' => array(
629
- 'aiosp_schema_markup' => 1,
630
- ),
631
- ),
632
- 'schema_social_profile_links' => array(
633
- /* translators: This is a setting where users can add links to their social media profiles. These are then output as schema.org markup. */
634
- 'name' => __( 'Social Profile Links', 'all-in-one-seo-pack' ),
635
- 'type' => 'textarea',
636
- 'cols' => 60,
637
- 'rows' => 5,
638
- 'condshow' => array(
639
- 'aiosp_schema_markup' => 1,
640
- ),
641
- ),
642
- 'schema_site_represents' => array(
643
- /* translators: This is a setting where users can indicate whether their website represents a person or organization. This is used for our schema.org markup. */
644
- 'name' => __( 'Person or Organization', 'all-in-one-seo-pack' ),
645
- 'type' => 'radio',
646
- 'default' => 'organization',
647
- 'initial_options' => array(
648
- 'organization' => __( 'Organization', 'all-in-one-seo-pack' ),
649
- 'person' => __( 'Person', 'all-in-one-seo-pack' ),
650
- ),
651
- 'condshow' => array(
652
- 'aiosp_schema_markup' => 1,
653
- ),
654
- ),
655
- 'schema_organization_name' => array(
656
- /* translators: This is a setting where users can enter the name of their organization. This is used for our schema.org markup. */
657
- 'name' => __( 'Organization Name', 'all-in-one-seo-pack' ),
658
- 'type' => 'text',
659
- 'default' => '',
660
- 'condshow' => array(
661
- 'aiosp_schema_markup' => 1,
662
- 'aiosp_schema_site_represents' => 'organization',
663
- ),
664
- ),
665
- 'schema_organization_logo' => array(
666
- /* translators: This is a setting where users can upload and select a logo for their organization. This is used for our schema.org markup. */
667
- 'name' => __( 'Organization Logo', 'all-in-one-seo-pack' ),
668
- 'type' => 'image',
669
- 'condshow' => array(
670
- 'aiosp_schema_markup' => 1,
671
- 'aiosp_schema_site_represents' => 'organization',
672
- ),
673
- ),
674
-
675
- 'schema_person_user' => array(
676
- /* translators: This is a dropdown setting where users can select the username of the person that the website is for. The profile from that user is then used for our schema.org markup.*/
677
- 'name' => __( 'Person\'s Username', 'all-in-one-seo-pack' ),
678
- 'type' => 'select',
679
- 'default' => 1,
680
- 'condshow' => array(
681
- 'aiosp_schema_markup' => 1,
682
- 'aiosp_schema_site_represents' => 'person',
683
- ),
684
- // Add initial options below.
685
- ),
686
- 'schema_person_manual_name' => array(
687
- /* translators: Option shown when 'Manually Enter' is selected in Person's Username. Users use this to enter the Person's name for schema Person. */
688
- 'name' => __( 'Person\'s Name', 'all-in-one-seo-pack' ),
689
- 'type' => 'text',
690
- 'condshow' => array(
691
- 'aiosp_schema_markup' => 1,
692
- 'aiosp_schema_site_represents' => 'person',
693
- 'aiosp_schema_person_user' => '-1',
694
- ),
695
- ),
696
- 'schema_person_manual_image' => array(
697
- /* translators: Option shown when 'Manually Enter' is selected in Person's Username. Users use this to enter the Person's image for schema Person. */
698
- 'name' => __( 'Person\'s Image', 'all-in-one-seo-pack' ),
699
- 'type' => 'image',
700
- 'condshow' => array(
701
- 'aiosp_schema_markup' => 1,
702
- 'aiosp_schema_site_represents' => 'person',
703
- 'aiosp_schema_person_user' => '-1',
704
- ),
705
- ),
706
- 'schema_phone_number' => array(
707
- /* translators: This is a setting where users can enter a phone number for their organization. This is used for our schema.org markup. */
708
- 'name' => __( 'Phone Number', 'all-in-one-seo-pack' ),
709
- 'type' => 'tel',
710
- 'autocomplete' => 'off',
711
- 'condshow' => array(
712
- 'aiosp_schema_markup' => 1,
713
- 'aiosp_schema_site_represents' => 'organization',
714
- ),
715
- ),
716
- 'schema_contact_type' => array(
717
- /* translators: This is a setting where users have to indicate what contact/department their phone number connects to (e.g. "Sales" or "Customer Support"). This is used for our schema.org markup. */
718
- 'name' => __( 'Type of Contact', 'all-in-one-seo-pack' ),
719
- 'type' => 'select',
720
- 'condshow' => array(
721
- 'aiosp_schema_markup' => 1,
722
- 'aiosp_schema_site_represents' => 'organization',
723
- ),
724
- 'initial_options' => array(
725
- /* translators: This is the placeholder we use in one of our dropdowns when no value has been selected yet. */
726
- 'none' => __( '-- Select --', 'all-in-one-seo-pack' ),
727
- 'customer support' => __( 'Customer Support', 'all-in-one-seo-pack' ),
728
- 'tech support' => __( 'Technical Support', 'all-in-one-seo-pack' ),
729
- /* translators: This is the support department of a business that handles all billing related enquiries. */
730
- 'billing support' => __( 'Billing Support', 'all-in-one-seo-pack' ),
731
- /* translators: This is the department of a business that handles payments of bills. */
732
- 'bill payment' => __( 'Bill Payment', 'all-in-one-seo-pack' ),
733
- 'sales' => __( 'Sales', 'all-in-one-seo-pack' ),
734
- 'reservations' => __( 'Reservations', 'all-in-one-seo-pack' ),
735
- 'credit card support' => __( 'Credit Card Support', 'all-in-one-seo-pack' ),
736
- 'emergency' => __( 'Emergency', 'all-in-one-seo-pack' ),
737
- /* translators: This is the department that handles baggage enquiries when e.g. baggage is lost or missing. */
738
- 'baggage tracking' => __( 'Baggage Tracking', 'all-in-one-seo-pack' ),
739
- 'roadside assistance' => __( 'Roadside Assistance', 'all-in-one-seo-pack' ),
740
- /* translators: This refers to the department of a package courier that handles enquiries when e.g. a package has not been delivered or is missing. */
741
- 'package tracking' => __( 'Package Tracking', 'all-in-one-seo-pack' ),
742
- ),
743
- ),
744
- 'use_categories' => array(
745
- /* translators: This is the name of a setting. By enabling it, the plugin will use the categories of the relevant post as meta keywords in addition to any user-specified keywords. */
746
- 'name' => __( 'Use Categories for META keywords', 'all-in-one-seo-pack' ),
747
- 'default' => 0,
748
- 'condshow' => array( 'aiosp_togglekeywords' => 0 ),
749
- ),
750
- 'use_tags_as_keywords' => array(
751
- /* translators: This is the name of a setting. By enabling it, the plugin will use the tags of the relevant post as meta keywords in addition to any user-specified keywords. */
752
- 'name' => __( 'Use Tags for META keywords', 'all-in-one-seo-pack' ),
753
- 'default' => 1,
754
- 'condshow' => array( 'aiosp_togglekeywords' => 0 ),
755
- ),
756
- 'dynamic_postspage_keywords' => array(
757
- /* translators: This a setting that allows you to dynamically output meta keywords on archive pages based on the keywords from the posts that are displayed by the archive page. */
758
- 'name' => __( 'Dynamically Generate Keywords for Posts Page/Archives', 'all-in-one-seo-pack' ),
759
- 'default' => 1,
760
- 'condshow' => array( 'aiosp_togglekeywords' => 0 ),
761
- ),
762
- 'category_noindex' => array(
763
- /* translators: This is a global setting that allows you to NOINDEX all your categories. */
764
- 'name' => __( 'Use noindex for Categories', 'all-in-one-seo-pack' ),
765
- 'default' => 1,
766
- ),
767
- 'archive_date_noindex' => array(
768
- /* translators: This is a global setting that allows you to NOINDEX all your date archive pages. */
769
- 'name' => __( 'Use noindex for Date Archives', 'all-in-one-seo-pack' ),
770
- 'default' => 1,
771
- ),
772
- 'archive_author_noindex' => array(
773
- /* translators: This is a global setting that allows you to NOINDEX all your author archive pages. */
774
- 'name' => __( 'Use noindex for Author Archives', 'all-in-one-seo-pack' ),
775
- 'default' => 1,
776
- ),
777
- 'tags_noindex' => array(
778
- /* translators: This is a global setting that allows you to NOINDEX all your tag archive pages. */
779
- 'name' => __( 'Use noindex for Tag Archives', 'all-in-one-seo-pack' ),
780
- 'default' => 0,
781
- ),
782
- 'search_noindex' => array(
783
- /* translators: This is a setting that allows you to NOINDEX your search results page. */
784
- 'name' => __( 'Use noindex for the Search page', 'all-in-one-seo-pack' ),
785
- 'default' => 0,
786
- ),
787
- '404_noindex' => array(
788
- /* translators: This is a setting that allows you to NOINDEX your 404 Not Found page. */
789
- 'name' => __( 'Use noindex for the 404 page', 'all-in-one-seo-pack' ),
790
- 'default' => 0,
791
- ),
792
- 'tax_noindex' => array(
793
- /* translators: This is a global setting that allows you to NOINDEX specific taxonomies. */
794
- 'name' => __( 'Use noindex for Taxonomy Archives', 'all-in-one-seo-pack' ),
795
- 'type' => 'multicheckbox',
796
- 'default' => array(),
797
- ),
798
- 'paginated_noindex' => array(
799
- /* translators: This is a global setting that allows you to NOINDEX all your paginated content (page 2 and higher). */
800
- 'name' => __( 'Use noindex for paginated pages/posts', 'all-in-one-seo-pack' ),
801
- 'default' => 0,
802
- ),
803
- 'paginated_nofollow' => array(
804
- /* translators: This is a global setting that allows you to NOFOLLOW all your paginated content. */
805
- 'name' => __( 'Use nofollow for paginated pages/posts', 'all-in-one-seo-pack' ),
806
- 'default' => 0,
807
- ),
808
- 'generate_descriptions' => array(
809
- /* translators: This is a setting that allows the plugin to automatically populate the meta description tag based on the excerpt or content of the post/page.*/
810
- 'name' => __( 'Autogenerate Descriptions', 'all-in-one-seo-pack' ),
811
- 'default' => 0,
812
- ),
813
- 'skip_excerpt' => array(
814
- /* translators: This is the name of a setting. By enabling it, the plugin will use the content of the post/page to automatically populate the meta description tag, instead of the excerpt. */
815
- 'name' => __( 'Use Content For Autogenerated Descriptions', 'all-in-one-seo-pack' ),
816
- 'default' => 0,
817
- 'condshow' => array( 'aiosp_generate_descriptions' => 'on' ),
818
- ),
819
- 'run_shortcodes' => array(
820
- /* translators: This is a setting that enables the plugin to execute shortcodes in the autogenerated descriptions. Shortcodes allow people to execute code inside WordPress posts, pages, and widgets without writing any code directly. */
821
- 'name' => __( 'Run Shortcodes In Autogenerated Descriptions', 'all-in-one-seo-pack' ),
822
- 'default' => 0,
823
- 'condshow' => array( 'aiosp_generate_descriptions' => 'on' ),
824
- ),
825
- 'hide_paginated_descriptions' => array(
826
- /* translators: This is a setting that, if enabled, removes the meta description for paginated content (page 2 and higher). */
827
- 'name' => __( 'Remove Descriptions For Paginated Pages', 'all-in-one-seo-pack' ),
828
- 'default' => 0,
829
- ),
830
- 'dont_truncate_descriptions' => array(
831
- /* translators: This is a setting that makes sure the plugin does not truncate the meta description tag if it is longer than what All in One SEO Pack recommends. */
832
- 'name' => __( 'Never Shorten Long Descriptions', 'all-in-one-seo-pack' ),
833
- 'default' => 0,
834
- ),
835
- 'redirect_attachement_parent' => array(
836
- /* translators: This is the name of a setting. By enabling it, the plugin will redirect attachment page requests to the post parent, or in other words, the post/page where the media is embedded. */
837
- 'name' => __( 'Redirect Attachments to Post Parent', 'all-in-one-seo-pack' ),
838
- 'default' => 0,
839
- ),
840
- 'ex_pages' => array(
841
- /* translators: This is a textarea setting where users can enter a list of pages that All in One SEO Pack should not affect. */
842
- 'name' => __( 'Exclude Pages', 'all-in-one-seo-pack' ),
843
- 'type' => 'textarea',
844
- 'default' => '',
845
- ),
846
- 'do_log' => array(
847
- /* translators: This is a setting that enables All in One SEO Pack to log important events to help with debugging. */
848
- 'name' => __( 'Log important events', 'all-in-one-seo-pack' ),
849
- 'default' => null,
850
- ),
851
- 'rss_content_before' => array(
852
- 'name' => __( 'Before Your Content', 'all-in-one-seo-pack' ),
853
- 'type' => 'textarea',
854
- 'rows' => 2,
855
- ),
856
- 'rss_content_after' => array(
857
- 'name' => __( 'After Your Content', 'all-in-one-seo-pack' ),
858
- 'type' => 'textarea',
859
- 'rows' => 2,
860
- 'default' => sprintf(
861
- /* translators: 1 - The post title, 2 - The site title. */
862
- __( 'The post %1$s first appeared on %2$s.', 'all-in-one-seo-pack' ),
863
- '%post_link%',
864
- '%site_link%'
865
- )
866
- ),
867
-
868
- 'usage_tracking' => array(
869
- 'name' => __( 'Allow Usage Tracking', 'all-in-one-seo-pack' ),
870
- 'default' => null,
871
- ),
872
- );
873
-
874
- if ( ! AIOSEOPPRO ) {
875
- unset( $this->default_options['taxactive'] );
876
- } else {
877
- unset( $this->default_options['usage_tracking'] );
878
- }
879
-
880
- $this->locations = array(
881
- 'default' => array(
882
- 'name' => $this->name,
883
- 'prefix' => 'aiosp_',
884
- 'type' => 'settings',
885
- 'options' => null,
886
- ),
887
- 'aiosp' => array(
888
- 'name' => $this->plugin_name,
889
- 'type' => 'metabox',
890
- 'prefix' => '',
891
- 'help_link' => 'https://semperplugins.com/documentation/post-settings/',
892
- 'options' => array(
893
- 'edit',
894
- 'nonce-aioseop-edit',
895
- 'snippet',
896
- 'title',
897
- 'description',
898
- 'keywords',
899
- 'custom_link',
900
- 'noindex',
901
- 'nofollow',
902
- 'sitemap_exclude',
903
- 'sitemap_priority',
904
- 'sitemap_frequency',
905
- 'disable',
906
- 'disable_analytics',
907
- ),
908
- 'default_options' => array(
909
- 'edit' => array(
910
- 'type' => 'hidden',
911
- 'default' => 'aiosp_edit',
912
- 'prefix' => true,
913
- 'nowrap' => 1,
914
- ),
915
- 'nonce-aioseop-edit' => array(
916
- 'type' => 'hidden',
917
- 'default' => null,
918
- 'prefix' => false,
919
- 'nowrap' => 1,
920
- ),
921
- 'upgrade' => array(
922
- 'type' => 'html',
923
- 'label' => 'none',
924
- 'default' => sprintf(
925
- '<a href="%1$s" target="_blank" title="%2$s" class="aioseop-metabox-pro-cta">%3$s</a>',
926
- aioseop_get_utm_url( 'metabox-main' ),
927
- sprintf(
928
- /* translators: %s: "All in One SEO Pack Pro". */
929
- __( 'Upgrade to %s', 'all-in-one-seo-pack' ),
930
- AIOSEOP_PLUGIN_NAME . '&nbsp;Pro'
931
- ),
932
- __( 'UPGRADE TO PRO VERSION', 'all-in-one-seo-pack' )
933
- ),
934
- ),
935
- 'snippet' => array(
936
- /* translators: The preview snippet shows how the page will look like in the search results (title, meta description and permalink). */
937
- 'name' => __( 'Preview Snippet', 'all-in-one-seo-pack' ),
938
- 'type' => 'custom',
939
- 'label' => 'top',
940
- 'default' => '<div class="preview_snippet"><div id="aioseop_snippet"><h3><a>%s</a></h3><div><div><cite id="aioseop_snippet_link">%s</cite></div><span id="aioseop_snippet_description">%s</span></div></div></div>',
941
- ),
942
- 'title' => array(
943
- 'name' => __( 'Title', 'all-in-one-seo-pack' ),
944
- 'type' => 'text',
945
- 'count' => true,
946
- 'size' => 60,
947
- ),
948
- 'description' => array(
949
- 'name' => __( 'Description', 'all-in-one-seo-pack' ),
950
- 'type' => 'textarea',
951
- 'count' => true,
952
- 'cols' => 80,
953
- 'rows' => 2,
954
- ),
955
-
956
- 'keywords' => array(
957
- 'name' => __( 'Keywords (comma separated)', 'all-in-one-seo-pack' ),
958
- 'type' => 'text',
959
- ),
960
- 'custom_link' => array(
961
- /* translators: This is a setting that users can enable to enter a custom canonical URL. */
962
- 'name' => __( 'Custom Canonical URL', 'all-in-one-seo-pack' ),
963
- 'type' => 'text',
964
- 'size' => 60,
965
- ),
966
- 'noindex' => array(
967
- /* translators: This is a setting that allows users to add the NOINDEX robots meta tag value to the current post/page. */
968
- 'name' => __( 'NOINDEX this page/post', 'all-in-one-seo-pack' ),
969
- 'default' => '',
970
-
971
- ),
972
- 'nofollow' => array(
973
- /* translators: This is a setting that allows users to add the NOFOLLOW robots meta tag value to the current post/page. */
974
- 'name' => __( 'NOFOLLOW this page/post', 'all-in-one-seo-pack' ),
975
- 'default' => '',
976
- ),
977
- 'sitemap_exclude' => array(
978
- 'name' => __( 'Exclude From Sitemap', 'all-in-one-seo-pack' ),
979
- 'condshow' => array(
980
- 'aiosp_noindex' => array(
981
- 'lhs' => 'aiosp_noindex',
982
- 'op' => '!=',
983
- 'rhs' => 'on',
984
- ),
985
- ),
986
- ),
987
- 'sitemap_priority' => array(
988
- /* translators: This is a setting that allows users to override the global sitemap priority value for a given post/term. */
989
- 'name' => __( 'Sitemap Priority', 'all-in-one-seo-pack' ),
990
- 'type' => 'select',
991
- 'condshow' => array(
992
- 'aiosp_noindex' => array(
993
- 'lhs' => 'aiosp_noindex',
994
- 'op' => '!=',
995
- 'rhs' => 'on',
996
- ),
997
- 'aiosp_sitemap_exclude' => array(
998
- 'lhs' => 'aiosp_sitemap_exclude',
999
- 'op' => '!=',
1000
- 'rhs' => 'on',
1001
- ),
1002
- ),
1003
- 'initial_options' => array(
1004
- '' => __( 'Do Not Override', 'all-in-one-seo-pack' ),
1005
- '0.1' => '10%',
1006
- '0.2' => '20%',
1007
- '0.3' => '30%',
1008
- '0.4' => '40%',
1009
- '0.5' => '50%',
1010
- '0.6' => '60%',
1011
- '0.7' => '70%',
1012
- '0.8' => '80%',
1013
- '0.9' => '90%',
1014
- '1.0' => '100%',
1015
- ),
1016
- ),
1017
- 'sitemap_frequency' => array(
1018
- /* translators: This is a setting that allows users to override the global sitemap frequency value for a given post/term. */
1019
- 'name' => __( 'Sitemap Frequency', 'all-in-one-seo-pack' ),
1020
- 'type' => 'select',
1021
- 'condshow' => array(
1022
- 'aiosp_noindex' => array(
1023
- 'lhs' => 'aiosp_noindex',
1024
- 'op' => '!=',
1025
- 'rhs' => 'on',
1026
- ),
1027
- 'aiosp_sitemap_exclude' => array(
1028
- 'lhs' => 'aiosp_sitemap_exclude',
1029
- 'op' => '!=',
1030
- 'rhs' => 'on',
1031
- ),
1032
- ),
1033
- 'initial_options' => array(
1034
- '' => __( 'Do Not Override', 'all-in-one-seo-pack' ),
1035
- 'always' => __( 'Always', 'all-in-one-seo-pack' ),
1036
- 'hourly' => __( 'Hourly', 'all-in-one-seo-pack' ),
1037
- 'daily' => __( 'Daily', 'all-in-one-seo-pack' ),
1038
- 'weekly' => __( 'Weekly', 'all-in-one-seo-pack' ),
1039
- 'monthly' => __( 'Monthly', 'all-in-one-seo-pack' ),
1040
- 'yearly' => __( 'Yearly', 'all-in-one-seo-pack' ),
1041
- 'never' => __( 'Never', 'all-in-one-seo-pack' ),
1042
- ),
1043
- ),
1044
- /* translators: This is a setting that allows users to disable All in One SEO Pack for the current post/page. */
1045
- 'disable' => array( 'name' => __( 'Disable on this page/post', 'all-in-one-seo-pack' ) ),
1046
- /* translators: This is a setting that allows users to exclude the current post/page from the sitemap. */
1047
- 'disable_analytics' => array(
1048
- /* translators: This is a setting that allows users to disable Google Analytics tracking for the current post/page. */
1049
- 'name' => __( 'Disable Google Analytics', 'all-in-one-seo-pack' ),
1050
- 'condshow' => array( 'aiosp_disable' => 'on' ),
1051
- ),
1052
- ),
1053
- // #1067: if SEO is disabled and an empty array is passed below, it will be overridden. So let's pass a post type that cannot possibly exist.
1054
- 'display' => ! empty( $aioseop_options['aiosp_cpostactive'] ) ? array( $aioseop_options['aiosp_cpostactive'] ) : array( '___null___' ),
1055
- ),
1056
- );
1057
-
1058
- if ( ! AIOSEOPPRO ) {
1059
- array_unshift( $this->locations['aiosp']['options'], 'upgrade' );
1060
- $this->locations['aiosp']['default_options']['sitemap_priority']['disabled'] = 'disabled';
1061
- $this->locations['aiosp']['default_options']['sitemap_frequency']['disabled'] = 'disabled';
1062
- }
1063
-
1064
- $this->layout = array(
1065
- 'default' => array(
1066
- /* translators: This is the name of the main menu. */
1067
- 'name' => __( 'General Settings', 'all-in-one-seo-pack' ),
1068
- 'help_link' => 'https://semperplugins.com/documentation/general-settings/',
1069
- 'options' => array(), // This is set below, to the remaining options -- pdb.
1070
- ),
1071
- 'home' => array(
1072
- 'name' => __( 'Home Page Settings', 'all-in-one-seo-pack' ),
1073
- 'help_link' => 'https://semperplugins.com/documentation/home-page-settings/',
1074
- 'options' => array( 'home_title', 'home_description', 'home_keywords', 'use_static_home_info' ),
1075
- ),
1076
- 'title' => array(
1077
- 'name' => __( 'Title Settings', 'all-in-one-seo-pack' ),
1078
- 'help_link' => 'https://semperplugins.com/documentation/title-settings/',
1079
- 'options' => array(
1080
- 'force_rewrites',
1081
- 'home_page_title_format',
1082
- 'page_title_format',
1083
- 'post_title_format',
1084
- 'category_title_format',
1085
- 'archive_title_format',
1086
- 'date_title_format',
1087
- 'author_title_format',
1088
- 'tag_title_format',
1089
- 'search_title_format',
1090
- 'description_format',
1091
- '404_title_format',
1092
- 'paged_format',
1093
- ),
1094
- ),
1095
- 'cpt' => array(
1096
- /* translators: This is the name of a settings section where users can indicate which post types and taxonomies they want to use All in One SEO Pack with. */
1097
- 'name' => __( 'Content Type Settings', 'all-in-one-seo-pack' ),
1098
- 'help_link' => 'https://semperplugins.com/documentation/custom-post-type-settings/',
1099
- 'options' => array( 'taxactive', 'cpostactive' ),
1100
- ),
1101
- 'display' => array(
1102
- /* translators: This is the name of a settings section where users can control how All in One SEO Pack appears in the WordPress Administrator Panel. */
1103
- 'name' => __( 'Display Settings', 'all-in-one-seo-pack' ),
1104
- 'help_link' => 'https://semperplugins.com/documentation/display-settings/',
1105
- 'options' => array( 'posttypecolumns' ),
1106
- ),
1107
- 'webmaster' => array(
1108
- /* translators: This is the name of a settings section where users can add verification codes of webmaster platforms such as Google Search Console, Bing Webmaster Tools, etc. */
1109
- 'name' => __( 'Webmaster Verification', 'all-in-one-seo-pack' ),
1110
- 'help_link' => 'https://semperplugins.com/sections/webmaster-verification/',
1111
- 'options' => array( 'google_verify', 'bing_verify', 'pinterest_verify', 'yandex_verify', 'baidu_verify' ),
1112
- ),
1113
- 'google' => array(
1114
- 'name' => __( 'Google Analytics', 'all-in-one-seo-pack' ),
1115
- 'help_link' => 'https://semperplugins.com/documentation/advanced-google-analytics-settings/',
1116
- 'options' => array(
1117
- 'google_analytics_id',
1118
- 'ga_advanced_options',
1119
- 'ga_domain',
1120
- 'ga_multi_domain',
1121
- 'ga_addl_domains',
1122
- 'ga_anonymize_ip',
1123
- 'ga_display_advertising',
1124
- 'ga_exclude_users',
1125
- 'ga_track_outbound_links',
1126
- 'ga_link_attribution',
1127
- 'ga_enhanced_ecommerce',
1128
- ),
1129
- ),
1130
- 'schema' => array(
1131
- 'name' => __( 'Schema Settings', 'all-in-one-seo-pack' ),
1132
- 'help_link' => 'https://semperplugins.com/documentation/schema-settings/',
1133
- 'options' => array(
1134
- 'schema_markup',
1135
- 'schema_search_results_page',
1136
- 'schema_social_profile_links',
1137
- 'schema_site_represents',
1138
- 'schema_organization_name',
1139
- 'schema_organization_logo',
1140
- 'schema_person_user',
1141
- 'schema_person_manual_name',
1142
- 'schema_person_manual_image',
1143
- 'schema_phone_number',
1144
- 'schema_contact_type',
1145
- ),
1146
- ),
1147
- 'noindex' => array(
1148
- 'name' => __( 'Noindex Settings', 'all-in-one-seo-pack' ),
1149
- 'help_link' => 'https://semperplugins.com/documentation/noindex-settings/',
1150
- 'options' => array(
1151
- 'cpostnoindex',
1152
- 'cpostnofollow',
1153
- 'category_noindex',
1154
- 'archive_date_noindex',
1155
- 'archive_author_noindex',
1156
- 'tags_noindex',
1157
- 'search_noindex',
1158
- '404_noindex',
1159
- 'tax_noindex',
1160
- 'paginated_noindex',
1161
- 'paginated_nofollow',
1162
- ),
1163
- ),
1164
- 'rss_content' => array(
1165
- 'name' => __( 'RSS Content Settings', 'all-in-one-seo-pack' ),
1166
- 'help_link' => 'https://semperplugins.com/documentation/rss-content-settings/',
1167
- 'options' => array(
1168
- 'rss_content_before',
1169
- 'rss_content_after',
1170
- ),
1171
- ),
1172
- 'advanced' => array(
1173
- 'name' => __( 'Advanced Settings', 'all-in-one-seo-pack' ),
1174
- 'help_link' => 'https://semperplugins.com/documentation/all-in-one-seo-pack-advanced-settings/',
1175
- 'options' => array(
1176
- 'generate_descriptions',
1177
- 'skip_excerpt',
1178
- 'run_shortcodes',
1179
- 'hide_paginated_descriptions',
1180
- 'dont_truncate_descriptions',
1181
- 'redirect_attachement_parent',
1182
- 'ex_pages'
1183
- ),
1184
- ),
1185
- 'keywords' => array(
1186
- 'name' => __( 'Keyword Settings', 'all-in-one-seo-pack' ),
1187
- 'help_link' => 'https://semperplugins.com/documentation/keyword-settings/',
1188
- 'options' => array(
1189
- 'togglekeywords',
1190
- 'use_categories',
1191
- 'use_tags_as_keywords',
1192
- 'dynamic_postspage_keywords',
1193
- ),
1194
- ),
1195
- );
1196
-
1197
- global $pagenow;
1198
- if ( 'admin.php' === $pagenow ) {
1199
- // Person's Username setting.
1200
- $this->default_options['schema_person_user']['initial_options'] = array(
1201
- 0 => __( '- Select -', 'all-in-one-seo-pack' ),
1202
- -1 => __( 'Manually Enter', 'all-in-one-seo-pack' ),
1203
- );
1204
-
1205
- global $wpdb;
1206
- $user_count = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->users}" );
1207
- if ( 50 < $user_count ) {
1208
- $this->default_options['schema_person_user']['initial_options'] = array(
1209
- -1 => __( 'Manually Enter', 'all-in-one-seo-pack' ),
1210
- );
1211
- } else {
1212
- $user_args = array(
1213
- 'role__in' => array(
1214
- 'administrator',
1215
- 'editor',
1216
- 'author',
1217
- ),
1218
- 'orderby' => 'nicename',
1219
- );
1220
- $users = get_users( $user_args );
1221
-
1222
- foreach ( $users as $user ) {
1223
- $this->default_options['schema_person_user']['initial_options'][ $user->ID ] = $user->data->user_nicename . ' (' . $user->data->display_name . ')';
1224
- }
1225
- }
1226
- }
1227
-
1228
- if ( AIOSEOPPRO ) {
1229
- // Add Pro options.
1230
- $this->default_options = aioseop_add_pro_opt( $this->default_options );
1231
- $this->layout = aioseop_add_pro_layout( $this->layout );
1232
- }
1233
-
1234
- if ( ! AIOSEOPPRO ) {
1235
- unset( $this->layout['cpt']['options']['0'] );
1236
- }
1237
-
1238
- $other_options = array();
1239
- foreach ( $this->layout as $k => $v ) {
1240
- $other_options = array_merge( $other_options, $v['options'] );
1241
- }
1242
-
1243
- $this->layout['default']['options'] = array_diff( array_keys( $this->default_options ), $other_options );
1244
-
1245
- if ( is_admin() ) {
1246
- add_action( 'aioseop_global_settings_header', array( $this, 'display_right_sidebar' ) );
1247
- add_action( 'output_option', array( $this, 'custom_output_option' ), 10, 2 );
1248
- add_action( 'admin_init', array( $this, 'visibility_warning' ) );
1249
- add_action( 'admin_init', array( $this, 'review_plugin_cta' ) );
1250
- add_action( 'admin_init', array( $this, 'woo_upgrade_notice' ) );
1251
- add_action( 'admin_init', array( $this, 'check_php_version' ) );
1252
- add_action( 'admin_init', array( 'AIOSEOP_Education', 'register_conflicting_plugin_notice' ) );
1253
- }
1254
- if ( AIOSEOPPRO ) {
1255
- add_action( 'split_shared_term', array( $this, 'split_shared_term' ), 10, 4 );
1256
- }
1257
- }
1258
-
1259
- // good candidate for pro dir.
1260
- /**
1261
- * Custom Output Option
1262
- *
1263
- * Use custom callback for outputting snippet
1264
- *
1265
- * @since ?
1266
- * @since 2.3.16 Decodes HTML entities on title, description and title length count.
1267
- *
1268
- * @param $buf
1269
- * @param $args
1270
- * @return string
1271
- */
1272
- function custom_output_option( $buf, $args ) {
1273
- if ( 'aiosp_snippet' === $args['name'] ) {
1274
- $args['options']['type'] = 'html';
1275
- $args['options']['nowrap'] = false;
1276
- $args['options']['save'] = false;
1277
- $info = $this->get_page_snippet_info();
1278
- } else {
1279
- return '';
1280
- }
1281
-
1282
- $args['options']['type'] = 'html';
1283
- $args['options']['nowrap'] = false;
1284
- $args['options']['save'] = false;
1285
- $info = $this->get_page_snippet_info();
1286
- $title = $info['title'];
1287
- $description = $info['description'];
1288
- $keywords = $info['keywords'];
1289
- $url = $info['url'];
1290
- $title_format = $info['title_format'];
1291
- $category = $info['category'];
1292
- $w = $info['w'];
1293
- $p = $info['p'];
1294
-
1295
- if ( AIOSEOP_PHP_Functions::strlen( $title ) > 70 ) {
1296
- $title = $this->trim_excerpt_without_filters(
1297
- $this->html_entity_decode( $title ),
1298
- 70
1299
- ) . '...';
1300
- }
1301
- if ( AIOSEOP_PHP_Functions::strlen( $description ) > 156 ) {
1302
- $description = $this->trim_excerpt_without_filters(
1303
- $this->html_entity_decode( $description ),
1304
- 156
1305
- ) . '...';
1306
- }
1307
- if ( empty( $title_format ) ) {
1308
- $title = '<span id="' . $args['name'] . '_title">' . esc_attr( wp_strip_all_tags( html_entity_decode( $title, ENT_COMPAT, 'UTF-8' ) ) ) . '</span>';
1309
- } else {
1310
- $title_format = $this->get_preview_snippet_title();
1311
- $title = $title_format;
1312
- }
1313
-
1314
- $args['value'] = sprintf( $args['value'], $title, esc_url( $url ), esc_attr( $description ) );
1315
- $buf = $this->get_option_row( $args['name'], $args['options'], $args );
1316
-
1317
- return $buf;
1318
- }
1319
-
1320
- /**
1321
- * The get_preview_snippet_title() function.
1322
- *
1323
- * Processes the title format for the snippet preview on the Edit screen.
1324
- *
1325
- * @since 2.4.9
1326
- * @since 3.2.0 Fix #1408 & #2526.
1327
- *
1328
- * @return mixed
1329
- */
1330
- public function get_preview_snippet_title() {
1331
- $info = $this->get_page_snippet_info();
1332
- $title = $info['title'];
1333
- $description = $info['description'];
1334
- $keywords = $info['keywords'];
1335
- $url = $info['url'];
1336
- $title_format = $info['title_format'];
1337
- $category = $info['category'];
1338
- $wp_query = $info['w'];
1339
- $post = $info['p'];
1340
-
1341
- // Posts page title doesn't need to be processed because get_aioseop_title() does this.
1342
- if ( is_home() ) {
1343
- return $this->get_preview_snippet_title_helper( $title );
1344
- }
1345
-
1346
- /**
1347
- * The aioseop_before_get_title_format action hook.
1348
- *
1349
- * Runs before we process the title format for the snippet preview is.
1350
- *
1351
- * @since 3.0.0
1352
- */
1353
- do_action( 'aioseop_before_get_title_format' );
1354
-
1355
- if ( false !== strpos( $title_format, '%site_title%', 0 ) ) {
1356
- $title_format = str_replace( '%site_title%', get_bloginfo( 'name' ), $title_format );
1357
- }
1358
- // %blog_title% macro is deprecated.
1359
- if ( false !== strpos( $title_format, '%blog_title%', 0 ) ) {
1360
- $title_format = str_replace( '%blog_title%', get_bloginfo( 'name' ), $title_format );
1361
- }
1362
- $title_format = $this->apply_cf_fields( $title_format );
1363
- if ( false !== strpos( $title_format, '%post_title%', 0 ) ) {
1364
- $title_format = str_replace( '%post_title%', $this->get_preview_snippet_title_helper( $title ), $title_format );
1365
- }
1366
- if ( false !== strpos( $title_format, '%page_title%', 0 ) ) {
1367
- $title_format = str_replace( '%page_title%', $this->get_preview_snippet_title_helper( $title ), $title_format );
1368
- }
1369
- if ( false !== strpos( $title_format, '%current_date%', 0 ) ) {
1370
- $title_format = str_replace( '%current_date%', aioseop_formatted_date(), $title_format );
1371
- }
1372
- if ( false !== strpos( $title_format, '%current_year%', 0 ) ) {
1373
- $title_format = str_replace( '%current_year%', date( 'Y' ), $title_format );
1374
- }
1375
- if ( false !== strpos( $title_format, '%current_month%', 0 ) ) {
1376
- $title_format = str_replace( '%current_month%', date( 'M' ), $title_format );
1377
- }
1378
- if ( false !== strpos( $title_format, '%current_month_i18n%', 0 ) ) {
1379
- $title_format = str_replace( '%current_month_i18n%', date_i18n( 'M' ), $title_format );
1380
- }
1381
- if ( false !== strpos( $title_format, '%post_date%', 0 ) ) {
1382
- $title_format = str_replace( '%post_date%', aioseop_formatted_date( get_the_time( 'U' ) ), $title_format );
1383
- }
1384
- if ( false !== strpos( $title_format, '%post_year%', 0 ) ) {
1385
- $title_format = str_replace( '%post_year%', get_the_date( 'Y' ), $title_format );
1386
- }
1387
- if ( false !== strpos( $title_format, '%post_month%', 0 ) ) {
1388
- $title_format = str_replace( '%post_month%', get_the_date( 'F' ), $title_format );
1389
- }
1390
- if ( $wp_query->is_category || $wp_query->is_tag || $wp_query->is_tax ) {
1391
- if ( AIOSEOPPRO && ! empty( $_GET ) && ! empty( $_GET['taxonomy'] ) && ! empty( $_GET['tag_ID'] ) && function_exists( 'wp_get_split_terms' ) ) {
1392
- $term_id = intval( $_GET['tag_ID'] );
1393
- $was_split = get_term_meta( $term_id, '_aioseop_term_was_split', true );
1394
- if ( ! $was_split ) {
1395
- $split_terms = wp_get_split_terms( $term_id, $_GET['taxonomy'] );
1396
- if ( ! empty( $split_terms ) ) {
1397
- foreach ( $split_terms as $new_tax => $new_term ) {
1398
- $this->split_shared_term( $term_id, $new_term );
1399
- }
1400
- }
1401
- }
1402
- }
1403
- if ( false !== strpos( $title_format, '%category_title%', 0 ) ) {
1404
- $title_format = str_replace( '%category_title%', $title, $title_format );
1405
- }
1406
- if ( false !== strpos( $title_format, '%taxonomy_title%', 0 ) ) {
1407
- $title_format = str_replace( '%taxonomy_title%', $title, $title_format );
1408
- }
1409
- } else {
1410
- if ( false !== strpos( $title_format, '%category%', 0 ) ) {
1411
- $title_format = str_replace( '%category%', $category, $title_format );
1412
- }
1413
- if ( false !== strpos( $title_format, '%category_title%', 0 ) ) {
1414
- $title_format = str_replace( '%category_title%', $category, $title_format );
1415
- }
1416
- if ( false !== strpos( $title_format, '%taxonomy_title%', 0 ) ) {
1417
- $title_format = str_replace( '%taxonomy_title%', $category, $title_format );
1418
- }
1419
- if ( AIOSEOPPRO ) {
1420
- if ( strpos( $title_format, '%tax_', 0 ) && ! empty( $post ) ) {
1421
- $taxes = get_object_taxonomies( $post, 'objects' );
1422
- if ( ! empty( $taxes ) ) {
1423
- foreach ( $taxes as $t ) {
1424
- if ( strpos( $title_format, "%tax_{$t->name}%", 0 ) ) {
1425
- $terms = $this->get_all_terms( $post->ID, $t->name );
1426
- $term = '';
1427
- if ( count( $terms ) > 0 ) {
1428
- $term = $terms[0];
1429
- }
1430
- $title_format = str_replace( "%tax_{$t->name}%", $term, $title_format );
1431
- }
1432
- }
1433
- }
1434
- }
1435
- }
1436
- }
1437
- if ( false !== strpos( $title_format, '%taxonomy_description%', 0 ) ) {
1438
- $title_format = str_replace( '%taxonomy_description%', $description, $title_format );
1439
- }
1440
-
1441
- /**
1442
- * The aioseop_title_format filter hook.
1443
- *
1444
- * Filter the title for the preview snippet after replacing all macros.
1445
- *
1446
- * @since 3.0.0
1447
- *
1448
- * @param string $title_format Title format to be filtered.
1449
- */
1450
- $title_format = apply_filters( 'aioseop_title_format', $title_format );
1451
-
1452
- /**
1453
- * The aioseop_after_format_title action hook.
1454
- *
1455
- * Runs after we have processed the title format for the snippet preview is.
1456
- *
1457
- * @since 3.0.0
1458
- */
1459
- do_action( 'aioseop_after_format_title' );
1460
-
1461
- return $title_format;
1462
- }
1463
-
1464
- /**
1465
- * The get_preview_snippet_title_helper() function.
1466
- *
1467
- * Wraps the page or post title for the preview snippet title in its HTML span element.
1468
- * Helper function for the get_preview_snippet_title() function.
1469
- *
1470
- * @since 3.2.0
1471
- *
1472
- * @param string $title_format
1473
- * @return string
1474
- */
1475
- private function get_preview_snippet_title_helper( $title_format ) {
1476
- return '<span id="aiosp_snippet_title">' . esc_attr( wp_strip_all_tags( html_entity_decode( $title_format, ENT_COMPAT, 'UTF-8' ) ) ) . '</span>';
1477
- }
1478
-
1479
- /**
1480
- * The get_page_snippet_info() function.
1481
- *
1482
- * Gets data that is needed to determine the preview snippet.
1483
- *
1484
- * @since ?
1485
- *
1486
- * @return array
1487
- */
1488
- function get_page_snippet_info() {
1489
- static $info = array();
1490
- if ( ! empty( $info ) ) {
1491
- return $info;
1492
- }
1493
- global $post, $aioseop_options, $wp_query;
1494
- $title = '';
1495
- $url = '';
1496
- $description = '';
1497
- $term = '';
1498
- $category = '';
1499
- $p = $post;
1500
- $w = $wp_query;
1501
- if ( ! is_object( $post ) ) {
1502
- $post = $this->get_queried_object();
1503
- }
1504
- if ( empty( $this->meta_opts ) ) {
1505
- $this->meta_opts = $this->get_current_options( array(), 'aiosp' );
1506
- }
1507
- if ( ! is_object( $post ) && is_admin() && ! empty( $_GET ) && ! empty( $_GET['post_type'] ) && ! empty( $_GET['taxonomy'] ) && ! empty( $_GET['tag_ID'] ) ) {
1508
- $term = get_term_by( 'id', $_GET['tag_ID'], $_GET['taxonomy'] );
1509
- }
1510
- if ( is_object( $post ) ) {
1511
- $opts = $this->meta_opts;
1512
- $post_id = $p->ID;
1513
- if ( empty( $post->post_modified_gmt ) ) {
1514
- $wp_query = new WP_Query(
1515
- array(
1516
- 'p' => $post_id,
1517
- 'post_type' => $post->post_type,
1518
- )
1519
- );
1520
- }
1521
- if ( 'page' === $post->post_type ) {
1522
- $wp_query->is_page = true;
1523
- } elseif ( 'attachment' === $post->post_type ) {
1524
- $wp_query->is_attachment = true;
1525
- } else {
1526
- $wp_query->is_single = true;
1527
- }
1528
- if ( empty( $this->is_front_page ) ) {
1529
- $this->is_front_page = false;
1530
- }
1531
- if ( 'page' === get_option( 'show_on_front' ) ) {
1532
- if ( is_page() && get_option( 'page_on_front' ) == $post->ID ) {
1533
- $this->is_front_page = true;
1534
- } elseif ( get_option( 'page_for_posts' ) == $post->ID ) {
1535
- $wp_query->is_home = true;
1536
- }
1537
- }
1538
- $wp_query->queried_object = $post;
1539
- if ( ! empty( $post ) && ! $wp_query->is_home && ! $this->is_front_page ) {
1540
- $title = $this->internationalize( get_post_meta( $post->ID, '_aioseop_title', true ) );
1541
- if ( empty( $title ) ) {
1542
- $title = $post->post_title;
1543
- }
1544
- }
1545
- $title_format = '';
1546
- if ( empty( $title ) ) {
1547
- $title = $this->wp_title();
1548
- }
1549
- $description = $this->get_main_description( $post );
1550
-
1551
- // All this needs to be in it's own function (class really).
1552
- if ( empty( $title_format ) ) {
1553
- if ( is_page() ) {
1554
- $title_format = $aioseop_options['aiosp_page_title_format'];
1555
-
1556
- } elseif ( is_single() || is_attachment() ) {
1557
- $title_format = $this->get_post_title_format( 'post', $post );
1558
- }
1559
- }
1560
- if ( empty( $title_format ) ) {
1561
- $title_format = '%post_title%';
1562
- }
1563
- $categories = $this->get_all_categories( $post_id );
1564
- $category = '';
1565
- if ( count( $categories ) > 0 ) {
1566
- $category = $categories[0];
1567
- }
1568
- } elseif ( is_object( $term ) ) {
1569
- if ( 'category' === $_GET['taxonomy'] ) {
1570
- query_posts( array( 'cat' => $_GET['tag_ID'] ) );
1571
- } elseif ( 'post_tag' === $_GET['taxonomy'] ) {
1572
- query_posts( array( 'tag' => $term->slug ) );
1573
- } else {
1574
- query_posts(
1575
- array(
1576
- 'page' => '',
1577
- $_GET['taxonomy'] => $term->slug,
1578
- 'post_type' => $_GET['post_type'],
1579
- )
1580
- );
1581
- }
1582
- if ( empty( $this->meta_opts ) ) {
1583
- $this->meta_opts = $this->get_current_options( array(), 'aiosp' );
1584
- }
1585
- $title = $this->get_tax_name( $_GET['taxonomy'] );
1586
- $title_format = $this->get_tax_title_format();
1587
- $opts = $this->meta_opts;
1588
- if ( ! empty( $opts ) ) {
1589
- $description = $opts['aiosp_description'];
1590
- }
1591
- if ( empty( $description ) ) {
1592
- $description = wp_strip_all_tags( term_description() );
1593
- }
1594
- $description = $this->internationalize( $description );
1595
- }
1596
- if ( true == $this->is_front_page ) {
1597
- // $title_format = $aioseop_options['aiosp_home_page_title_format'];
1598
- $title_format = ''; // Not sure why this needs to be this way, but we should extract all this out to figure out what's going on.
1599
- }
1600
- $show_page = true;
1601
- if ( ! empty( $aioseop_options['aiosp_no_paged_canonical_links'] ) ) {
1602
- $show_page = false;
1603
- }
1604
- if ( $aioseop_options['aiosp_can'] ) {
1605
- if ( ! empty( $opts['aiosp_custom_link'] ) ) {
1606
- $url = $opts['aiosp_custom_link'];
1607
- }
1608
- if ( empty( $url ) ) {
1609
- $url = $this->aiosp_mrt_get_url( $wp_query, $show_page );
1610
- }
1611
- $url = apply_filters( 'aioseop_canonical_url', $url );
1612
- }
1613
- if ( ! $url ) {
1614
- $url = aioseop_get_permalink();
1615
- }
1616
-
1617
- $title = $this->apply_cf_fields( $title );
1618
- $description = $this->apply_cf_fields( $description );
1619
- $description = apply_filters( 'aioseop_description', $description );
1620
-
1621
- $keywords = $this->get_main_keywords();
1622
- $keywords = $this->apply_cf_fields( $keywords );
1623
- $keywords = apply_filters( 'aioseop_keywords', $keywords );
1624
-
1625
- $info = array(
1626
- 'title' => $title,
1627
- 'description' => $description,
1628
- 'keywords' => $keywords,
1629
- 'url' => $url,
1630
- 'title_format' => $title_format,
1631
- 'category' => $category,
1632
- 'w' => $wp_query,
1633
- 'p' => $post,
1634
- );
1635
- wp_reset_postdata();
1636
- $wp_query = $w;
1637
- $post = $p;
1638
-
1639
- return $info;
1640
- }
1641
-
1642
- /**
1643
- * Get Queried Object
1644
- *
1645
- * @since ?
1646
- *
1647
- * @return null|object|WP_Post
1648
- */
1649
- function get_queried_object() {
1650
- static $p = null;
1651
- global $wp_query, $post;
1652
- if ( null !== $p && ! defined( 'AIOSEOP_UNIT_TESTING' ) ) {
1653
- return $p;
1654
- }
1655
- if ( is_object( $post ) ) {
1656
- $p = $post;
1657
- } else {
1658
- if ( ! $wp_query ) {
1659
- return null;
1660
- }
1661
- $p = $wp_query->get_queried_object();
1662
- }
1663
-
1664
- return $p;
1665
- }
1666
-
1667
- /**
1668
- * Get Current Option
1669
- *
1670
- * @since ?
1671
- *
1672
- * @param array $opts
1673
- * @param null $location
1674
- * @param null $defaults
1675
- * @param null $post
1676
- * @return array
1677
- */
1678
- function get_current_options( $opts = array(), $location = null, $defaults = null, $post = null ) {
1679
- if ( ( 'aiosp' === $location ) && ( 'metabox' == $this->locations[ $location ]['type'] ) ) {
1680
- if ( null === $post ) {
1681
- global $post;
1682
- }
1683
-
1684
- // TODO Fetch correct ID for static posts page/Woocommerce shop page - #2729.
1685
- $post_id = $post;
1686
- if ( is_object( $post_id ) ) {
1687
- $post_id = $post_id->ID;
1688
- }
1689
- $get_opts = $this->default_options( $location );
1690
- $optlist = array(
1691
- 'keywords',
1692
- 'description',
1693
- 'title',
1694
- 'custom_link',
1695
- 'sitemap_exclude',
1696
- 'disable',
1697
- 'disable_analytics',
1698
- 'noindex',
1699
- 'nofollow',
1700
- 'sitemap_priority',
1701
- 'sitemap_frequency',
1702
- );
1703
- if ( ! ( ! empty( $this->options['aiosp_can'] ) ) ) {
1704
- unset( $optlist['custom_link'] );
1705
- }
1706
- foreach ( $optlist as $f ) {
1707
- $meta = '';
1708
- $field = "aiosp_$f";
1709
-
1710
- if ( AIOSEOPPRO ) {
1711
- if ( ( isset( $_GET['taxonomy'] ) && isset( $_GET['tag_ID'] ) ) || is_category() || is_tag() || is_tax() ) {
1712
- if ( is_admin() && isset( $_GET['tag_ID'] ) ) {
1713
- $meta = get_term_meta( $_GET['tag_ID'], '_aioseop_' . $f, true );
1714
- } else {
1715
- $queried_object = get_queried_object();
1716
- if ( ! empty( $queried_object ) && ! empty( $queried_object->term_id ) ) {
1717
- $meta = get_term_meta( $queried_object->term_id, '_aioseop_' . $f, true );
1718
- }
1719
- }
1720
- } else {
1721
- $meta = get_post_meta( $post_id, '_aioseop_' . $f, true );
1722
- }
1723
- if ( 'title' === $f || 'description' === $f ) {
1724
- $get_opts[ $field ] = htmlspecialchars( $meta, ENT_COMPAT, 'UTF-8' );
1725
- } else {
1726
- $get_opts[ $field ] = htmlspecialchars( stripslashes( $meta ), ENT_COMPAT, 'UTF-8' );
1727
- }
1728
- } else {
1729
- if ( ! is_category() && ! is_tag() && ! is_tax() ) {
1730
- $field = "aiosp_$f";
1731
- $meta = get_post_meta( $post_id, '_aioseop_' . $f, true );
1732
- if ( 'title' === $f || 'description' === $f ) {
1733
- $get_opts[ $field ] = htmlspecialchars( $meta, ENT_COMPAT, 'UTF-8' );
1734
- } else {
1735
- $get_opts[ $field ] = htmlspecialchars( stripslashes( $meta ), ENT_COMPAT, 'UTF-8' );
1736
- }
1737
- }
1738
- }
1739
- }
1740
- $opts = wp_parse_args( $opts, $get_opts );
1741
-
1742
- return $opts;
1743
- } else {
1744
- $options = parent::get_current_options( $opts, $location, $defaults );
1745
-
1746
- return $options;
1747
- }
1748
- }
1749
-
1750
- /**
1751
- * Internationalize
1752
- *
1753
- * @since ?
1754
- *
1755
- * @param $in
1756
- * @return mixed|void
1757
- */
1758
- function internationalize( $in ) {
1759
- if ( function_exists( 'langswitch_filter_langs_with_message' ) ) {
1760
- $in = langswitch_filter_langs_with_message( $in );
1761
- }
1762
-
1763
- if ( function_exists( 'polyglot_filter' ) ) {
1764
- $in = polyglot_filter( $in );
1765
- }
1766
-
1767
- if ( function_exists( 'qtrans_useCurrentLanguageIfNotFoundUseDefaultLanguage' ) ) {
1768
- $in = qtrans_useCurrentLanguageIfNotFoundUseDefaultLanguage( $in );
1769
- } elseif ( function_exists( 'ppqtrans_useCurrentLanguageIfNotFoundUseDefaultLanguage' ) ) {
1770
- $in = ppqtrans_useCurrentLanguageIfNotFoundUseDefaultLanguage( $in );
1771
- } elseif ( function_exists( 'qtranxf_useCurrentLanguageIfNotFoundUseDefaultLanguage' ) ) {
1772
- $in = qtranxf_useCurrentLanguageIfNotFoundUseDefaultLanguage( $in );
1773
- }
1774
-
1775
- return apply_filters( 'localization', $in );
1776
- }
1777
-
1778
- /**
1779
- * WP Title
1780
- *
1781
- * Used to filter wp_title(), get our title.
1782
- *
1783
- * @since ?
1784
- *
1785
- * @return mixed|void
1786
- */
1787
- function wp_title() {
1788
- if ( ! $this->is_seo_enabled_for_cpt() ) {
1789
- return;
1790
- }
1791
-
1792
- global $aioseop_options;
1793
- $title = false;
1794
- $post = $this->get_queried_object();
1795
- $title = $this->get_aioseop_title( $post );
1796
- $title = $this->apply_cf_fields( $title );
1797
-
1798
- if ( false === $title ) {
1799
- $title = $this->get_original_title();
1800
- }
1801
-
1802
- return apply_filters( 'aioseop_title', $title );
1803
- }
1804
-
1805
- /**
1806
- * Get AIOSEOP Title
1807
- *
1808
- * Gets the title that will be used by AIOSEOP for title rewrites or returns false.
1809
- *
1810
- * @param WP_Post $post the post object
1811
- * @param bool $use_original_title_format should the original title format be used viz. post_title | blog_title. This parameter was introduced
1812
- * to resolve issue#986
1813
- * @return bool|string
1814
- */
1815
- function get_aioseop_title( $post, $use_original_title_format = true ) {
1816
- global $aioseop_options;
1817
- // the_search_query() is not suitable, it cannot just return.
1818
- global $s, $STagging; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
1819
- $opts = $this->meta_opts;
1820
- if ( is_front_page() ) {
1821
- if ( ! empty( $aioseop_options['aiosp_use_static_home_info'] ) ) {
1822
- global $post;
1823
- if ( 'page' == get_option( 'show_on_front' ) && is_page() && get_option( 'page_on_front' ) == $post->ID ) {
1824
- $title = $this->internationalize( get_post_meta( $post->ID, '_aioseop_title', true ) );
1825
- if ( ! $title ) {
1826
- $title = $this->internationalize( $post->post_title );
1827
- }
1828
- if ( ! $title ) {
1829
- $title = $this->internationalize( $this->get_original_title( '', false ) );
1830
- }
1831
- if ( ! empty( $aioseop_options['aiosp_home_page_title_format'] ) ) {
1832
- $title = $this->apply_page_title_format( $title, $post, $aioseop_options['aiosp_home_page_title_format'] );
1833
- }
1834
- $title = $this->paged_title( $title );
1835
- $title = apply_filters( 'aioseop_home_page_title', $title );
1836
- }
1837
- } else {
1838
- $title = $this->internationalize( $aioseop_options['aiosp_home_title'] );
1839
- if ( ! empty( $aioseop_options['aiosp_home_page_title_format'] ) ) {
1840
- $title = $this->apply_page_title_format( $title, null, $aioseop_options['aiosp_home_page_title_format'] );
1841
- }
1842
- }
1843
- if ( empty( $title ) ) {
1844
- $title = $this->internationalize( get_option( 'blogname' ) ) . ' | ' . $this->internationalize( get_bloginfo( 'description' ) );
1845
- }
1846
-
1847
- // #1616 - Avoid trying to get property of non-object when no posts are present on the homepage.
1848
- global $post;
1849
-
1850
- if ( null === $post ) {
1851
- $post_id = get_option( 'page_on_front' );
1852
- } else {
1853
- $post_id = $post->ID;
1854
- }
1855
-
1856
- if ( is_post_type_archive() && is_post_type_archive( 'product' ) && function_exists( 'wc_get_page_id' ) ) {
1857
- $post_id = wc_get_page_id( 'shop' );
1858
- if ( $post_id ) {
1859
- $post = get_post( $post_id );
1860
- if ( wc_get_page_id( 'shop' ) == get_option( 'page_on_front' ) && ! empty( $aioseop_options['aiosp_use_static_home_info'] ) ) {
1861
- $title = $this->internationalize( get_post_meta( $post->ID, '_aioseop_title', true ) );
1862
- }
1863
- // $title = $this->internationalize( $aioseop_options['aiosp_home_title'] );
1864
- if ( ! $title ) {
1865
- $title = $this->internationalize( get_post_meta( $post_id, '_aioseop_title', true ) );
1866
- } // This is/was causing the first product to come through.
1867
- if ( ! $title ) {
1868
- $title = $this->internationalize( $post->post_title );
1869
- }
1870
- if ( ! $title ) {
1871
- $title = $this->internationalize( $this->get_original_title( '', false ) );
1872
- }
1873
-
1874
- $title = $this->apply_page_title_format( $title, $post );
1875
- $title = $this->paged_title( $title );
1876
- $title = apply_filters( 'aioseop_title_page', $title );
1877
-
1878
- return $title;
1879
- }
1880
- }
1881
-
1882
- // this is returned for woo.
1883
- return $this->paged_title( $title );
1884
- } elseif ( is_attachment() ) {
1885
- if ( null === $post ) {
1886
- return false;
1887
- }
1888
- $title = get_post_meta( $post->ID, '_aioseop_title', true );
1889
- if ( empty( $title ) ) {
1890
- $title = $post->post_title;
1891
- }
1892
- if ( empty( $title ) ) {
1893
- $title = $this->get_original_title( '', false );
1894
- }
1895
- if ( empty( $title ) ) {
1896
- $title = get_the_title( $post->post_parent );
1897
- }
1898
- $title = apply_filters( 'aioseop_attachment_title', $this->internationalize( $this->apply_post_title_format( $title, '', $post ) ) );
1899
-
1900
- return $title;
1901
- } elseif ( is_page() || $this->is_static_posts_page() || ( is_home() && ! $this->is_static_posts_page() ) ) {
1902
- if ( null === $post ) {
1903
- return false;
1904
- }
1905
-
1906
- $home_title = $this->internationalize( $aioseop_options['aiosp_home_title'] );
1907
- if ( $this->is_static_front_page() && ( $home_title ) ) {
1908
- if ( ! empty( $aioseop_options['aiosp_home_page_title_format'] ) ) {
1909
- $home_title = $this->apply_page_title_format( $home_title, $post, $aioseop_options['aiosp_home_page_title_format'] );
1910
- }
1911
-
1912
- // Home title filter.
1913
- return apply_filters( 'aioseop_home_page_title', $home_title );
1914
- } else {
1915
- $page_for_posts = '';
1916
- if ( is_home() ) {
1917
- $page_for_posts = get_option( 'page_for_posts' );
1918
- }
1919
- if ( $page_for_posts ) {
1920
- $title = $this->internationalize( get_post_meta( $page_for_posts, '_aioseop_title', true ) );
1921
- if ( ! $title ) {
1922
- $post_page = get_post( $page_for_posts );
1923
- $title = $this->internationalize( $post_page->post_title );
1924
- }
1925
- } else {
1926
- $title = $this->internationalize( get_post_meta( $post->ID, '_aioseop_title', true ) );
1927
- if ( ! $title ) {
1928
- $title = $this->internationalize( $post->post_title );
1929
- }
1930
- }
1931
- if ( ! $title ) {
1932
- $title = $this->internationalize( $this->get_original_title( '', false ) );
1933
- }
1934
-
1935
- $title = $this->apply_page_title_format( $title, $post );
1936
- $title = $this->paged_title( $title );
1937
- $title = apply_filters( 'aioseop_title_page', $title );
1938
- if ( $this->is_static_posts_page() ) {
1939
- $title = apply_filters( 'single_post_title', $title );
1940
- }
1941
-
1942
- return $title;
1943
- }
1944
- } elseif ( is_post_type_archive( 'product' ) && function_exists( 'wc_get_page_id' ) ) {
1945
- $post_id = wc_get_page_id( 'shop' );
1946
- if ( $post_id ) {
1947
- $post = get_post( $post_id );
1948
- // Too far down? -mrt.
1949
- $title = $this->internationalize( get_post_meta( $post->ID, '_aioseop_title', true ) );
1950
- if ( ! $title ) {
1951
- $title = $this->internationalize( $post->post_title );
1952
- }
1953
- if ( ! $title ) {
1954
- $title = $this->internationalize( $this->get_original_title( '', false ) );
1955
- }
1956
- $title = $this->apply_page_title_format( $title, $post );
1957
- $title = $this->paged_title( $title );
1958
- $title = apply_filters( 'aioseop_title_page', $title );
1959
-
1960
- return $title;
1961
- }
1962
- } elseif ( is_single() || $this->check_singular() ) {
1963
- // We're not in the loop :(.
1964
- if ( null === $post ) {
1965
- return false;
1966
- }
1967
- $categories = $this->get_all_categories();
1968
- $category = '';
1969
- if ( count( $categories ) > 0 ) {
1970
- $category = $categories[0];
1971
- }
1972
- $title = $this->internationalize( get_post_meta( $post->ID, '_aioseop_title', true ) );
1973
- if ( ! $title ) {
1974
- $title = $this->internationalize( get_post_meta( $post->ID, 'title_tag', true ) );
1975
- if ( ! $title && $use_original_title_format ) {
1976
- $title = $this->internationalize( $this->get_original_title( '', false ) );
1977
- }
1978
- }
1979
- if ( empty( $title ) ) {
1980
- $title = $post->post_title;
1981
- }
1982
- if ( ! empty( $title ) && $use_original_title_format ) {
1983
- $title = $this->apply_post_title_format( $title, $category, $post );
1984
- }
1985
- $title = $this->paged_title( $title );
1986
-
1987
- return apply_filters( 'aioseop_title_single', $title );
1988
- } elseif ( is_search() && isset( $s ) && ! empty( $s ) ) {
1989
- $search = esc_attr( stripslashes( $s ) );
1990
- $title_format = $aioseop_options['aiosp_search_title_format'];
1991
- $title = str_replace( '%site_title%', $this->internationalize( get_bloginfo( 'name' ) ), $title_format );
1992
- if ( false !== strpos( $title, '%blog_title%', 0 ) ) {
1993
- $title = str_replace( '%blog_title%', $this->internationalize( get_bloginfo( 'name' ) ), $title );
1994
- }
1995
- if ( false !== strpos( $title, '%site_description%', 0 ) ) {
1996
- $title = str_replace( '%site_description%', $this->internationalize( get_bloginfo( 'description' ) ), $title );
1997
- }
1998
- if ( false !== strpos( $title, '%blog_description%', 0 ) ) {
1999
- $title = str_replace( '%blog_description%', $this->internationalize( get_bloginfo( 'description' ) ), $title );
2000
- }
2001
- if ( false !== strpos( $title, '%search%', 0 ) ) {
2002
- $title = str_replace( '%search%', $search, $title );
2003
- }
2004
- $title = $this->paged_title( $title );
2005
-
2006
- return $title;
2007
- } elseif ( is_tag() ) {
2008
- global $utw;
2009
- $tag = '';
2010
- $tag_description = '';
2011
- if ( $utw ) {
2012
- $tags = $utw->GetCurrentTagSet();
2013
- $tag = $tags[0]->tag;
2014
- $tag = str_replace( '-', ' ', $tag );
2015
- } else {
2016
- if ( AIOSEOPPRO ) {
2017
- if ( ! empty( $opts ) && ! empty( $opts['aiosp_title'] ) ) {
2018
- $tag = $opts['aiosp_title'];
2019
- }
2020
- if ( ! empty( $opts ) ) {
2021
- if ( ! empty( $opts['aiosp_title'] ) ) {
2022
- $tag = $opts['aiosp_title'];
2023
- }
2024
- if ( ! empty( $opts['aiosp_description'] ) ) {
2025
- $tag_description = $opts['aiosp_description'];
2026
- }
2027
- }
2028
- }
2029
- if ( empty( $tag ) ) {
2030
- $tag = $this->get_original_title( '', false );
2031
- }
2032
- if ( empty( $tag_description ) ) {
2033
- $tag_description = tag_description();
2034
- }
2035
- $tag = $this->internationalize( $tag );
2036
- $tag_description = $this->internationalize( $tag_description );
2037
- }
2038
- if ( $tag ) {
2039
- $title_format = $aioseop_options['aiosp_tag_title_format'];
2040
- $title = str_replace( '%site_title%', $this->internationalize( get_bloginfo( 'name' ) ), $title_format );
2041
- if ( false !== strpos( $title, '%blog_title%', 0 ) ) {
2042
- $title = str_replace( '%blog_title%', $this->internationalize( get_bloginfo( 'name' ) ), $title );
2043
- }
2044
- if ( false !== strpos( $title, '%site_description%', 0 ) ) {
2045
- $title = str_replace( '%site_description%', $this->internationalize( get_bloginfo( 'description' ) ), $title );
2046
- }
2047
- if ( false !== strpos( $title, '%blog_description%', 0 ) ) {
2048
- $title = str_replace( '%blog_description%', $this->internationalize( get_bloginfo( 'description' ) ), $title );
2049
- }
2050
- if ( false !== strpos( $title, '%tag%', 0 ) ) {
2051
- $title = str_replace( '%tag%', $tag, $title );
2052
- }
2053
- if ( false !== strpos( $title, '%tag_description%', 0 ) ) {
2054
- $title = str_replace( '%tag_description%', $tag_description, $title );
2055
- }
2056
- if ( false !== strpos( $title, '%taxonomy_description%', 0 ) ) {
2057
- $title = str_replace( '%taxonomy_description%', $tag_description, $title );
2058
- }
2059
- $title = trim( wp_strip_all_tags( $title ) );
2060
- $title = str_replace( array( '"', "\r\n", "\n" ), array( '&quot;', ' ', ' ' ), $title );
2061
- $title = $this->paged_title( $title );
2062
-
2063
- return $title;
2064
- }
2065
- } elseif ( ( is_tax() || is_category() ) && ! is_feed() ) {
2066
- return $this->get_tax_title();
2067
-
2068
- } elseif ( isset( $STagging ) && $STagging->is_tag_view() ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
2069
- // Simple tagging support.
2070
- $tag = $STagging->search_tag; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
2071
- if ( $tag ) {
2072
- $title_format = $aioseop_options['aiosp_tag_title_format'];
2073
- $title = str_replace( '%site_title%', $this->internationalize( get_bloginfo( 'name' ) ), $title_format );
2074
- if ( false !== strpos( $title, '%blog_title%', 0 ) ) {
2075
- $title = str_replace( '%blog_title%', $this->internationalize( get_bloginfo( 'name' ) ), $title );
2076
- }
2077
- if ( false !== strpos( $title, '%site_description%', 0 ) ) {
2078
- $title = str_replace( '%site_description%', $this->internationalize( get_bloginfo( 'description' ) ), $title );
2079
- }
2080
- if ( false !== strpos( $title, '%blog_description%', 0 ) ) {
2081
- $title = str_replace( '%blog_description%', $this->internationalize( get_bloginfo( 'description' ) ), $title );
2082
- }
2083
- if ( false !== strpos( $title, '%tag%', 0 ) ) {
2084
- $title = str_replace( '%tag%', $tag, $title );
2085
- }
2086
- $title = $this->paged_title( $title );
2087
-
2088
- return $title;
2089
- }
2090
- } elseif ( is_archive() || is_post_type_archive() ) {
2091
- if ( is_author() ) {
2092
- $author = $this->internationalize( $this->get_original_title( '', false ) );
2093
- $title_format = $aioseop_options['aiosp_author_title_format'];
2094
- $new_title = str_replace( '%author%', $author, $title_format );
2095
- } elseif ( is_date() ) {
2096
- global $wp_query;
2097
- $date = $this->internationalize( $this->get_original_title( '', false ) );
2098
- $title_format = $aioseop_options['aiosp_date_title_format'];
2099
- $new_title = str_replace( '%date%', $date, $title_format );
2100
- $day = get_query_var( 'day' );
2101
- if ( empty( $day ) ) {
2102
- $day = '';
2103
- }
2104
- $new_title = str_replace( '%day%', $day, $new_title );
2105
- $monthnum = get_query_var( 'monthnum' );
2106
- $year = get_query_var( 'year' );
2107
- if ( empty( $monthnum ) || is_year() ) {
2108
- $month = '';
2109
- $monthnum = 0;
2110
- }
2111
- $month = date( 'F', mktime( 0, 0, 0, (int) $monthnum, 1, (int) $year ) );
2112
- $new_title = str_replace( '%monthnum%', $monthnum, $new_title );
2113
- if ( false !== strpos( $new_title, '%month%', 0 ) ) {
2114
- $new_title = str_replace( '%month%', $month, $new_title );
2115
- }
2116
- if ( false !== strpos( $new_title, '%year%', 0 ) ) {
2117
- $new_title = str_replace( '%year%', get_query_var( 'year' ), $new_title );
2118
- }
2119
- } elseif ( is_post_type_archive() ) {
2120
- if ( empty( $title ) ) {
2121
- $title = $this->get_original_title( '', false );
2122
- }
2123
- $new_title = apply_filters( 'aioseop_archive_title', $this->apply_archive_title_format( $title ) );
2124
- } else {
2125
- return false;
2126
- }
2127
- $new_title = str_replace( '%site_title%', $this->internationalize( get_bloginfo( 'name' ) ), $new_title );
2128
- if ( false !== strpos( $new_title, '%blog_title%', 0 ) ) {
2129
- $new_title = str_replace( '%blog_title%', $this->internationalize( get_bloginfo( 'name' ) ), $new_title );
2130
- }
2131
- if ( false !== strpos( $new_title, '%site_description%', 0 ) ) {
2132
- $new_title = str_replace( '%site_description%', $this->internationalize( get_bloginfo( 'description' ) ), $new_title );
2133
- }
2134
- if ( false !== strpos( $new_title, '%blog_description%', 0 ) ) {
2135
- $new_title = str_replace( '%blog_description%', $this->internationalize( get_bloginfo( 'description' ) ), $new_title );
2136
- }
2137
- $title = trim( $new_title );
2138
- $title = $this->paged_title( $title );
2139
-
2140
- return $title;
2141
- } elseif ( is_404() ) {
2142
- $title_format = $aioseop_options['aiosp_404_title_format'];
2143
- $new_title = str_replace( '%site_title%', $this->internationalize( get_bloginfo( 'name' ) ), $title_format );
2144
- if ( false !== strpos( $new_title, '%blog_title%', 0 ) ) {
2145
- $new_title = str_replace( '%blog_title%', $this->internationalize( get_bloginfo( 'name' ) ), $new_title );
2146
- }
2147
- if ( false !== strpos( $new_title, '%site_description%', 0 ) ) {
2148
- $new_title = str_replace( '%site_description%', $this->internationalize( get_bloginfo( 'description' ) ), $new_title );
2149
- }
2150
- if ( false !== strpos( $new_title, '%blog_description%', 0 ) ) {
2151
- $new_title = str_replace( '%blog_description%', $this->internationalize( get_bloginfo( 'description' ) ), $new_title );
2152
- }
2153
- if ( false !== strpos( $new_title, '%request_url%', 0 ) ) {
2154
- $new_title = str_replace( '%request_url%', $_SERVER['REQUEST_URI'], $new_title );
2155
- }
2156
- if ( false !== strpos( $new_title, '%request_words%', 0 ) ) {
2157
- $new_title = str_replace( '%request_words%', $this->request_as_words( $_SERVER['REQUEST_URI'] ), $new_title );
2158
- }
2159
- if ( false !== strpos( $new_title, '%404_title%', 0 ) ) {
2160
- $new_title = str_replace( '%404_title%', $this->internationalize( $this->get_original_title( '', false ) ), $new_title );
2161
- }
2162
-
2163
- return $new_title;
2164
- }
2165
-
2166
- return false;
2167
- }
2168
-
2169
- /**
2170
- * Get Original Title
2171
- *
2172
- * @since ?
2173
- *
2174
- * @param string $sep
2175
- * @param bool $echo
2176
- * @param string $seplocation
2177
- * @return string The original title as delivered by WP (well, in most cases).
2178
- */
2179
- function get_original_title( $sep = '|', $echo = false, $seplocation = '' ) {
2180
- global $aioseop_options;
2181
- if ( ! empty( $aioseop_options['aiosp_use_original_title'] ) ) {
2182
- $has_filter = has_filter( 'wp_title', array( $this, 'wp_title' ) );
2183
- if ( false !== $has_filter ) {
2184
- remove_filter( 'wp_title', array( $this, 'wp_title' ), $has_filter );
2185
- }
2186
- if ( current_theme_supports( 'title-tag' ) ) {
2187
- $sep = '|';
2188
- $echo = false;
2189
- $seplocation = 'right';
2190
- }
2191
- $title = wp_title( $sep, $echo, $seplocation );
2192
- if ( false !== $has_filter ) {
2193
- add_filter( 'wp_title', array( $this, 'wp_title' ), $has_filter );
2194
- }
2195
- $title = trim( $title );
2196
- if ( $title ) {
2197
- return trim( $title );
2198
- }
2199
- }
2200
-
2201
- // the_search_query() is not suitable, it cannot just return.
2202
- global $s;
2203
-
2204
- $title = null;
2205
-
2206
- if ( is_home() ) {
2207
- $title = get_option( 'blogname' );
2208
- } elseif ( is_single() ) {
2209
- $title = $this->internationalize( single_post_title( '', false ) );
2210
- } elseif ( is_search() && isset( $s ) && ! empty( $s ) ) {
2211
- $search = esc_attr( stripslashes( $s ) );
2212
- $title = $search;
2213
- } elseif ( ( is_tax() || is_category() ) && ! is_feed() ) {
2214
- $category_name = AIOSEOP_PHP_Functions::ucwords( $this->internationalize( single_cat_title( '', false ) ) );
2215
- $title = $category_name;
2216
- } elseif ( is_page() ) {
2217
- $title = $this->internationalize( single_post_title( '', false ) );
2218
- } elseif ( is_tag() ) {
2219
- global $utw;
2220
- if ( $utw ) {
2221
- $tags = $utw->GetCurrentTagSet();
2222
- $tag = $tags[0]->tag;
2223
- $tag = str_replace( '-', ' ', $tag );
2224
- } else {
2225
- // For WordPress > 2.3.
2226
- $tag = $this->internationalize( single_term_title( '', false ) );
2227
- }
2228
- if ( $tag ) {
2229
- $title = $tag;
2230
- }
2231
- } elseif ( is_author() ) {
2232
- $author = get_userdata( get_query_var( 'author' ) );
2233
- if ( false === $author ) {
2234
- global $wp_query;
2235
- $author = $wp_query->get_queried_object();
2236
- }
2237
- if ( false !== $author ) {
2238
- $title = $author->display_name;
2239
- }
2240
- } elseif ( is_day() ) {
2241
- $title = get_the_date();
2242
- } elseif ( is_month() ) {
2243
- $title = get_the_date( 'F, Y' );
2244
- } elseif ( is_year() ) {
2245
- $title = get_the_date( 'Y' );
2246
- } elseif ( is_archive() ) {
2247
- $title = $this->internationalize( post_type_archive_title( '', false ) );
2248
- } elseif ( is_404() ) {
2249
- $title_format = $aioseop_options['aiosp_404_title_format'];
2250
- $new_title = str_replace( '%site_title%', $this->internationalize( get_bloginfo( 'name' ) ), $title_format );
2251
- if ( false !== strpos( $new_title, '%blog_title%', 0 ) ) {
2252
- $new_title = str_replace( '%blog_title%', $this->internationalize( get_bloginfo( 'name' ) ), $new_title );
2253
- }
2254
- if ( false !== strpos( $new_title, '%site_description%', 0 ) ) {
2255
- $new_title = str_replace( '%site_description%', $this->internationalize( get_bloginfo( 'description' ) ), $new_title );
2256
- }
2257
- if ( false !== strpos( $new_title, '%blog_description%', 0 ) ) {
2258
- $new_title = str_replace( '%blog_description%', $this->internationalize( get_bloginfo( 'description' ) ), $new_title );
2259
- }
2260
- if ( false !== strpos( $new_title, '%request_url%', 0 ) ) {
2261
- $new_title = str_replace( '%request_url%', $_SERVER['REQUEST_URI'], $new_title );
2262
- }
2263
- if ( false !== strpos( $new_title, '%request_words%', 0 ) ) {
2264
- $new_title = str_replace( '%request_words%', $this->request_as_words( $_SERVER['REQUEST_URI'] ), $new_title );
2265
- }
2266
- $title = $new_title;
2267
- }
2268
-
2269
- return trim( $title );
2270
- }
2271
-
2272
- /**
2273
- * Request as Words
2274
- *
2275
- * @since ?
2276
- *
2277
- * @param $request
2278
- * @return string User -readable nice words for a given request.
2279
- */
2280
- function request_as_words( $request ) {
2281
- $request = htmlspecialchars( $request, ENT_COMPAT, 'UTF-8' );
2282
- $request = str_replace( '.html', ' ', $request );
2283
- $request = str_replace( '.htm', ' ', $request );
2284
- $request = str_replace( '.', ' ', $request );
2285
- $request = str_replace( '/', ' ', $request );
2286
- $request = str_replace( '-', ' ', $request );
2287
- $request_a = explode( ' ', $request );
2288
- $request_new = array();
2289
- foreach ( $request_a as $token ) {
2290
- $request_new[] = AIOSEOP_PHP_Functions::ucwords( trim( $token ) );
2291
- }
2292
- $request = implode( ' ', $request_new );
2293
-
2294
- return $request;
2295
- }
2296
-
2297
- /**
2298
- * Apply Page Title Format
2299
- *
2300
- * @since ?
2301
- *
2302
- * @param $title
2303
- * @param null $p
2304
- * @param string $title_format
2305
- * @return string
2306
- */
2307
- function apply_page_title_format( $title, $p = null, $title_format = '' ) {
2308
- global $aioseop_options;
2309
- if ( null === $p ) {
2310
- global $post;
2311
- } else {
2312
- $post = $p;
2313
- }
2314
- if ( empty( $title_format ) ) {
2315
- $title_format = $aioseop_options['aiosp_page_title_format'];
2316
- }
2317
-
2318
- return $this->title_placeholder_helper( $title, $post, 'page', $title_format );
2319
- }
2320
-
2321
- /**
2322
- * Title Placeholder Helper
2323
- *
2324
- * Replace doc title templates inside % symbol on the frontend.
2325
- *
2326
- * @since ?
2327
- *
2328
- * @param $title
2329
- * @param $post
2330
- * @param string $type
2331
- * @param string $title_format
2332
- * @param string $category
2333
- * @return string
2334
- */
2335
- function title_placeholder_helper( $title, $post, $type = 'post', $title_format = '', $category = '' ) {
2336
-
2337
- /**
2338
- * Runs before applying the formatting for the doc title on the frontend.
2339
- *
2340
- * @since 3.0
2341
- */
2342
- do_action( 'aioseop_before_title_placeholder_helper' );
2343
-
2344
- if ( ! empty( $post ) ) {
2345
- $authordata = get_userdata( $post->post_author );
2346
- } else {
2347
- $authordata = new WP_User();
2348
- }
2349
- $new_title = str_replace( '%site_title%', $this->internationalize( get_bloginfo( 'name' ) ), $title_format );
2350
- if ( false !== strpos( $new_title, '%blog_title%', 0 ) ) {
2351
- $new_title = str_replace( '%blog_title%', $this->internationalize( get_bloginfo( 'name' ) ), $new_title );
2352
- }
2353
- if ( false !== strpos( $new_title, '%site_description%', 0 ) ) {
2354
- $new_title = str_replace( '%site_description%', $this->internationalize( get_bloginfo( 'description' ) ), $new_title );
2355
- }
2356
- if ( false !== strpos( $new_title, '%blog_description%', 0 ) ) {
2357
- $new_title = str_replace( '%blog_description%', $this->internationalize( get_bloginfo( 'description' ) ), $new_title );
2358
- }
2359
- if ( false !== strpos( $new_title, "%{$type}_title%", 0 ) ) {
2360
- $new_title = str_replace( "%{$type}_title%", $title, $new_title );
2361
- }
2362
- if ( 'post' == $type ) {
2363
- if ( false !== strpos( $new_title, '%category%', 0 ) ) {
2364
- $new_title = str_replace( '%category%', $category, $new_title );
2365
- }
2366
- if ( false !== strpos( $new_title, '%category_title%', 0 ) ) {
2367
- $new_title = str_replace( '%category_title%', $category, $new_title );
2368
- }
2369
- if ( false !== strpos( $new_title, '%tax_', 0 ) && ! empty( $post ) ) {
2370
- $taxes = get_object_taxonomies( $post, 'objects' );
2371
- if ( ! empty( $taxes ) ) {
2372
- foreach ( $taxes as $t ) {
2373
- if ( false !== strpos( $new_title, "%tax_{$t->name}%", 0 ) ) {
2374
- $terms = $this->get_all_terms( $post->ID, $t->name );
2375
- $term = '';
2376
- if ( count( $terms ) > 0 ) {
2377
- $term = $terms[0];
2378
- }
2379
- $new_title = str_replace( "%tax_{$t->name}%", $term, $new_title );
2380
- }
2381
- }
2382
- }
2383
- }
2384
- }
2385
- if ( false !== strpos( $new_title, "%{$type}_author_login%", 0 ) ) {
2386
- $new_title = str_replace( "%{$type}_author_login%", $authordata->user_login, $new_title );
2387
- }
2388
- if ( false !== strpos( $new_title, "%{$type}_author_nicename%", 0 ) ) {
2389
- $new_title = str_replace( "%{$type}_author_nicename%", $authordata->user_nicename, $new_title );
2390
- }
2391
- if ( false !== strpos( $new_title, "%{$type}_author_firstname%", 0 ) ) {
2392
- $new_title = str_replace( "%{$type}_author_firstname%", AIOSEOP_PHP_Functions::ucwords( $authordata->first_name ), $new_title );
2393
- }
2394
- if ( false !== strpos( $new_title, "%{$type}_author_lastname%", 0 ) ) {
2395
- $new_title = str_replace( "%{$type}_author_lastname%", AIOSEOP_PHP_Functions::ucwords( $authordata->last_name ), $new_title );
2396
- }
2397
- if ( false !== strpos( $new_title, '%current_date%', 0 ) ) {
2398
- $new_title = str_replace( '%current_date%', aioseop_formatted_date(), $new_title );
2399
- }
2400
- if ( false !== strpos( $new_title, '%current_year%', 0 ) ) {
2401
- $new_title = str_replace( '%current_year%', date( 'Y' ), $new_title );
2402
- }
2403
- if ( false !== strpos( $new_title, '%current_month%', 0 ) ) {
2404
- $new_title = str_replace( '%current_month%', date( 'M' ), $new_title );
2405
- }
2406
- if ( false !== strpos( $new_title, '%current_month_i18n%', 0 ) ) {
2407
- $new_title = str_replace( '%current_month_i18n%', date_i18n( 'M' ), $new_title );
2408
- }
2409
- if ( false !== strpos( $new_title, '%post_date%', 0 ) ) {
2410
- $new_title = str_replace( '%post_date%', aioseop_formatted_date( get_the_date( 'U' ) ), $new_title );
2411
- }
2412
- if ( false !== strpos( $new_title, '%post_year%', 0 ) ) {
2413
- $new_title = str_replace( '%post_year%', get_the_date( 'Y' ), $new_title );
2414
- }
2415
- if ( false !== strpos( $new_title, '%post_month%', 0 ) ) {
2416
- $new_title = str_replace( '%post_month%', get_the_date( 'F' ), $new_title );
2417
- }
2418
-
2419
- /**
2420
- * Filters document title after applying the formatting.
2421
- *
2422
- * @since 3.0
2423
- *
2424
- * @param string $new_title Document title to be filtered.
2425
- */
2426
- $new_title = apply_filters( 'aioseop_title_format', $new_title );
2427
-
2428
- /**
2429
- * Runs after applying the formatting for the doc title on the frontend.
2430
- *
2431
- * @since 3.0
2432
- */
2433
- do_action( 'aioseop_after_title_placeholder_helper' );
2434
-
2435
- $title = trim( $new_title );
2436
-
2437
- return $title;
2438
- }
2439
-
2440
- /**
2441
- * Get All Terms
2442
- *
2443
- * @since ?
2444
- *
2445
- * @param $id
2446
- * @param $taxonomy
2447
- * @return array
2448
- */
2449
- function get_all_terms( $id, $taxonomy ) {
2450
- $keywords = array();
2451
- $terms = get_the_terms( $id, $taxonomy );
2452
- if ( ! empty( $terms ) ) {
2453
- foreach ( $terms as $term ) {
2454
- $keywords[] = $this->internationalize( $term->name );
2455
- }
2456
- }
2457
-
2458
- return $keywords;
2459
- }
2460
-
2461
- /**
2462
- * Paged Title
2463
- *
2464
- * @since ?
2465
- *
2466
- * @param $title
2467
- * @return string
2468
- */
2469
- function paged_title( $title ) {
2470
- // The page number if paged.
2471
- global $paged;
2472
- global $aioseop_options;
2473
- // phpcs:disable WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
2474
- // Simple tagging support.
2475
- global $STagging;
2476
- $page = get_query_var( 'page' );
2477
- if ( $paged > $page ) {
2478
- $page = $paged;
2479
- }
2480
- if ( is_paged() || ( isset( $STagging ) && $STagging->is_tag_view() && $paged ) || ( $page > 1 ) ) {
2481
- $part = $this->internationalize( $aioseop_options['aiosp_paged_format'] );
2482
- if ( isset( $part ) || ! empty( $part ) ) {
2483
- $part = ' ' . trim( $part );
2484
- $part = str_replace( '%page%', $page, $part );
2485
- $this->log( "paged_title() [$title] [$part]" );
2486
- $title .= $part;
2487
- }
2488
- }
2489
- // phpcs:enable
2490
-
2491
- return $title;
2492
- }
2493
-
2494
- /**
2495
- * Log
2496
- *
2497
- * @since ?
2498
- *
2499
- * @param $message
2500
- */
2501
- function log( $message ) {
2502
- if ( $this->do_log ) {
2503
- // @codingStandardsIgnoreStart
2504
- @error_log( date( 'Y-m-d H:i:s' ) . ' ' . $message . "\n", 3, $this->log_file );
2505
- // @codingStandardsIgnoreEnd
2506
- }
2507
- }
2508
-
2509
- /**
2510
- * Apply Post Title Format
2511
- *
2512
- * @since ?
2513
- *
2514
- * @param $title
2515
- * @param string $category
2516
- * @param null $p
2517
- * @return string
2518
- */
2519
- function apply_post_title_format( $title, $category = '', $p = null ) {
2520
- if ( null === $p ) {
2521
- global $post;
2522
- } else {
2523
- $post = $p;
2524
- }
2525
- $title_format = $this->get_post_title_format( 'post', $post );
2526
-
2527
- return $this->title_placeholder_helper( $title, $post, 'post', $title_format, $category );
2528
- }
2529
-
2530
- /**
2531
- * Get Post Title Format
2532
- *
2533
- * @since ?
2534
- *
2535
- * @param string $title_type
2536
- * @param null $p
2537
- * @return bool|string
2538
- */
2539
- function get_post_title_format( $title_type = 'post', $p = null ) {
2540
- global $aioseop_options;
2541
- if ( ( 'post' != $title_type ) && ( 'archive' != $title_type ) ) {
2542
- return false;
2543
- }
2544
- $title_format = "%{$title_type}_title% | %site_title%";
2545
- if ( isset( $aioseop_options[ "aiosp_{$title_type}_title_format" ] ) ) {
2546
- $title_format = $aioseop_options[ "aiosp_{$title_type}_title_format" ];
2547
- }
2548
-
2549
- if ( ! empty( $aioseop_options['aiosp_cpostactive'] ) ) {
2550
- $wp_post_types = $aioseop_options['aiosp_cpostactive'];
2551
-
2552
- $is_post_type_archive = ( 'archive' == $title_type ) && is_post_type_archive( $wp_post_types );
2553
- $is_singular_post = ( 'post' == $title_type ) && $this->is_singular( $wp_post_types, $p );
2554
-
2555
- if ( $is_post_type_archive || $is_singular_post ) {
2556
- if ( $is_post_type_archive ) {
2557
- $prefix = "aiosp_{$title_type}_";
2558
- } else {
2559
- $prefix = 'aiosp_';
2560
- }
2561
-
2562
- $post_type = get_post_type( $p );
2563
-
2564
- if ( ! empty( $aioseop_options[ "{$prefix}{$post_type}_title_format" ] ) ) {
2565
- $title_format = $aioseop_options[ "{$prefix}{$post_type}_title_format" ];
2566
- }
2567
- }
2568
- }
2569
-
2570
- return $title_format;
2571
- }
2572
-
2573
- /**
2574
- * Is Singular
2575
- *
2576
- * @since ?
2577
- *
2578
- * @param array $post_types
2579
- * @param null $post
2580
- * @return bool
2581
- */
2582
- function is_singular( $post_types = array(), $post = null ) {
2583
- if ( ! empty( $post_types ) && is_object( $post ) ) {
2584
- return in_array( $post->post_type, (array) $post_types );
2585
- } else {
2586
- return is_singular( $post_types );
2587
- }
2588
- }
2589
-
2590
- /**
2591
- * Is Static Posts Page
2592
- *
2593
- * @since ?
2594
- *
2595
- * @return bool|null
2596
- */
2597
- function is_static_posts_page() {
2598
- static $is_posts_page = null;
2599
- if ( null !== $is_posts_page ) {
2600
- return $is_posts_page;
2601
- }
2602
- $post = $this->get_queried_object();
2603
- $is_posts_page = ( 'page' == get_option( 'show_on_front' ) && is_home() && ! empty( $post ) && get_option( 'page_for_posts' ) == $post->ID );
2604
-
2605
- return $is_posts_page;
2606
- }
2607
-
2608
- /**
2609
- * Is Static Front Page
2610
- *
2611
- * @since ?
2612
- *
2613
- * @return bool|null
2614
- */
2615
- function is_static_front_page() {
2616
- if ( isset( $this->is_front_page ) && null !== $this->is_front_page ) {
2617
- return $this->is_front_page;
2618
- }
2619
- $post = $this->get_queried_object();
2620
- $this->is_front_page = ( 'page' == get_option( 'show_on_front' ) && is_page() && ! empty( $post ) && get_option( 'page_on_front' ) == $post->ID );
2621
-
2622
- return $this->is_front_page;
2623
- }
2624
-
2625
- /**
2626
- * Get All Categories
2627
- *
2628
- * @since ?
2629
- *
2630
- * @param int $id
2631
- * @return array
2632
- */
2633
- function get_all_categories( $id = 0 ) {
2634
- $keywords = array();
2635
- $categories = get_the_category( $id );
2636
- if ( ! empty( $categories ) ) {
2637
- foreach ( $categories as $category ) {
2638
- $keywords[] = $this->internationalize( $category->cat_name );
2639
- }
2640
- }
2641
-
2642
- return $keywords;
2643
- }
2644
-
2645
- /**
2646
- * Get Taxonomy Title
2647
- *
2648
- * @since ?
2649
- *
2650
- * @param string $tax
2651
- * @return string
2652
- */
2653
- function get_tax_title( $tax = '' ) {
2654
-
2655
- if ( AIOSEOPPRO ) {
2656
- if ( empty( $this->meta_opts ) ) {
2657
- $this->meta_opts = $this->get_current_options( array(), 'aiosp' );
2658
- }
2659
- }
2660
- if ( empty( $tax ) ) {
2661
- if ( is_category() ) {
2662
- $tax = 'category';
2663
- } else {
2664
- $tax = get_query_var( 'taxonomy' );
2665
- }
2666
- }
2667
- $name = $this->get_tax_name( $tax );
2668
- $desc = $this->get_tax_desc( $tax );
2669
-
2670
- return $this->apply_tax_title_format( $name, $desc, $tax );
2671
- }
2672
-
2673
- /**
2674
- * Gets Taxonomy Name
2675
- *
2676
- * @param $tax
2677
- *
2678
- * @since ?
2679
- * @since 2.3.10 Remove option for capitalize categories. We still respect the option,
2680
- * and the default (true) or a legacy option in the db can be overridden with the new filter hook aioseop_capitalize_categories
2681
- * @since 2.3.15 Remove category capitalization completely
2682
- *
2683
- * @return mixed|void
2684
- */
2685
- function get_tax_name( $tax ) {
2686
- global $aioseop_options;
2687
- if ( AIOSEOPPRO ) {
2688
- $opts = $this->meta_opts;
2689
- if ( ! empty( $opts ) ) {
2690
- $name = $opts['aiosp_title'];
2691
- }
2692
- } else {
2693
- $name = '';
2694
- }
2695
- if ( empty( $name ) ) {
2696
- $name = single_term_title( '', false );
2697
- }
2698
-
2699
- return $this->internationalize( $name );
2700
- }
2701
-
2702
- /**
2703
- * Get Taxonomy Description
2704
- *
2705
- * @since ?
2706
- *
2707
- * @param $tax
2708
- * @return mixed|void
2709
- */
2710
- function get_tax_desc( $tax ) {
2711
- if ( AIOSEOPPRO ) {
2712
- $opts = $this->meta_opts;
2713
- if ( ! empty( $opts ) ) {
2714
- $desc = $opts['aiosp_description'];
2715
- }
2716
- } else {
2717
- $desc = '';
2718
- }
2719
- if ( empty( $desc ) ) {
2720
- $desc = wp_strip_all_tags( term_description( '', $tax ) );
2721
- }
2722
-
2723
- return $this->internationalize( $desc );
2724
- }
2725
-
2726
- /**
2727
- * Apply Taxonomy Title Format
2728
- *
2729
- * @since ?
2730
- *
2731
- * @param $category_name
2732
- * @param $category_description
2733
- * @param string $tax
2734
- * @return string
2735
- */
2736
- function apply_tax_title_format( $category_name, $category_description, $tax = '' ) {
2737
-
2738
- /**
2739
- * Runs before applying the formatting for the taxonomy title.
2740
- *
2741
- * @since 3.0
2742
- */
2743
- do_action( 'aioseop_before_tax_title_format' );
2744
-
2745
- if ( empty( $tax ) ) {
2746
- $tax = get_query_var( 'taxonomy' );
2747
- }
2748
- $title_format = $this->get_tax_title_format( $tax );
2749
- $title = str_replace( '%taxonomy_title%', $category_name, $title_format );
2750
- if ( false !== strpos( $title, '%taxonomy_description%', 0 ) ) {
2751
- $title = str_replace( '%taxonomy_description%', $category_description, $title );
2752
- }
2753
- if ( false !== strpos( $title, '%category_title%', 0 ) ) {
2754
- $title = str_replace( '%category_title%', $category_name, $title );
2755
- }
2756
- if ( false !== strpos( $title, '%category_description%', 0 ) ) {
2757
- $title = str_replace( '%category_description%', $category_description, $title );
2758
- }
2759
- if ( false !== strpos( $title, '%site_title%', 0 ) ) {
2760
- $title = str_replace( '%site_title%', $this->internationalize( get_bloginfo( 'name' ) ), $title );
2761
- }
2762
- if ( false !== strpos( $title, '%blog_title%', 0 ) ) {
2763
- $title = str_replace( '%blog_title%', $this->internationalize( get_bloginfo( 'name' ) ), $title );
2764
- }
2765
- if ( false !== strpos( $title, '%site_description%', 0 ) ) {
2766
- $title = str_replace( '%site_description%', $this->internationalize( get_bloginfo( 'description' ) ), $title );
2767
- }
2768
- if ( false !== strpos( $title, '%blog_description%', 0 ) ) {
2769
- $title = str_replace( '%blog_description%', $this->internationalize( get_bloginfo( 'description' ) ), $title );
2770
- }
2771
- if ( false !== strpos( $title, '%current_year%', 0 ) ) {
2772
- $title = str_replace( '%current_year%', date( 'Y' ), $title );
2773
- }
2774
- if ( false !== strpos( $title, '%current_month%', 0 ) ) {
2775
- $title = str_replace( '%current_month%', date( 'M' ), $title );
2776
- }
2777
- if ( false !== strpos( $title, '%current_month_i18n%', 0 ) ) {
2778
- $title = str_replace( '%current_month_i18n%', date_i18n( 'M' ), $title );
2779
- }
2780
-
2781
- /**
2782
- * Filters document title after applying the formatting.
2783
- *
2784
- * @since 3.0
2785
- *
2786
- * @param string $title Document title to be filtered.
2787
- */
2788
- $title = apply_filters( 'aioseop_title_format', $title );
2789
-
2790
- $title = wp_strip_all_tags( $title );
2791
-
2792
- /**
2793
- * Runs after applying the formatting for the taxonomy title.
2794
- *
2795
- * @since 3.0
2796
- */
2797
- do_action( 'aioseop_after_tax_title_format' );
2798
-
2799
- return $this->paged_title( $title );
2800
- }
2801
-
2802
- /**
2803
- * Get Taxonomy Title Format
2804
- *
2805
- * @since ?
2806
- *
2807
- * @param string $tax
2808
- * @return string
2809
- */
2810
- function get_tax_title_format( $tax = '' ) {
2811
- global $aioseop_options;
2812
- if ( AIOSEOPPRO ) {
2813
- $title_format = '%taxonomy_title% | %site_title%';
2814
- if ( is_category() ) {
2815
- $title_format = $aioseop_options['aiosp_category_title_format'];
2816
- } else {
2817
- $taxes = $aioseop_options['aiosp_taxactive'];
2818
- if ( empty( $tax ) ) {
2819
- $tax = get_query_var( 'taxonomy' );
2820
- }
2821
- if ( ! empty( $aioseop_options[ "aiosp_{$tax}_tax_title_format" ] ) ) {
2822
- $title_format = $aioseop_options[ "aiosp_{$tax}_tax_title_format" ];
2823
- }
2824
- }
2825
- if ( empty( $title_format ) ) {
2826
- $title_format = '%category_title% | %site_title%';
2827
- }
2828
- } else {
2829
- $title_format = '%category_title% | %site_title%';
2830
- if ( ! empty( $aioseop_options['aiosp_category_title_format'] ) ) {
2831
- $title_format = $aioseop_options['aiosp_category_title_format'];
2832
- }
2833
-
2834
- return $title_format;
2835
- }
2836
-
2837
- return $title_format;
2838
- }
2839
-
2840
- /**
2841
- * Apply Archive Title Format
2842
- *
2843
- * @since ?
2844
- *
2845
- * @param $title
2846
- * @param string $category
2847
- * @return string
2848
- */
2849
- function apply_archive_title_format( $title, $category = '' ) {
2850
- $title_format = $this->get_archive_title_format();
2851
- $r_title = array( '%site_title%', '%site_description%', '%archive_title%' );
2852
- $d_title = array(
2853
- $this->internationalize( get_bloginfo( 'name' ) ),
2854
- $this->internationalize( get_bloginfo( 'description' ) ),
2855
- post_type_archive_title( '', false ),
2856
- );
2857
- $title = trim( str_replace( $r_title, $d_title, $title_format ) );
2858
-
2859
- return $title;
2860
- }
2861
-
2862
- /**
2863
- * Get Archive Title Format
2864
- *
2865
- * @since ?
2866
- *
2867
- * @return bool|string
2868
- */
2869
- function get_archive_title_format() {
2870
- return $this->get_post_title_format( 'archive' );
2871
- }
2872
-
2873
- /**
2874
- * Get Main Description
2875
- *
2876
- * @since ?
2877
- * @since 2.3.14 #932 Adds filter "aioseop_description", removes extra filtering.
2878
- * @since 2.4 #951 Trim/truncates occurs inside filter "aioseop_description".
2879
- * @since 2.4.4.1 #1395 Longer Meta Descriptions & don't trim manual Descriptions.
2880
- *
2881
- * @param null $post
2882
- * @return mixed|string|void
2883
- */
2884
- function get_main_description( $post = null ) {
2885
- global $aioseop_options;
2886
- $opts = $this->meta_opts;
2887
- $description = '';
2888
- if ( is_author() && $this->show_page_description() ) {
2889
- $description = $this->internationalize( get_the_author_meta( 'description' ) );
2890
- } elseif ( function_exists( 'wc_get_page_id' ) && is_post_type_archive( 'product' ) ) {
2891
- $post_id = wc_get_page_id( 'shop' );
2892
- if ( $post_id ) {
2893
- $post = get_post( $post_id );
2894
- // $description = $this->get_post_description( $post );
2895
- // $description = $this->apply_cf_fields( $description );
2896
- if ( ! ( wc_get_page_id( 'shop' ) == get_option( 'page_on_front' ) ) ) {
2897
- $description = trim( $this->internationalize( get_post_meta( $post->ID, '_aioseop_description', true ) ) );
2898
- } elseif ( wc_get_page_id( 'shop' ) == get_option( 'page_on_front' ) && ! empty( $aioseop_options['aiosp_use_static_home_info'] ) ) {
2899
- // $description = $this->get_aioseop_description( $post );
2900
- $description = trim( $this->internationalize( get_post_meta( $post->ID, '_aioseop_description', true ) ) );
2901
- } elseif ( wc_get_page_id( 'shop' ) == get_option( 'page_on_front' ) && empty( $aioseop_options['aiosp_use_static_home_info'] ) ) {
2902
- $description = $this->get_aioseop_description( $post );
2903
- }
2904
- }
2905
- } elseif ( is_front_page() ) {
2906
- $description = $this->get_aioseop_description( $post );
2907
- } elseif ( is_single() || is_page() || is_attachment() || is_home() || $this->is_static_posts_page() || $this->check_singular() ) {
2908
- $description = $this->get_aioseop_description( $post );
2909
- } elseif ( ( is_category() || is_tag() || is_tax() ) && $this->show_page_description() ) {
2910
- if ( ! empty( $opts ) && AIOSEOPPRO ) {
2911
- $description = $opts['aiosp_description'];
2912
- }
2913
- if ( empty( $description ) ) {
2914
- $description = wp_strip_all_tags( term_description() );
2915
- }
2916
- $description = $this->internationalize( $description );
2917
- }
2918
-
2919
- // #1308 - we want to make sure we are ignoring php version only in the admin area while editing the post, so that it does not impact #932.
2920
- $screen = is_admin() ? get_current_screen() : null;
2921
- $ignore_php_version = $screen && isset( $screen->id ) && 'post' === $screen->id;
2922
-
2923
- $truncate = false;
2924
- $aioseop_desc = '';
2925
- if ( ! empty( $post->ID ) ) {
2926
- $aioseop_desc = get_post_meta( $post->ID, '_aioseop_description', true );
2927
- }
2928
-
2929
- if ( empty( $aioseop_desc ) && isset( $aioseop_options['aiosp_generate_descriptions'] ) && 'on' === $aioseop_options['aiosp_generate_descriptions'] && empty( $aioseop_options['aiosp_dont_truncate_descriptions'] ) ) {
2930
- $truncate = true;
2931
- }
2932
-
2933
- $description = apply_filters(
2934
- 'aioseop_description',
2935
- $description,
2936
- $truncate,
2937
- $ignore_php_version
2938
- );
2939
-
2940
- return $description;
2941
- }
2942
-
2943
- /**
2944
- * Show Page Description
2945
- *
2946
- * @since ?
2947
- *
2948
- * @return bool
2949
- */
2950
- function show_page_description() {
2951
- global $aioseop_options;
2952
- if ( ! empty( $aioseop_options['aiosp_hide_paginated_descriptions'] ) ) {
2953
- $page = aioseop_get_page_number();
2954
- if ( ! empty( $page ) && ( $page > 1 ) ) {
2955
- return false;
2956
- }
2957
- }
2958
-
2959
- return true;
2960
- }
2961
-
2962
- /**
2963
- * Get AIOSEOP Description
2964
- *
2965
- * @since ?
2966
- * @since 2.4 #1395 Longer Meta Descriptions & don't trim manual Descriptions.
2967
- *
2968
- * @param null $post
2969
- *
2970
- * @return mixed|string
2971
- */
2972
- function get_aioseop_description( $post = null ) {
2973
- global $aioseop_options;
2974
- if ( null === $post ) {
2975
- $post = $GLOBALS['post'];
2976
- }
2977
- $blog_page = aiosp_common::get_blog_page();
2978
- $description = '';
2979
- if ( is_front_page() && empty( $aioseop_options['aiosp_use_static_home_info'] ) ) {
2980
- $description = trim( $this->internationalize( $aioseop_options['aiosp_home_description'] ) );
2981
- } elseif ( ! empty( $blog_page ) ) {
2982
- $description = $this->get_post_description( $blog_page );
2983
- }
2984
- if ( empty( $description ) && is_object( $post ) && ! is_archive() && empty( $blog_page ) ) {
2985
- $description = $this->get_post_description( $post );
2986
- }
2987
- $description = $this->apply_cf_fields( $description );
2988
-
2989
- return $description;
2990
- }
2991
-
2992
- /**
2993
- * Gets Post Description
2994
- *
2995
- * Auto-generates description if settings are ON.
2996
- *
2997
- * @since 2.3.13 #899 Fixes non breacking space, applies filter "aioseop_description".
2998
- * @since 2.3.14 #932 Removes filter "aioseop_description".
2999
- * @since 2.4 #951 Removes "wp_strip_all_tags" and "trim_excerpt_without_filters", they are done later in filter.
3000
- * @since 2.4 #1395 Longer Meta Descriptions & don't trim manual Descriptions.
3001
- *
3002
- * @param object $post Post object.
3003
- * @return mixed|string
3004
- */
3005
- function get_post_description( $post ) {
3006
- global $aioseop_options;
3007
- if ( ! $this->show_page_description() ) {
3008
- return '';
3009
- }
3010
- $description = trim( $this->internationalize( get_post_meta( $post->ID, '_aioseop_description', true ) ) );
3011
- if ( ! empty( $post ) && post_password_required( $post ) ) {
3012
- return $description;
3013
- }
3014
- if ( ! $description && ! empty( $aioseop_options['aiosp_generate_descriptions'] ) ) {
3015
- if ( empty( $aioseop_options['aiosp_skip_excerpt'] ) ) {
3016
- $description = $post->post_excerpt;
3017
- }
3018
- if ( ! $description ) {
3019
- if ( ! AIOSEOPPRO || ( AIOSEOPPRO && apply_filters( 'aiosp_generate_descriptions_from_content', true, $post ) ) ) {
3020
- $content = $post->post_content;
3021
- if ( ! empty( $aioseop_options['aiosp_run_shortcodes'] ) ) {
3022
- $content = aioseop_do_shortcodes( $content );
3023
- }
3024
- $description = $content;
3025
- } else {
3026
- $description = $post->post_excerpt;
3027
- }
3028
- }
3029
-
3030
- $description = $this->trim_text_without_filters_full_length( $this->internationalize( $description ) );
3031
- }
3032
-
3033
- return $description;
3034
- }
3035
-
3036
- /**
3037
- * Trim Text without Filter Full Length
3038
- *
3039
- * @since ?
3040
- * @since 2.3.15 Brackets not longer replaced from filters.
3041
- *
3042
- * @param $text
3043
- * @return string
3044
- */
3045
- function trim_text_without_filters_full_length( $text ) {
3046
- $text = str_replace( ']]>', ']]&gt;', $text );
3047
- $text = strip_shortcodes( $text );
3048
- $text = wp_strip_all_tags( $text );
3049
-
3050
- return trim( $text );
3051
- }
3052
-
3053
- /**
3054
- * Trim Excerpt without Filters
3055
- *
3056
- * @since ?
3057
- * @since 2.3.15 Brackets not longer replaced from filters.
3058
- *
3059
- * @param $text
3060
- * @param int $max
3061
- * @return string
3062
- */
3063
- function trim_excerpt_without_filters( $text, $max = 0 ) {
3064
- $text = str_replace( ']]>', ']]&gt;', $text );
3065
- $text = strip_shortcodes( $text );
3066
- $text = wp_strip_all_tags( $text );
3067
- // Treat other common word-break characters like a space.
3068
- $text2 = preg_replace( '/[,._\-=+&!\?;:*]/s', ' ', $text );
3069
- if ( ! $max ) {
3070
- $max = $this->maximum_description_length;
3071
- }
3072
- $max_orig = $max;
3073
- $len = AIOSEOP_PHP_Functions::strlen( $text2 );
3074
- if ( $max < $len ) {
3075
- if ( function_exists( 'mb_strrpos' ) ) {
3076
- $pos = mb_strrpos( $text2, ' ', - ( $len - $max ), 'UTF-8' );
3077
- if ( false === $pos ) {
3078
- $pos = $max;
3079
- }
3080
- if ( $pos > $this->minimum_description_length ) {
3081
- $max = $pos;
3082
- } else {
3083
- $max = $this->minimum_description_length;
3084
- }
3085
- } else {
3086
- while ( ' ' != $text2[ $max ] && $max > $this->minimum_description_length ) {
3087
- $max --;
3088
- }
3089
- }
3090
-
3091
- // Probably no valid chars to break on?
3092
- if ( $len > $max_orig && $max < intval( $max_orig / 2 ) ) {
3093
- $max = $max_orig;
3094
- }
3095
- }
3096
- $text = AIOSEOP_PHP_Functions::substr( $text, 0, $max );
3097
-
3098
- return trim( $text );
3099
- }
3100
-
3101
- /**
3102
- * AIOSEOP Get URL
3103
- *
3104
- * @since ?
3105
- *
3106
- * @todo Change name to `*_get_url`.
3107
- *
3108
- * @param $query
3109
- * @param bool $show_page
3110
- * @return bool|false|string
3111
- */
3112
- function aiosp_mrt_get_url( $query, $show_page = true ) {
3113
- if ( $query->is_404 || $query->is_search ) {
3114
- return false;
3115
- }
3116
-
3117
- // this boolean will determine if any additional parameters will be added to the final link or not.
3118
- // this is especially useful in issues such as #491.
3119
- $add_query_params = false;
3120
- $link = '';
3121
- $haspost = false;
3122
- if ( ! empty( $query->posts ) ) {
3123
- $haspost = count( $query->posts ) > 0;
3124
- }
3125
-
3126
- if ( get_query_var( 'm' ) ) {
3127
- $m = preg_replace( '/[^0-9]/', '', get_query_var( 'm' ) );
3128
- switch ( AIOSEOP_PHP_Functions::strlen( $m ) ) {
3129
- case 4:
3130
- $link = get_year_link( $m );
3131
- break;
3132
- case 6:
3133
- $link = get_month_link( AIOSEOP_PHP_Functions::substr( $m, 0, 4 ), AIOSEOP_PHP_Functions::substr( $m, 4, 2 ) );
3134
- break;
3135
- case 8:
3136
- $link = get_day_link( AIOSEOP_PHP_Functions::substr( $m, 0, 4 ), AIOSEOP_PHP_Functions::substr( $m, 4, 2 ), AIOSEOP_PHP_Functions::substr( $m, 6, 2 ) );
3137
- break;
3138
- default:
3139
- return false;
3140
- }
3141
- $add_query_params = true;
3142
- } elseif ( $query->is_home && ( get_option( 'show_on_front' ) == 'page' ) ) {
3143
- $pageid = get_option( 'page_for_posts' );
3144
- if ( $pageid ) {
3145
- $link = aioseop_get_permalink( $pageid );
3146
- }
3147
- } elseif ( is_front_page() || ( $query->is_home && ( get_option( 'show_on_front' ) != 'page' || ! get_option( 'page_for_posts' ) ) ) ) {
3148
- if ( function_exists( 'icl_get_home_url' ) ) {
3149
- $link = icl_get_home_url();
3150
- } else {
3151
- $link = trailingslashit( home_url() );
3152
- }
3153
- } elseif ( ( $query->is_single || $query->is_page ) && $haspost ) {
3154
- $post = $query->posts[0];
3155
- $link = aioseop_get_permalink( $post->ID );
3156
- } elseif ( $query->is_author && $haspost ) {
3157
- $author = get_userdata( get_query_var( 'author' ) );
3158
- if ( false === $author ) {
3159
- return false;
3160
- }
3161
- $link = get_author_posts_url( $author->ID, $author->user_nicename );
3162
- } elseif ( $query->is_category && $haspost ) {
3163
- $link = get_category_link( get_query_var( 'cat' ) );
3164
- } elseif ( $query->is_tag && $haspost ) {
3165
- $tag = get_term_by( 'slug', get_query_var( 'tag' ), 'post_tag' );
3166
- if ( ! empty( $tag->term_id ) ) {
3167
- $link = get_tag_link( $tag->term_id );
3168
- }
3169
- } elseif ( $query->is_day && $haspost ) {
3170
- $link = get_day_link( get_query_var( 'year' ), get_query_var( 'monthnum' ), get_query_var( 'day' ) );
3171
- $add_query_params = true;
3172
- } elseif ( $query->is_month && $haspost ) {
3173
- $link = get_month_link( get_query_var( 'year' ), get_query_var( 'monthnum' ) );
3174
- $add_query_params = true;
3175
- } elseif ( $query->is_year && $haspost ) {
3176
- $link = get_year_link( get_query_var( 'year' ) );
3177
- $add_query_params = true;
3178
- } elseif ( $query->is_tax && $haspost ) {
3179
- $taxonomy = get_query_var( 'taxonomy' );
3180
- $term = get_query_var( 'term' );
3181
- if ( ! empty( $term ) ) {
3182
- $link = get_term_link( $term, $taxonomy );
3183
- }
3184
- } elseif ( $query->is_archive && function_exists( 'get_post_type_archive_link' ) ) {
3185
- $post_type = get_query_var( 'post_type' );
3186
- if ( $post_type && is_array( $post_type ) ) {
3187
- $post_type = reset( $post_type );
3188
- }
3189
- $link = get_post_type_archive_link( $post_type );
3190
- } else {
3191
- return false;
3192
- }
3193
- if ( empty( $link ) || ! is_string( $link ) ) {
3194
- return false;
3195
- }
3196
- if ( apply_filters( 'aioseop_canonical_url_pagination', $show_page ) ) {
3197
- $link = $this->get_paged( $link );
3198
- }
3199
-
3200
- if ( $add_query_params ) {
3201
- $post_type = get_query_var( 'post_type' );
3202
- if ( ! empty( $post_type ) ) {
3203
- $link = add_query_arg( 'post_type', $post_type, $link );
3204
- }
3205
- }
3206
-
3207
- return $link;
3208
- }
3209
-
3210
- /**
3211
- * Get Paged
3212
- *
3213
- * @since ?
3214
- *
3215
- * @param $link
3216
- * @return string
3217
- */
3218
- function get_paged( $link ) {
3219
- global $wp_rewrite;
3220
- $page = aioseop_get_page_number();
3221
- $page_name = 'page';
3222
- if ( ! empty( $wp_rewrite ) && ! empty( $wp_rewrite->pagination_base ) ) {
3223
- $page_name = $wp_rewrite->pagination_base;
3224
- }
3225
- if ( ! empty( $page ) && $page > 1 ) {
3226
- if ( get_query_var( 'page' ) == $page ) {
3227
- if ( get_query_var( 'p' ) ) {
3228
- // non-pretty urls.
3229
- $link = add_query_arg( 'page', $page, $link );
3230
- } else {
3231
- $link = trailingslashit( $link ) . "$page";
3232
- }
3233
- } else {
3234
- if ( get_query_var( 'p' ) ) {
3235
- // non-pretty urls.
3236
- $link = add_query_arg( 'page', $page, trailingslashit( $link ) . $page_name );
3237
- } else {
3238
- $link = trailingslashit( $link ) . trailingslashit( $page_name ) . $page;
3239
- }
3240
- }
3241
- $link = user_trailingslashit( $link, 'paged' );
3242
- }
3243
-
3244
- return $link;
3245
- }
3246
-
3247
- /**
3248
- * Get Main Keywords
3249
- *
3250
- * @since ?
3251
- *
3252
- * @return comma|string
3253
- */
3254
- function get_main_keywords() {
3255
- global $aioseop_options;
3256
- global $aioseop_keywords;
3257
- global $post;
3258
-
3259
- $opts = $this->meta_opts;
3260
- $blog_page = aiosp_common::get_blog_page( $post );
3261
- if ( ( is_front_page() && $aioseop_options['aiosp_home_keywords'] && ! $this->is_static_posts_page() ) || $this->is_static_front_page() ) {
3262
- if ( ! empty( $aioseop_options['aiosp_use_static_home_info'] ) ) {
3263
- $keywords = $this->get_all_keywords();
3264
- } else {
3265
- $keywords = trim( $this->internationalize( $aioseop_options['aiosp_home_keywords'] ) );
3266
- }
3267
- } elseif ( empty( $aioseop_options['aiosp_dynamic_postspage_keywords'] ) && $this->is_static_posts_page() ) {
3268
- $keywords = stripslashes( $this->internationalize( $opts['aiosp_keywords'] ) ); // And if option = use page set keywords instead of keywords from recent posts.
3269
- } elseif ( $blog_page && empty( $aioseop_options['aiosp_dynamic_postspage_keywords'] ) ) {
3270
- $keywords = stripslashes( $this->internationalize( get_post_meta( $blog_page->ID, '_aioseop_keywords', true ) ) );
3271
- } elseif ( empty( $aioseop_options['aiosp_dynamic_postspage_keywords'] ) && ( is_archive() || is_post_type_archive() ) ) {
3272
- $keywords = '';
3273
- } else {
3274
- $keywords = $this->get_all_keywords();
3275
- }
3276
-
3277
- return $keywords;
3278
- }
3279
-
3280
- /**
3281
- * Get All Keywords
3282
- *
3283
- * @since ?
3284
- *
3285
- * @return string|null comma-separated list of unique keywords
3286
- */
3287
- function get_all_keywords() {
3288
- global $posts;
3289
- global $aioseop_options;
3290
- if ( is_404() ) {
3291
- return null;
3292
- }
3293
- // If we are on synthetic pages.
3294
- if ( ! is_home() && ! is_page() && ! is_single() && ! $this->is_static_front_page() && ! $this->is_static_posts_page() && ! is_archive() && ! is_post_type_archive() && ! is_category() && ! is_tag() && ! is_tax() && ! $this->check_singular() ) {
3295
- return null;
3296
- }
3297
- $keywords = array();
3298
- $opts = $this->meta_opts;
3299
- if ( ! empty( $opts['aiosp_keywords'] ) ) {
3300
- $traverse = $this->keyword_string_to_list( $this->internationalize( $opts['aiosp_keywords'] ) );
3301
- if ( ! empty( $traverse ) ) {
3302
- foreach ( $traverse as $keyword ) {
3303
- $keywords[] = $keyword;
3304
- }
3305
- }
3306
- }
3307
- if ( empty( $posts ) ) {
3308
- global $post;
3309
- $post_arr = array( $post );
3310
- } else {
3311
- $post_arr = $posts;
3312
- }
3313
- if ( is_array( $post_arr ) ) {
3314
- $postcount = count( $post_arr );
3315
- foreach ( $post_arr as $p ) {
3316
- if ( $p ) {
3317
- $id = $p->ID;
3318
- if ( 1 == $postcount || ! empty( $aioseop_options['aiosp_dynamic_postspage_keywords'] ) ) {
3319
- // Custom field keywords.
3320
- $keywords_i = null;
3321
- $keywords_i = stripslashes( $this->internationalize( get_post_meta( $id, '_aioseop_keywords', true ) ) );
3322
- if ( is_attachment() ) {
3323
- $id = $p->post_parent;
3324
- if ( empty( $keywords_i ) ) {
3325
- $keywords_i = stripslashes( $this->internationalize( get_post_meta( $id, '_aioseop_keywords', true ) ) );
3326
- }
3327
- }
3328
- $traverse = $this->keyword_string_to_list( $keywords_i );
3329
- if ( ! empty( $traverse ) ) {
3330
- foreach ( $traverse as $keyword ) {
3331
- $keywords[] = $keyword;
3332
- }
3333
- }
3334
- }
3335
-
3336
- if ( ! empty( $aioseop_options['aiosp_use_tags_as_keywords'] ) ) {
3337
- $keywords = array_merge( $keywords, $this->get_all_tags( $id ) );
3338
- }
3339
- // Autometa.
3340
- $autometa = stripslashes( get_post_meta( $id, 'autometa', true ) );
3341
- if ( isset( $autometa ) && ! empty( $autometa ) ) {
3342
- $autometa_array = explode( ' ', $autometa );
3343
- foreach ( $autometa_array as $e ) {
3344
- $keywords[] = $e;
3345
- }
3346
- }
3347
-
3348
- if ( isset( $aioseop_options['aiosp_use_categories'] ) && $aioseop_options['aiosp_use_categories'] && ! is_page() ) {
3349
- $keywords = array_merge( $keywords, $this->get_all_categories( $id ) );
3350
- }
3351
- }
3352
- }
3353
- }
3354
-
3355
- return $this->get_unique_keywords( $keywords );
3356
- }
3357
-
3358
- /**
3359
- * Keyword String to List
3360
- *
3361
- * @since ?
3362
- *
3363
- * @param $keywords
3364
- * @return array
3365
- */
3366
- function keyword_string_to_list( $keywords ) {
3367
- $traverse = array();
3368
- $keywords_i = str_replace( '"', '', $keywords );
3369
- if ( isset( $keywords_i ) && ! empty( $keywords_i ) ) {
3370
- $traverse = explode( ',', $keywords_i );
3371
- }
3372
-
3373
- return $traverse;
3374
- }
3375
-
3376
- /**
3377
- * Get All Tags
3378
- *
3379
- * @since ?
3380
- *
3381
- * @param int $id
3382
- * @return array
3383
- */
3384
- function get_all_tags( $id = 0 ) {
3385
- $keywords = array();
3386
- $tags = get_the_tags( $id );
3387
- if ( ! empty( $tags ) && is_array( $tags ) ) {
3388
- foreach ( $tags as $tag ) {
3389
- $keywords[] = $this->internationalize( $tag->name );
3390
- }
3391
- }
3392
- // Ultimate Tag Warrior integration.
3393
- global $utw;
3394
- if ( $utw ) {
3395
- $tags = $utw->GetTagsForPost( $p );
3396
- if ( is_array( $tags ) ) {
3397
- foreach ( $tags as $tag ) {
3398
- $tag = $tag->tag;
3399
- $tag = str_replace( '_', ' ', $tag );
3400
- $tag = str_replace( '-', ' ', $tag );
3401
- $tag = stripslashes( $tag );
3402
- $keywords[] = $tag;
3403
- }
3404
- }
3405
- }
3406
-
3407
- return $keywords;
3408
- }
3409
-
3410
- /**
3411
- * Get Unique Keywords
3412
- *
3413
- * @since ?
3414
- *
3415
- * @param $keywords
3416
- * @return string
3417
- */
3418
- function get_unique_keywords( $keywords ) {
3419
- return implode( ',', $this->clean_keyword_list( $keywords ) );
3420
- }
3421
-
3422
- /**
3423
- * Clean Keyword List
3424
- *
3425
- * @since ?
3426
- *
3427
- * @param $keywords
3428
- * @return array
3429
- */
3430
- function clean_keyword_list( $keywords ) {
3431
- $small_keywords = array();
3432
- if ( ! is_array( $keywords ) ) {
3433
- $keywords = $this->keyword_string_to_list( $keywords );
3434
- }
3435
- if ( ! empty( $keywords ) ) {
3436
- foreach ( $keywords as $word ) {
3437
- $small_keywords[] = trim( AIOSEOP_PHP_Functions::strtolower( $word ) );
3438
- }
3439
- }
3440
-
3441
- return array_unique( $small_keywords );
3442
- }
3443
-
3444
- /**
3445
- * Split Share Term
3446
- *
3447
- * @since ?
3448
- *
3449
- * @param $term_id
3450
- * @param $new_term_id
3451
- * @param string $term_taxonomy_id
3452
- * @param string $taxonomy
3453
- */
3454
- function split_shared_term( $term_id, $new_term_id, $term_taxonomy_id = '', $taxonomy = '' ) {
3455
- $terms = $this->get_all_term_data( $term_id );
3456
- if ( ! empty( $terms ) ) {
3457
- $new_terms = $this->get_all_term_data( $new_term_id );
3458
- if ( empty( $new_terms ) ) {
3459
- foreach ( $terms as $k => $v ) {
3460
- add_term_meta( $new_term_id, $k, $v, true );
3461
- }
3462
- add_term_meta( $term_id, '_aioseop_term_was_split', true, true );
3463
- }
3464
- }
3465
- }
3466
-
3467
- /**
3468
- * Get All Term Data
3469
- *
3470
- * @since ?
3471
- *
3472
- * @param $term_id
3473
- * @return array
3474
- */
3475
- function get_all_term_data( $term_id ) {
3476
- $terms = array();
3477
- $optlist = array(
3478
- 'keywords',
3479
- 'description',
3480
- 'title',
3481
- 'custom_link',
3482
- 'sitemap_exclude',
3483
- 'disable',
3484
- 'disable_analytics',
3485
- 'noindex',
3486
- 'nofollow',
3487
- 'sitemap_priority',
3488
- 'sitemap_frequency',
3489
- );
3490
- foreach ( $optlist as $f ) {
3491
- $meta = get_term_meta( $term_id, '_aioseop_' . $f, true );
3492
- if ( ! empty( $meta ) ) {
3493
- $terms[ '_aioseop_' . $f ] = $meta;
3494
- }
3495
- }
3496
-
3497
- return $terms;
3498
- }
3499
-
3500
- function add_page_icon() {
3501
- wp_enqueue_script( 'wp-pointer', false, array( 'jquery' ) );
3502
- wp_enqueue_style( 'wp-pointer' );
3503
- // $this->add_admin_pointers();
3504
- // TODO Enqueue script as a JS file.
3505
- ?>
3506
- <script>
3507
- function aioseop_show_pointer(handle, value) {
3508
- if (typeof( jQuery ) != 'undefined') {
3509
- var p_edge = 'bottom';
3510
- var p_align = 'center';
3511
- if (typeof( jQuery(value.pointer_target).pointer) != 'undefined') {
3512
- if (typeof( value.pointer_edge ) != 'undefined') p_edge = value.pointer_edge;
3513
- if (typeof( value.pointer_align ) != 'undefined') p_align = value.pointer_align;
3514
- jQuery(value.pointer_target).pointer({
3515
- content: value.pointer_text,
3516
- position: {
3517
- edge: p_edge,
3518
- align: p_align
3519
- },
3520
- close: function () {
3521
- jQuery.post(ajaxurl, {
3522
- pointer: handle,
3523
- action: 'dismiss-wp-pointer'
3524
- });
3525
- }
3526
- }).pointer('open');
3527
- }
3528
- }
3529
- }
3530
- <?php
3531
- if ( ! empty( $this->pointers ) ) {
3532
- ?>
3533
- if (typeof( jQuery ) != 'undefined') {
3534
- jQuery(document).ready(function () {
3535
- var admin_pointer;
3536
- var admin_index;
3537
- <?php
3538
- foreach ( $this->pointers as $k => $p ) {
3539
- if ( ! empty( $p['pointer_scope'] ) && ( 'global' === $p['pointer_scope'] ) ) {
3540
- ?>
3541
- admin_index = "<?php echo esc_attr( $k ); ?>";
3542
- admin_pointer = <?php echo json_encode( $p ); ?>;
3543
- aioseop_show_pointer(admin_index, admin_pointer);
3544
- <?php
3545
- }
3546
- }
3547
- ?>
3548
- });
3549
- }
3550
- <?php } ?>
3551
- </script>
3552
- <?php
3553
- }
3554
-
3555
- /*
3556
- * Admin Pointer function.
3557
- * Not in use at the moment. Below is an example of we can implement them.
3558
- *
3559
- function add_admin_pointers() {
3560
-
3561
- $pro = '';
3562
- if ( AIOSEOPPRO ) {
3563
- $pro = '-pro';
3564
- }
3565
-
3566
- $this->pointers['aioseop_menu_2640'] = array(
3567
- 'pointer_target' => "#toplevel_page_all-in-one-seo-pack$pro-aioseop_class",
3568
- 'pointer_text' => '<h3>' . __( 'Review Your Settings', 'all-in-one-seo-pack' )
3569
- . '</h3><p>' . sprintf( __( 'Welcome to version %1$s. Thank you for running the latest and greatest %2$s ever! Please review your settings, as we\'re always adding new features for you!', 'all-in-one-seo-pack' ), AIOSEOP_VERSION, AIOSEOP_PLUGIN_NAME ) . '</p>',
3570
- 'pointer_edge' => 'top',
3571
- 'pointer_align' => 'left',
3572
- 'pointer_scope' => 'global',
3573
- );
3574
- $this->filter_pointers();
3575
- }
3576
- */
3577
-
3578
- /**
3579
- * Add Page Hooks
3580
- *
3581
- * @since ?
3582
- */
3583
- function add_page_hooks() {
3584
-
3585
- global $aioseop_options;
3586
-
3587
- $post_objs = get_post_types( '', 'objects' );
3588
- $pt = array_keys( $post_objs );
3589
- $rempost = array( 'revision', 'nav_menu_item', 'custom_css', 'customize_changeset' ); // Don't show these built-in types as options for CPT SEO.
3590
- $pt = array_diff( $pt, $rempost );
3591
- $post_types = array();
3592
-
3593
- foreach ( $pt as $p ) {
3594
- if ( ! empty( $post_objs[ $p ]->label ) ) {
3595
- $post_types[ $p ] = $post_objs[ $p ]->label;
3596
- } else {
3597
- $post_types[ $p ] = $p;
3598
- }
3599
- }
3600
-
3601
- foreach ( $pt as $p ) {
3602
- if ( ! empty( $post_objs[ $p ]->label ) ) {
3603
- $all_post_types[ $p ] = $post_objs[ $p ]->label;
3604
- }
3605
- }
3606
-
3607
- if ( isset( $post_types['attachment'] ) ) {
3608
- /* translators: This refers to entries in the Media Library (images, videos, recordings and other files) and their attachment pages. */
3609
- $post_types['attachment'] = __( 'Media / Attachments', 'all-in-one-seo-pack' );
3610
- }
3611
- if ( isset( $all_post_types['attachment'] ) ) {
3612
- $all_post_types['attachment'] = __( 'Media / Attachments', 'all-in-one-seo-pack' );
3613
- }
3614
-
3615
- $taxes = get_taxonomies( '', 'objects' );
3616
- $tx = array_keys( $taxes );
3617
- $remtax = array( 'nav_menu', 'link_category', 'post_format' );
3618
- $tx = array_diff( $tx, $remtax );
3619
- $tax_types = array();
3620
- foreach ( $tx as $t ) {
3621
- if ( ! empty( $taxes[ $t ]->label ) ) {
3622
- $tax_types[ $t ] = $taxes[ $t ]->label;
3623
- } else {
3624
- $taxes[ $t ] = $t;
3625
- }
3626
- }
3627
-
3628
- /**
3629
- * Allows users to filter the taxonomies that are shown in the General Settings menu.
3630
- *
3631
- * @since 3.0.0
3632
- *
3633
- * @param array $tax_types All registered taxonomies.
3634
- */
3635
- $tax_types = apply_filters( 'aioseop_pre_tax_types_setting', $tax_types );
3636
-
3637
- $this->default_options['posttypecolumns']['initial_options'] = $post_types;
3638
- $this->default_options['cpostactive']['initial_options'] = $all_post_types;
3639
- $this->default_options['cpostnoindex']['initial_options'] = $post_types;
3640
- $this->default_options['cpostnofollow']['initial_options'] = $post_types;
3641
- if ( AIOSEOPPRO ) {
3642
- $this->default_options['taxactive']['initial_options'] = $tax_types;
3643
- }
3644
-
3645
- foreach ( $all_post_types as $p => $pt ) {
3646
- $field = $p . '_title_format';
3647
- $name = $post_objs[ $p ]->labels->singular_name;
3648
- if ( ! isset( $this->default_options[ $field ] ) ) {
3649
- $this->default_options[ $field ] = array(
3650
- /* translators: The title format is the template that is used to format the title for each post of a certain post type (Posts, Pages, etc.). */
3651
- 'name' => "$name " . __( 'Title Format', 'all-in-one-seo-pack' ) . "<br />($p)",
3652
- 'type' => 'text',
3653
- 'default' => '%post_title% | %site_title%',
3654
- 'condshow' => array(
3655
- 'aiosp_cpostactive\[\]' => $p,
3656
- ),
3657
- );
3658
- $this->layout['cpt']['options'][] = $field;
3659
- }
3660
- }
3661
- global $wp_roles;
3662
- if ( ! isset( $wp_roles ) ) {
3663
- $wp_roles = new WP_Roles();
3664
- }
3665
- $role_names = $wp_roles->get_names();
3666
- ksort( $role_names );
3667
- $this->default_options['ga_exclude_users']['initial_options'] = $role_names;
3668
-
3669
- unset( $tax_types['category'] );
3670
- unset( $tax_types['post_tag'] );
3671
- $this->default_options['tax_noindex']['initial_options'] = $tax_types;
3672
- if ( empty( $tax_types ) ) {
3673
- unset( $this->default_options['tax_noindex'] );
3674
- }
3675
-
3676
- if ( AIOSEOPPRO ) {
3677
- foreach ( $tax_types as $p => $pt ) {
3678
- $field = $p . '_tax_title_format';
3679
- $name = $pt;
3680
- if ( ! isset( $this->default_options[ $field ] ) ) {
3681
- $this->default_options[ $field ] = array(
3682
- /* translators: The taxonomy title format is the template that is used to format the title for each taxonomy term of a certain taxonomy (Categories, Tags, etc.). */
3683
- 'name' => "$name " . __( 'Taxonomy Title Format:', 'all-in-one-seo-pack' ),
3684
- 'type' => 'text',
3685
- 'default' => '%taxonomy_title% | %site_title%',
3686
- 'condshow' => array(
3687
- 'aiosp_taxactive\[\]' => $p,
3688
- ),
3689
- );
3690
- $this->layout['cpt']['options'][] = $field;
3691
- }
3692
- }
3693
- }
3694
- $this->setting_options();
3695
-
3696
- if ( AIOSEOPPRO ) {
3697
- global $aioseop_update_checker;
3698
- add_action( "{$this->prefix}update_options", array( $aioseop_update_checker, 'license_change_check' ), 10, 2 );
3699
- add_action( "{$this->prefix}settings_update", array( $aioseop_update_checker, 'update_check' ), 10, 2 );
3700
- }
3701
-
3702
- add_filter( "{$this->prefix}display_options", array( $this, 'filter_options' ), 10, 2 );
3703
- parent::add_page_hooks();
3704
- }
3705
-
3706
- function settings_page_init() {
3707
- add_filter( "{$this->prefix}submit_options", array( $this, 'filter_submit' ) );
3708
- }
3709
-
3710
- /**
3711
- * Admin Enqueue Styles All (Screens)
3712
- *
3713
- * Enqueue style on all admin screens.
3714
- *
3715
- * @since 2.9
3716
- *
3717
- * @param $hook_suffix
3718
- */
3719
- public function admin_enqueue_styles_all( $hook_suffix ) {
3720
- wp_enqueue_style(
3721
- 'aiosp_admin_style',
3722
- AIOSEOP_PLUGIN_URL . 'css/aiosp_admin.css',
3723
- array(),
3724
- AIOSEOP_VERSION
3725
- );
3726
- }
3727
-
3728
- /**
3729
- * Admin Enqueue Scripts
3730
- *
3731
- * @since 2.5.0
3732
- * @since 2.9 Refactor code to `admin_enqueue_scripts` hook, and move enqueue stylesheet to \All_in_One_SEO_Pack::admin_enqueue_styles_all().
3733
- *
3734
- * @uses All_in_One_SEO_Pack_Module::admin_enqueue_scripts();
3735
- *
3736
- * @param string $hook_suffix
3737
- */
3738
- public function admin_enqueue_scripts( $hook_suffix ) {
3739
- global $current_screen;
3740
- global $aioseop_options;
3741
-
3742
- add_filter( "{$this->prefix}display_settings", array( $this, 'filter_settings' ), 10, 3 );
3743
- add_filter( "{$this->prefix}display_options", array( $this, 'filter_options' ), 10, 2 );
3744
-
3745
- $count_chars_data = array();
3746
- switch ( $hook_suffix ) {
3747
- case 'term.php':
3748
- // Legacy code for taxonomy terms until we refactor all title format related code.
3749
- $count_chars_data['aiosp_title_extra'] = 0;
3750
- wp_enqueue_script(
3751
- 'aioseop-count-chars-old',
3752
- AIOSEOP_PLUGIN_URL . 'js/admin/aioseop-count-chars-old.js',
3753
- array(),
3754
- AIOSEOP_VERSION,
3755
- true
3756
- );
3757
- wp_localize_script( 'aioseop-count-chars-old', 'aioseop_count_chars', $count_chars_data );
3758
- break;
3759
- case 'post.php':
3760
- case 'post-new.php':
3761
- $title_format = $this->get_preview_snippet_title();
3762
- $extra_title_length = strlen( preg_replace( '/<span.*\/span>/', '', html_entity_decode( $title_format, ENT_QUOTES ) ) );
3763
-
3764
- $snippet_preview_data = array(
3765
- 'autogenerateDescriptions' => isset( $aioseop_options['aiosp_generate_descriptions'] ) ? $aioseop_options['aiosp_generate_descriptions'] : '',
3766
- 'skipExcerpt' => isset( $aioseop_options['aiosp_skip_excerpt'] ) ? $aioseop_options['aiosp_skip_excerpt'] : '',
3767
- 'dontTruncateDescriptions' => isset( $aioseop_options['aiosp_dont_truncate_descriptions'] ) ? $aioseop_options['aiosp_dont_truncate_descriptions'] : '',
3768
- );
3769
-
3770
- $count_chars_data['extraTitleLength'] = $extra_title_length;
3771
- $count_chars_data['autogenerateDescriptions'] = $aioseop_options['aiosp_generate_descriptions'];
3772
-
3773
- wp_enqueue_script(
3774
- 'aioseop-preview-snippet',
3775
- AIOSEOP_PLUGIN_URL . 'js/admin/aioseop-preview-snippet.js',
3776
- array(),
3777
- AIOSEOP_VERSION
3778
- );
3779
- wp_localize_script( 'aioseop-preview-snippet', 'aioseop_preview_snippet', $snippet_preview_data );
3780
-
3781
- /*
3782
- * @see XRegExp
3783
- * @link http://xregexp.com/
3784
- * @link https://github.com/slevithan/xregexp
3785
- */
3786
- wp_enqueue_script(
3787
- 'xregexp',
3788
- AIOSEOP_PLUGIN_URL . 'js/admin/xregexp-v3.2.0/xregexp-all.min.js',
3789
- array(),
3790
- AIOSEOP_VERSION
3791
- );
3792
- // No break required.
3793
- case 'toplevel_page_' . AIOSEOP_PLUGIN_DIRNAME . '/aioseop_class':
3794
- $count_chars_data['pluginDirName'] = AIOSEOP_PLUGIN_DIRNAME;
3795
- $count_chars_data['currentPage'] = $hook_suffix;
3796
-
3797
- wp_enqueue_script(
3798
- 'aioseop-admin-functions',
3799
- AIOSEOP_PLUGIN_URL . 'js/admin/aioseop-admin-functions.js',
3800
- array(),
3801
- AIOSEOP_VERSION
3802
- );
3803
-
3804
- wp_enqueue_script(
3805
- 'aioseop-count-chars',
3806
- AIOSEOP_PLUGIN_URL . 'js/admin/aioseop-count-chars.js',
3807
- array(),
3808
- AIOSEOP_VERSION,
3809
- true
3810
- );
3811
- wp_localize_script( 'aioseop-count-chars', 'aioseopCharacterCounter', $count_chars_data );
3812
- break;
3813
- default:
3814
- break;
3815
- }
3816
- parent::admin_enqueue_scripts( $hook_suffix );
3817
- }
3818
-
3819
- /**
3820
- * Filter Submit
3821
- *
3822
- * @since ?
3823
- *
3824
- * @param $submit
3825
- * @return mixed
3826
- */
3827
- function filter_submit( $submit ) {
3828
- $submit['Submit_Default'] = array(
3829
- 'type' => 'submit',
3830
- 'class' => 'aioseop_reset_settings_button button-secondary',
3831
- /* translators: This is the text of a button that allows users to reset the General Settings to their default values. */
3832
- 'value' => __( 'Reset General Settings to Defaults', 'all-in-one-seo-pack' ) . ' &raquo;',
3833
- );
3834
- $submit['Submit_All_Default'] = array(
3835
- 'type' => 'submit',
3836
- 'class' => 'aioseop_reset_settings_button button-secondary',
3837
- /* translators: This is the text of a button that allows users to reset all settings across the entire plugin to their default values. */
3838
- 'value' => __( 'Reset ALL Settings to Defaults', 'all-in-one-seo-pack' ) . ' &raquo;',
3839
- );
3840
-
3841
- return $submit;
3842
- }
3843
-
3844
- /**
3845
- * Reset Options
3846
- *
3847
- * Handle resetting options to defaults, but preserve the license key if pro.
3848
- *
3849
- * @since ?
3850
- *
3851
- * @param null $location
3852
- * @param bool $delete
3853
- */
3854
- function reset_options( $location = null, $delete = false ) {
3855
- if ( AIOSEOPPRO ) {
3856
- global $aioseop_update_checker;
3857
- }
3858
- if ( true === $delete ) {
3859
-
3860
- if ( AIOSEOPPRO ) {
3861
- $license_key = '';
3862
- if ( isset( $this->options ) && isset( $this->options['aiosp_license_key'] ) ) {
3863
- $license_key = $this->options['aiosp_license_key'];
3864
- }
3865
- }
3866
-
3867
- $this->delete_class_option( $delete );
3868
-
3869
- if ( AIOSEOPPRO ) {
3870
- $this->options = array( 'aiosp_license_key' => $license_key );
3871
- } else {
3872
- $this->options = array();
3873
- }
3874
- }
3875
- $default_options = $this->default_options( $location );
3876
-
3877
- if ( AIOSEOPPRO ) {
3878
- foreach ( $default_options as $k => $v ) {
3879
- if ( 'aiosp_license_key' != $k ) {
3880
- $this->options[ $k ] = $v;
3881
- }
3882
- }
3883
- $aioseop_update_checker->license_key = $this->options['aiosp_license_key'];
3884
- } else {
3885
- foreach ( $default_options as $k => $v ) {
3886
- $this->options[ $k ] = $v;
3887
- }
3888
- }
3889
- $this->update_class_option( $this->options );
3890
- }
3891
-
3892
- /**
3893
- * Filter Settings
3894
- *
3895
- * @since ?
3896
- * @since 2.3.16 Forces HTML entity decode on placeholder values.
3897
- *
3898
- * @param $settings
3899
- * @param $location
3900
- * @param $current
3901
- * @return mixed
3902
- */
3903
- function filter_settings( $settings, $location, $current ) {
3904
- if ( null == $location ) {
3905
- $prefix = $this->prefix;
3906
-
3907
- foreach ( array( 'seopostcol', 'seocustptcol', 'debug_info', 'max_words_excerpt' ) as $opt ) {
3908
- unset( $settings[ "{$prefix}$opt" ] );
3909
- }
3910
-
3911
- if ( AIOSEOPPRO ) {
3912
- if ( ! empty( $this->options['aiosp_license_key'] ) ) {
3913
- $settings['aiosp_license_key']['type'] = 'password';
3914
- $settings['aiosp_license_key']['size'] = 38;
3915
- }
3916
- }
3917
- } elseif ( 'aiosp' == $location ) {
3918
- global $post, $aioseop_sitemap;
3919
- $prefix = $this->get_prefix( $location ) . $location . '_';
3920
- if ( ! empty( $post ) ) {
3921
- $post_type = get_post_type( $post );
3922
- if ( ! empty( $this->options['aiosp_cpostnoindex'] ) && in_array( $post_type, $this->options['aiosp_cpostnoindex'] ) ) {
3923
- $settings[ "{$prefix}noindex" ]['type'] = 'select';
3924
- $settings[ "{$prefix}noindex" ]['initial_options'] = array(
3925
- /* translators: This indicates that the current post/page is using the default value for its post type, which is NOINDEX. */
3926
- '' => __( 'Default - noindex', 'all-in-one-seo-pack' ),
3927
- 'off' => __( 'index', 'all-in-one-seo-pack' ),
3928
- 'on' => __( 'noindex', 'all-in-one-seo-pack' ),
3929
- );
3930
- }
3931
- if ( ! empty( $this->options['aiosp_cpostnofollow'] ) && in_array( $post_type, $this->options['aiosp_cpostnofollow'] ) ) {
3932
- $settings[ "{$prefix}nofollow" ]['type'] = 'select';
3933
- $settings[ "{$prefix}nofollow" ]['initial_options'] = array(
3934
- /* translators: This indicates that the current post/page is using the default value for its post type, which is NOFOLLOW. */
3935
- '' => __( 'Default - nofollow', 'all-in-one-seo-pack' ),
3936
- 'off' => __( 'follow', 'all-in-one-seo-pack' ),
3937
- 'on' => __( 'nofollow', 'all-in-one-seo-pack' ),
3938
- );
3939
- }
3940
-
3941
- global $post;
3942
- $info = $this->get_page_snippet_info();
3943
-
3944
- $title = $info['title'];
3945
- $description = $info['description'];
3946
- $keywords = $info['keywords'];
3947
-
3948
- $settings[ "{$prefix}title" ]['placeholder'] = $this->html_entity_decode( $title );
3949
- $settings[ "{$prefix}description" ]['placeholder'] = $this->html_entity_decode( $description );
3950
- $settings[ "{$prefix}keywords" ]['placeholder'] = $keywords;
3951
- }
3952
-
3953
- if ( ! AIOSEOPPRO ) {
3954
- if ( ! current_user_can( 'update_plugins' ) ) {
3955
- unset( $settings[ "{$prefix}upgrade" ] );
3956
- }
3957
- }
3958
-
3959
- if ( ! is_object( $aioseop_sitemap ) ) {
3960
- unset( $settings['aiosp_sitemap_priority'] );
3961
- unset( $settings['aiosp_sitemap_frequency'] );
3962
- unset( $settings['aiosp_sitemap_exclude'] );
3963
- }
3964
-
3965
- if ( ! empty( $this->options[ $this->prefix . 'togglekeywords' ] ) ) {
3966
- unset( $settings[ "{$prefix}keywords" ] );
3967
- unset( $settings[ "{$prefix}togglekeywords" ] );
3968
- } elseif ( ! empty( $current[ "{$prefix}togglekeywords" ] ) ) {
3969
- unset( $settings[ "{$prefix}keywords" ] );
3970
- }
3971
- if ( empty( $this->options['aiosp_can'] ) ) {
3972
- unset( $settings[ "{$prefix}custom_link" ] );
3973
- }
3974
- }
3975
-
3976
- return $settings;
3977
- }
3978
-
3979
- /**
3980
- * Filter Options
3981
- *
3982
- * @since ?
3983
- *
3984
- * @param $options
3985
- * @param $location
3986
- * @return mixed
3987
- */
3988
- function filter_options( $options, $location ) {
3989
- if ( 'aiosp' == $location ) {
3990
- global $post;
3991
- if ( ! empty( $post ) ) {
3992
- $prefix = $this->prefix;
3993
- $post_type = get_post_type( $post );
3994
- foreach ( array( 'noindex', 'nofollow' ) as $no ) {
3995
- if ( empty( $this->options[ 'aiosp_cpost' . $no ] ) || ( ! in_array( $post_type, $this->options[ 'aiosp_cpost' . $no ] ) ) ) {
3996
- if ( isset( $options[ "{$prefix}{$no}" ] ) && ( 'on' != $options[ "{$prefix}{$no}" ] ) ) {
3997
- unset( $options[ "{$prefix}{$no}" ] );
3998
- }
3999
- }
4000
- }
4001
- }
4002
- }
4003
- if ( null == $location ) {
4004
- $prefix = $this->prefix;
4005
- if ( isset( $options[ "{$prefix}use_original_title" ] ) && ( '' === $options[ "{$prefix}use_original_title" ] ) ) {
4006
- $options[ "{$prefix}use_original_title" ] = 0;
4007
- }
4008
- }
4009
-
4010
- return $options;
4011
- }
4012
-
4013
- /**
4014
- * Template Redirect
4015
- *
4016
- * @since ?
4017
- */
4018
- function template_redirect() {
4019
- global $aioseop_options;
4020
-
4021
- $post = $this->get_queried_object();
4022
-
4023
- if ( ! $this->is_page_included() ) {
4024
- return;
4025
- }
4026
-
4027
- $force_rewrites = 1;
4028
- if ( isset( $aioseop_options['aiosp_force_rewrites'] ) ) {
4029
- $force_rewrites = $aioseop_options['aiosp_force_rewrites'];
4030
- }
4031
- if ( $force_rewrites ) {
4032
- ob_start( array( $this, 'output_callback_for_title' ) );
4033
- } else {
4034
- add_filter( 'wp_title', array( $this, 'wp_title' ), 20 );
4035
- }
4036
- }
4037
-
4038
- /**
4039
- * The is_page_included() function.
4040
- *
4041
- * Checks whether All in One SEO Pack is enabled for this page.
4042
- *
4043
- * @since ?
4044
- * @since 3.3 Show Google Analytics if post type isn't checked in options.
4045
- *
4046
- * @return bool
4047
- */
4048
- function is_page_included() {
4049
- global $aioseop_options;
4050
- if ( is_feed() ) {
4051
- return false;
4052
- }
4053
- if ( aioseop_mrt_exclude_this_page() ) {
4054
- return false;
4055
- }
4056
- $post = $this->get_queried_object();
4057
- $post_type = '';
4058
- if ( ! empty( $post ) && ! empty( $post->post_type ) ) {
4059
- $post_type = $post->post_type;
4060
- }
4061
-
4062
- $wp_post_types = $aioseop_options['aiosp_cpostactive'];
4063
- if ( empty( $wp_post_types ) ) {
4064
- $wp_post_types = array();
4065
- }
4066
- if ( AIOSEOPPRO ) {
4067
- if ( is_tax() ) {
4068
- if ( empty( $aioseop_options['aiosp_taxactive'] ) || ! is_tax( $aioseop_options['aiosp_taxactive'] ) ) {
4069
- return false;
4070
- }
4071
- } elseif ( is_category() ) {
4072
- if ( empty( $aioseop_options['aiosp_taxactive'] ) || ! in_array( 'category', $aioseop_options['aiosp_taxactive'] ) ) {
4073
- return false;
4074
- }
4075
- } elseif ( is_tag() ) {
4076
- if ( empty( $aioseop_options['aiosp_taxactive'] ) || ! in_array( 'post_tag', $aioseop_options['aiosp_taxactive'] ) ) {
4077
- return false;
4078
- }
4079
- } elseif ( ! in_array( $post_type, $wp_post_types ) && ! is_front_page() && ! is_post_type_archive( $wp_post_types ) && ! is_404() && ! is_search() ) {
4080
- return false;
4081
- }
4082
- } else {
4083
-
4084
- if ( is_singular() && ! in_array( $post_type, $wp_post_types ) && ! is_front_page() ) {
4085
- return false;
4086
- }
4087
- if ( is_post_type_archive() && ! is_post_type_archive( $wp_post_types ) ) {
4088
- return false;
4089
- }
4090
- }
4091
-
4092
- $this->meta_opts = $this->get_current_options( array(), 'aiosp' );
4093
-
4094
- $aiosp_disable = false;
4095
-
4096
- if ( ! empty( $this->meta_opts ) ) {
4097
- if ( isset( $this->meta_opts['aiosp_disable'] ) ) {
4098
- $aiosp_disable = $this->meta_opts['aiosp_disable'];
4099
- }
4100
- }
4101
-
4102
- $aiosp_disable = apply_filters( 'aiosp_disable', $aiosp_disable ); // API filter to disable AIOSEOP.
4103
-
4104
- if ( $aiosp_disable ) {
4105
- return false;
4106
- }
4107
-
4108
- if ( ! empty( $this->meta_opts ) && true == $this->meta_opts['aiosp_disable'] ) {
4109
- return false;
4110
- }
4111
-
4112
- return true;
4113
- }
4114
-
4115
- /**
4116
- * Output Callback for Title
4117
- *
4118
- * @since ?
4119
- *
4120
- * @param $content
4121
- * @return mixed|string
4122
- */
4123
- function output_callback_for_title( $content ) {
4124
- return $this->rewrite_title( $content );
4125
- }
4126
-
4127
- /**
4128
- * Rewrite Title
4129
- *
4130
- * Used for forcing title rewrites.
4131
- *
4132
- * @since ?
4133
- *
4134
- * @param $header
4135
- * @return mixed|string
4136
- */
4137
- function rewrite_title( $header ) {
4138
-
4139
- global $wp_query;
4140
- if ( ! $wp_query ) {
4141
- $header .= "<!-- AIOSEOP no wp_query found! -->\n";
4142
- return $header;
4143
- }
4144
-
4145
- // Check if we're in the main query to support bad themes and plugins.
4146
- $old_wp_query = null;
4147
- if ( ! $wp_query->is_main_query() ) {
4148
- $old_wp_query = $wp_query;
4149
- wp_reset_query();
4150
- }
4151
-
4152
- $title = $this->wp_title();
4153
- if ( ! empty( $title ) ) {
4154
- $header = $this->replace_title( $header, $title );
4155
- }
4156
-
4157
- if ( ! empty( $old_wp_query ) ) {
4158
- global $wp_query;
4159
- $wp_query = $old_wp_query;
4160
- // Change the query back after we've finished.
4161
- unset( $old_wp_query );
4162
- }
4163
- return $header;
4164
- }
4165
-
4166
- /**
4167
- * Replace Title
4168
- *
4169
- * @since ?
4170
- *
4171
- * @param $content
4172
- * @param $title
4173
- * @return mixed
4174
- */
4175
- function replace_title( $content, $title ) {
4176
- // We can probably improve this... I'm not sure half of this is even being used.
4177
- $title = trim( strip_tags( $title ) );
4178
- $title_tag_start = '<title';
4179
- $title_tag_end = '</title';
4180
- $start = AIOSEOP_PHP_Functions::strpos( $content, $title_tag_start, 0 );
4181
- $end = AIOSEOP_PHP_Functions::strpos( $content, $title_tag_end, 0 );
4182
- $this->title_start = $start;
4183
- $this->title_end = $end;
4184
- $this->orig_title = $title;
4185
-
4186
- return preg_replace( '/<title([^>]*?)\s*>([^<]*?)<\/title\s*>/is', '<title\\1>' . preg_replace( '/(\$|\\\\)(?=\d)/', '\\\\\1', strip_tags( $title ) ) . '</title>', $content, 1 );
4187
- }
4188
-
4189
- /**
4190
- * Add Hooks
4191
- *
4192
- * Adds WordPress hooks.
4193
- *
4194
- * @since ?
4195
- * @since 2.3.13 #899 Adds filter:aioseop_description.
4196
- * @since 2.3.14 #593 Adds filter:aioseop_title.
4197
- * @since 2.4 #951 Increases filter:aioseop_description arguments number.
4198
- */
4199
- function add_hooks() {
4200
- global $aioseop_options, $aioseop_update_checker;
4201
-
4202
- if ( is_admin() ) {
4203
- // this checks if the settiongs options exist and if they dont, it sets the defaults.
4204
- // let's do this only in backend.
4205
- aioseop_update_settings_check();
4206
- }
4207
- add_filter( 'user_contactmethods', 'aioseop_add_contactmethods' );
4208
- if ( is_user_logged_in() && is_admin_bar_showing() && current_user_can( 'aiosp_manage_seo' ) ) {
4209
- add_action( 'admin_bar_menu', array( $this, 'admin_bar_menu' ), 1000 );
4210
- }
4211
-
4212
- if ( is_admin() ) {
4213
- if ( is_multisite() ) {
4214
- add_action( 'network_admin_menu', array( $this, 'admin_menu' ) );
4215
- }
4216
- add_action( 'admin_menu', array( $this, 'admin_menu' ) );
4217
-
4218
- add_action( 'admin_head', array( $this, 'add_page_icon' ) );
4219
- add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_styles_all' ) );
4220
- add_action( 'admin_init', 'aioseop_addmycolumns', 1 );
4221
- add_action( 'admin_init', 'aioseop_handle_ignore_notice' );
4222
- add_action( 'shutdown', array( $this, 'check_recently_activated_modules' ), 99 );
4223
- if ( AIOSEOPPRO ) {
4224
- if ( current_user_can( 'update_plugins' ) ) {
4225
- add_action( 'admin_notices', array( $aioseop_update_checker, 'key_warning' ) );
4226
- }
4227
- add_action( 'admin_init', array( $this, 'checkIfLicensed' ) );
4228
- add_action( 'after_plugin_row_' . AIOSEOP_PLUGIN_BASENAME, array( $aioseop_update_checker, 'add_plugin_row' ) );
4229
- }
4230
- } else {
4231
- if ( '1' == $aioseop_options['aiosp_can'] || 'on' == $aioseop_options['aiosp_can'] ) {
4232
- remove_action( 'wp_head', 'rel_canonical' );
4233
- }
4234
- add_action( 'aioseop_modules_wp_head', array( $this, 'aiosp_google_analytics' ) );
4235
- add_action( 'wp_head', array( $this, 'wp_head' ), apply_filters( 'aioseop_wp_head_priority', 1 ) );
4236
- add_action( 'template_redirect', array( $this, 'template_redirect' ), 0 );
4237
- }
4238
- add_filter( 'aioseop_description', array( &$this, 'filter_description' ), 10, 3 );
4239
- add_filter( 'aioseop_title', array( &$this, 'filter_title' ) );
4240
-
4241
- // Plugin compatibility hooks.
4242
- // AMP.
4243
- $this->add_hooks_amp();
4244
-
4245
- // TODO Move WooCommerce hooks here from __construct().
4246
- }
4247
-
4248
- /**
4249
- * Add Hooks for AMP.
4250
- *
4251
- * @since 3.3.0
4252
- */
4253
- protected function add_hooks_amp() {
4254
- if ( is_admin() ) {
4255
- return;
4256
- }
4257
- global $aioseop_options;
4258
-
4259
- // Add AIOSEOP's output to AMP.
4260
- add_action( 'amp_post_template_head', array( $this, 'amp_head' ), 11 );
4261
-
4262
- /**
4263
- * AIOSEOP AMP Schema Enable/Disable
4264
- *
4265
- * Allows or prevents the use of schema on AMP generated posts/pages.
4266
- *
4267
- * @since 3.3.0
4268
- *
4269
- * @param bool $var True to enable, and false to disable.
4270
- */
4271
- $use_schema = apply_filters( 'aioseop_amp_schema', true );
4272
- if ( ! empty( $aioseop_options['aiosp_schema_markup'] ) && (bool) $aioseop_options['aiosp_schema_markup'] && $use_schema ) {
4273
- // Removes AMP's Schema data to prevent any conflicts/duplications with AIOSEOP's.
4274
- add_action( 'amp_post_template_head', array( $this, 'remove_hooks_amp_schema' ), 9 );
4275
- }
4276
- }
4277
-
4278
- /**
4279
- * Remove Hooks with AMP's Schema.
4280
- *
4281
- * @since 3.3.0
4282
- */
4283
- public function remove_hooks_amp_schema() {
4284
- // Remove AMP Schema hook used for outputting data.
4285
- remove_action( 'amp_post_template_head', 'amp_print_schemaorg_metadata' );
4286
- }
4287
-
4288
- /**
4289
- * Visibility Warning
4290
- *
4291
- * Checks if 'Search Engine Visibility' is enabled in Settings > Reading.
4292
- *
4293
- * @todo Change to earlier hook. Before `admin_enqueue` if possible.
4294
- *
4295
- * @since ?
4296
- * @since 3.0 Changed to AIOSEOP_Notices class.
4297
- *
4298
- * @see `self::constructor()` with 'all_admin_notices' Filter Hook
4299
- */
4300
- function visibility_warning() {
4301
- global $aioseop_notices;
4302
- if ( '0' === get_option( 'blog_public' ) ) {
4303
- $aioseop_notices->activate_notice( 'blog_public_disabled' );
4304
- } elseif ( '1' === get_option( 'blog_public' ) ) {
4305
- $aioseop_notices->deactivate_notice( 'blog_public_disabled' );
4306
- }
4307
- }
4308
-
4309
- /**
4310
- * Check the current PHP version and display a notice if on unsupported PHP.
4311
- *
4312
- * @since 3.4.0
4313
- */
4314
- function check_php_version() {
4315
- global $aioseop_notices;
4316
- $aioseop_notices->deactivate_notice( 'check_php_version' );
4317
-
4318
- // Display for PHP below 5.6
4319
- if ( version_compare( PHP_VERSION, '5.4', '>=' ) ) {
4320
- return;
4321
- }
4322
-
4323
- // Display for admins only.
4324
- if ( ! is_super_admin() ) {
4325
- return;
4326
- }
4327
-
4328
- // Display on Dashboard page only.
4329
- if ( isset( $GLOBALS['pagenow'] ) && 'index.php' !== $GLOBALS['pagenow'] ) {
4330
- return;
4331
- }
4332
-
4333
- $aioseop_notices->reset_notice( 'check_php_version' );
4334
- $aioseop_notices->activate_notice( 'check_php_version' );
4335
- }
4336
-
4337
- /**
4338
- * Review CTA
4339
- *
4340
- * Asks user if they are enjoying the plugin and subsequently points them to a different URL for a review.
4341
- *
4342
- * @since 3.4
4343
- *
4344
- * @see `self::constructor()` with 'all_admin_notices' Filter Hook
4345
- */
4346
- function review_plugin_cta() {
4347
- global $aioseop_notices;
4348
- $aioseop_notices->activate_notice( 'review_plugin_cta' );
4349
- }
4350
-
4351
- /**
4352
- * WooCommerce Upgrade Notice
4353
- *
4354
- * @since ?
4355
- * @since 3.0 Changed to AIOSEOP Notices.
4356
- */
4357
- public function woo_upgrade_notice() {
4358
- global $aioseop_notices;
4359
- if ( class_exists( 'WooCommerce' ) && ! AIOSEOPPRO ) {
4360
- $aioseop_notices->activate_notice( 'woocommerce_detected' );
4361
- } else {
4362
- global $aioseop_notices;
4363
- $aioseop_notices->deactivate_notice( 'woocommerce_detected' );
4364
- }
4365
- }
4366
-
4367
- /**
4368
- * Make Unique Attachment Description
4369
- *
4370
- * @since ?
4371
- *
4372
- * @param $description
4373
- * @return string
4374
- */
4375
- function make_unique_att_desc( $description ) {
4376
- global $wp_query;
4377
- if ( is_attachment() ) {
4378
-
4379
- $url = $this->aiosp_mrt_get_url( $wp_query );
4380
- $unique_desc = '';
4381
- if ( $url ) {
4382
- $matches = array();
4383
- preg_match_all( '/(\d+)/', $url, $matches );
4384
- if ( is_array( $matches ) ) {
4385
- $unique_desc = join( '', $matches[0] );
4386
- }
4387
- }
4388
- $description .= ' ' . $unique_desc;
4389
- }
4390
-
4391
- return $description;
4392
- }
4393
-
4394
- /**
4395
- * AMP Head
4396
- *
4397
- * Adds meta description to AMP pages.
4398
- *
4399
- * @todo Change void returns to empty string returns.
4400
- *
4401
- * @since 2.3.11.5
4402
- * @since 3.3.0 Fix loose comparator reading empty string in $description as false and returning. #2875
4403
- * @since 3.3.0 Add Schema to AMP. #506
4404
- *
4405
- * @return void
4406
- */
4407
- function amp_head() {
4408
- if ( ! $this->is_seo_enabled_for_cpt() ) {
4409
- return;
4410
- }
4411
-
4412
- $post = $this->get_queried_object();
4413
- /**
4414
- * AIOSEOP AMP Description.
4415
- *
4416
- * To disable AMP meta description just __return_false on the aioseop_amp_description filter.
4417
- *
4418
- * @since ?
4419
- *
4420
- * @param string $post_description
4421
- */
4422
- $description = apply_filters( 'aioseop_amp_description', $this->get_main_description( $post ) ); // Get the description.
4423
-
4424
- global $aioseop_options;
4425
-
4426
- // Handle the description format.
4427
- if ( isset( $description ) && false !== $description && ( AIOSEOP_PHP_Functions::strlen( $description ) > $this->minimum_description_length ) && ! ( is_front_page() && is_paged() ) ) {
4428
- $description = $this->trim_description( $description );
4429
- if ( ! isset( $meta_string ) ) {
4430
- $meta_string = '';
4431
- }
4432
- // Description format.
4433
- $description = apply_filters( 'aioseop_amp_description_full', $this->apply_description_format( $description, $post ) );
4434
- $desc_attr = '';
4435
- if ( ! empty( $aioseop_options['aiosp_schema_markup'] ) ) {
4436
- $desc_attr = '';
4437
- }
4438
- $desc_attr = apply_filters( 'aioseop_amp_description_attributes', $desc_attr );
4439
- $meta_string .= sprintf( "<meta name=\"description\" %s content=\"%s\" />\n", $desc_attr, $description );
4440
- }
4441
- if ( ! empty( $meta_string ) ) {
4442
- echo $meta_string;
4443
- }
4444
-
4445
- // Handle Schema.
4446
- /**
4447
- * AIOSEOP AMP Schema Enable/Disable
4448
- *
4449
- * Allows or prevents the use of schema on AMP generated posts/pages. Use __return_false to disable.
4450
- *
4451
- * @since 3.3.0
4452
- *
4453
- * @param bool $var True to enable, and false to disable.
4454
- */
4455
- $use_schema = apply_filters( 'aioseop_amp_schema', true );
4456
- if ( $use_schema && ! empty( $aioseop_options['aiosp_schema_markup'] ) && (bool) $aioseop_options['aiosp_schema_markup'] ) {
4457
- $aioseop_schema = new AIOSEOP_Schema_Builder();
4458
- $aioseop_schema->display_json_ld_head_script();
4459
- }
4460
- }
4461
-
4462
- /**
4463
- * Is SEO Enabled for CPT
4464
- *
4465
- * Checks whether the current CPT should show the SEO tags.
4466
- *
4467
- * @since 2.9.0
4468
- *
4469
- * @todo Remove this as it is only a simple boolean check.
4470
- *
4471
- * @return bool
4472
- */
4473
- private function is_seo_enabled_for_cpt() {
4474
- global $aioseop_options;
4475
- return empty( $post_type ) || in_array( get_post_type(), $aioseop_options['aiosp_cpostactive'], true );
4476
- }
4477
-
4478
- /**
4479
- * Checks to see if Google Analytics should be excluded from the current page.
4480
- *
4481
- * Looks at both the individual post settings and the General Settings.
4482
- *
4483
- * @since 3.3.0
4484
- *
4485
- * @return bool
4486
- */
4487
- function analytics_excluded() {
4488
-
4489
- $this->meta_opts = $this->get_current_options( array(), 'aiosp' ); // Get page-specific options.
4490
-
4491
- $aiosp_disable_analytics = false;
4492
-
4493
- if ( isset( $this->meta_opts['aiosp_disable_analytics'] ) ) {
4494
- $aiosp_disable_analytics = $this->meta_opts['aiosp_disable_analytics'];
4495
- }
4496
-
4497
- if ( $aiosp_disable_analytics || ! aioseop_option_isset( 'aiosp_google_analytics_id' ) ) {
4498
- return true;
4499
- }
4500
- return false;
4501
- }
4502
-
4503
- /**
4504
- * WP Head
4505
- *
4506
- * @since ?
4507
- * @since 2.3.14 #932 Removes filter "aioseop_description".
4508
- */
4509
- function wp_head() {
4510
- // Check if we're in the main query to support bad themes and plugins.
4511
- global $wp_query;
4512
- $old_wp_query = null;
4513
- if ( ! $wp_query->is_main_query() ) {
4514
- $old_wp_query = $wp_query;
4515
- wp_reset_query();
4516
- }
4517
-
4518
- if ( ! $this->is_page_included() ) {
4519
-
4520
- $aioseop_robots_meta = new AIOSEOP_Robots_Meta();
4521
- $robots_meta = $aioseop_robots_meta->get_robots_meta_tag();
4522
-
4523
- if ( ! empty( $robots_meta ) ) {
4524
- echo $robots_meta;
4525
- }
4526
-
4527
- if ( ! empty( $old_wp_query ) ) {
4528
- // Change the query back after we've finished.
4529
- global $wp_query;
4530
- $wp_query = $old_wp_query;
4531
- unset( $old_wp_query );
4532
- }
4533
-
4534
- if ( ! $this->analytics_excluded() ) {
4535
- remove_action( 'aioseop_modules_wp_head', array( $this, 'aiosp_google_analytics' ) );
4536
- add_action( 'wp_head', array( $this, 'aiosp_google_analytics' ) );
4537
- }
4538
-
4539
- return;
4540
- }
4541
-
4542
- if ( ! $this->is_seo_enabled_for_cpt() ) {
4543
- return;
4544
- }
4545
-
4546
- $opts = $this->meta_opts;
4547
- global $aioseop_update_checker, $wp_query, $aioseop_options, $posts;
4548
- static $aioseop_dup_counter = 0;
4549
- $aioseop_dup_counter ++;
4550
-
4551
- if ( ! defined( 'AIOSEOP_UNIT_TESTING' ) && $aioseop_dup_counter > 1 ) {
4552
-
4553
- /* translators: %1$s, %2$s and %3$s are placeholders and should not be translated. %1$s expands to the name of the plugin, All in One SEO Pack, %2$s to the name of a filter function and %3$s is replaced with a number. */
4554
- echo "\n<!-- " . sprintf( __( 'Debug Warning: %1$s meta data was included again from %2$s filter. Called %3$s times!', 'all-in-one-seo-pack' ), AIOSEOP_PLUGIN_NAME, current_filter(), $aioseop_dup_counter ) . " -->\n";
4555
- if ( ! empty( $old_wp_query ) ) {
4556
- // Change the query back after we've finished.
4557
- global $wp_query;
4558
- $wp_query = $old_wp_query;
4559
- unset( $old_wp_query );
4560
- }
4561
-
4562
- return;
4563
- }
4564
- if ( is_home() && ! is_front_page() ) {
4565
- $post = aiosp_common::get_blog_page();
4566
- } else {
4567
- $post = $this->get_queried_object();
4568
- }
4569
- $meta_string = null;
4570
- $description = '';
4571
- // Logging - rewrite handler check for output buffering.
4572
- $this->check_rewrite_handler();
4573
-
4574
- printf( "\n<!-- " . AIOSEOP_PLUGIN_NAME . ' ' . $this->version );
4575
-
4576
- if ( $this->ob_start_detected ) {
4577
- echo 'ob_start_detected ';
4578
- }
4579
- echo "[$this->title_start,$this->title_end] ";
4580
- echo "-->\n";
4581
- if ( AIOSEOPPRO ) {
4582
- echo '<!-- ' . __( 'Debug String', 'all-in-one-seo-pack' ) . ': ' . $aioseop_update_checker->get_verification_code() . " -->\n";
4583
- }
4584
- $blog_page = aiosp_common::get_blog_page( $post );
4585
- $save_posts = $posts;
4586
-
4587
- // This outputs robots meta tags and custom canonical URl on WooCommerce product archive page.
4588
- // See Github issue https://github.com/awesomemotive/all-in-one-seo-pack/issues/755.
4589
- if ( function_exists( 'wc_get_page_id' ) && is_post_type_archive( 'product' ) ) {
4590
- $post_id = wc_get_page_id( 'shop' );
4591
- if ( $post_id ) {
4592
- $post = get_post( $post_id );
4593
-
4594
- global $posts;
4595
- $opts = $this->get_current_options( array(), 'aiosp', null, $post );
4596
- $this->meta_opts = $this->get_current_options( array(), 'aiosp', null, $post );
4597
- $posts = array();
4598
- $posts[] = $post;
4599
- }
4600
- }
4601
-
4602
- $posts = $save_posts;
4603
- // Handle the description format.
4604
- // We are not going to mandate that post description needs to be present because the content could be derived from a custom field too.
4605
- if ( ! ( is_front_page() && is_paged() ) ) {
4606
- $description = $this->get_main_description( $post ); // Get the description.
4607
- $description = $this->trim_description( $description );
4608
- if ( ! isset( $meta_string ) ) {
4609
- $meta_string = '';
4610
- }
4611
- // Description format.
4612
- $description = apply_filters( 'aioseop_description_full', $this->apply_description_format( $description, $post ) );
4613
- $desc_attr = '';
4614
- if ( ! empty( $aioseop_options['aiosp_schema_markup'] ) ) {
4615
- $desc_attr = '';
4616
- }
4617
- $desc_attr = apply_filters( 'aioseop_description_attributes', $desc_attr );
4618
- if ( ! empty( $description ) ) {
4619
- $meta_string .= sprintf( "<meta name=\"description\" %s content=\"%s\" />\n", $desc_attr, $description );
4620
- }
4621
- }
4622
- // Get the keywords.
4623
- $togglekeywords = 0;
4624
- if ( isset( $aioseop_options['aiosp_togglekeywords'] ) ) {
4625
- $togglekeywords = $aioseop_options['aiosp_togglekeywords'];
4626
- }
4627
- if ( 0 == $togglekeywords && ! ( is_front_page() && is_paged() ) ) {
4628
- $keywords = $this->get_main_keywords();
4629
- $keywords = $this->apply_cf_fields( $keywords );
4630
- $keywords = apply_filters( 'aioseop_keywords', $keywords );
4631
-
4632
- if ( isset( $keywords ) && ! empty( $keywords ) ) {
4633
- if ( isset( $meta_string ) ) {
4634
- $meta_string .= "\n";
4635
- }
4636
- $keywords = wp_filter_nohtml_kses( str_replace( '"', '', $keywords ) );
4637
- $key_attr = apply_filters( 'aioseop_keywords_attributes', '' );
4638
- $meta_string .= sprintf( "<meta name=\"keywords\" %s content=\"%s\" />\n", $key_attr, $keywords );
4639
- }
4640
- }
4641
-
4642
- $aioseop_robots_meta = new AIOSEOP_Robots_Meta();
4643
- $robots_meta = $aioseop_robots_meta->get_robots_meta_tag();
4644
-
4645
- if ( ! empty( $robots_meta ) ) {
4646
- $meta_string .= $robots_meta;
4647
- }
4648
-
4649
- // Handle site verification.
4650
- if ( is_front_page() ) {
4651
- foreach (
4652
- array(
4653
- 'google' => 'google-site-verification',
4654
- 'bing' => 'msvalidate.01',
4655
- 'pinterest' => 'p:domain_verify',
4656
- 'yandex' => 'yandex-verification',
4657
- 'baidu' => 'baidu-site-verification',
4658
- ) as $k => $v
4659
- ) {
4660
- if ( ! empty( $aioseop_options[ "aiosp_{$k}_verify" ] ) ) {
4661
- $meta_string .= '<meta name="' . $v . '" content="' . trim( strip_tags( $aioseop_options[ "aiosp_{$k}_verify" ] ) ) . '" />' . "\n";
4662
- }
4663
- }
4664
- }
4665
- $prev_next = $this->get_prev_next_links( $post );
4666
- $prev = apply_filters( 'aioseop_prev_link', $prev_next['prev'] );
4667
- $next = apply_filters( 'aioseop_next_link', $prev_next['next'] );
4668
- if ( ! empty( $prev ) ) {
4669
- $meta_string .= '<link rel="prev" href="' . esc_url( $prev ) . "\" />\n";
4670
- }
4671
- if ( ! empty( $next ) ) {
4672
- $meta_string .= '<link rel="next" href="' . esc_url( $next ) . "\" />\n";
4673
- }
4674
- if ( null != $meta_string ) {
4675
- echo "$meta_string\n";
4676
- }
4677
-
4678
- /**
4679
- * The aioseop_disable_schema filter hook.
4680
- *
4681
- * Used to disable schema.org output programatically.
4682
- *
4683
- * @since 3.2.8
4684
- *
4685
- * @return boolean
4686
- */
4687
- if ( ! apply_filters( 'aioseop_disable_schema', false ) ) {
4688
- // Handle Schema.
4689
- if ( version_compare( PHP_VERSION, '5.5', '>=' ) ) {
4690
- if ( ! empty( $aioseop_options['aiosp_schema_markup'] ) && boolval( $aioseop_options['aiosp_schema_markup'] ) ) { // phpcs:ignore PHPCompatibility.FunctionUse.NewFunctions.boolvalFound
4691
- $aioseop_schema = new AIOSEOP_Schema_Builder();
4692
- $aioseop_schema->display_json_ld_head_script();
4693
- }
4694
- } else {
4695
- if ( ! empty( $aioseop_options['aiosp_schema_markup'] ) && (bool) $aioseop_options['aiosp_schema_markup'] ) {
4696
- $aioseop_schema = new AIOSEOP_Schema_Builder();
4697
- $aioseop_schema->display_json_ld_head_script();
4698
- }
4699
- }
4700
- }
4701
-
4702
- // Handle canonical links.
4703
- $show_page = true;
4704
- if ( ! empty( $aioseop_options['aiosp_no_paged_canonical_links'] ) ) {
4705
- $show_page = false;
4706
- }
4707
-
4708
- if ( isset( $aioseop_options['aiosp_can'] ) && $aioseop_options['aiosp_can'] ) {
4709
- $url = '';
4710
- if ( ! empty( $opts['aiosp_custom_link'] ) && ! is_home() ) {
4711
- $url = $opts['aiosp_custom_link'];
4712
- if ( apply_filters( 'aioseop_canonical_url_pagination', $show_page ) ) {
4713
- $url = $this->get_paged( $url );
4714
- }
4715
- }
4716
- if ( empty( $url ) ) {
4717
- $url = $this->aiosp_mrt_get_url( $wp_query, $show_page );
4718
- }
4719
-
4720
- $url = $this->validate_url_scheme( $url );
4721
-
4722
- $url = apply_filters( 'aioseop_canonical_url', $url );
4723
- if ( ! empty( $url ) ) {
4724
- echo '<link rel="canonical" href="' . esc_url( $url ) . '" />' . "\n";
4725
- }
4726
- }
4727
- do_action( 'aioseop_modules_wp_head' );
4728
- echo sprintf( "<!-- %s -->\n", AIOSEOP_PLUGIN_NAME );
4729
-
4730
- if ( ! empty( $old_wp_query ) ) {
4731
- // Change the query back after we've finished.
4732
- global $wp_query;
4733
- $wp_query = $old_wp_query;
4734
- unset( $old_wp_query );
4735
- }
4736
-
4737
- }
4738
- /**
4739
- * Check Rewrite Handler
4740
- *
4741
- * @since ?
4742
- */
4743
- function check_rewrite_handler() {
4744
- global $aioseop_options;
4745
-
4746
- $force_rewrites = 1;
4747
- if ( isset( $aioseop_options['aiosp_force_rewrites'] ) ) {
4748
- $force_rewrites = $aioseop_options['aiosp_force_rewrites'];
4749
- }
4750
-
4751
- if ( $force_rewrites ) {
4752
- // Make the title rewrite as short as possible.
4753
- if ( function_exists( 'ob_list_handlers' ) ) {
4754
- $active_handlers = ob_list_handlers();
4755
- } else {
4756
- $active_handlers = array();
4757
- }
4758
- if (
4759
- sizeof( $active_handlers ) > 0 &&
4760
- AIOSEOP_PHP_Functions::strtolower( $active_handlers[ sizeof( $active_handlers ) - 1 ] ) == AIOSEOP_PHP_Functions::strtolower( 'All_in_One_SEO_Pack::output_callback_for_title' )
4761
- ) {
4762
- ob_end_flush();
4763
- } else {
4764
- $this->log( 'another plugin interfering?' );
4765
- // If we get here there *could* be trouble with another plugin :(.
4766
- $this->ob_start_detected = true;
4767
-
4768
- // Try alternate method -- pdb.
4769
- add_filter( 'wp_title', array( $this, 'wp_title' ), 20 );
4770
-
4771
- if ( function_exists( 'ob_list_handlers' ) ) {
4772
- foreach ( ob_list_handlers() as $handler ) {
4773
- $this->log( "detected output handler $handler" );
4774
- }
4775
- }
4776
- }
4777
- }
4778
- }
4779
-
4780
- /**
4781
- * Trim Description
4782
- *
4783
- * @since ?
4784
- *
4785
- * @param $description
4786
- * @return mixed|string
4787
- */
4788
- function trim_description( $description ) {
4789
- $description = trim( wp_strip_all_tags( $description ) );
4790
- $description = str_replace( '"', '&quot;', $description );
4791
- $description = str_replace( "\r\n", ' ', $description );
4792
- $description = str_replace( "\n", ' ', $description );
4793
-
4794
- return $description;
4795
- }
4796
-
4797
- /**
4798
- * Apply Description Format
4799
- *
4800
- * @since ?
4801
- *
4802
- * @param $description
4803
- * @param null $post
4804
- * @return mixed
4805
- */
4806
- function apply_description_format( $description, $post = null ) {
4807
-
4808
- /**
4809
- * Runs before applying the formatting for the meta description.
4810
- *
4811
- * @since 3.0
4812
- */
4813
- do_action( 'aioseop_before_apply_description_format' );
4814
-
4815
- global $aioseop_options;
4816
- $description_format = $aioseop_options['aiosp_description_format'];
4817
- if ( ! isset( $description_format ) || empty( $description_format ) ) {
4818
- $description_format = '%description%';
4819
- }
4820
- $description = str_replace( '%description%', apply_filters( 'aioseop_description_override', $description ), $description_format );
4821
- if ( false !== strpos( $description, '%site_title%', 0 ) ) {
4822
- $description = str_replace( '%site_title%', get_bloginfo( 'name' ), $description );
4823
- }
4824
- if ( false !== strpos( $description, '%blog_title%', 0 ) ) {
4825
- $description = str_replace( '%blog_title%', get_bloginfo( 'name' ), $description );
4826
- }
4827
- if ( false !== strpos( $description, '%site_description%', 0 ) ) {
4828
- $description = str_replace( '%site_description%', get_bloginfo( 'description' ), $description );
4829
- }
4830
- if ( false !== strpos( $description, '%blog_description%', 0 ) ) {
4831
- $description = str_replace( '%blog_description%', get_bloginfo( 'description' ), $description );
4832
- }
4833
- if ( false !== strpos( $description, '%wp_title%', 0 ) ) {
4834
- $description = str_replace( '%wp_title%', $this->get_original_title(), $description );
4835
- }
4836
- if ( false !== strpos( $description, '%post_title%', 0 ) ) {
4837
- $description = str_replace( '%post_title%', $this->get_aioseop_title( $post, false ), $description );
4838
- }
4839
- if ( false !== strpos( $description, '%current_date%', 0 ) ) {
4840
- $description = str_replace( '%current_date%', date_i18n( get_option( 'date_format' ) ), $description );
4841
- }
4842
- if ( false !== strpos( $description, '%current_year%', 0 ) ) {
4843
- $description = str_replace( '%current_year%', date( 'Y' ), $description );
4844
- }
4845
- if ( false !== strpos( $description, '%current_month%', 0 ) ) {
4846
- $description = str_replace( '%current_month%', date( 'M' ), $description );
4847
- }
4848
- if ( false !== strpos( $description, '%current_month_i18n%', 0 ) ) {
4849
- $description = str_replace( '%current_month_i18n%', date_i18n( 'M' ), $description );
4850
- }
4851
- if ( false !== strpos( $description, '%post_date%', 0 ) ) {
4852
- $description = str_replace( '%post_date%', get_the_date(), $description );
4853
- }
4854
- if ( false !== strpos( $description, '%post_year%', 0 ) ) {
4855
- $description = str_replace( '%post_year%', get_the_date( 'Y' ), $description );
4856
- }
4857
- if ( false !== strpos( $description, '%post_month%', 0 ) ) {
4858
- $description = str_replace( '%post_month%', get_the_date( 'F' ), $description );
4859
- }
4860
-
4861
- /*
4862
- * This was intended to make attachment descriptions unique if pulling from the parent... let's remove it and see if there are any problems
4863
- * on the roadmap is to have a better hierarchy for attachment description pulling
4864
- * if ($aioseop_options['aiosp_can']) $description = $this->make_unique_att_desc($description);
4865
- */
4866
- $description = $this->apply_cf_fields( $description );
4867
-
4868
- /**
4869
- * Runs after applying the formatting for the meta description.
4870
- *
4871
- * @since 3.0
4872
- */
4873
- do_action( 'aioseop_after_apply_description_format' );
4874
-
4875
- return esc_html( $description );
4876
- }
4877
-
4878
- /**
4879
- * Check Singular
4880
- *
4881
- * Determine if the post is 'like' singular. In some specific instances, such as when the Reply post type of bbpress is loaded in its own page,
4882
- * it reflects as singular intead of single
4883
- *
4884
- * @since 2.4.2
4885
- *
4886
- * @return bool
4887
- */
4888
- private function check_singular() {
4889
- global $wp_query, $post;
4890
- $is_singular = false;
4891
- if ( is_singular() ) {
4892
- // #1297 - support for bbpress 'reply' post type.
4893
- if ( $post && 'reply' === $post->post_type ) {
4894
- $is_singular = true;
4895
- }
4896
- }
4897
- return $is_singular;
4898
- }
4899
-
4900
- /**
4901
- * Get Previous/Next Links
4902
- *
4903
- * @since ?
4904
- *
4905
- * @param null $post
4906
- * @return array
4907
- */
4908
- function get_prev_next_links( $post = null ) {
4909
- $prev = '';
4910
- $next = '';
4911
- $page = aioseop_get_page_number();
4912
- if ( is_home() || is_archive() || is_paged() ) {
4913
- global $wp_query;
4914
- $max_page = $wp_query->max_num_pages;
4915
- if ( $page > 1 ) {
4916
- $prev = get_previous_posts_page_link();
4917
- }
4918
- if ( $page < $max_page ) {
4919
- $paged = $GLOBALS['paged'];
4920
- if ( ! is_single() ) {
4921
- if ( ! $paged ) {
4922
- $paged = 1;
4923
- }
4924
- $nextpage = intval( $paged ) + 1;
4925
- if ( ! $max_page || $max_page >= $nextpage ) {
4926
- $next = get_pagenum_link( $nextpage );
4927
- }
4928
- }
4929
- }
4930
- } elseif ( is_page() || is_single() ) {
4931
- $numpages = 1;
4932
- $multipage = 0;
4933
- $page = get_query_var( 'page' );
4934
- if ( ! $page ) {
4935
- $page = 1;
4936
- }
4937
- if ( is_single() || is_page() || is_feed() ) {
4938
- $more = 1;
4939
- }
4940
- $content = $post->post_content;
4941
- if ( false !== strpos( $content, '<!--nextpage-->', 0 ) ) {
4942
- if ( $page > 1 ) {
4943
- $more = 1;
4944
- }
4945
- $content = str_replace( "\n<!--nextpage-->\n", '<!--nextpage-->', $content );
4946
- $content = str_replace( "\n<!--nextpage-->", '<!--nextpage-->', $content );
4947
- $content = str_replace( "<!--nextpage-->\n", '<!--nextpage-->', $content );
4948
- // Ignore nextpage at the beginning of the content.
4949
- if ( 0 === strpos( $content, '<!--nextpage-->', 0 ) ) {
4950
- $content = substr( $content, 15 );
4951
- }
4952
- $pages = explode( '<!--nextpage-->', $content );
4953
- $numpages = count( $pages );
4954
- if ( $numpages > 1 ) {
4955
- $multipage = 1;
4956
- }
4957
- } else {
4958
- $page = null;
4959
- }
4960
- if ( ! empty( $page ) ) {
4961
- if ( $page > 1 ) {
4962
- // Cannot use `wp_link_page()` since it is for rendering purposes and has no control over the page number.
4963
- // TODO Investigate alternate wp concept. If none is found, keep private function in case of any future WP changes.
4964
- $prev = _wp_link_page( $page - 1 );
4965
- }
4966
- if ( $page + 1 <= $numpages ) {
4967
- // Cannot use `wp_link_page()` since it is for rendering purposes and has no control over the page number.
4968
- // TODO Investigate alternate wp concept. If none is found, keep private function in case of any future WP changes.
4969
- $next = _wp_link_page( $page + 1 );
4970
- }
4971
- }
4972
-
4973
- if ( ! empty( $prev ) ) {
4974
- $dom = new DOMDocument();
4975
- $dom->loadHTML( $prev );
4976
- $prev = $dom->getElementsByTagName( 'a' )->item( 0 )->getAttribute( 'href' );
4977
- }
4978
- if ( ! empty( $next ) ) {
4979
- $dom = new DOMDocument();
4980
- $dom->loadHTML( $next );
4981
- $next = $dom->getElementsByTagName( 'a' )->item( 0 )->getAttribute( 'href' );
4982
- }
4983
- }
4984
-
4985
- return array(
4986
- 'prev' => $prev,
4987
- 'next' => $next,
4988
- );
4989
- }
4990
-
4991
- /**
4992
- * Validate URL Scheme
4993
- *
4994
- * Validates whether the url should be https or http.
4995
- *
4996
- * Mainly we're just using this for canonical URLS, but eventually it may be useful for other things
4997
- *
4998
- * @since 2.3.5
4999
- * @since 2.3.11 Removed check for legacy protocol setting. Added filter.
5000
- *
5001
- * @param $url
5002
- * @return string $url
5003
- */
5004
- function validate_url_scheme( $url ) {
5005
-
5006
- // TODO we should check for the site setting in the case of auto.
5007
- global $aioseop_options;
5008
-
5009
- $scheme = apply_filters( 'aioseop_canonical_protocol', false );
5010
-
5011
- if ( 'http' === $scheme ) {
5012
- $url = preg_replace( '/^https:/i', 'http:', $url );
5013
- }
5014
- if ( 'https' === $scheme ) {
5015
- $url = preg_replace( '/^http:/i', 'https:', $url );
5016
- }
5017
-
5018
- return $url;
5019
- }
5020
-
5021
- /**
5022
- * Google Analytics
5023
- *
5024
- * @since ?
5025
- * @since 3.3.0 Added support for Google Analytics.
5026
- *
5027
- * @param $options
5028
- * @param $location
5029
- * @param $settings
5030
- * @return mixed
5031
- */
5032
- function aiosp_google_analytics() {
5033
- if ( AIOSEOPPRO ) {
5034
- new AIOSEOP_Pro_Google_Tag_Manager;
5035
- }
5036
- new aioseop_google_analytics;
5037
- }
5038
-
5039
- /**
5040
- * Saves the data of our metabox settings for a post.
5041
- *
5042
- * @since ?
5043
- * @since 3.4.0 Added support for priority/frequency + minor refactoring.
5044
- *
5045
- * @param int $id The ID of the post.
5046
- * @return bool Returns false if there is no POST data.
5047
- */
5048
- function save_post_data( $id ) {
5049
- $awmp_edit = null;
5050
- $nonce = null;
5051
-
5052
- if ( empty( $_POST ) ) {
5053
- return false;
5054
- }
5055
-
5056
- if ( isset( $_POST['aiosp_edit'] ) ) {
5057
- $awmp_edit = $_POST['aiosp_edit'];
5058
- }
5059
-
5060
- if ( isset( $_POST['nonce-aioseop-edit'] ) ) {
5061
- $nonce = $_POST['nonce-aioseop-edit'];
5062
- }
5063
-
5064
- if ( isset( $awmp_edit ) && ! empty( $awmp_edit ) && wp_verify_nonce( $nonce, 'edit-aioseop-nonce' ) ) {
5065
-
5066
- $optlist = array(
5067
- 'keywords',
5068
- 'description',
5069
- 'title',
5070
- 'custom_link',
5071
- 'sitemap_exclude',
5072
- 'disable',
5073
- 'disable_analytics',
5074
- 'noindex',
5075
- 'nofollow',
5076
- 'sitemap_priority',
5077
- 'sitemap_frequency',
5078
- );
5079
-
5080
- if ( empty( $this->options['aiosp_can'] ) ) {
5081
- unset( $optlist['custom_link'] );
5082
- }
5083
-
5084
- if ( ! AIOSEOPPRO ) {
5085
- $optlist = array_diff( $optlist, array( 'sitemap_priority', 'sitemap_frequency' ) );
5086
- }
5087
-
5088
- foreach ( $optlist as $optionName ) {
5089
- $value = isset( $_POST[ "aiosp_$optionName" ] ) ? $_POST[ "aiosp_$optionName" ] : '';
5090
- update_post_meta( $id, "_aioseop_$optionName", aioseop_sanitize( $value ) );
5091
- }
5092
- }
5093
- }
5094
-
5095
- /**
5096
- * Display Tabbed Metabox
5097
- *
5098
- * @since ?
5099
- *
5100
- * @param $post
5101
- * @param $metabox
5102
- */
5103
- function display_tabbed_metabox( $post, $metabox ) {
5104
- $tabs = $metabox['args'];
5105
- echo '<div class="aioseop_tabs">';
5106
- $header = $this->get_metabox_header( $tabs );
5107
- echo $header;
5108
- $active = '';
5109
- foreach ( $tabs as $m ) {
5110
- echo '<div id="' . $m['id'] . '" class="aioseop_tab"' . $active . '>';
5111
- if ( ! $active ) {
5112
- $active = ' style="display:none;"';
5113
- }
5114
- $m['args'] = $m['callback_args'];
5115
- $m['callback'][0]->{$m['callback'][1]}( $post, $m );
5116
- echo '</div>';
5117
- }
5118
- echo '</div>';
5119
- }
5120
-
5121
- /**
5122
- * Get Metabox Header
5123
- *
5124
- * @since ?
5125
- *
5126
- * @param $tabs
5127
- *
5128
- * @return string
5129
- */
5130
- function get_metabox_header( $tabs ) {
5131
- $header = '<ul class="aioseop_header_tabs hide">';
5132
- $active = ' active';
5133
- foreach ( $tabs as $t ) {
5134
- if ( $active ) {
5135
- /* translators: This is the name of the main tab of the All in One SEO Pack meta box that appears on the Edit screen. */
5136
- $title = __( 'Main Settings', 'all-in-one-seo-pack' );
5137
- } else {
5138
- $title = $t['title'];
5139
- }
5140
- $header .= '<li><label class="aioseop_header_nav"><a class="aioseop_header_tab' . $active . '" href="#' . $t['id'] . '">' . $title . '</a></label></li>';
5141
- $active = '';
5142
- }
5143
- $header .= '</ul>';
5144
-
5145
- return $header;
5146
- }
5147
-
5148
- /**
5149
- * Admin Bar Menu
5150
- *
5151
- * @since ?
5152
- */
5153
- function admin_bar_menu() {
5154
-
5155
- if ( apply_filters( 'aioseo_show_in_admin_bar', true ) === false ) {
5156
- // API filter hook to disable showing SEO in admin bar.
5157
- return;
5158
- }
5159
-
5160
- global $wp_admin_bar, $aioseop_admin_menu, $post, $aioseop_options;
5161
-
5162
- $toggle = '';
5163
- if ( isset( $_POST['aiosp_use_original_title'] ) && isset( $_POST['aiosp_admin_bar'] ) && AIOSEOPPRO ) {
5164
- $toggle = 'on';
5165
- }
5166
- if ( isset( $_POST['aiosp_use_original_title'] ) && ! isset( $_POST['aiosp_admin_bar'] ) && AIOSEOPPRO ) {
5167
- $toggle = 'off';
5168
- }
5169
-
5170
- if ( ( ! isset( $aioseop_options['aiosp_admin_bar'] ) && 'off' !== $toggle ) || ( ! empty( $aioseop_options['aiosp_admin_bar'] ) && 'off' !== $toggle ) || isset( $_POST['aiosp_admin_bar'] ) || true == apply_filters( 'aioseo_show_in_admin_bar', false ) ) {
5171
-
5172
- if ( apply_filters( 'aioseo_show_in_admin_bar', true ) === false ) {
5173
- // API filter hook to disable showing SEO in admin bar.
5174
- return;
5175
- }
5176
-
5177
- $menu_slug = plugin_basename( __FILE__ );
5178
-
5179
- $url = '';
5180
- if ( function_exists( 'menu_page_url' ) ) {
5181
- $url = menu_page_url( $menu_slug, 0 );
5182
- }
5183
- if ( empty( $url ) ) {
5184
- $url = esc_url( admin_url( 'admin.php?page=' . $menu_slug ) );
5185
- }
5186
-
5187
- // Check if there are new notifications.
5188
- $notifications = '';
5189
- $notices = new AIOSEOP_Notices();
5190
- if ( count( $notices->remote_notices ) ) {
5191
- $count = count( $notices->remote_notices ) < 10 ? count( $notices->remote_notices ) : '!';
5192
- $notifications = ' <div class="aioseo-menu-notification-counter"><span>' . $count . '</span></div>';
5193
- }
5194
-
5195
- $wp_admin_bar->add_menu(
5196
- array(
5197
- 'id' => AIOSEOP_PLUGIN_DIRNAME,
5198
- 'title' => '<span class="ab-icon aioseop-admin-bar-logo"></span>' . __( 'SEO', 'all-in-one-seo-pack' ) . $notifications,
5199
- )
5200
- );
5201
-
5202
- if ( $notifications ) {
5203
- $wp_admin_bar->add_menu(
5204
- array(
5205
- 'id' => 'aioseop-notifications',
5206
- 'parent' => AIOSEOP_PLUGIN_DIRNAME,
5207
- 'title' => __( 'Notifications', 'all-in-one-seo-pack' ) . $notifications,
5208
- 'href' => $url,
5209
- )
5210
- );
5211
- }
5212
-
5213
- if ( ! is_admin() ) {
5214
- $wp_admin_bar->add_menu(
5215
- array(
5216
- 'id' => 'aioseop-settings',
5217
- 'parent' => AIOSEOP_PLUGIN_DIRNAME,
5218
- 'title' => __( 'SEO Settings', 'all-in-one-seo-pack' ),
5219
- )
5220
- );
5221
- }
5222
-
5223
- $wp_admin_bar->add_menu(
5224
- array(
5225
- 'id' => 'aioseop-settings-general',
5226
- 'parent' => is_admin() ? AIOSEOP_PLUGIN_DIRNAME : 'aioseop-settings',
5227
- 'title' => __( 'General Settings', 'all-in-one-seo-pack' ),
5228
- 'href' => $url,
5229
- )
5230
- );
5231
-
5232
- if ( ! is_admin() ) {
5233
- AIOSEOP_Education::external_tools( $wp_admin_bar );
5234
- }
5235
-
5236
- $aioseop_admin_menu = 1;
5237
- if ( ! empty( $post ) ) {
5238
-
5239
- $blog_page = aiosp_common::get_blog_page( $post );
5240
- if ( ! empty( $blog_page ) ) {
5241
- $post = $blog_page;
5242
- }
5243
- // Don't show if we're on the home page and the home page is the latest posts.
5244
- if ( ! is_home() || ( ! is_front_page() && ! is_home() ) ) {
5245
- global $wp_the_query;
5246
- $current_object = $wp_the_query->get_queried_object();
5247
-
5248
- if ( is_singular() ) {
5249
- if ( ! empty( $current_object ) && ! empty( $current_object->post_type ) ) {
5250
- // Try the main query.
5251
- $edit_post_link = get_edit_post_link( $current_object->ID );
5252
- $wp_admin_bar->add_menu(
5253
- array(
5254
- 'id' => 'aiosp_edit_' . $current_object->ID,
5255
- 'parent' => AIOSEOP_PLUGIN_DIRNAME,
5256
- 'title' => 'Edit SEO',
5257
- 'href' => $edit_post_link . '#aiosp',
5258
- )
5259
- );
5260
- } else {
5261
- // Try the post object.
5262
- $wp_admin_bar->add_menu(
5263
- array(
5264
- 'id' => 'aiosp_edit_' . $post->ID,
5265
- 'parent' => AIOSEOP_PLUGIN_DIRNAME,
5266
- 'title' => __( 'Edit SEO', 'all-in-one-seo-pack' ),
5267
- 'href' => get_edit_post_link( $post->ID ) . '#aiosp',
5268
- )
5269
- );
5270
- }
5271
- }
5272
-
5273
- if ( AIOSEOPPRO && ( is_category() || is_tax() || is_tag() ) ) {
5274
- // SEO for taxonomies are only available in Pro version.
5275
- $edit_term_link = get_edit_term_link( $current_object->term_id, $current_object->taxonomy );
5276
- $wp_admin_bar->add_menu(
5277
- array(
5278
- 'id' => 'aiosp_edit_' . $post->ID,
5279
- 'parent' => AIOSEOP_PLUGIN_DIRNAME,
5280
- 'title' => __( 'Edit SEO', 'all-in-one-seo-pack' ),
5281
- 'href' => $edit_term_link . '#aiosp',
5282
- )
5283
- );
5284
- }
5285
- }
5286
- }
5287
-
5288
- if ( current_user_can( 'update_plugins' ) && ! AIOSEOPPRO ) {
5289
- $href = aioseop_get_utm_url( 'admin-bar' );
5290
-
5291
- $wp_admin_bar->add_menu(
5292
- array(
5293
- 'parent' => AIOSEOP_PLUGIN_DIRNAME,
5294
- 'title' => __( 'Upgrade to Pro', 'all-in-one-seo-pack' ),
5295
- 'id' => 'aioseop-pro-upgrade',
5296
- 'href' => $href,
5297
- 'meta' => array( 'target' => '_blank' ),
5298
- )
5299
- );
5300
- }
5301
- }
5302
- }
5303
-
5304
- /**
5305
- * Menu Order
5306
- *
5307
- * @since ?
5308
- *
5309
- * Order for adding the menus for the aioseop_modules_add_menus hook.
5310
- */
5311
- function menu_order() {
5312
- return 5;
5313
- }
5314
-
5315
- /**
5316
- * Displays our metabox for taxonomy terms.
5317
- *
5318
- * @since ?
5319
- * @since 3.4.0 Renamed function to better reflect purpose.
5320
- *
5321
- * @param $tax The taxonomy object.
5322
- */
5323
- function display_term_metabox( $tax ) {
5324
- $screen = 'edit-' . $tax->taxonomy;
5325
- ?>
5326
- <div id="poststuff">
5327
- <?php do_meta_boxes( '', 'advanced', $tax ); ?>
5328
- </div>
5329
- <?php
5330
- }
5331
-
5332
- /**
5333
- * Saves the data of our metabox settings for a taxonomy term.
5334
- *
5335
- * @since ?
5336
- * @since 3.4.0 Added support for priority/frequency + minor refactoring. Renamed function to better reflect purpose.
5337
- *
5338
- * @param int $id The ID of the taxonomy term.
5339
- * @return bool Returns false if there is no POST data.
5340
- */
5341
- function save_term_data( $id ) {
5342
- $awmp_edit = null;
5343
- $nonce = null;
5344
-
5345
- if ( isset( $_POST['aiosp_edit'] ) ) {
5346
- $awmp_edit = $_POST['aiosp_edit'];
5347
- }
5348
-
5349
- if ( isset( $_POST['nonce-aioseop-edit'] ) ) {
5350
- $nonce = $_POST['nonce-aioseop-edit'];
5351
- }
5352
-
5353
- if ( isset( $awmp_edit ) && ! empty( $awmp_edit ) && wp_verify_nonce( $nonce, 'edit-aioseop-nonce' ) ) {
5354
-
5355
- $optlist = array(
5356
- 'keywords',
5357
- 'description',
5358
- 'title',
5359
- 'custom_link',
5360
- 'disable',
5361
- 'disable_analytics',
5362
- 'noindex',
5363
- 'nofollow',
5364
- 'sitemap_exclude',
5365
- 'sitemap_priority',
5366
- 'sitemap_frequency',
5367
- );
5368
-
5369
- if ( empty( $this->options['aiosp_can'] ) ) {
5370
- unset( $optlist['custom_link'] );
5371
- }
5372
-
5373
- if ( ! AIOSEOPPRO ) {
5374
- $optlist = array_diff( $optlist, array( 'sitemap_priority', 'sitemap_frequency' ) );
5375
- }
5376
-
5377
-
5378
- foreach ( $optlist as $optionName ) {
5379
- $value = isset( $_POST[ "aiosp_$optionName" ] ) ? $_POST[ "aiosp_$optionName" ] : '';
5380
- update_term_meta( $id, "_aioseop_$optionName", aioseop_sanitize( $value ) );
5381
- }
5382
- }
5383
- }
5384
-
5385
-
5386
- /**
5387
- * Admin Menu
5388
- *
5389
- * @since ?
5390
- */
5391
- function admin_menu() {
5392
- $file = plugin_basename( __FILE__ );
5393
- $menu_name = 'All in One SEO';
5394
-
5395
- $this->locations['aiosp']['default_options']['nonce-aioseop-edit']['default'] = wp_create_nonce( 'edit-aioseop-nonce' );
5396
-
5397
- $custom_menu_order = false;
5398
- global $aioseop_options;
5399
- if ( ! isset( $aioseop_options['custom_menu_order'] ) ) {
5400
- $custom_menu_order = true;
5401
- }
5402
-
5403
- $this->update_options();
5404
-
5405
- if ( isset( $_POST ) && isset( $_POST['module'] ) && isset( $_POST['nonce-aioseop'] ) && ( 'All_in_One_SEO_Pack' == $_POST['module'] ) && wp_verify_nonce( $_POST['nonce-aioseop'], 'aioseop-nonce' ) ) {
5406
- if ( isset( $_POST['Submit'] ) && AIOSEOPPRO ) {
5407
- if ( isset( $_POST['aiosp_custom_menu_order'] ) ) {
5408
- $custom_menu_order = $_POST['aiosp_custom_menu_order'];
5409
- } else {
5410
- $custom_menu_order = false;
5411
- }
5412
- } elseif ( isset( $_POST['Submit_Default'] ) || isset( $_POST['Submit_All_Default'] ) ) {
5413
- $custom_menu_order = true;
5414
- }
5415
- } else {
5416
- if ( isset( $this->options['aiosp_custom_menu_order'] ) ) {
5417
- $custom_menu_order = $this->options['aiosp_custom_menu_order'];
5418
- }
5419
- }
5420
-
5421
- if ( ( $custom_menu_order && false !== apply_filters( 'aioseo_custom_menu_order', $custom_menu_order ) ) || true === apply_filters( 'aioseo_custom_menu_order', $custom_menu_order ) ) {
5422
- add_filter( 'custom_menu_order', '__return_true' );
5423
- add_filter( 'menu_order', array( $this, 'set_menu_order' ), 11 );
5424
- }
5425
-
5426
- if ( ! AIOSEOPPRO ) {
5427
- if ( ! empty( $this->pointers ) ) {
5428
- foreach ( $this->pointers as $k => $p ) {
5429
- if ( ! empty( $p['pointer_scope'] ) && ( 'global' == $p['pointer_scope'] ) ) {
5430
- unset( $this->pointers[ $k ] );
5431
- }
5432
- }
5433
- }
5434
-
5435
- $this->filter_pointers();
5436
- }
5437
-
5438
- if ( AIOSEOPPRO ) {
5439
- if ( is_array( $this->options['aiosp_cpostactive'] ) ) {
5440
- $this->locations['aiosp']['display'] = $this->options['aiosp_cpostactive'];
5441
- } else {
5442
- $this->locations['aiosp']['display'][] = $this->options['aiosp_cpostactive']; // Store as an array in case there are taxonomies to add also.
5443
- }
5444
-
5445
- if ( ! empty( $this->options['aiosp_taxactive'] ) ) {
5446
- foreach ( $this->options['aiosp_taxactive'] as $tax ) {
5447
- $this->locations['aiosp']['display'][] = 'edit-' . $tax;
5448
- add_action( "{$tax}_edit_form", array( $this, 'display_term_metabox' ) );
5449
- add_action( "edited_{$tax}", array( $this, 'save_term_data' ) );
5450
- }
5451
- }
5452
- } else {
5453
- if ( ! empty( $this->options['aiosp_cpostactive'] ) ) {
5454
- $this->locations['aiosp']['display'] = $this->options['aiosp_cpostactive'];
5455
- } else {
5456
- $this->locations['aiosp']['display'] = array();
5457
- }
5458
- }
5459
-
5460
- add_menu_page(
5461
- $menu_name,
5462
- $menu_name,
5463
- apply_filters( 'manage_aiosp', 'aiosp_manage_seo' ),
5464
- $file,
5465
- array( $this, 'display_settings_page' ),
5466
- aioseop_get_menu_icon()
5467
- );
5468
-
5469
- if ( ! AIOSEOPPRO ) {
5470
- add_meta_box(
5471
- 'aioseop-about',
5472
- AIOSEOP_PLUGIN_NAME . '&nbsp;Pro',
5473
- array( 'aiosp_metaboxes', 'display_extra_metaboxes' ),
5474
- 'aioseop_metaboxes',
5475
- 'side',
5476
- 'core'
5477
- );
5478
- }
5479
- add_meta_box(
5480
- 'aioseop-support',
5481
- __( 'Support', 'all-in-one-seo-pack' ),
5482
- array( 'aiosp_metaboxes', 'display_extra_metaboxes' ),
5483
- 'aioseop_metaboxes',
5484
- 'side',
5485
- 'core'
5486
- );
5487
- add_meta_box(
5488
- 'aioseop-list',
5489
- __( 'Join Our Mailing List', 'all-in-one-seo-pack' ),
5490
- array( 'aiosp_metaboxes', 'display_extra_metaboxes' ),
5491
- 'aioseop_metaboxes',
5492
- 'side',
5493
- 'core'
5494
- );
5495
-
5496
- add_action( 'aioseop_modules_add_menus', array( $this, 'add_menu' ), 5 );
5497
- do_action( 'aioseop_modules_add_menus', $file );
5498
-
5499
- $metaboxes = apply_filters( 'aioseop_add_post_metabox', array() );
5500
-
5501
- if ( ! empty( $metaboxes ) ) {
5502
- if ( $this->tabbed_metaboxes ) {
5503
- $tabs = array();
5504
- $tab_num = 0;
5505
- foreach ( $metaboxes as $m ) {
5506
- if ( ! isset( $tabs[ $m['post_type'] ] ) ) {
5507
- $tabs[ $m['post_type'] ] = array();
5508
- }
5509
- $tabs[ $m['post_type'] ][] = $m;
5510
- }
5511
-
5512
- if ( ! empty( $tabs ) ) {
5513
- foreach ( $tabs as $p => $m ) {
5514
- $tab_num = count( $m );
5515
- $title = $m[0]['title'];
5516
- if ( $title != $this->plugin_name ) {
5517
- $title = $this->plugin_name . ' - ' . $title;
5518
- }
5519
- if ( $tab_num <= 1 ) {
5520
- add_meta_box( $m[0]['id'], $title, $m[0]['callback'], $m[0]['post_type'], $m[0]['context'], $m[0]['priority'], $m[0]['callback_args'] );
5521
- } elseif ( $tab_num > 1 ) {
5522
- add_meta_box(
5523
- $m[0]['id'] . '_tabbed',
5524
- $title,
5525
- array( $this, 'display_tabbed_metabox' ),
5526
- $m[0]['post_type'],
5527
- $m[0]['context'],
5528
- $m[0]['priority'],
5529
- $m
5530
- );
5531
- }
5532
- }
5533
- }
5534
- } else {
5535
- foreach ( $metaboxes as $m ) {
5536
- $title = $m['title'];
5537
- if ( $title != $this->plugin_name ) {
5538
- $title = $this->plugin_name . ' - ' . $title;
5539
- }
5540
- if ( ! empty( $m['help_link'] ) ) {
5541
- $title .= "<a class='aioseop_help_text_link aioseop_meta_box_help' target='_blank' href='" . $m['help_link'] . "'><span>" . __( 'Help', 'all-in-one-seo-pack' ) . '</span></a>';
5542
- }
5543
- add_meta_box( $m['id'], $title, $m['callback'], $m['post_type'], $m['context'], $m['priority'], $m['callback_args'] );
5544
- }
5545
- }
5546
- }
5547
- }
5548
-
5549
- /**
5550
- * Set Menu Order
5551
- *
5552
- * @since ?
5553
- *
5554
- * @param $menu_order
5555
- * @return array
5556
- */
5557
- function set_menu_order( $menu_order ) {
5558
- $order = array();
5559
- $file = plugin_basename( __FILE__ );
5560
- foreach ( $menu_order as $index => $item ) {
5561
- if ( $item != $file ) {
5562
- $order[] = $item;
5563
- }
5564
- if ( 0 == $index ) {
5565
- $order[] = $file;
5566
- }
5567
- }
5568
-
5569
- return $order;
5570
- }
5571
-
5572
- /**
5573
- * Filters title and meta titles and applies cleanup.
5574
- * - Decode HTML entities.
5575
- * - Encodes to SEO ready HTML entities.
5576
- * Returns cleaned value.
5577
- *
5578
- * @since 2.3.14
5579
- *
5580
- * @param string $value Value to filter.
5581
- *
5582
- * @return string
5583
- */
5584
- public function filter_title( $value ) {
5585
- // Decode entities.
5586
- $value = $this->html_entity_decode( $value );
5587
- // Encode to valid SEO html entities.
5588
- return $this->seo_entity_encode( $value );
5589
- }
5590
-
5591
- /**
5592
- * Filters meta value and applies generic cleanup.
5593
- * - Decode HTML entities.
5594
- * - Removal of urls.
5595
- * - Internal trim.
5596
- * - External trim.
5597
- * - Strips HTML except anchor texts.
5598
- * - Returns cleaned value.
5599
- *
5600
- * @since 2.3.13
5601
- * @since 2.3.14 Strips excerpt anchor texts.
5602
- * @since 2.3.14 Encodes to SEO ready HTML entities.
5603
- * @since 2.3.14 #593 encode/decode refactored.
5604
- * @since 2.4 #951 Reorders filters/encodings/decondings applied and adds additional param.
5605
- *
5606
- * @param string $value Value to filter.
5607
- * @param bool $truncate Flag that indicates if value should be truncated/cropped.
5608
- * @param bool $ignore_php_version Flag that indicates if the php version check should be ignored.
5609
- *
5610
- * @return string
5611
- */
5612
- public function filter_description( $value, $truncate = false, $ignore_php_version = false ) {
5613
- // TODO: change preg_match to version_compare someday when the reason for this condition is understood better.
5614
- if ( $ignore_php_version || preg_match( '/5.2[\s\S]+/', PHP_VERSION ) ) {
5615
- $value = htmlspecialchars( wp_strip_all_tags( htmlspecialchars_decode( $value ) ), ENT_COMPAT, 'UTF-8' );
5616
- }
5617
- // Decode entities.
5618
- $value = $this->html_entity_decode( $value );
5619
- // External trim.
5620
- $value = trim( $value );
5621
- // Internal whitespace trim.
5622
- $value = preg_replace( '/\s\s+/u', ' ', $value );
5623
- // Truncate / crop.
5624
- if ( ! empty( $truncate ) && $truncate ) {
5625
- $value = $this->trim_excerpt_without_filters( $value );
5626
- }
5627
- // Encode to valid SEO html entities.
5628
- return $this->seo_entity_encode( $value );
5629
- }
5630
-
5631
- /**
5632
- * Returns string with decoded html entities.
5633
- * - Custom html_entity_decode supported on PHP 5.2
5634
- *
5635
- * @since 2.3.14
5636
- * @since 2.3.14.2 Hot fix on apostrophes.
5637
- * @since 2.3.16 &#039; Added to the list of apostrophes.
5638
- *
5639
- * @param string $value Value to decode.
5640
- *
5641
- * @return string
5642
- */
5643
- private function html_entity_decode( $value ) {
5644
- // Special conversions.
5645
- $value = preg_replace(
5646
- array(
5647
- // Double quotes.
5648
- '/\“|\”|&#[xX]00022;|&#34;|&[lLrRbB](dquo|DQUO)(?:[rR])?;|&#[xX]0201[dDeE];'
5649
- . '|&[OoCc](pen|lose)[Cc]urly[Dd]ouble[Qq]uote;|&#822[012];|&#[xX]27;/',
5650
- // Apostrophes.
5651
- '/&#039;|&#8217;|&apos;/',
5652
- ),
5653
- array(
5654
- // Double quotes.
5655
- '"',
5656
- // Apostrophes.
5657
- '\'',
5658
- ),
5659
- $value
5660
- );
5661
- return html_entity_decode( $value, ENT_COMPAT, 'UTF-8' );
5662
- }
5663
-
5664
- /**
5665
- * Returns SEO ready string with encoded HTML entitites.
5666
- *
5667
- * @since 2.3.14
5668
- * @since 2.3.14.1 Hot fix on apostrophes.
5669
- *
5670
- * @param string $value Value to encode.
5671
- *
5672
- * @return string
5673
- */
5674
- private function seo_entity_encode( $value ) {
5675
- return preg_replace(
5676
- array(
5677
- '/\"|\“|\”|\„/', // Double quotes.
5678
- '/\'|\’|\‘/', // Apostrophes.
5679
- ),
5680
- array(
5681
- '&quot;', // Double quotes.
5682
- '&#039;', // Apostrophes.
5683
- ),
5684
- esc_html( $value )
5685
- );
5686
- }
5687
-
5688
- function display_right_sidebar() {
5689
- global $wpdb;
5690
-
5691
- if ( ! get_option( 'aioseop_options' ) ) {
5692
- $msg = "<div style='text-align:center;'><p><strong>Your database options need to be updated.</strong><em>(Back up your database before updating.)</em>
5693
- <FORM action='' method='post' name='aioseop-migrate-options'>
5694
- <input type='hidden' name='nonce-aioseop-migrate-options' value='" . wp_create_nonce( 'aioseop-migrate-nonce-options' ) . "' />
5695
- <input type='submit' name='aioseop_migrate_options' class='button-primary' value='Update Database Options'>
5696
- </FORM>
5697
- </p></div>";
5698
- aioseop_output_dismissable_notice( $msg, '', 'error' );
5699
- }
5700
- ?>
5701
- <div class="aioseop_top">
5702
- <div class="aioseop_top_sidebar aioseop_options_wrapper">
5703
- <?php do_meta_boxes( 'aioseop_metaboxes', 'normal', array( 'test' ) ); ?>
5704
- </div>
5705
- </div>
5706
-
5707
- <div class="aioseop_right_sidebar aioseop_options_wrapper">
5708
-
5709
- <div class="aioseop_sidebar">
5710
- <?php
5711
- do_meta_boxes( 'aioseop_metaboxes', 'side', array( 'test' ) );
5712
- ?>
5713
- <script>
5714
- //<![CDATA[
5715
- jQuery(document).ready(function ($) {
5716
- // Close postboxes that should be closed.
5717
- $('.if-js-closed').removeClass('if-js-closed').addClass('closed');
5718
- // Postboxes setup.
5719
- if (typeof postboxes !== 'undefined')
5720
- postboxes.add_postbox_toggles('<?php echo $this->pagehook; ?>');
5721
- });
5722
- //]]>
5723
- </script>
5724
- </div>
5725
- </div>
5726
- <?php
5727
- }
5728
-
5729
- /**
5730
- * Checks which module(s) have been (de)activated just now and fires a corresponding action.
5731
- */
5732
- function check_recently_activated_modules() {
5733
- global $aioseop_options;
5734
- $options = get_option( 'aioseop_options', array() );
5735
- $modules_before = array();
5736
- $modules_now = array();
5737
- if ( array_key_exists( 'modules', $aioseop_options ) && array_key_exists( 'aiosp_feature_manager_options', $aioseop_options['modules'] ) ) {
5738
- foreach ( $aioseop_options['modules']['aiosp_feature_manager_options'] as $module => $state ) {
5739
- if ( ! empty( $state ) ) {
5740
- $modules_before[] = $module;
5741
- }
5742
- }
5743
- }
5744
- if ( array_key_exists( 'modules', $options ) && array_key_exists( 'aiosp_feature_manager_options', $options['modules'] ) ) {
5745
- foreach ( $options['modules']['aiosp_feature_manager_options'] as $module => $state ) {
5746
- if ( ! empty( $state ) ) {
5747
- $modules_now[] = $module;
5748
- }
5749
- }
5750
- }
5751
-
5752
- $action = 'deactivate';
5753
- $diff = array_diff( $modules_before, $modules_now );
5754
- if ( count( $modules_now ) > count( $modules_before ) ) {
5755
- $action = 'activate';
5756
- $diff = array_diff( $modules_now, $modules_before );
5757
- }
5758
-
5759
- if ( $diff ) {
5760
- foreach ( $diff as $module ) {
5761
- $name = str_replace( 'aiosp_feature_manager_enable_', '', $module );
5762
- do_action( $this->prefix . $action . '_' . $name );
5763
- }
5764
- }
5765
- }
5766
-
5767
- /**
5768
- * Checks if the plugin has a license key set, and otherwise wipes the addons/plan settings.
5769
- *
5770
- * @since 3.6.0
5771
- *
5772
- * @return void
5773
- */
5774
- public function checkIfLicensed() {
5775
- global $aioseop_options;
5776
- if ( ! isset( $aioseop_options['aiosp_license_key'] ) ) {
5777
- return;
5778
- }
5779
-
5780
- if ( empty( $aioseop_options['aiosp_license_key'] ) ) {
5781
- if ( isset( $aioseop_options['addons'] ) ) {
5782
- $aioseop_options['addons'] = '';
5783
- }
5784
- if ( isset( $aioseop_options['plan'] ) ) {
5785
- $aioseop_options['plan'] = 'unlicensed';
5786
- }
5787
- }
5788
- update_option( 'aioseop_options', $aioseop_options );
5789
- }
5790
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
all_in_one_seo_pack.php CHANGED
@@ -1,62 +1,78 @@
1
  <?php
2
- /*
3
- Plugin Name: All In One SEO Pack
4
- Plugin URI: https://semperplugins.com/all-in-one-seo-pack-pro-version/
5
- Description: Out-of-the-box SEO for WordPress. Features like XML Sitemaps, SEO for custom post types, SEO for blogs or business sites, SEO for ecommerce sites, and much more. More than 50 million downloads since 2007.
6
- Version: 3.7.1
7
- Author: All in One SEO Team
8
- Author URI: https://semperplugins.com/all-in-one-seo-pack-pro-version/
9
- Text Domain: all-in-one-seo-pack
10
- Domain Path: /i18n/
11
- */
12
-
13
- /*
14
- Copyright (C) 2007-2020 All in One SEO, https://semperplugins.com
15
-
16
- This program is free software; you can redistribute it and/or modify
17
- it under the terms of the GNU General Public License as published by
18
- the Free Software Foundation; version 2 of the License.
19
-
20
- This program is distributed in the hope that it will be useful,
21
- but WITHOUT ANY WARRANTY; without even the implied warranty of
22
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
- GNU General Public License for more details.
24
-
25
- You should have received a copy of the GNU General Public License
26
- along with this program. If not, see <http://www.gnu.org/licenses/>.
27
- */
 
 
 
28
 
 
29
  if ( ! defined( 'ABSPATH' ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  return;
31
  }
32
 
33
- if ( ! defined( 'AIOSEO_PLUGIN_DIR' ) ) {
34
- /**
35
- * Plugin Directory
36
- *
37
- * @since 3.4
38
- *
39
- * @var string $AIOSEOP_PLUGIN_DIR Plugin folder directory path. Eg. `C:\WebProjects\UW-WPDev-aioseop\src-plugins/all-in-one-seo-pack/`
40
- */
41
- define( 'AIOSEO_PLUGIN_DIR', dirname( __FILE__ ) );
42
  }
43
 
44
- if ( ! defined( 'AIOSEO_PLUGIN_FILE' ) ) {
 
 
 
 
 
45
 
46
- /**
47
- * Plugin File
48
- *
49
- * @since 3.4
50
- *
51
- * @var string $AIOSEOP_PLUGIN_FILE Plugin folder directory path. Eg. `C:\WebProjects\UW-WPDev-aioseop\src-plugins/all-in-one-seo-pack/`
52
- */
53
- define( 'AIOSEO_PLUGIN_FILE', __FILE__ );
54
  }
55
 
56
- if ( ! class_exists( 'AIOSEOP_Core' ) ) {
57
- require_once plugin_dir_path( __FILE__ ) . 'class-aioseop-core.php';
58
- global $aioseop_core;
59
- if ( is_null( $aioseop_core ) ) {
60
- $aioseop_core = new AIOSEOP_Core();
61
- }
62
  }
 
 
 
 
 
1
  <?php
2
+ /**
3
+ * Plugin Name: All in One SEO
4
+ * Plugin URI: https://aioseo.com/
5
+ * Description: SEO for WordPress. Features like XML Sitemaps, SEO for custom post types, SEO for blogs, business sites, ecommerce sites, and much more. More than 65 million downloads since 2007.
6
+ * Author: All in One SEO Team
7
+ * Author URI: https://aioseo.com/
8
+ * Version: 4.0.7
9
+ * Text Domain: all-in-one-seo-pack
10
+ * Domain Path: /i18n/
11
+ *
12
+ * All in One SEO is free software: you can redistribute it and/or modify
13
+ * it under the terms of the GNU General Public License as published by
14
+ * the Free Software Foundation, either version 2 of the License, or
15
+ * any later version.
16
+ *
17
+ * All in One SEO is distributed in the hope that it will be useful,
18
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
+ * GNU General Public License for more details.
21
+ *
22
+ * You should have received a copy of the GNU General Public License
23
+ * along with AIOSEO. If not, see <https://www.gnu.org/licenses/>.
24
+ *
25
+ * @since 4.0.0
26
+ * @author All in One SEO Team
27
+ * @package AIOSEO\Plugin
28
+ * @license GPL-2.0+
29
+ * @copyright Copyright (c) 2020, All in One SEO
30
+ */
31
 
32
+ // Exit if accessed directly.
33
  if ( ! defined( 'ABSPATH' ) ) {
34
+ exit;
35
+ }
36
+
37
+ if ( ! defined( 'AIOSEO_PHP_VERSION_DIR' ) ) {
38
+ define( 'AIOSEO_PHP_VERSION_DIR', basename( dirname( __FILE__ ) ) );
39
+ }
40
+
41
+ require_once( dirname( __FILE__ ) . '/app/init/notices.php' );
42
+ require_once( dirname( __FILE__ ) . '/app/init/activation.php' );
43
+
44
+ // We require PHP 5.4+ for the whole plugin to work.
45
+ if ( version_compare( PHP_VERSION, '5.4', '<' ) ) {
46
+ add_action( 'admin_notices', 'aioseo_php_notice' );
47
+
48
+ // Do not process the plugin code further.
49
  return;
50
  }
51
 
52
+ if ( ! defined( 'AIOSEO_DIR' ) ) {
53
+ define( 'AIOSEO_DIR', __DIR__ );
54
+ }
55
+ if ( ! defined( 'AIOSEO_FILE' ) ) {
56
+ define( 'AIOSEO_FILE', __FILE__ );
 
 
 
 
57
  }
58
 
59
+ // Don't allow multiple versions to be active.
60
+ if ( function_exists( 'aioseo' ) ) {
61
+ add_action( 'activate_all-in-one-seo-pack/all_in_one_seo_pack.php', 'aioseo_lite_just_activated' );
62
+ add_action( 'deactivate_all-in-one-seo-pack/all_in_one_seo_pack.php', 'aioseo_lite_just_deactivated' );
63
+ add_action( 'activate_all-in-one-seo-pack-pro/all_in_one_seo_pack.php', 'aioseo_pro_just_activated' );
64
+ add_action( 'admin_notices', 'aioseo_lite_notice' );
65
 
66
+ // Do not process the plugin code further.
67
+ return;
 
 
 
 
 
 
68
  }
69
 
70
+ // We will be deprecating these versions of PHP in the future, so let's let the user know.
71
+ if ( version_compare( PHP_VERSION, '5.5', '<' ) ) {
72
+ add_action( 'admin_notices', 'aioseo_php_notice_deprecated' );
 
 
 
73
  }
74
+
75
+ // Define the class and the function.
76
+ require_once( dirname( __FILE__ ) . '/app/AIOSEO.php' );
77
+
78
+ aioseo();
app/AIOSEO.php ADDED
@@ -0,0 +1,341 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin {
3
+
4
+ /**
5
+ * Main AIOSEO class.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ final class AIOSEO {
10
+ /**
11
+ * Holds the instance of the plugin currently in use.
12
+ *
13
+ * @since 4.0.0
14
+ *
15
+ * @var AIOSEO\Plugin\AIOSEO
16
+ */
17
+ private static $instance;
18
+
19
+ /**
20
+ * Plugin version for enqueueing, etc.
21
+ * The value is retrieved from the AIOSEO_VERSION constant.
22
+ *
23
+ * @since 4.0.0
24
+ *
25
+ * @var string
26
+ */
27
+ public $version = '';
28
+
29
+ /**
30
+ * Paid returns true, free (Lite) returns false.
31
+ *
32
+ * @since 4.0.0
33
+ *
34
+ * @var boolean
35
+ */
36
+ public $pro = false;
37
+
38
+ /**
39
+ * Returns 'Pro' or 'Lite'.
40
+ *
41
+ * @since 4.0.0
42
+ *
43
+ * @var boolean
44
+ */
45
+ public $versionPath = 'Lite';
46
+
47
+ /**
48
+ * The AIOSEO options.
49
+ *
50
+ * @since 4.0.0
51
+ *
52
+ * @var array
53
+ */
54
+ public $options = [];
55
+
56
+ /**
57
+ * The WordPress filters to run.
58
+ *
59
+ * @since 4.0.0
60
+ *
61
+ * @var Filters
62
+ */
63
+ private $filters = null;
64
+
65
+ /**
66
+ * For usage tracking when enabled.
67
+ *
68
+ * @since 4.0.0
69
+ *
70
+ * @var Filters
71
+ */
72
+ private $usage = null;
73
+
74
+ /**
75
+ * For WP site health.
76
+ *
77
+ * @since 4.0.0
78
+ *
79
+ * @var Filters
80
+ */
81
+ private $siteHealth = null;
82
+
83
+ /**
84
+ * For auto updates.
85
+ *
86
+ * @since 4.0.0
87
+ *
88
+ * @var Filters
89
+ */
90
+ private $autoUpdates = null;
91
+
92
+ /**
93
+ * Main AIOSEO Instance.
94
+ *
95
+ * Insures that only one instance of AIOSEO exists in memory at any one
96
+ * time. Also prevents needing to define globals all over the place.
97
+ *
98
+ * @since 4.0.0
99
+ *
100
+ * @return AIOSEO The aioseo instance.
101
+ */
102
+ public static function instance() {
103
+ if ( null === self::$instance || ! self::$instance instanceof self ) {
104
+ self::$instance = new self();
105
+
106
+ // Plugin Slug - Determine plugin type and set slug accordingly.
107
+ if (
108
+ ( ! defined( 'AIOSEO_DEV_VERSION' ) || 'pro' === AIOSEO_DEV_VERSION ) &&
109
+ is_dir( plugin_dir_path( AIOSEO_FILE ) . 'app/Pro' )
110
+ ) {
111
+ self::$instance->pro = true;
112
+ self::$instance->versionPath = 'Pro';
113
+ }
114
+
115
+ self::$instance->init();
116
+
117
+ // Load our addons on the action right after plugins_loaded.
118
+ add_action( 'sanitize_comment_cookies', [ self::$instance, 'loadAddons' ] );
119
+ }
120
+
121
+ return self::$instance;
122
+ }
123
+
124
+ /**
125
+ * Initialize All in One SEO!
126
+ *
127
+ * @return void
128
+ */
129
+ private function init() {
130
+ $this->constants();
131
+ $this->includes();
132
+ $this->preLoad();
133
+ $this->load();
134
+ }
135
+
136
+ /**
137
+ * Setup plugin constants.
138
+ * All the path/URL related constants are defined in main plugin file.
139
+ *
140
+ * @since 4.0.0
141
+ *
142
+ * @return void
143
+ */
144
+ private function constants() {
145
+ $defaultHeaders = [
146
+ 'name' => 'Plugin Name',
147
+ 'version' => 'Version',
148
+ ];
149
+
150
+ $pluginData = get_file_data( AIOSEO_FILE, $defaultHeaders );
151
+
152
+ $constants = [
153
+ 'AIOSEO_PLUGIN_BASENAME' => plugin_basename( AIOSEO_FILE ),
154
+ 'AIOSEO_PLUGIN_NAME' => $pluginData['name'],
155
+ 'AIOSEO_PLUGIN_SHORT_NAME' => 'AIOSEO',
156
+ 'AIOSEO_PLUGIN_URL' => plugin_dir_url( AIOSEO_FILE ),
157
+ 'AIOSEO_VERSION' => $pluginData['version'],
158
+ 'AIOSEO_MARKETING_URL' => 'https://aioseo.com/',
159
+ 'AIOSEO_MARKETING_DOMAIN' => 'aioseo.com'
160
+ ];
161
+
162
+ foreach ( $constants as $constant => $value ) {
163
+ if ( ! defined( $constant ) ) {
164
+ define( $constant, $value );
165
+ }
166
+ }
167
+
168
+ $this->version = AIOSEO_VERSION;
169
+ }
170
+
171
+ /**
172
+ * Including the new files with PHP 5.3 style.
173
+ *
174
+ * @since 4.0.0
175
+ *
176
+ * @return void
177
+ */
178
+ private function includes() {
179
+ $dependencies = [
180
+ '/vendor/autoload.php',
181
+ '/vendor/woocommerce/action-scheduler/action-scheduler.php',
182
+ ];
183
+
184
+ foreach ( $dependencies as $path ) {
185
+ if ( ! file_exists( AIOSEO_DIR . $path ) ) {
186
+ // Something is not right.
187
+ status_header( 500 );
188
+ wp_die( esc_html__( 'Plugin is missing required dependencies. Please contact support for more information.', 'all-in-one-seo-pack' ) );
189
+ }
190
+ require AIOSEO_DIR . $path;
191
+ }
192
+
193
+ add_action( 'plugins_loaded', [ $this, 'actionScheduler' ], 10 );
194
+ }
195
+
196
+ /**
197
+ * Ensure our action scheduler tables are always set.
198
+ *
199
+ * @since 4.0.0
200
+ *
201
+ * @return void
202
+ */
203
+ public function actionScheduler() {
204
+ if ( class_exists( 'ActionScheduler' ) && class_exists( 'ActionScheduler_ListTable' ) ) {
205
+ new Common\Utils\ActionScheduler(
206
+ \ActionScheduler::store(),
207
+ \ActionScheduler::logger(),
208
+ \ActionScheduler::runner()
209
+ );
210
+ }
211
+ }
212
+
213
+ /**
214
+ * Runs before we load the plugin.
215
+ *
216
+ * @since 4.0.0
217
+ *
218
+ * @return void
219
+ */
220
+ private function preLoad() {
221
+ $this->db = new Common\Utils\Database();
222
+ }
223
+
224
+ /**
225
+ * Load our classes.
226
+ *
227
+ * @since 4.0.0
228
+ *
229
+ * @return void
230
+ */
231
+ public function load() {
232
+ // Load external translations if this is a Pro install.
233
+ if ( $this->pro ) {
234
+ $translations = new Pro\Main\Translations(
235
+ 'plugin',
236
+ 'all-in-one-seo-pack',
237
+ 'https://packages.translationspress.com/aioseo/all-in-one-seo-pack/packages.json'
238
+ );
239
+ $translations->init();
240
+
241
+ $translations = new Pro\Main\Translations(
242
+ 'plugin',
243
+ 'aioseo-pro',
244
+ 'https://packages.translationspress.com/aioseo/aioseo-pro/packages.json'
245
+ );
246
+ $translations->init();
247
+ }
248
+
249
+ $this->helpers = $this->pro ? new Pro\Utils\Helpers() : new Common\Utils\Helpers();
250
+ $this->addons = new Common\Utils\Addons();
251
+ $this->tags = $this->pro ? new Pro\Utils\Tags() : new Common\Utils\Tags();
252
+ $this->badBotBlocker = new Common\Tools\BadBotBlocker();
253
+ $this->internalOptions = $this->pro ? new Pro\Utils\InternalOptions() : new Lite\Utils\InternalOptions();
254
+ $this->options = $this->pro ? new Pro\Utils\Options() : new Lite\Utils\Options();
255
+ $this->backup = new Common\Utils\Backup();
256
+ $this->access = $this->pro ? new Pro\Utils\Access() : new Common\Utils\Access();
257
+ $this->usage = $this->pro ? new Pro\Admin\Usage() : new Lite\Admin\Usage();
258
+ $this->siteHealth = $this->pro ? new Pro\Admin\SiteHealth() : new Common\Admin\SiteHealth();
259
+ $this->license = $this->pro ? new Pro\Admin\License() : null;
260
+ $this->autoUpdates = new Common\Admin\AutoUpdates();
261
+ $this->updates = $this->pro ? new Pro\Main\Updates() : new Common\Main\Updates();
262
+ $this->meta = $this->pro ? new Pro\Meta\Meta() : new Common\Meta\Meta();
263
+ $this->social = $this->pro ? new Pro\Social\Social() : new Common\Social\Social();
264
+ $this->robotsTxt = new Common\Tools\RobotsTxt();
265
+ $this->htaccess = new Common\Tools\Htaccess();
266
+ $this->term = $this->pro ? new Pro\Admin\Term() : null;
267
+ $this->notices = $this->pro ? new Pro\Admin\Notices\Notices() : new Lite\Admin\Notices\Notices();
268
+ $this->admin = $this->pro ? new Pro\Admin\Admin() : new Lite\Admin\Admin();
269
+ $this->conflictingPlugins = $this->pro ? new Pro\Admin\ConflictingPlugins() : new Common\Admin\ConflictingPlugins();
270
+ $this->migration = $this->pro ? new Pro\Migration\Migration() : new Common\Migration\Migration();
271
+ $this->importExport = $this->pro ? new Pro\ImportExport\ImportExport() : new Common\ImportExport\ImportExport();
272
+ $this->sitemap = $this->pro ? new Pro\Sitemap\Sitemap() : new Common\Sitemap\Sitemap();
273
+
274
+ if ( ! wp_doing_ajax() && ! wp_doing_cron() ) {
275
+ $this->rss = new Common\Rss();
276
+ $this->main = $this->pro ? new Pro\Main\Main() : new Common\Main\Main();
277
+ $this->schema = new Common\Schema\Schema();
278
+ $this->head = $this->pro ? new Pro\Main\Head() : new Common\Main\Head();
279
+ $this->activate = $this->pro ? new Pro\Main\Activate() : new Lite\Main\Activate();
280
+ $this->filters = $this->pro ? new Pro\Main\Filters() : new Lite\Main\Filters();
281
+ $this->dashboard = $this->pro ? new Pro\Admin\Dashboard() : new Common\Admin\Dashboard();
282
+ $this->api = $this->pro ? new Pro\Api\Api() : new Lite\Api\Api();
283
+ $this->postSettings = $this->pro ? new Pro\Admin\PostSettings() : new Lite\Admin\PostSettings();
284
+ $this->filter = new Common\Utils\Filter();
285
+ $this->localBusinessSeo = new Common\Admin\LocalBusinessSeo();
286
+ $this->help = new Common\Help\Help();
287
+ }
288
+
289
+ if ( wp_doing_ajax() || wp_doing_cron() ) {
290
+ return;
291
+ }
292
+
293
+ add_action( 'init', [ $this, 'loadInit' ], 999 );
294
+ }
295
+
296
+ /**
297
+ * Things that need to load after init.
298
+ *
299
+ * @since 4.0.0
300
+ *
301
+ * @return void
302
+ */
303
+ public function loadInit() {
304
+ $this->settings = new Common\Utils\VueSettings( '_aioseo_settings' );
305
+ $this->sitemap->init();
306
+ $this->sitemap->ping->init();
307
+
308
+ $this->badBotBlocker->init();
309
+
310
+ // We call this again to reset any post types/taxonomies that have not yet been set up.
311
+ $this->options->refresh();
312
+ }
313
+
314
+ /**
315
+ * Loads our addons.
316
+ *
317
+ * Runs right after the plugins_loaded hook.
318
+ *
319
+ * @since 4.0.0
320
+ *
321
+ * @return void
322
+ */
323
+ public function loadAddons() {
324
+ do_action( 'aioseo_loaded' );
325
+ }
326
+ }
327
+ }
328
+
329
+ namespace {
330
+
331
+ /**
332
+ * The function which returns the one AIOSEO instance.
333
+ *
334
+ * @since 4.0.0
335
+ *
336
+ * @return AIOSEO\Plugin\AIOSEO The instance.
337
+ */
338
+ function aioseo() {
339
+ return AIOSEO\Plugin\AIOSEO::instance();
340
+ }
341
+ }
app/Common/Admin/Admin.php ADDED
@@ -0,0 +1,1080 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Admin;
3
+
4
+ use AIOSEO\Plugin\Common\Models;
5
+ use AIOSEO\Plugin\Common\Migration;
6
+
7
+ /**
8
+ * Abstract class that Pro and Lite both extend.
9
+ *
10
+ * @since 4.0.0
11
+ */
12
+ class Admin {
13
+ /**
14
+ * The page slug for the sidebar.
15
+ *
16
+ * @since 4.0.0
17
+ *
18
+ * @var string
19
+ */
20
+ protected $pageSlug = 'aioseo';
21
+
22
+ /**
23
+ * Sidebar menu name.
24
+ *
25
+ * @since 4.0.0
26
+ *
27
+ * @var string
28
+ */
29
+ public $menuName = 'All in One SEO';
30
+
31
+ /**
32
+ * An array of pages for the admin.
33
+ *
34
+ * @since 4.0.0
35
+ *
36
+ * @var array
37
+ */
38
+ protected $pages = [];
39
+
40
+ /**
41
+ * An array of items to add to the admin bar.
42
+ *
43
+ * @since 4.0.0
44
+ *
45
+ * @var array
46
+ */
47
+ protected $adminBarMenuItems = [];
48
+
49
+ /**
50
+ * Construct method.
51
+ *
52
+ * @since 4.0.0
53
+ */
54
+ public function __construct() {
55
+ if ( wp_doing_ajax() || wp_doing_cron() ) {
56
+ return;
57
+ }
58
+
59
+ $this->pages = [
60
+ $this->pageSlug => [
61
+ 'menu_title' => esc_html__( 'Dashboard', 'all-in-one-seo-pack' ),
62
+ 'capability' => apply_filters( 'aioseo_manage_seo', 'aioseo_manage_seo' ),
63
+ 'parent' => $this->pageSlug
64
+ ],
65
+ 'aioseo-settings' => [
66
+ 'menu_title' => esc_html__( 'General Settings', 'all-in-one-seo-pack' ),
67
+ 'capability' => 'aioseo_general_settings',
68
+ 'parent' => $this->pageSlug
69
+ ],
70
+ 'aioseo-search-appearance' => [
71
+ 'menu_title' => esc_html__( 'Search Appearance', 'all-in-one-seo-pack' ),
72
+ 'capability' => 'aioseo_search_appearance_settings',
73
+ 'parent' => $this->pageSlug
74
+ ],
75
+ 'aioseo-social-networks' => [
76
+ 'menu_title' => esc_html__( 'Social Networks', 'all-in-one-seo-pack' ),
77
+ 'capability' => 'aioseo_social_networks_settings',
78
+ 'parent' => $this->pageSlug
79
+ ],
80
+ 'aioseo-sitemaps' => [
81
+ 'menu_title' => esc_html__( 'Sitemaps', 'all-in-one-seo-pack' ),
82
+ 'capability' => 'aioseo_sitemap_settings',
83
+ 'parent' => $this->pageSlug
84
+ ],
85
+ // 'aioseo-internal-links' => [
86
+ // 'menu_title' => esc_html__( 'Internal Links', 'all-in-one-seo-pack' ),
87
+ // 'capability' => 'aioseo_internal_links_settings',
88
+ // 'parent' => $this->pageSlug
89
+ // ],
90
+ // 'aioseo-redirects' => [
91
+ // 'menu_title' => esc_html__( 'Redirects', 'all-in-one-seo-pack' ),
92
+ // 'capability' => 'aioseo_redirects_settings',
93
+ // 'parent' => $this->pageSlug
94
+ // ],
95
+ 'aioseo-local-seo' => [
96
+ 'menu_title' => esc_html__( 'Local SEO', 'all-in-one-seo-pack' ),
97
+ 'capability' => 'aioseo_local_seo_settings',
98
+ 'parent' => $this->pageSlug
99
+ ],
100
+ 'aioseo-seo-analysis' => [
101
+ 'menu_title' => esc_html__( 'SEO Analysis', 'all-in-one-seo-pack' ),
102
+ 'capability' => 'aioseo_seo_analysis_settings',
103
+ 'parent' => $this->pageSlug
104
+ ],
105
+ 'aioseo-tools' => [
106
+ 'menu_title' => esc_html__( 'Tools', 'all-in-one-seo-pack' ),
107
+ 'capability' => 'aioseo_tools_settings',
108
+ 'parent' => $this->pageSlug
109
+ ],
110
+ 'aioseo-feature-manager' => [
111
+ 'menu_title' => esc_html__( 'Feature Manager', 'all-in-one-seo-pack' ),
112
+ 'capability' => 'aioseo_feature_manager_settings',
113
+ 'parent' => $this->pageSlug
114
+ ],
115
+ 'aioseo-monsterinsights' => [
116
+ 'menu_title' => esc_html__( 'Analytics', 'all-in-one-seo-pack' ),
117
+ 'capability' => apply_filters( 'aioseo_manage_seo', 'aioseo_manage_seo' ),
118
+ 'parent' => $this->pageSlug
119
+ ],
120
+ 'aioseo-about' => [
121
+ 'menu_title' => esc_html__( 'About Us', 'all-in-one-seo-pack' ),
122
+ 'capability' => apply_filters( 'aioseo_manage_seo', 'aioseo_manage_seo' ),
123
+ 'parent' => $this->pageSlug
124
+ ]
125
+ ];
126
+
127
+ add_action( 'sanitize_comment_cookies', [ $this, 'init' ], 20 );
128
+
129
+ $this->setupWizard = new SetupWizard();
130
+ }
131
+
132
+ /**
133
+ * Initialize the admin.
134
+ *
135
+ * @since 4.0.0
136
+ *
137
+ * @return void
138
+ */
139
+ public function init() {
140
+ // Add the admin bar menu.
141
+ if ( is_user_logged_in() && current_user_can( 'aioseo_manage_seo' ) && ( ! is_multisite() || ! is_network_admin() ) ) {
142
+ add_action( 'admin_bar_menu', [ $this, 'adminBarMenu' ], 1000 );
143
+ }
144
+
145
+ if ( is_admin() ) {
146
+ // Add the menu to the sidebar.
147
+ add_action( 'admin_menu', [ $this, 'addMenu' ] );
148
+ if ( is_multisite() ) {
149
+ add_action( 'network_admin_menu', [ $this, 'addRobotsMenu' ] );
150
+ }
151
+
152
+ // Add the columns to page/posts.
153
+ add_action( 'current_screen', [ $this, 'addPostColumns' ], 1 );
154
+
155
+ // Add Score to Publish metabox.
156
+ add_action( 'post_submitbox_misc_actions', [ $this, 'addPublishScore' ] );
157
+
158
+ add_action( 'admin_init', [ $this, 'addPluginScripts' ] );
159
+
160
+ add_action( 'wp_enqueue_editor', [ $this, 'addClassicLinkFormatScript' ], 999999 );
161
+
162
+ global $wp_version;
163
+ include_once ABSPATH . 'wp-admin/includes/plugin.php';
164
+ if ( version_compare( $wp_version, '5.3', '>=' ) || is_plugin_active( 'gutenberg/gutenberg.php' ) ) {
165
+ add_action( 'admin_init', [ $this, 'addGutenbergLinkFormatScript' ] );
166
+ add_action( 'enqueue_block_editor_assets', function() {
167
+ wp_enqueue_script( 'aioseo-link-format' );
168
+ } );
169
+ }
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Enqueues the plugins script.
175
+ *
176
+ * @since 4.0.0
177
+ *
178
+ * @return void
179
+ */
180
+ public function addPluginScripts() {
181
+ global $pagenow;
182
+
183
+ if ( 'plugins.php' !== $pagenow ) {
184
+ return;
185
+ }
186
+
187
+ aioseo()->helpers->enqueueScript(
188
+ 'aioseo-plugins',
189
+ 'js/plugins.js',
190
+ false
191
+ );
192
+
193
+ wp_localize_script(
194
+ 'aioseo-plugins',
195
+ 'aioseoPlugins',
196
+ [
197
+ 'basename' => AIOSEO_PLUGIN_BASENAME
198
+ ]
199
+ );
200
+ }
201
+
202
+ /**
203
+ * Enqueues our link format for the Classic Editor.
204
+ *
205
+ * @since 4.0.0
206
+ *
207
+ * @return void
208
+ */
209
+ public function addClassicLinkFormatScript() {
210
+ wp_deregister_script( 'wplink' );
211
+
212
+ wp_enqueue_script(
213
+ 'wplink',
214
+ aioseo()->helpers->getScriptUrl( 'js/link-format-classic.js', false ),
215
+ [ 'jquery', 'wp-a11y' ],
216
+ aioseo()->version,
217
+ true
218
+ );
219
+
220
+ wp_localize_script(
221
+ 'wplink',
222
+ 'aioseoL10n',
223
+ [
224
+ 'update' => esc_html__( 'Update', 'all-in-one-seo-pack' ),
225
+ 'save' => esc_html__( 'Add Link', 'all-in-one-seo-pack' ),
226
+ 'noTitle' => esc_html__( '(no title)', 'all-in-one-seo-pack' ),
227
+ 'labelTitle' => esc_html__( 'Title', 'all-in-one-seo-pack' ),
228
+ 'noMatchesFound' => esc_html__( 'No results found.', 'all-in-one-seo-pack' ),
229
+ 'linkInserted' => esc_html__( 'Link has been inserted.', 'all-in-one-seo-pack' ),
230
+ // Translators: 1 - HTML whitespace character, 2 - Opening HTML code tag, 3 - Closing HTML code tag.
231
+ 'noFollow' => sprintf( esc_html__( '%1$sAdd %2$srel="nofollow"%3$s to link', 'all-in-one-seo-pack' ), '&nbsp;', '<code>', '</code>' ),
232
+ // Translators: 1 - HTML whitespace character, 2 - Opening HTML code tag, 3 - Closing HTML code tag.
233
+ 'sponsored' => sprintf( esc_html__( '%1$sAdd %2$srel="sponsored"%3$s to link', 'all-in-one-seo-pack' ), '&nbsp;', '<code>', '</code>' ),
234
+ // Translators: 1 - HTML whitespace character, 2 - Opening HTML code tag, 3 - Closing HTML code tag.
235
+ 'ugc' => sprintf( esc_html__( '%1$sAdd %2$srel="UGC"%3$s to link', 'all-in-one-seo-pack' ), '&nbsp;', '<code>', '</code>' ),
236
+ ]
237
+ );
238
+ }
239
+
240
+ /**
241
+ * Registers our link format for the Block Editor.
242
+ *
243
+ * @since 4.0.0
244
+ *
245
+ * @return void
246
+ */
247
+ public function addGutenbergLinkFormatScript() {
248
+ $linkFormat = 'block';
249
+ if ( is_plugin_active( 'gutenberg/gutenberg.php' ) ) {
250
+ $data = get_plugin_data( ABSPATH . 'wp-content/plugins/gutenberg/gutenberg.php', false, false );
251
+ if ( version_compare( $data['Version'], '7.4.0', '<' ) ) {
252
+ $linkFormat = 'block-old';
253
+ }
254
+ } else {
255
+ if ( version_compare( get_bloginfo( 'version' ), '5.4', '<' ) ) {
256
+ $linkFormat = 'block-old';
257
+ }
258
+ }
259
+
260
+ wp_register_script(
261
+ 'aioseo-link-format',
262
+ aioseo()->helpers->getScriptUrl( "js/link-format-$linkFormat.js", false ),
263
+ [
264
+ 'wp-blocks',
265
+ 'wp-i18n',
266
+ 'wp-element',
267
+ 'wp-plugins',
268
+ 'wp-components',
269
+ 'wp-edit-post',
270
+ 'wp-api',
271
+ 'wp-editor',
272
+ 'wp-hooks',
273
+ 'lodash'
274
+ ],
275
+ aioseo()->version,
276
+ true
277
+ );
278
+ }
279
+
280
+ /**
281
+ * Adds All in One SEO to the Admin Bar.
282
+ *
283
+ * @since 4.0.0
284
+ *
285
+ * @return void
286
+ */
287
+ public function adminBarMenu() {
288
+ if ( false === apply_filters( 'aioseo_show_in_admin_bar', true ) ) {
289
+ // API filter hook to disable showing SEO in admin bar.
290
+ return;
291
+ }
292
+
293
+ $count = count( Models\Notification::getAllActiveNotifications() );
294
+ $count = 10 > $count ? $count : '!';
295
+ $count = $count ? '<div class="aioseo-menu-notification-counter">' . $count . '</div>' : '';
296
+
297
+ $this->adminBarMenuItems[] = [
298
+ 'id' => 'aioseo-main',
299
+ 'title' => '<div class="ab-item aioseo-logo svg"></div><span class="text">' . esc_html__( 'SEO', 'all-in-one-seo-pack' ) . '</span>' . wp_kses_post( $count ),
300
+ 'href' => esc_url( admin_url( 'admin.php?page=aioseo' ) )
301
+ ];
302
+
303
+ if ( $count ) {
304
+ $this->adminBarMenuItems[] = [
305
+ 'parent' => 'aioseo-main',
306
+ 'id' => 'aioseo-notifications',
307
+ 'title' => esc_html__( 'Notifications', 'all-in-one-seo-pack' ) . ' <div class="aioseo-menu-notification-indicator"></div>',
308
+ 'href' => admin_url( 'admin.php?page=aioseo&notifications=true' ),
309
+ ];
310
+ }
311
+
312
+ if ( ! is_admin() ) {
313
+ $this->addPageAnalyzerMenuItems();
314
+ }
315
+
316
+ $this->addSettingsMenuItems();
317
+ $this->addPostMenuItems();
318
+
319
+ // Add the menu bar items.
320
+ $this->addAdminBarMenuItems();
321
+ }
322
+
323
+ /**
324
+ * Actually adds the menu items to the admin bar.
325
+ *
326
+ * @return void
327
+ */
328
+ protected function addAdminBarMenuItems() {
329
+ global $wp_admin_bar;
330
+ foreach ( $this->adminBarMenuItems as $key => $item ) {
331
+ $wp_admin_bar->add_menu( $item );
332
+ }
333
+ }
334
+
335
+ /**
336
+ * Adds the Analyze this Page menu item to the admin bar.
337
+ *
338
+ * @since 4.0.0
339
+ *
340
+ * @return void
341
+ */
342
+ public function addPageAnalyzerMenuItems() {
343
+ global $wp;
344
+ $url = home_url( $wp->request );
345
+
346
+ if ( ! $url ) {
347
+ return;
348
+ }
349
+
350
+ $this->adminBarMenuItems[] = [
351
+ 'id' => 'aioseo-analyze-page',
352
+ 'parent' => 'aioseo-main',
353
+ 'title' => esc_html__( 'Analyze this page', 'all-in-one-seo-pack' ),
354
+ ];
355
+
356
+ $url = urlencode( $url );
357
+
358
+ $submenuItems = [
359
+ [
360
+ 'id' => 'aioseo-analyze-page-inlinks',
361
+ 'title' => esc_html__( 'Check links to this URL', 'all-in-one-seo-pack' ),
362
+ 'href' => 'https://search.google.com/search-console/links/drilldown?resource_id=' . urlencode( get_option( 'siteurl' ) ) . '&type=EXTERNAL&target=' . $url . '&domain=',
363
+ ],
364
+ [
365
+ 'id' => 'aioseo-analyze-page-cache',
366
+ 'title' => esc_html__( 'Check Google Cache', 'all-in-one-seo-pack' ),
367
+ 'href' => '//webcache.googleusercontent.com/search?strip=1&q=cache:' . $url,
368
+ ],
369
+ [
370
+ 'id' => 'aioseo-analyze-page-structureddata',
371
+ 'title' => esc_html__( 'Google Structured Data Test', 'all-in-one-seo-pack' ),
372
+ 'href' => 'https://search.google.com/test/rich-results?url=' . $url,
373
+ ],
374
+ [
375
+ 'id' => 'aioseo-analyze-page-facebookdebug',
376
+ 'title' => esc_html__( 'Facebook Debugger', 'all-in-one-seo-pack' ),
377
+ 'href' => 'https://developers.facebook.com/tools/debug/?q=' . $url,
378
+ ],
379
+ [
380
+ 'id' => 'aioseo-analyze-page-pinterestvalidator',
381
+ 'title' => esc_html__( 'Pinterest Rich Pins Validator', 'all-in-one-seo-pack' ),
382
+ 'href' => 'https://developers.pinterest.com/tools/url-debugger/?link=' . $url,
383
+ ],
384
+ [
385
+ 'id' => 'aioseo-analyze-page-htmlvalidation',
386
+ 'title' => esc_html__( 'HTML Validator', 'all-in-one-seo-pack' ),
387
+ 'href' => '//validator.w3.org/check?uri=' . $url,
388
+ ],
389
+ [
390
+ 'id' => 'aioseo-analyze-page-cssvalidation',
391
+ 'title' => esc_html__( 'CSS Validator', 'all-in-one-seo-pack' ),
392
+ 'href' => '//jigsaw.w3.org/css-validator/validator?uri=' . $url,
393
+ ],
394
+ [
395
+ 'id' => 'aioseo-analyze-page-pagespeed',
396
+ 'title' => esc_html__( 'Google Page Speed Test', 'all-in-one-seo-pack' ),
397
+ 'href' => '//developers.google.com/speed/pagespeed/insights/?url=' . $url,
398
+ ],
399
+ [
400
+ 'id' => 'aioseo-analyze-page-google-mobile-friendly',
401
+ 'title' => esc_html__( 'Mobile-Friendly Test', 'all-in-one-seo-pack' ),
402
+ 'href' => 'https://www.google.com/webmasters/tools/mobile-friendly/?url=' . $url,
403
+ ],
404
+ [
405
+ 'id' => 'aioseo-external-tools-linkedin-post-inspector',
406
+ 'title' => esc_html__( 'LinkedIn Post Inspector', 'all-in-one-seo-pack' ),
407
+ 'href' => "https://www.linkedin.com/post-inspector/inspect/$url"
408
+ ]
409
+ ];
410
+
411
+ foreach ( $submenuItems as $item ) {
412
+ $this->adminBarMenuItems[] = [
413
+ 'parent' => 'aioseo-analyze-page',
414
+ 'id' => $item['id'],
415
+ 'title' => $item['title'],
416
+ 'href' => $item['href'],
417
+ 'meta' => [ 'target' => '_blank' ],
418
+ ];
419
+ }
420
+ }
421
+
422
+ /**
423
+ * Adds the current post menu items to the admin bar.
424
+ *
425
+ * @since 4.0.0
426
+ *
427
+ * @return void
428
+ */
429
+ protected function addPostMenuItems() {
430
+ $blogPage = aioseo()->helpers->getBlogPage();
431
+ if ( ! $blogPage ) {
432
+ return;
433
+ }
434
+
435
+ $addMenu = $this->getAdminBarMenuData( $blogPage );
436
+ if ( ! empty( $addMenu ) ) {
437
+ $this->adminBarMenuItems[] = [
438
+ 'id' => 'aioseo-edit-' . $addMenu['id'],
439
+ 'parent' => 'aioseo-main',
440
+ 'title' => esc_html__( 'Edit SEO', 'all-in-one-seo-pack' ),
441
+ 'href' => $addMenu['link'],
442
+ ];
443
+ }
444
+ }
445
+
446
+ /**
447
+ * Add the settings items to the menu bar.
448
+ *
449
+ * @since 4.0.0
450
+ *
451
+ * @return void
452
+ */
453
+ protected function addSettingsMenuItems() {
454
+ if ( ! is_admin() ) {
455
+ $this->adminBarMenuItems[] = [
456
+ 'id' => 'aioseo-settings-main',
457
+ 'parent' => 'aioseo-main',
458
+ // Translators: This is an action link users can click to open the General Settings menu.
459
+ 'title' => esc_html__( 'SEO Settings', 'all-in-one-seo-pack' )
460
+ ];
461
+ }
462
+
463
+ $parent = is_admin() ? 'aioseo-main' : 'aioseo-settings-main';
464
+ foreach ( $this->pages as $id => $page ) {
465
+ $this->adminBarMenuItems[] = [
466
+ 'id' => $id,
467
+ 'parent' => $parent,
468
+ 'title' => $page['menu_title'],
469
+ 'href' => esc_url( admin_url( 'admin.php?page=' . $id ) )
470
+ ];
471
+ }
472
+ }
473
+
474
+ /**
475
+ * Retreive data to build the admin bar.
476
+ * @since 4.0.0
477
+ *
478
+ * @param WP_Post $post The post object.
479
+ * @return array An array of data to build a menu link.
480
+ */
481
+ protected function getAdminBarMenuData( $post ) {
482
+ // Don't show if we're on the home page and the home page is the latest posts.
483
+ if ( ! is_home() || ( ! is_front_page() && ! is_home() ) ) {
484
+ global $wp_the_query;
485
+ $currentObject = $wp_the_query->get_queried_object();
486
+
487
+ if ( is_singular() ) {
488
+ if ( ! empty( $currentObject ) && ! empty( $currentObject->post_type ) ) {
489
+ // Try the main query.
490
+ $editPostLink = get_edit_post_link( $currentObject->ID );
491
+ return [
492
+ 'id' => $currentObject->ID,
493
+ 'link' => $editPostLink . '#aioseo'
494
+ ];
495
+ } else {
496
+ // Try the post object.
497
+ return [
498
+ 'id' => $post->ID,
499
+ 'link' => get_edit_post_link( $post->ID ) . '#aioseo'
500
+ ];
501
+ }
502
+ }
503
+ }
504
+ }
505
+
506
+ /**
507
+ * Add the menu inside of WordPress.
508
+ *
509
+ * @since 4.0.0
510
+ *
511
+ * @return void
512
+ */
513
+ public function addMenu() {
514
+ $this->addMainMenu();
515
+
516
+ foreach ( $this->pages as $slug => $page ) {
517
+ $hook = add_submenu_page(
518
+ $page['parent'],
519
+ ! empty( $page['page_title'] ) ? $page['page_title'] : $page['menu_title'],
520
+ $page['menu_title'],
521
+ $page['capability'],
522
+ $slug,
523
+ [ $this, 'page' ]
524
+ );
525
+ add_action( "load-{$hook}", [ $this, 'hooks' ] );
526
+
527
+ if ( ! current_user_can( 'aioseo_admin' ) ) {
528
+ remove_submenu_page( 'aioseo', 'aioseo' );
529
+ }
530
+ }
531
+ }
532
+
533
+ /**
534
+ * Add the robots only menu inside of the WordPress network admin.
535
+ *
536
+ * @since 4.0.0
537
+ *
538
+ * @return void
539
+ */
540
+ public function addRobotsMenu() {
541
+ $this->addMainMenu( 'aioseo-tools' );
542
+
543
+ $page = $this->pages['aioseo-tools'];
544
+ $hook = add_submenu_page(
545
+ $page['parent'],
546
+ ! empty( $page['page_title'] ) ? $page['page_title'] : $page['menu_title'],
547
+ $page['menu_title'],
548
+ $page['capability'],
549
+ 'aioseo-tools',
550
+ [ $this, 'page' ]
551
+ );
552
+ add_action( "load-{$hook}", [ $this, 'hooks' ] );
553
+ }
554
+
555
+ /**
556
+ * Add the main menu.
557
+ *
558
+ * @since 4.0.0
559
+ *
560
+ * @param string $slug which slug to use.
561
+ * @return void
562
+ */
563
+ private function addMainMenu( $slug = 'aioseo' ) {
564
+ add_menu_page(
565
+ $this->menuName,
566
+ $this->menuName,
567
+ apply_filters( 'aioseo_manage_seo', 'aioseo_manage_seo' ),
568
+ $slug,
569
+ '__return_true',
570
+ 'data:image/svg+xml;base64,' . base64_encode( aioseo()->helpers->logo( 16, 16, '#A0A5AA' ) ),
571
+ '80.01234567890'
572
+ );
573
+ }
574
+
575
+ /**
576
+ * Output the HTML for the page.
577
+ *
578
+ * @since 4.0.0
579
+ *
580
+ * @return void
581
+ */
582
+ public function page() {
583
+ echo '<div id="aioseo-app"></div>';
584
+ }
585
+
586
+ /**
587
+ * Hooks for loading our pages.
588
+ *
589
+ * @since 4.0.0
590
+ *
591
+ * @return void
592
+ */
593
+ public function hooks() {
594
+ $currentScreen = function_exists( 'get_current_screen' ) ? get_current_screen() : false;
595
+ global $admin_page_hooks;
596
+
597
+ if ( ! is_object( $currentScreen ) || empty( $currentScreen->id ) || empty( $admin_page_hooks ) ) {
598
+ return;
599
+ }
600
+
601
+ $pages = [
602
+ 'dashboard',
603
+ 'settings',
604
+ 'search-appearance',
605
+ 'social-networks',
606
+ 'sitemaps',
607
+ 'internal-links',
608
+ 'redirects',
609
+ 'local-seo',
610
+ 'seo-analysis',
611
+ 'tools',
612
+ 'feature-manager',
613
+ 'monsterinsights',
614
+ 'about'
615
+ ];
616
+
617
+ foreach ( $pages as $page ) {
618
+ $addScripts = false;
619
+
620
+ if ( 'toplevel_page_aioseo' === $currentScreen->id ) {
621
+ $addScripts = true;
622
+ }
623
+
624
+ if ( ! empty( $admin_page_hooks['aioseo'] ) && $currentScreen->id === $admin_page_hooks['aioseo'] ) {
625
+ $addScripts = true;
626
+ }
627
+
628
+ if ( strpos( $currentScreen->id, 'aioseo-' . $page ) !== false ) {
629
+ $addScripts = true;
630
+ }
631
+
632
+ if ( ! $addScripts ) {
633
+ continue;
634
+ }
635
+
636
+ if ( 'tools' === $page ) {
637
+ $this->checkAdminQueryArgs();
638
+ }
639
+
640
+ // Redirect our Analytics page to the appropriate plugin page.
641
+ if ( 'monsterinsights' === $page ) {
642
+
643
+ $pluginData = aioseo()->helpers->getPluginData();
644
+
645
+ if (
646
+ (
647
+ $pluginData['miLite']['activated'] ||
648
+ $pluginData['miPro']['activated']
649
+ ) &&
650
+ function_exists( 'MonsterInsights' ) &&
651
+ function_exists( 'monsterinsights_get_ua' )
652
+ ) {
653
+ if ( (bool) monsterinsights_get_ua() ) {
654
+ wp_safe_redirect( $pluginData['miLite']['adminUrl'] );
655
+ exit;
656
+ }
657
+ }
658
+
659
+ if (
660
+ (
661
+ $pluginData['emLite']['activated'] ||
662
+ $pluginData['emPro']['activated']
663
+ ) &&
664
+ function_exists( 'ExactMetrics' ) &&
665
+ function_exists( 'exactmetrics_get_ua' )
666
+ ) {
667
+ if ( (bool) exactmetrics_get_ua() ) {
668
+ wp_safe_redirect( $pluginData['emLite']['adminUrl'] );
669
+ exit;
670
+ }
671
+ }
672
+ }
673
+
674
+ // We don't want any plugin adding notices to our screens. Let's clear them out here.
675
+ remove_all_actions( 'admin_notices' );
676
+ remove_all_actions( 'all_admin_notices' );
677
+
678
+ // Scripts.
679
+ aioseo()->helpers->enqueueScript(
680
+ 'aioseo-vendors',
681
+ 'js/chunk-vendors.js'
682
+ );
683
+ aioseo()->helpers->enqueueScript(
684
+ 'aioseo-common',
685
+ 'js/chunk-common.js'
686
+ );
687
+ aioseo()->helpers->enqueueScript(
688
+ 'aioseo-' . $page . '-script',
689
+ 'js/' . $page . '.js'
690
+ );
691
+
692
+ // Styles.
693
+ $rtl = is_rtl() ? '.rtl' : '';
694
+ aioseo()->helpers->enqueueStyle(
695
+ 'aioseo-vendors',
696
+ "css/chunk-vendors$rtl.css"
697
+ );
698
+ aioseo()->helpers->enqueueStyle(
699
+ 'aioseo-common',
700
+ "css/chunk-common$rtl.css"
701
+ );
702
+ // aioseo()->helpers->enqueueStyle(
703
+ // 'aioseo-' . $page . '-style',
704
+ // 'css/' . $page . $rtl . '.css'
705
+ // );
706
+ // aioseo()->helpers->enqueueStyle(
707
+ // 'aioseo-' . $page . '-vendors-style',
708
+ // 'css/chunk-' . $page . $rtl . '-vendors.css'
709
+ // );
710
+
711
+ wp_localize_script(
712
+ 'aioseo-' . $page . '-script',
713
+ 'aioseo',
714
+ aioseo()->helpers->getVueData( $page )
715
+ );
716
+
717
+ add_action( 'admin_footer_text', [ $this, 'addFooterText' ] );
718
+
719
+ // Only enqueue the media library if we need it in our module
720
+ if ( in_array( $page, [
721
+ 'social-networks',
722
+ 'search-appearance',
723
+ 'local-seo'
724
+ ], true ) ) {
725
+ wp_enqueue_media();
726
+ }
727
+
728
+ break;
729
+ }
730
+ }
731
+
732
+ /**
733
+ * Add footer text to the WordPress admin screens.
734
+ *
735
+ * @since 4.0.0
736
+ *
737
+ * @return void
738
+ */
739
+ public function addFooterText() {
740
+ $linkText = esc_html__( 'Give us a 5-star rating!', 'all-in-one-seo-pack' );
741
+ $href = 'https://wordpress.org/support/plugin/all-in-one-seo-pack/reviews/?filter=5#new-post';
742
+
743
+ $link1 = sprintf(
744
+ '<a href="%1$s" target="_blank" title="%2$s">&#9733;&#9733;&#9733;&#9733;&#9733;</a>',
745
+ $href,
746
+ $linkText
747
+ );
748
+
749
+ $link2 = sprintf(
750
+ '<a href="%1$s" target="_blank" title="%2$s">WordPress.org</a>',
751
+ $href,
752
+ $linkText
753
+ );
754
+
755
+ printf(
756
+ // Translators: 1 - The plugin name ("All in One SEO"), - 2 - This placeholder will be replaced with star icons, - 3 - "WordPress.org" - 4 - The plugin name ("All in One SEO").
757
+ esc_html__( 'Please rate %1$s %2$s on %3$s to help us spread the word. Thank you!', 'all-in-one-seo-pack' ),
758
+ sprintf( '<strong>%1$s</strong>', esc_html( AIOSEO_PLUGIN_NAME ) ),
759
+ wp_kses_post( $link1 ),
760
+ wp_kses_post( $link2 )
761
+ );
762
+ }
763
+
764
+ /**
765
+ * Check to get if the screen should be shown.
766
+ *
767
+ * @since 4.0.0
768
+ *
769
+ * @return bool
770
+ */
771
+ public function isAllowedScreen( $screen, $postType ) {
772
+ if ( 'edit' === $screen || 'upload' === $screen ) {
773
+ if ( aioseo()->options->advanced->postTypes->all ) {
774
+ return true;
775
+ }
776
+
777
+ $postTypes = aioseo()->options->advanced->postTypes->included;
778
+ if ( in_array( $postType, $postTypes, true ) ) {
779
+ return true;
780
+ }
781
+ }
782
+ return false;
783
+ }
784
+
785
+ /**
786
+ * Adds the columns to the page/post types.
787
+ *
788
+ * @since 4.0.0
789
+ *
790
+ * @return void
791
+ */
792
+ public function addPostColumns() {
793
+ $screen = get_current_screen();
794
+ if ( $this->isAllowedScreen( $screen->base, $screen->post_type ) ) {
795
+ add_action( 'admin_enqueue_scripts', [ $this, 'enqueuePostsScripts' ] );
796
+
797
+ if ( 'product' === $screen->post_type ) {
798
+ add_filter( 'manage_edit-product_columns', [ $this, 'postColumns' ] );
799
+ add_action( 'manage_posts_custom_column', [ $this, 'renderColumn' ], 10, 2 );
800
+ } elseif ( 'attachment' === $screen->post_type ) {
801
+ $enabled = apply_filters( 'aioseo_image_seo_media_columns', true );
802
+
803
+ if ( ! $enabled ) {
804
+ return;
805
+ }
806
+
807
+ add_filter( 'manage_media_columns', [ $this, 'postColumns' ] );
808
+ add_action( 'manage_media_custom_column', [ $this, 'renderColumn' ], 10, 2 );
809
+ } else {
810
+ add_filter( "manage_edit-{$screen->post_type}_columns", [ $this, 'postColumns' ] );
811
+ add_action( "manage_{$screen->post_type}_posts_custom_column", [ $this, 'renderColumn' ], 10, 2 );
812
+ }
813
+ }
814
+ }
815
+
816
+ /**
817
+ * Enqueues the JS/CSS for the page/posts table page.
818
+ *
819
+ * @since 4.0.0
820
+ *
821
+ * @return void
822
+ */
823
+ public function enqueuePostsScripts() {
824
+ // Scripts.
825
+ aioseo()->helpers->enqueueScript(
826
+ 'aioseo-posts-table',
827
+ 'js/posts-table.js'
828
+ );
829
+ aioseo()->helpers->enqueueScript(
830
+ 'aioseo-vendors',
831
+ 'js/chunk-vendors.js'
832
+ );
833
+ aioseo()->helpers->enqueueScript(
834
+ 'aioseo-common',
835
+ 'js/chunk-common.js'
836
+ );
837
+
838
+ $data = aioseo()->helpers->getVueData();
839
+ $data['posts'] = [];
840
+ $data['terms'] = [];
841
+ wp_localize_script(
842
+ 'aioseo-posts-table',
843
+ 'aioseo',
844
+ $data
845
+ );
846
+
847
+ // Styles.
848
+ $rtl = is_rtl() ? '.rtl' : '';
849
+ aioseo()->helpers->enqueueStyle(
850
+ 'aioseo-vendors',
851
+ "css/chunk-vendors$rtl.css"
852
+ );
853
+ aioseo()->helpers->enqueueStyle(
854
+ 'aioseo-common',
855
+ "css/chunk-common$rtl.css"
856
+ );
857
+ aioseo()->helpers->enqueueStyle(
858
+ 'aioseo-posts-table-style',
859
+ "css/posts-table$rtl.css"
860
+ );
861
+ }
862
+
863
+ /**
864
+ * Adds columns to the page/post tables in the admin.
865
+ *
866
+ * @since 4.0.0
867
+ *
868
+ * @param array $columns The columns we are adding ours onto.
869
+ * @return array The modified columns.
870
+ */
871
+ public function postColumns( $columns ) {
872
+ $pageAnalysisCapability = aioseo()->access->hasCapability( 'aioseo_page_analysis' );
873
+ $generalSettingsCapability = aioseo()->access->hasCapability( 'aioseo_page_general_settings' );
874
+ if (
875
+ ! current_user_can( 'aioseo_manage_seo' ) ||
876
+ ( empty( $pageAnalysisCapability ) && empty( $generalSettingsCapability ) )
877
+ ) {
878
+ return $columns;
879
+ }
880
+
881
+ // Translators: 1 - The short plugin name ("AIOSEO").
882
+ $columns['aioseo-details'] = sprintf( esc_html__( '%1$s Details', 'all-in-one-seo-pack' ), AIOSEO_PLUGIN_SHORT_NAME );
883
+
884
+ return $columns;
885
+ }
886
+
887
+ /**
888
+ * Renders the column in the page/post table.
889
+ *
890
+ * @since 4.0.0
891
+ *
892
+ * @param string $columnName The column name.
893
+ * @param int $postId The current rows, post id.
894
+ * @return void
895
+ */
896
+ public function renderColumn( $columnName, $postId ) {
897
+ if ( ! current_user_can( 'edit_post', $postId ) && ! current_user_can( 'aioseo_manage_seo' ) ) {
898
+ return;
899
+ }
900
+ if ( 'aioseo-details' === $columnName ) {
901
+ // Add this column/post to the localized array.
902
+ global $wp_scripts;
903
+
904
+ $data = $wp_scripts->get_data( 'aioseo-posts-table', 'data' );
905
+
906
+ if ( ! is_array( $data ) ) {
907
+ $data = json_decode( str_replace( 'var aioseo = ', '', substr( $data, 0, -1 ) ), true );
908
+ }
909
+
910
+ $nonce = wp_create_nonce( "aioseo_meta_{$columnName}_{$postId}" );
911
+ $posts = $data['posts'];
912
+ $thePost = Models\Post::getPost( $postId );
913
+ $posts[] = [
914
+ 'id' => $postId,
915
+ 'columnName' => $columnName,
916
+ 'nonce' => $nonce,
917
+ 'title' => $thePost->title,
918
+ 'titleParsed' => aioseo()->meta->title->getPostTitle( $postId ),
919
+ 'defaultTitle' => aioseo()->meta->title->getPostTypeTitle( get_post_type( $postId ) ),
920
+ 'description' => $thePost->description,
921
+ 'descriptionParsed' => aioseo()->meta->description->getPostDescription( $postId ),
922
+ 'defaultDescription' => aioseo()->meta->description->getPostTypeDescription( get_post_type( $postId ) ),
923
+ 'value' => (int) $thePost->seo_score,
924
+ 'showMedia' => false,
925
+ 'isSpecialPage' => aioseo()->helpers->isSpecialPage( $postId )
926
+ ];
927
+
928
+ $data['posts'] = $posts;
929
+
930
+ $wp_scripts->add_data( 'aioseo-posts-table', 'data', '' );
931
+ wp_localize_script( 'aioseo-posts-table', 'aioseo', $data );
932
+
933
+ require( AIOSEO_DIR . '/app/Common/Views/admin/posts/columns.php' );
934
+ }
935
+ }
936
+
937
+ /**
938
+ * Renders the column in the media/attachment table.
939
+ *
940
+ * @since 4.0.0
941
+ *
942
+ * @param string $columnName The column name.
943
+ * @param int $postId The current rows, post id.
944
+ * @return void
945
+ */
946
+ public function renderMediaColumn( $columnName, $postId ) {
947
+ $screen = get_current_screen();
948
+ if ( $this->isAllowedScreen( $screen->base, $screen->post_type ) ) {
949
+ $this->renderColumn( $columnName, $postId );
950
+ }
951
+ return null;
952
+ }
953
+
954
+ /**
955
+ * Renders the Score button in the publish metabox.
956
+ *
957
+ * @since 4.0.0
958
+ *
959
+ * @param WP_Post $postObj The post object.
960
+ * @return void
961
+ */
962
+ public function addPublishScore( $postObj ) {
963
+ $pageAnalysisCapability = aioseo()->access->hasCapability( 'aioseo_page_analysis' );
964
+ if ( empty( $pageAnalysisCapability ) || ( aioseo()->helpers->isWooCommerceActive() && 'product' === $postObj->post_type ) ) {
965
+ return;
966
+ }
967
+ $postTypes = aioseo()->helpers->getPublicPostTypes();
968
+ $showTruSeo = aioseo()->options->advanced->truSeo;
969
+ $isSpecialPage = aioseo()->helpers->isSpecialPage( $postObj->ID );
970
+
971
+ $postTypesMB = [];
972
+ foreach ( $postTypes as $pt ) {
973
+ if ( class_exists( 'bbPress' ) ) {
974
+ if (
975
+ 'attachment' !== $pt['name'] &&
976
+ 'forum' !== $pt['name'] &&
977
+ 'topic' !== $pt['name'] &&
978
+ 'reply' !== $pt['name']
979
+ ) {
980
+ $postTypesMB[] = $pt['name'];
981
+ }
982
+ } else {
983
+ if ( 'attachment' !== $pt['name'] ) {
984
+ $postTypesMB[] = $pt['name'];
985
+ }
986
+ }
987
+ }
988
+
989
+ if ( in_array( $postObj->post_type, $postTypesMB, true ) && $showTruSeo && ! $isSpecialPage ) {
990
+ $score = (int) Models\Post::getPost( $postObj->ID )->seo_score;
991
+ $path = 'M10 20C15.5228 20 20 15.5228 20 10C20 4.47715 15.5228 0 10 0C4.47716 0 0 4.47715 0 10C0 15.5228 4.47716 20 10 20ZM8.40767 3.65998C8.27222 3.45353 8.02129 3.357 7.79121 3.43828C7.52913 3.53087 7.27279 3.63976 7.02373 3.76429C6.80511 3.87361 6.69542 4.12332 6.74355 4.36686L6.91501 5.23457C6.95914 5.45792 6.86801 5.68459 6.69498 5.82859C6.42152 6.05617 6.16906 6.31347 5.94287 6.59826C5.80229 6.77526 5.58046 6.86908 5.36142 6.82484L4.51082 6.653C4.27186 6.60473 4.02744 6.71767 3.92115 6.94133C3.86111 7.06769 3.80444 7.19669 3.75129 7.32826C3.69815 7.45983 3.64929 7.59212 3.60464 7.72495C3.52562 7.96007 3.62107 8.21596 3.82396 8.35351L4.54621 8.84316C4.73219 8.96925 4.82481 9.19531 4.80234 9.42199C4.7662 9.78671 4.76767 10.1508 4.80457 10.5089C4.82791 10.7355 4.73605 10.9619 4.55052 11.0886L3.82966 11.5811C3.62734 11.7193 3.53274 11.9753 3.61239 12.2101C3.70314 12.4775 3.80985 12.7391 3.93188 12.9932C4.03901 13.2163 4.28373 13.3282 4.5224 13.2791L5.37279 13.1042C5.59165 13.0591 5.8138 13.1521 5.95491 13.3287C6.17794 13.6077 6.43009 13.8653 6.70918 14.0961C6.88264 14.2396 6.97459 14.4659 6.93122 14.6894L6.76282 15.5574C6.71551 15.8013 6.8262 16.0507 7.04538 16.1591C7.16921 16.2204 7.29563 16.2782 7.42457 16.3324C7.55352 16.3867 7.68316 16.4365 7.81334 16.4821C8.19418 16.6154 8.72721 16.1383 9.1213 15.7855C9.31563 15.6116 9.4355 15.3654 9.43677 15.1018C9.43677 15.1004 9.43678 15.099 9.43678 15.0976L9.43677 13.6462C9.43677 13.6308 9.43736 13.6155 9.43852 13.6004C8.27454 13.3165 7.40918 12.248 7.40918 10.9732V9.43198C7.40918 9.31483 7.50224 9.21986 7.61706 9.21986H8.338V7.70343C8.338 7.49405 8.50433 7.32432 8.70952 7.32432C8.9147 7.32432 9.08105 7.49405 9.08105 7.70343V9.21986H11.0316V7.70343C11.0316 7.49405 11.1979 7.32432 11.4031 7.32432C11.6083 7.32432 11.7746 7.49405 11.7746 7.70343V9.21986H12.4956C12.6104 9.21986 12.7034 9.31483 12.7034 9.43198V10.9732C12.7034 12.2883 11.7825 13.3838 10.5628 13.625C10.5631 13.632 10.5632 13.6391 10.5632 13.6462L10.5632 15.0914C10.5632 15.36 10.6867 15.6107 10.8868 15.7853C11.2879 16.1351 11.8302 16.6079 12.2088 16.4742C12.4708 16.3816 12.7272 16.2727 12.9762 16.1482C13.1949 16.0389 13.3046 15.7891 13.2564 15.5456L13.085 14.6779C13.0408 14.4545 13.132 14.2278 13.305 14.0838C13.5785 13.8563 13.8309 13.599 14.0571 13.3142C14.1977 13.1372 14.4195 13.0434 14.6385 13.0876L15.4892 13.2595C15.7281 13.3077 15.9725 13.1948 16.0788 12.9711C16.1389 12.8448 16.1955 12.7158 16.2487 12.5842C16.3018 12.4526 16.3507 12.3204 16.3953 12.1875C16.4744 11.9524 16.3789 11.6965 16.176 11.559L15.4537 11.0693C15.2678 10.9432 15.1752 10.7171 15.1976 10.4905C15.2338 10.1258 15.2323 9.76167 15.1954 9.40357C15.1721 9.17699 15.2639 8.95062 15.4495 8.82387L16.1703 8.33141C16.3726 8.1932 16.4672 7.93715 16.3876 7.70238C16.2968 7.43495 16.1901 7.17337 16.0681 6.91924C15.961 6.69615 15.7162 6.58422 15.4776 6.63333L14.6272 6.8083C14.4083 6.85333 14.1862 6.76033 14.0451 6.58377C13.822 6.30474 13.5699 6.04713 13.2908 5.81632C13.1173 5.67287 13.0254 5.44652 13.0688 5.22301L13.2372 4.35503C13.2845 4.11121 13.1738 3.86179 12.9546 3.75334C12.8308 3.69208 12.7043 3.63424 12.5754 3.58002C12.4465 3.52579 12.3168 3.47593 12.1866 3.43037C11.9562 3.34974 11.7055 3.44713 11.5707 3.65416L11.0908 4.39115C10.9672 4.58093 10.7457 4.67543 10.5235 4.65251C10.1661 4.61563 9.80932 4.61712 9.45837 4.65477C9.23633 4.6786 9.01448 4.58486 8.89027 4.39554L8.40767 3.65998Z'; // phpcs:ignore Generic.Files.LineLength.MaxExceeded
992
+ ?>
993
+ <div class="misc-pub-section aioseo-score-settings">
994
+ <svg viewBox="0 0 20 20" width="20" height="20" xmlns="http://www.w3.org/2000/svg">
995
+ <path fill-rule="evenodd" clip-rule="evenodd" d="<?php echo esc_attr( $path ); ?>" fill="#82878C" />
996
+ </svg>
997
+ <span>
998
+ <?php
999
+ // Translators: 1 - The short plugin name ("AIOSEO").
1000
+ echo sprintf( esc_html__( '%1$s Score', 'all-in-one-seo-pack' ), esc_html( AIOSEO_PLUGIN_SHORT_NAME ) );
1001
+ ?>
1002
+ </span>
1003
+ <div id="aioseo-post-settings-sidebar-button" class="aioseo-score-button classic-editor <?php echo esc_attr( aioseo()->helpers->getScoreClass( $score ) ); ?>">
1004
+ <span id="aioseo-post-score"><?php echo esc_attr( $score . '/100' ); ?></span>
1005
+ </div>
1006
+ </div>
1007
+ <?php
1008
+ }
1009
+ }
1010
+
1011
+ /**
1012
+ * Checks the admin query args to run appropriate tasks.
1013
+ *
1014
+ * @since 4.0.0
1015
+ *
1016
+ * @return void
1017
+ */
1018
+ protected function checkAdminQueryArgs() {
1019
+ // Migrate the post/term meta manually.
1020
+ if ( isset( $_GET['aioseo-v3-migrate-now'] ) ) {
1021
+ aioseo()->migration->oldOptions = ( new Migration\OldOptions() )->oldOptions;
1022
+ aioseo()->migration->meta->migratePostMeta();
1023
+ }
1024
+
1025
+ // Redo the migration from the beginning.
1026
+ if ( isset( $_GET['aioseo-v3-migration'] ) && 'i-want-to-migrate' === wp_unslash( $_GET['aioseo-v3-migration'] ) ) { // phpcs:ignore HM.Security.ValidatedSanitizedInput.InputNotSanitized
1027
+ Migration\Helpers::redoMigration();
1028
+ }
1029
+
1030
+ // Remove all AIOSEO transients.
1031
+ if ( isset( $_GET['aioseo-clear-cache'] ) ) {
1032
+ $table = aioseo()->db->db->options;
1033
+ aioseo()->db->db->query( "DELETE FROM {$table} WHERE option_name LIKE '\_transient\_aioseo\_%'" );
1034
+ aioseo()->db->db->query( "DELETE FROM {$table} WHERE option_name LIKE '\_transient\_timeout\_aioseo\_%'" );
1035
+ }
1036
+
1037
+ $this->updateDeprecatedOptions();
1038
+ }
1039
+
1040
+ /**
1041
+ * Updates deprecated options.
1042
+ *
1043
+ * @since 4.0.0
1044
+ *
1045
+ * @return void
1046
+ */
1047
+ protected function updateDeprecatedOptions() {
1048
+ // Check if the user is forcefully wanting to add a deprecated option.
1049
+ $allDeprecatedOptions = aioseo()->internalOptions->getAllDeprecatedOptions();
1050
+ $deprecatedOptions = aioseo()->internalOptions->internal->deprecatedOptions;
1051
+ if ( isset( $_GET['aioseo-enable-option'] ) ) {
1052
+ $changed = false;
1053
+ foreach ( $allDeprecatedOptions as $deprecatedOption ) {
1054
+ if ( $deprecatedOption === $_GET['aioseo-enable-option'] && ! in_array( $deprecatedOption, $deprecatedOptions, true ) ) {
1055
+ $changed = true;
1056
+ array_push( $deprecatedOptions, $deprecatedOption );
1057
+ }
1058
+ }
1059
+
1060
+ if ( $changed ) {
1061
+ aioseo()->internalOptions->internal->deprecatedOptions = array_values( $deprecatedOptions );
1062
+ }
1063
+ }
1064
+
1065
+ if ( isset( $_GET['aioseo-disable-option'] ) ) {
1066
+ $changed = false;
1067
+ foreach ( $allDeprecatedOptions as $deprecatedOption ) {
1068
+ if ( $deprecatedOption === $_GET['aioseo-disable-option'] && in_array( $deprecatedOption, $deprecatedOptions, true ) ) {
1069
+ $changed = true;
1070
+ $key = array_search( $deprecatedOption, $deprecatedOptions, true );
1071
+ unset( $deprecatedOptions[ $key ] );
1072
+ }
1073
+ }
1074
+
1075
+ if ( $changed ) {
1076
+ aioseo()->internalOptions->internal->deprecatedOptions = array_values( $deprecatedOptions );
1077
+ }
1078
+ }
1079
+ }
1080
+ }
app/Common/Admin/AutoUpdates.php ADDED
@@ -0,0 +1,226 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Admin;
3
+
4
+ /**
5
+ * Auto updater class.
6
+ *
7
+ * Notes about the autoupdater:
8
+ * This runs on the normal WordPress auto-update sequence:
9
+ * 1. In wp-includes/update.php, wp_version_check() is called by the WordPress update cron (every 8 or 12 hours; can be overriden to be faster/long or turned off by plugins)
10
+ * 2. In wp-includes/update.php, wp_version_check() ends with a action call to do_action( 'wp_maybe_auto_update' ) if cron is running
11
+ * 3. In wp-includes/update.php, wp_maybe_auto_update() hooks into wp_maybe_auto_update action, creates a new WP_Automatic_Updater instance and calls WP_Automatic_Updater->run
12
+ * 4. In wp-admin/includes/class-wp-automatic-updater.php $this->run() checks to make sure we're on the main site if on a network,
13
+ * and also if the autoupdates are disabled (by plugin, by being on a version controlled site, etc )
14
+ * 5. In wp-admin/includes/class-wp-automatic-updater.php $this->run() then checks to see which plugins have new versions (version/update check)
15
+ * 6. In wp-admin/includes/class-wp-automatic-updater.php $this->run() then calls $this->update() for each plugin installed who has an upgrade.
16
+ * 7 In wp-admin/includes/class-wp-automatic-updater.php $this->update() double checks filesystem access and then installs the plugin if able
17
+ *
18
+ * Notes:
19
+ * - This autoupdater only works if WordPress core detects no version control. If you want to test this, do it on a new WP site without any .git folders anywhere.
20
+ * - This autoupdater only works if the file access is able to be written to
21
+ * - This autoupdater only works if a new version has been detected, and will run not the second the update is released,
22
+ * but whenever the cron for wp_version_check is next released. This is generally run every 8-12 hours.
23
+ * - However, that cron can be disabled, the autoupdater can be turned off via constant or filter, version control or file lock can be detected,
24
+ * and other plugins can be installed (incl in functions of theme) that turn off all automatic plugin updates.
25
+ * - If you want to test this is working, you have to manually run the wp_version_check cron. Install the WP Crontrol plugin or Core Control plugin, and run the cron manually using it.
26
+ * - Again, because you skimmed over it the first time, if you want to test this manually you need to test this on a new WP install without version control for core,
27
+ * plugins, etc, without file lock, with license key entered (for pro only)
28
+ * and use the WP Crontrol or Core Control plugin to run wp_version_check
29
+ * - You may have to manually remove an option called "auto_update.lock" from the WP options table
30
+ * - You may need to run wp_version_check multiple times (note though that they must be spaced at least 60 seconds apart)
31
+ * - Because WP's updater asks the OS if the file is writable, make sure you do not have any files/folders for the plugin you are trying to autoupdate open when testing.
32
+ * - You may need to delete the plugin info transient to get it to hard refresh the plugin info.
33
+ *
34
+ * @since 4.0.0
35
+ */
36
+ class AutoUpdates {
37
+ /**
38
+ * Class Constructor.
39
+ *
40
+ * @since 4.0.0
41
+ */
42
+ public function __construct() {
43
+ add_filter( 'auto_update_plugin', [ $this, 'automaticUpdates' ], 10, 2 );
44
+ add_filter( 'plugin_auto_update_setting_html', [ $this, 'modifyWordPressAutoupdaterSetting' ], 10, 3 );
45
+ }
46
+
47
+ /**
48
+ * Filters the auto update plugin routine to allow MonsterInsights to be
49
+ * automatically updated.
50
+ *
51
+ * @since 4.0.0
52
+ *
53
+ * @param bool $update Flag to update the plugin or not.
54
+ * @param array $item Update data about a specific plugin.
55
+ * @return bool The new update state.
56
+ */
57
+ public function automaticUpdates( $update, $item ) {
58
+ $item = (array) $item;
59
+ $pluginInfo = get_site_transient( 'update_plugins' );
60
+ $response = $pluginInfo->response;
61
+ $noUpdate = $pluginInfo->no_update;
62
+ $plugin = isset( $item['plugin'] ) && isset( $response[ $item['plugin'] ] )
63
+ ? (array) $response[ $item['plugin'] ]
64
+ : (
65
+ isset( $noUpdate[ $item['plugin'] ] )
66
+ ? (array) $noUpdate[ $item['plugin'] ]
67
+ : null
68
+ );
69
+
70
+ if ( empty( $plugin ) ) {
71
+ return $update;
72
+ }
73
+
74
+ $isFree = 'all-in-one-seo-pack' === $item['slug'];
75
+ $isPaid = isset( $plugin['aioseo'] ); // see updater class
76
+ $isAddon = $this->isPluginOurAddon( $item['plugin'] );
77
+
78
+ $automaticUpdates = aioseo()->options->advanced->autoUpdates;
79
+
80
+ // When used in the context of Plugins page.
81
+ if ( function_exists( 'get_current_screen' ) ) {
82
+ $screen = get_current_screen();
83
+ if ( ! empty( $screen ) &&
84
+ ! empty( $screen->id ) &&
85
+ in_array( $screen->id, [ 'plugins', 'plugins-network' ], true )
86
+ ) {
87
+ $isPro = aioseo()->pro;
88
+ $isMainPro = $isPro && plugin_basename( AIOSEO_FILE ) === $item['plugin'];
89
+
90
+ if (
91
+ $isFree ||
92
+ $isMainPro ||
93
+ (
94
+ $isAddon &&
95
+ $isPro
96
+ )
97
+ ) {
98
+ return in_array( $automaticUpdates, [ 'all', 'minor' ], true );
99
+ } elseif ( $isAddon && ! $isPro ) {
100
+ return false;
101
+ }
102
+ }
103
+ }
104
+
105
+ // If this is multisite and is not on the main site, return early.
106
+ if ( is_multisite() && ! is_main_site() ) {
107
+ return $update;
108
+ }
109
+
110
+ // If we don't have everything we need, return early.
111
+ if ( ! isset( $item['new_version'] ) || ! isset( $item['slug'] ) ) {
112
+ return $update;
113
+ }
114
+
115
+ // If the plugin isn't ours, return early.
116
+
117
+ if ( ! $isFree && ! $isPaid || ( $isFree && ! defined( 'AIOSEO_VERSION' ) ) ) {
118
+ return $update;
119
+ }
120
+
121
+ $version = $isFree ? AIOSEO_VERSION : $plugin['oldVersion'];
122
+ $currentMajor = $this->getMajorVersion( $version );
123
+ $newMajor = $this->getMajorVersion( $plugin['new_version'] );
124
+
125
+ // If the opt in update allows major updates but there is no major version update, return early.
126
+ if ( $currentMajor < $newMajor ) {
127
+ if ( 'all' === $automaticUpdates ) {
128
+ return true;
129
+ }
130
+
131
+ return $update;
132
+ }
133
+
134
+ // If the opt in update allows minor updates but there is no minor version update, return early.
135
+ if ( $currentMajor === $newMajor ) {
136
+ if ( 'all' === $automaticUpdates || 'minor' === $automaticUpdates ) {
137
+ return true;
138
+ }
139
+
140
+ return $update;
141
+ }
142
+
143
+ // All our checks have passed - this plugin can be updated!
144
+ return true;
145
+ }
146
+
147
+ /**
148
+ * Add Manage Auto-updates link for MI pro and its add-ons on Plugins page
149
+ *
150
+ * @since 1.0.0
151
+ *
152
+ * @param string $html
153
+ * @param string $pluginFile
154
+ * @return string
155
+ */
156
+ public function modifyWordPressAutoupdaterSetting( $html, $pluginFile, $pluginData ) {
157
+ if ( empty( $pluginData['slug'] ) ) {
158
+ return $html;
159
+ }
160
+
161
+ $isPro = aioseo()->pro;
162
+ $isAddon = $this->isPluginOurAddon( $pluginFile );
163
+ $isMainFree = 'all-in-one-seo-pack' === $pluginData['slug'];
164
+ $isMainPro = $isPro && plugin_basename( AIOSEO_FILE ) === $pluginFile;
165
+ $hasPermission = current_user_can( 'aioseo_manage_seo' );
166
+
167
+ if ( $isAddon && ! $isPro ) {
168
+ $html = sprintf(
169
+ '<a href="%s" target="_blank">%s</a>',
170
+ aioseo()->helpers->utmUrl( AIOSEO_MARKETING_URL . 'docs/how-to-upgrade-from-all-in-one-seo-lite-to-pro', 'plugins-autoupdate', 'upgrade-to-autoupdate' ),
171
+ // Translators: 1 - "AIOSEO Pro"
172
+ sprintf( __( 'Enable the %1$s plugin to manage auto-updates', 'all-in-one-seo-pack' ), 'AIOSEO Pro' )
173
+ );
174
+ add_filter( "aioseo_is_autoupdate_setting_html_filtered_$pluginFile", '__return_true' );
175
+ } elseif ( $hasPermission &&
176
+ ( $isMainFree || $isMainPro || ( $isAddon && $isPro ) )
177
+ ) {
178
+ $text = __( 'Manage auto-updates', 'all-in-one-seo-pack' );
179
+ $html .= '<br>' . sprintf( '<a href="%s">%s</a>', admin_url( 'admin.php?page=aioseo-settings#/advanced' ), $text );
180
+ add_filter( "aioseo_is_autoupdate_setting_html_filtered_$pluginFile", '__return_true' );
181
+ }
182
+
183
+ return $html;
184
+ }
185
+
186
+ /**
187
+ * Checks if the plugin is one of ours.
188
+ *
189
+ * @since 4.0.0
190
+ *
191
+ * @param string $pluginFile The plugin file to check against.
192
+ * @return bool True if it is, false if not.
193
+ */
194
+ private function isPluginOurAddon( $pluginFile ) {
195
+ $addons = aioseo()->addons->getAddons();
196
+
197
+ if ( ! is_array( $addons ) ) {
198
+ return false;
199
+ }
200
+
201
+ foreach ( $addons as $addon ) {
202
+ if ( $pluginFile === $addon->basename ) {
203
+ return true;
204
+ }
205
+ }
206
+
207
+ return false;
208
+ }
209
+
210
+ /**
211
+ * Split out a version number and return the major version portion.
212
+ *
213
+ * @since 4.0.0
214
+ *
215
+ * @param string $version The version to check.
216
+ * @return string The major version portion of the version.
217
+ */
218
+ private function getMajorVersion( $version ) {
219
+ $version = explode( '.', $version );
220
+ if ( isset( $version[2] ) ) {
221
+ return $version[0] . '.' . $version[1] . '.' . $version[2];
222
+ }
223
+
224
+ return $version[0] . '.' . $version[1] . '.0';
225
+ }
226
+ }
app/Common/Admin/ConflictingPlugins.php ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Admin;
3
+
4
+ use AIOSEO\Plugin\Common\Models;
5
+
6
+ /**
7
+ * Checks for conflicting plugins.
8
+ *
9
+ * @since 4.0.0
10
+ */
11
+ class ConflictingPlugins {
12
+ /**
13
+ * Class Constructor.
14
+ *
15
+ * @since 4.0.0
16
+ */
17
+ public function __construct() {
18
+ add_action( 'init', [ $this, 'init' ] );
19
+ }
20
+
21
+ /**
22
+ * Initialize the conflicting plugins check.
23
+ *
24
+ * @since 4.0.0
25
+ *
26
+ * @return void
27
+ */
28
+ public function init() {
29
+ // Only do this for users who can install/deactivate plugins.
30
+ if ( ! current_user_can( 'install_plugins' ) ) {
31
+ return;
32
+ }
33
+
34
+ $conflictingPlugins = $this->getAllConflictingPlugins();
35
+
36
+ $notification = Models\Notification::getNotificationByName( 'conflicting-plugins' );
37
+ if ( empty( $conflictingPlugins ) ) {
38
+ if ( ! $notification->exists() ) {
39
+ return;
40
+ }
41
+
42
+ Models\Notification::deleteNotificationByName( 'conflicting-plugins' );
43
+ return;
44
+ }
45
+
46
+ aioseo()->notices->conflictingPlugins( $conflictingPlugins );
47
+ }
48
+
49
+ /**
50
+ * Get a list of all conflicting plugins.
51
+ *
52
+ * @since 4.0.0
53
+ *
54
+ * @return array An array of conflicting plugins.
55
+ */
56
+ protected function getAllConflictingPlugins() {
57
+ $conflictingSeoPlugins = $this->getConflictingPlugins( 'seo' );
58
+ $conflictingSitemapPlugins = [];
59
+
60
+ if (
61
+ aioseo()->options->sitemap->general->enable ||
62
+ aioseo()->options->sitemap->rss->enable
63
+ ) {
64
+ $conflictingSitemapPlugins = $this->getConflictingPlugins( 'sitemap' );
65
+ }
66
+
67
+ return array_merge( $conflictingSeoPlugins, $conflictingSitemapPlugins );
68
+ }
69
+
70
+ /**
71
+ * Get a list of conflicting plugins for AIOSEO.
72
+ *
73
+ * @since 4.0.0
74
+ *
75
+ * @param string $type A type to look for.
76
+ * @return array An array of conflicting plugins.
77
+ */
78
+ public function getConflictingPlugins( $type ) {
79
+ $activePlugins = get_option( 'active_plugins' );
80
+
81
+ $conflictingPlugins = [];
82
+ switch ( $type ) {
83
+ case 'seo':
84
+ $conflictingPlugins = [
85
+ 'Yoast SEO' => 'wordpress-seo/wp-seo.php',
86
+ 'Yoast SEO Premium' => 'wordpress-seo-premium/wp-seo-premium.php',
87
+ 'Rank Math SEO' => 'seo-by-rank-math/rank-math.php',
88
+ 'SEOPress' => 'wp-seopress/seopress.php',
89
+ 'The SEO Framework' => 'autodescription/autodescription.php',
90
+ ];
91
+ break;
92
+ case 'sitemap':
93
+ $conflictingPlugins = [
94
+ 'Google XML Sitemaps' => 'google-sitemap-generator/sitemap.php',
95
+ 'XML Sitemap & Google News' => 'xml-sitemap-feed/xml-sitemap.php',
96
+ 'Google XML Sitemap Generator' => 'www-xml-sitemap-generator-org/www-xml-sitemap-generator-org.php',
97
+ 'Sitemap by BestWebSoft' => 'google-sitemap-plugin/google-sitemap-plugin.php',
98
+ ];
99
+ break;
100
+ }
101
+
102
+ return array_intersect( $conflictingPlugins, $activePlugins );
103
+ }
104
+ }
app/Common/Admin/Dashboard.php ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Admin;
3
+
4
+ /**
5
+ * Class that holds our dashboard widget.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class Dashboard {
10
+ /**
11
+ * Class Constructor.
12
+ *
13
+ * @since 4.0.0
14
+ */
15
+ public function __construct() {
16
+ add_action( 'wp_dashboard_setup', [ $this, 'addDashboardWidget' ] );
17
+ }
18
+
19
+ /**
20
+ * Add Dashboard Widget
21
+ *
22
+ * @since 2.3.10
23
+ */
24
+ public function addDashboardWidget() {
25
+ if ( current_user_can( 'install_plugins' ) && $this->showWidget() ) {
26
+ wp_add_dashboard_widget(
27
+ 'aioseo-rss-feed',
28
+ esc_html__( 'SEO News', 'all-in-one-seo-pack' ),
29
+ [
30
+ $this,
31
+ 'displayRssDashboardWidget',
32
+ ]
33
+ );
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Whether or not to show the widget.
39
+ *
40
+ * @since 4.0.0
41
+ *
42
+ * @return boolean True if yes, false otherwise.
43
+ */
44
+ protected function showWidget() {
45
+ // API filter hook to disable showing SEO News dashboard widget.
46
+ if ( false === apply_filters( 'aioseo_show_seo_news', true ) ) {
47
+ return false;
48
+ }
49
+
50
+ return true;
51
+ }
52
+
53
+ /**
54
+ * Display RSS Dashboard Widget
55
+ *
56
+ * @since 4.0.0
57
+ */
58
+ public function displayRssDashboardWidget() {
59
+ // check if the user has chosen not to display this widget through screen options.
60
+ $currentScreen = get_current_screen();
61
+ $hiddenWidgets = get_user_meta( get_current_user_id(), 'metaboxhidden_' . $currentScreen->id );
62
+ if ( $hiddenWidgets && count( $hiddenWidgets ) > 0 && is_array( $hiddenWidgets[0] ) && in_array( 'aioseo-rss-feed', $hiddenWidgets[0], true ) ) {
63
+ return;
64
+ }
65
+
66
+ include_once( ABSPATH . WPINC . '/feed.php' );
67
+
68
+ $rssItems = get_transient( 'aioseo_rss_feed' );
69
+ if ( false === $rssItems ) {
70
+
71
+ $rss = fetch_feed( 'https://aioseo.com/feed/' );
72
+ if ( is_wp_error( $rss ) ) {
73
+ esc_html_e( 'Temporarily unable to load feed.', 'all-in-one-seo-pack' );
74
+
75
+ return;
76
+ }
77
+ $rssItems = $rss->get_items( 0, 4 ); // Show four items.
78
+ $cached = [];
79
+ foreach ( $rssItems as $item ) {
80
+ $cached[] = [
81
+ 'url' => $item->get_permalink(),
82
+ 'title' => $item->get_title(),
83
+ 'date' => $item->get_date( 'M jS Y' ),
84
+ 'content' => substr( wp_strip_all_tags( $item->get_content() ), 0, 128 ) . '...',
85
+ ];
86
+ }
87
+ $rssItems = $cached;
88
+
89
+ set_transient( 'aioseo_rss_feed', $cached, 12 * HOUR_IN_SECONDS );
90
+
91
+ }
92
+
93
+ ?>
94
+
95
+ <ul>
96
+ <?php
97
+ if ( false === $rssItems ) {
98
+ echo '<li>No items</li>';
99
+
100
+ return;
101
+ }
102
+
103
+ foreach ( $rssItems as $item ) {
104
+ ?>
105
+ <li>
106
+ <a target="_blank" href="<?php echo esc_url( $item['url'] ); ?>">
107
+ <?php echo esc_html( $item['title'] ); ?>
108
+ </a>
109
+ <span class="aioseop-rss-date"><?php echo esc_html( $item['date'] ); ?></span>
110
+ <div class="aioseop_news">
111
+ <?php echo esc_html( wp_strip_all_tags( $item['content'] ) ) . '...'; ?>
112
+ </div>
113
+ </li>
114
+ <?php
115
+ }
116
+
117
+ ?>
118
+ </ul>
119
+
120
+ <?php
121
+
122
+ }
123
+ }
app/Common/Admin/LocalBusinessSeo.php ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Admin;
3
+
4
+ /**
5
+ * Abstract class that Pro and Lite both extend.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class LocalBusinessSeo {
10
+
11
+ private $blocks = [];
12
+
13
+ /**
14
+ * Initialize the admin.
15
+ *
16
+ * @since 4.0.0
17
+ *
18
+ * @return void
19
+ */
20
+ public function __construct() {
21
+ if ( is_admin() ) {
22
+ // Load Gutenberg components
23
+ // add_action( 'admin_enqueue_scripts', [ $this, 'enqueueLocalBusinessSeoScripts' ] );
24
+
25
+ // Add metabox
26
+ // add_action( 'add_meta_boxes', [ $this, 'addLocalBusinessSeoSettingsMetabox' ] );
27
+
28
+ // Add Gutenberg Blocks
29
+ // $this->blocks[] = 'address';
30
+ // $this->blocks[] = 'opening';
31
+ // add_filter( 'block_categories', [ $this, 'registerBlockCategory' ], 10, 3 );
32
+ // add_action( 'init', [ $this, 'registerBlocks' ] );
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Enqueues the JS/CSS for the on Local SEO settings.
38
+ *
39
+ * @return void
40
+ */
41
+ public function enqueueLocalBusinessSeoScripts() {
42
+ $screen = get_current_screen();
43
+ if (
44
+ 'edit-tags' === $screen->base ||
45
+ 'term' === $screen->base ||
46
+ 'post' === $screen->base
47
+ ) {
48
+ aioseo()->helpers->enqueueScript(
49
+ 'aioseo-localbusinessseo-settings-metabox',
50
+ 'js/local-business-seo.js'
51
+ );
52
+ wp_localize_script(
53
+ 'aioseo-localbusinessseo-settings-metabox',
54
+ 'aioseo',
55
+ aioseo()->helpers->getVueData()
56
+ );
57
+ $rtl = is_rtl() ? '.rtl' : '';
58
+ aioseo()->helpers->enqueueStyle(
59
+ 'aioseo-localbusinessseo-settings-metabox',
60
+ "css/local-business-seo$rtl.css"
61
+ );
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Adds a meta box to Local SEO posts.
67
+ *
68
+ * @return void
69
+ */
70
+ public function addLocalBusinessSeoSettingsMetabox() {
71
+ add_meta_box(
72
+ 'aioseo-localbusinessseo',
73
+ 'AIOSEO Local',
74
+ [ $this, 'localBusinessSEOSettingsMetabox' ],
75
+ [ 'aioseo-location' ],
76
+ 'normal',
77
+ 'high'
78
+ );
79
+ }
80
+
81
+ /**
82
+ * Render the Local SEO settings metabox with Vue App wrapper.
83
+ *
84
+ * @param WP_Post $post The current post.
85
+ * @return string
86
+ */
87
+ public function localBusinessSEOSettingsMetabox( $post ) {
88
+ $path = 'M13.6449 2.35C12.1949 0.9 10.2049 0 7.99488 0C3.57488 0 0.00488281 3.58 0.00488281 8C0.00488281 12.42 3.57488 16 7.99488 16C11.7249 16 14.8349 13.45 15.7249 10H13.6449C12.8249 12.33 10.6049 14 7.99488 14C4.68488 14 1.99488 11.31 1.99488 8C1.99488 4.69 4.68488 2 7.99488 2C9.65488 2 11.1349 2.69 12.2149 3.78L8.99488 7H15.9949V0L13.6449 2.35Z'; // phpcs:ignore Generic.Files.LineLength.MaxExceeded
89
+ ?>
90
+ <div id="aioseo-localbusinessseo-metabox">
91
+ <svg class="loading" width="32" height="64" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
92
+ <path d="<?php echo esc_attr( $path ); ?>" fill="currentcolor"/>
93
+ </svg>
94
+ </div>
95
+ <?php
96
+ }
97
+
98
+ // public function registerBlockCategory( $categories, $post ) {
99
+ // return array_merge(
100
+ // $categories,
101
+ // [
102
+ // [
103
+ // 'slug' => 'aioseo',
104
+ // 'title' => AIOSEO_PLUGIN_SHORT_NAME,
105
+ // ],
106
+ // ]
107
+ // );
108
+ // }
109
+
110
+ // public function registerBlocks() {
111
+ // foreach ( $this->blocks as $block ) {
112
+
113
+ // require AIOSEO_DIR . '/app/Common/Blocks/' . $block . '/index.php';
114
+
115
+ // register_block_type(
116
+ // 'aioseo/' . $block,
117
+ // [
118
+ // 'render_callback' => 'renderCallback',
119
+ // ]
120
+ // );
121
+ // }
122
+ // }
123
+
124
+ // public function renderCallback( $block_attributes, $content ) {
125
+ // error_log( 'renderCallback' );
126
+ // return '<h1>Example block</h1>';
127
+ // }
128
+ }
app/Common/Admin/Notices/Import.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Admin\Notices;
3
+
4
+ /**
5
+ * Plugin import notice.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class Import {
10
+
11
+ /**
12
+ * Go through all the checks to see if we should show the notice.
13
+ *
14
+ * @since 4.0.0
15
+ *
16
+ * @return void
17
+ */
18
+ public function maybeShowNotice() {
19
+ $transients = aioseo()->db
20
+ ->start( 'options' )
21
+ ->select( 'option_name as name' )
22
+ ->whereRaw( "`option_name` LIKE '%transient_aioseo_%'" )
23
+ ->run()
24
+ ->result();
25
+
26
+ $foundImportTransient = false;
27
+ foreach ( $transients as $transient ) {
28
+ if ( preg_match( '#_aioseo_import_.*_meta_.*#', $transient->name ) ) {
29
+ $foundImportTransient = true;
30
+ break;
31
+ }
32
+ }
33
+
34
+ if ( ! $foundImportTransient ) {
35
+ return;
36
+ }
37
+
38
+ $this->showNotice();
39
+ }
40
+
41
+ /**
42
+ * Register the notice so that it appears.
43
+ *
44
+ * @since 4.0.0
45
+ *
46
+ * @return void
47
+ */
48
+ public function showNotice() {
49
+ $string1 = __( 'SEO Meta Import In Progress', 'all-in-one-seo-pack' );
50
+ // Translators: 1 - The plugin name ("All in One SEO").
51
+ $string2 = sprintf( __( '%1$s is importing your existing SEO data in the background.', 'all-in-one-seo-pack' ), AIOSEO_PLUGIN_NAME );
52
+ $string3 = __( 'This notice will automatically disappear as soon as the import has completed. Meanwhile, everything should continue to work as expected.', 'all-in-one-seo-pack' );
53
+ ?>
54
+ <div class="notice notice-info aioseo-migration">
55
+ <p><strong><?php echo esc_html( $string1 ); ?></strong></p>
56
+ <p><?php echo esc_html( $string2 ); ?></p>
57
+ <p><?php echo esc_html( $string3 ); ?></p>
58
+ </div>
59
+ <style>
60
+ </style>
61
+ <?php
62
+ }
63
+ }
app/Common/Admin/Notices/Migration.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Admin\Notices;
3
+
4
+ /**
5
+ * V3 to V4 migration notice.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class Migration {
10
+ /**
11
+ * Go through all the checks to see if we should show the notice.
12
+ *
13
+ * @since 4.0.0
14
+ *
15
+ * @return void
16
+ */
17
+ public function maybeShowNotice() {
18
+ $transientPosts = get_transient( 'aioseo_v3_migration_in_progress_posts' );
19
+ $transientTerms = get_transient( 'aioseo_v3_migration_in_progress_terms' );
20
+ if ( ! $transientPosts && ! $transientTerms ) {
21
+ return;
22
+ }
23
+
24
+ $this->showNotice();
25
+ }
26
+
27
+ /**
28
+ * Register the notice so that it appears.
29
+ *
30
+ * @since 4.0.0
31
+ *
32
+ * @return void
33
+ */
34
+ public function showNotice() {
35
+ // Translators: 1 - The plugin name ("AIOSEO).
36
+ $string1 = sprintf( __( '%1$s V3->V4 Migration In Progress', 'all-in-one-seo-pack' ), AIOSEO_PLUGIN_SHORT_NAME );
37
+ // Translators: 1 - The plugin name ("All in One SEO").
38
+ $string2 = sprintf( __( '%1$s is currently upgrading your database and migrating your SEO data in the background.', 'all-in-one-seo-pack' ), AIOSEO_PLUGIN_NAME );
39
+ $string3 = __( 'This notice will automatically disappear as soon as the migration has completed. Meanwhile, everything should continue to work as expected.', 'all-in-one-seo-pack' );
40
+ ?>
41
+ <div class="notice notice-info aioseo-migration">
42
+ <p><strong><?php echo esc_html( $string1 ); ?></strong></p>
43
+ <p><?php echo esc_html( $string2 ); ?></p>
44
+ <p><?php echo esc_html( $string3 ); ?></p>
45
+ </div>
46
+ <style>
47
+ </style>
48
+ <?php
49
+ }
50
+ }
app/Common/Admin/Notices/Notices.php ADDED
@@ -0,0 +1,579 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Admin\Notices;
3
+
4
+ use AIOSEO\Plugin\Common\Models;
5
+
6
+ /**
7
+ * Abstract class that Pro and Lite both extend.
8
+ *
9
+ * @since 4.0.0
10
+ */
11
+ class Notices {
12
+ /**
13
+ * Source of notifications content.
14
+ *
15
+ * @since 4.0.0
16
+ *
17
+ * @var string
18
+ */
19
+ private $url = 'https://plugin-cdn.aioseo.com/wp-content/notifications.json';
20
+
21
+ /**
22
+ * Class Constructor.
23
+ *
24
+ * @since 4.0.0
25
+ */
26
+ public function __construct() {
27
+ add_action( 'updated_option', [ $this, 'maybeResetBlogVisibility' ], 10, 3 );
28
+ add_action( 'init', [ $this, 'init' ], 2 );
29
+
30
+ if ( is_admin() ) {
31
+ $this->review = new Review();
32
+ $this->migration = new Migration();
33
+ $this->import = new Import();
34
+
35
+ add_action( 'aioseo_admin_notifications_update', [ $this, 'update' ] );
36
+ add_action( 'admin_notices', [ $this, 'notice' ] );
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Initialize notifications.
42
+ *
43
+ * @since 4.0.0
44
+ *
45
+ * @return void
46
+ */
47
+ public function init() {
48
+ // If our tables do not exist, create them now.
49
+ if ( ! aioseo()->db->tableExists( 'aioseo_notifications' ) ) {
50
+ aioseo()->updates->addInitialCustomTablesForV4();
51
+ }
52
+
53
+ $this->maybeUpdate();
54
+ $this->initInternalNotices();
55
+ $this->deleteInternalNotices();
56
+ }
57
+
58
+ /**
59
+ * Checks if we should update our notifications.
60
+ *
61
+ * @since 4.0.0
62
+ *
63
+ * @return void
64
+ */
65
+ private function maybeUpdate() {
66
+ if ( ! aioseo()->options->advanced->announcements ) {
67
+ return false;
68
+ }
69
+
70
+ try {
71
+ if ( ! as_next_scheduled_action( 'aioseo_admin_notifications_update' ) ) {
72
+ as_schedule_recurring_action( strtotime( 'tomorrow + 1 hour' ), DAY_IN_SECONDS, 'aioseo_admin_notifications_update', [], 'aioseo' );
73
+
74
+ // Run the task immediately using an async action.
75
+ as_enqueue_async_action( 'aioseo_admin_notifications_update', [], 'aioseo' );
76
+ }
77
+ } catch ( \Exception $e ) {
78
+ // Do nothing.
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Update Notifications from the server.
84
+ *
85
+ * @since 4.0.0
86
+ *
87
+ * @return void
88
+ */
89
+ public function update() {
90
+ $notifications = $this->fetch();
91
+ foreach ( $notifications as $notification ) {
92
+ // First, let's check to see if this notification already exists. If so, we want to override it.
93
+ $n = aioseo()->db
94
+ ->start( 'aioseo_notifications' )
95
+ ->where( 'notification_id', $notification->id )
96
+ ->run()
97
+ ->model( 'AIOSEO\\Plugin\\Common\\Models\\Notification' );
98
+
99
+ $buttons = [
100
+ 'button1' => [
101
+ 'label' => ! empty( $notification->btns->main->text ) ? $notification->btns->main->text : null,
102
+ 'url' => ! empty( $notification->btns->main->url ) ? $notification->btns->main->url : null
103
+ ],
104
+ 'button2' => [
105
+ 'label' => ! empty( $notification->btns->alt->text ) ? $notification->btns->alt->text : null,
106
+ 'url' => ! empty( $notification->btns->alt->url ) ? $notification->btns->alt->url : null
107
+ ]
108
+ ];
109
+
110
+ if ( $n->exists() ) {
111
+ $n->title = $notification->title;
112
+ $n->content = $notification->content;
113
+ $n->type = ! empty( $notification->notification_type ) ? $notification->notification_type : 'info';
114
+ $n->level = $notification->type;
115
+ $n->notification_id = $notification->id;
116
+ $n->start = ! empty( $notification->start ) ? $notification->start : null;
117
+ $n->end = ! empty( $notification->end ) ? $notification->end : null;
118
+ $n->button1_label = $buttons['button1']['label'];
119
+ $n->button1_action = $buttons['button1']['url'];
120
+ $n->button2_label = $buttons['button2']['label'];
121
+ $n->button2_action = $buttons['button2']['url'];
122
+ $n->save();
123
+ continue;
124
+ }
125
+
126
+ $n = new Models\Notification();
127
+ $n->slug = uniqid();
128
+ $n->title = $notification->title;
129
+ $n->content = $notification->content;
130
+ $n->type = ! empty( $notification->notification_type ) ? $notification->notification_type : 'info';
131
+ $n->level = $notification->type;
132
+ $n->notification_id = $notification->id;
133
+ $n->start = ! empty( $notification->start ) ? $notification->start : null;
134
+ $n->end = ! empty( $notification->end ) ? $notification->end : null;
135
+ $n->button1_label = $buttons['button1']['label'];
136
+ $n->button1_action = $buttons['button1']['url'];
137
+ $n->button2_label = $buttons['button2']['label'];
138
+ $n->button2_action = $buttons['button2']['url'];
139
+ $n->dismissed = 0;
140
+ $n->save();
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Fetches the feed of notifications.
146
+ *
147
+ * @since 4.0.0
148
+ *
149
+ * @return array An array of notifications.
150
+ */
151
+ private function fetch() {
152
+ error_log( $this->getUrl() . '?' . time() );
153
+ $response = wp_remote_get( $this->getUrl() . '?' . time() );
154
+
155
+ if ( is_wp_error( $response ) ) {
156
+ return [];
157
+ }
158
+
159
+ $body = wp_remote_retrieve_body( $response );
160
+
161
+ if ( empty( $body ) ) {
162
+ return [];
163
+ }
164
+
165
+ return $this->verify( json_decode( $body ) );
166
+ }
167
+
168
+ /**
169
+ * Verify notification data before it is saved.
170
+ *
171
+ * @since 4.0.0
172
+ *
173
+ * @param array $notifications Array of notifications items to verify.
174
+ * @return array An array of verified notifications.
175
+ */
176
+ private function verify( $notifications ) {
177
+ $data = [];
178
+ if ( ! is_array( $notifications ) || empty( $notifications ) ) {
179
+ return $data;
180
+ }
181
+
182
+ foreach ( $notifications as $notification ) {
183
+ // The message and license should never be empty, if they are, ignore.
184
+ if ( empty( $notification->content ) || empty( $notification->type ) ) {
185
+ continue;
186
+ }
187
+
188
+ if ( ! is_array( $notification->type ) ) {
189
+ $notification->type = [ $notification->type ];
190
+ }
191
+ foreach ( $notification->type as $type ) {
192
+ // Ignore if type does not match.
193
+ if ( ! $this->validateType( $type ) ) {
194
+ continue 2;
195
+ }
196
+ }
197
+
198
+ // Ignore if expired.
199
+ if ( ! empty( $notification->end ) && time() > strtotime( $notification->end ) ) {
200
+ continue;
201
+ }
202
+
203
+ // Ignore if notification existed before installing AIOSEO.
204
+ // Prevents bombarding the user with notifications after activation.
205
+ $activated = aioseo()->internalOptions->internal->firstActivated( time() );
206
+ if (
207
+ ! empty( $notification->start ) &&
208
+ $activated > strtotime( $notification->start )
209
+ ) {
210
+ continue;
211
+ }
212
+
213
+ $data[] = $notification;
214
+ }
215
+
216
+ return $data;
217
+ }
218
+
219
+ /**
220
+ * Validates the notification type.
221
+ *
222
+ * @since 4.0.0
223
+ *
224
+ * @param string $type The notification type we are targeting.
225
+ * @return boolean True if yes, false if no.
226
+ */
227
+ public function validateType( $type ) {
228
+ $validated = false;
229
+
230
+ if ( 'all' === $type ) {
231
+ $validated = true;
232
+ }
233
+
234
+ // Store notice if version matches.
235
+ if ( $this->versionMatch( aioseo()->version, $type ) ) {
236
+ $validated = true;
237
+ }
238
+
239
+ return $validated;
240
+ }
241
+
242
+ /**
243
+ * Version Compare.
244
+ *
245
+ * @since 4.0.0
246
+ *
247
+ * @param string $currentVersion The current version being used.
248
+ * @param string|array $compareVersion The version to compare with.
249
+ * @return bool True if we match, false if not.
250
+ */
251
+ public function versionMatch( $currentVersion, $compareVersion ) {
252
+ if ( is_array( $compareVersion ) ) {
253
+ foreach ( $compareVersion as $compare_single ) {
254
+ $recursiveResult = $this->versionMatch( $currentVersion, $compare_single );
255
+ if ( $recursiveResult ) {
256
+ return true;
257
+ }
258
+ }
259
+
260
+ return false;
261
+ }
262
+
263
+ $currentParse = explode( '.', $currentVersion );
264
+ if ( strpos( $compareVersion, '-' ) ) {
265
+ $compareParse = explode( '-', $compareVersion );
266
+ } elseif ( strpos( $compareVersion, '.' ) ) {
267
+ $compareParse = explode( '.', $compareVersion );
268
+ } else {
269
+ return false;
270
+ }
271
+
272
+ $currentCount = count( $currentParse );
273
+ $compareCount = count( $compareParse );
274
+ for ( $i = 0; $i < $currentCount || $i < $compareCount; $i++ ) {
275
+ if ( isset( $compareParse[ $i ] ) && 'x' === strtolower( $compareParse[ $i ] ) ) {
276
+ unset( $compareParse[ $i ] );
277
+ }
278
+
279
+ if ( ! isset( $currentParse[ $i ] ) ) {
280
+ unset( $compareParse[ $i ] );
281
+ } elseif ( ! isset( $compareParse[ $i ] ) ) {
282
+ unset( $currentParse[ $i ] );
283
+ }
284
+ }
285
+
286
+ foreach ( $compareParse as $index => $subNumber ) {
287
+ if ( $currentParse[ $index ] !== $subNumber ) {
288
+ return false;
289
+ }
290
+ }
291
+
292
+ return true;
293
+ }
294
+
295
+
296
+ /**
297
+ * Gets the URL for the notifications api.
298
+ *
299
+ * @since 4.0.0
300
+ *
301
+ * @return string The URL to use for the api requests.
302
+ */
303
+ private function getUrl() {
304
+ if ( defined( 'AIOSEO_NOTIFICATIONS_URL' ) ) {
305
+ return AIOSEO_NOTIFICATIONS_URL;
306
+ }
307
+
308
+ return $this->url;
309
+ }
310
+
311
+ /**
312
+ * Add notices.
313
+ *
314
+ * @since 4.0.0
315
+ *
316
+ * @return void
317
+ */
318
+ public function notice() {
319
+ $this->review->maybeShowNotice();
320
+ $this->migration->maybeShowNotice();
321
+ $this->import->maybeShowNotice();
322
+ }
323
+
324
+ /**
325
+ * Initialize the internal notices.
326
+ *
327
+ * @since 4.0.0
328
+ *
329
+ * @return void
330
+ */
331
+ protected function initInternalNotices() {
332
+ $this->blogVisibility();
333
+ $this->descriptionFormat();
334
+ }
335
+
336
+ /**
337
+ * Deletes internal notices we no longer need.
338
+ *
339
+ * @since 4.0.0
340
+ *
341
+ * @return void
342
+ */
343
+ protected function deleteInternalNotices() {
344
+ $pluginData = aioseo()->helpers->getPluginData();
345
+ if ( $pluginData['miPro']['installed'] || $pluginData['miLite']['installed'] ) {
346
+ $notification = Models\Notification::getNotificationByName( 'install-mi' );
347
+ if ( ! $notification->exists() ) {
348
+ return;
349
+ }
350
+
351
+ Models\Notification::deleteNotificationByName( 'install-mi' );
352
+ }
353
+ }
354
+
355
+ /**
356
+ * Extends a notice by a (default) 1 week start date.
357
+ *
358
+ * @since 4.0.0
359
+ *
360
+ * @param string $notice The notice to extend.
361
+ * @param string $start How long to extend.
362
+ * @return void
363
+ */
364
+ public function remindMeLater( $notice, $start = '+1 week' ) {
365
+ $notification = Models\Notification::getNotificationByName( $notice );
366
+ if ( ! $notification->exists() ) {
367
+ return;
368
+ }
369
+
370
+ $notification->start = gmdate( 'Y-m-d H:i:s', strtotime( $start ) );
371
+ $notification->save();
372
+ }
373
+
374
+ /**
375
+ * Add a notice if the blog is set to hidden.
376
+ *
377
+ * @since 4.0.0
378
+ *
379
+ * @return void
380
+ */
381
+ private function blogVisibility() {
382
+ $notification = Models\Notification::getNotificationByName( 'blog-visibility' );
383
+ if ( get_option( 'blog_public' ) ) {
384
+ if ( $notification->exists() ) {
385
+ Models\Notification::deleteNotificationByName( 'blog-visibility' );
386
+ }
387
+ return;
388
+ }
389
+
390
+ if ( $notification->exists() ) {
391
+ return;
392
+ }
393
+
394
+ Models\Notification::addNotification( [
395
+ 'slug' => uniqid(),
396
+ 'notification_name' => 'blog-visibility',
397
+ 'title' => __( 'Search Engines Blocked', 'all-in-one-seo-pack' ),
398
+ 'content' => sprintf(
399
+ // Translators: 1 - The plugin short name ("AIOSEO").
400
+ __( 'Warning: %1$s has detected that you are blocking access to search engines. You can change this in Settings > Reading if this was unintended.', 'all-in-one-seo-pack' ),
401
+ AIOSEO_PLUGIN_SHORT_NAME
402
+ ),
403
+ 'type' => 'error',
404
+ 'level' => [ 'all' ],
405
+ 'button1_label' => __( 'Fix Now', 'all-in-one-seo-pack' ),
406
+ 'button1_action' => admin_url( 'options-reading.php' ),
407
+ 'button2_label' => __( 'Remind Me Later', 'all-in-one-seo-pack' ),
408
+ 'button2_action' => 'http://action#notification/blog-visibility-reminder',
409
+ 'start' => gmdate( 'Y-m-d H:i:s' )
410
+ ] );
411
+ }
412
+
413
+ /**
414
+ * Add a notice if the description format is missing the Description tag.
415
+ *
416
+ * @since 4.0.5
417
+ *
418
+ * @return void
419
+ */
420
+ private function descriptionFormat() {
421
+ $notification = Models\Notification::getNotificationByName( 'description-format' );
422
+ if ( ! in_array( 'descriptionFormat', aioseo()->internalOptions->deprecatedOptions, true ) ) {
423
+ if ( $notification->exists() ) {
424
+ Models\Notification::deleteNotificationByName( 'description-format' );
425
+ }
426
+ return;
427
+ }
428
+
429
+ $descriptionFormat = aioseo()->options->deprecated->searchAppearance->global->descriptionFormat;
430
+ if ( false !== strpos( $descriptionFormat, '#description' ) ) {
431
+ if ( $notification->exists() ) {
432
+ Models\Notification::deleteNotificationByName( 'description-format' );
433
+ }
434
+ return;
435
+ }
436
+
437
+ if ( $notification->exists() ) {
438
+ return;
439
+ }
440
+
441
+ Models\Notification::addNotification( [
442
+ 'slug' => uniqid(),
443
+ 'notification_name' => 'description-format',
444
+ 'title' => __( 'Invalid Description Format', 'all-in-one-seo-pack' ),
445
+ 'content' => sprintf(
446
+ // Translators: 1 - The plugin short name ("AIOSEO").
447
+ __( 'Warning: %1$s has detected that you may have an invalid description format. This could lead to descriptions not being properly applied to your content.', 'all-in-one-seo-pack' ),
448
+ AIOSEO_PLUGIN_SHORT_NAME
449
+ ) . ' ' . __( 'A Description tag is required in order to properly display your meta descriptions on your site.', 'all-in-one-seo-pack' ),
450
+ 'type' => 'error',
451
+ 'level' => [ 'all' ],
452
+ 'button1_label' => __( 'Fix Now', 'all-in-one-seo-pack' ),
453
+ 'button1_action' => 'http://route#aioseo-search-appearance&aioseo-scroll=description-format&aioseo-highlight=description-format:advanced',
454
+ 'button2_label' => __( 'Remind Me Later', 'all-in-one-seo-pack' ),
455
+ 'button2_action' => 'http://action#notification/description-format-reminder',
456
+ 'start' => gmdate( 'Y-m-d H:i:s' )
457
+ ] );
458
+ }
459
+
460
+ /**
461
+ * Check if blog visibility is changing and add/delete the appropriate notification.
462
+ *
463
+ * @since 4.0.0
464
+ *
465
+ * @param string $optionName The name of the option we are checking.
466
+ * @param mixed $oldValue The old value.
467
+ * @param mixed $newValue The new value.
468
+ * @return void
469
+ */
470
+ public function maybeResetBlogVisibility( $optionName, $oldValue, $newValue ) {
471
+ if ( 'blog_public' === $optionName ) {
472
+ if ( 1 === intval( $newValue ) ) {
473
+ $notification = Models\Notification::getNotificationByName( 'blog-visibility' );
474
+ if ( ! $notification->exists() ) {
475
+ return;
476
+ }
477
+
478
+ Models\Notification::deleteNotificationByName( 'blog-visibility' );
479
+ return;
480
+ }
481
+
482
+ $this->blogVisibility();
483
+ }
484
+ }
485
+
486
+ /**
487
+ * Add a notice if the blog is set to hidden.
488
+ *
489
+ * @since 4.0.0
490
+ *
491
+ * @return void
492
+ */
493
+ public function conflictingPlugins( $plugins = [] ) {
494
+ if ( empty( $plugins ) ) {
495
+ return;
496
+ }
497
+
498
+ $content = sprintf(
499
+ // Translators: 1 - The plugin short name ("AIOSEO").
500
+ __( 'Warning: %1$s has detected other active SEO or sitemap plugins. We recommend that you deactivate the following plugins to prevent any conflicts:', 'all-in-one-seo-pack' ),
501
+ AIOSEO_PLUGIN_SHORT_NAME
502
+ ) . '<ul>';
503
+
504
+ foreach ( $plugins as $pluginName => $pluginPath ) {
505
+ $content .= '<li><strong>' . $pluginName . '</strong></li>';
506
+ }
507
+
508
+ $content .= '</ul>';
509
+
510
+ // Update an existing notice.
511
+ $notification = Models\Notification::getNotificationByName( 'conflicting-plugins' );
512
+ if ( $notification->exists() ) {
513
+ $notification->content = $content;
514
+ $notification->save();
515
+ return;
516
+ }
517
+
518
+ // Create a new one if it doesn't exist.
519
+ Models\Notification::addNotification( [
520
+ 'slug' => uniqid(),
521
+ 'notification_name' => 'conflicting-plugins',
522
+ 'title' => __( 'Conflicting Plugins Detected', 'all-in-one-seo-pack' ),
523
+ 'content' => $content,
524
+ 'type' => 'error',
525
+ 'level' => [ 'all' ],
526
+ 'button1_label' => __( 'Fix Now', 'all-in-one-seo-pack' ),
527
+ 'button1_action' => 'http://action#sitemap/deactivate-conflicting-plugins?refresh',
528
+ 'button2_label' => __( 'Remind Me Later', 'all-in-one-seo-pack' ),
529
+ 'button2_action' => 'http://action#notification/conflicting-plugins-reminder',
530
+ 'start' => gmdate( 'Y-m-d H:i:s' )
531
+ ] );
532
+ }
533
+
534
+ /**
535
+ * Add a notice if the user is using deprecated filters.
536
+ *
537
+ * @since 4.0.0
538
+ *
539
+ * @return void
540
+ */
541
+ public function deprecatedFilters( $filters = [] ) {
542
+ if ( empty( $filters ) ) {
543
+ return;
544
+ }
545
+
546
+ $content = sprintf(
547
+ // Translators: 1 - The plugin short name ("AIOSEO").
548
+ __( 'Warning: %1$s has detected the use of filters that have been deprecated on your site. These filters may be in use by another plugin or your theme. If that is the case, these filters most likely will not work with this plugin. Please check for an update immediately or contact our support for more information.', 'all-in-one-seo-pack' ), // phpcs:ignore Generic.Files.LineLength.MaxExceeded
549
+ AIOSEO_PLUGIN_SHORT_NAME
550
+ ) . '<ul>';
551
+
552
+ foreach ( $filters as $filter ) {
553
+ $content .= '<li><strong>' . $filter . '</strong></li>';
554
+ }
555
+
556
+ $content .= '</ul>';
557
+
558
+ // Update an existing notice.
559
+ $notification = Models\Notification::getNotificationByName( 'deprecated-filters' );
560
+ if ( $notification->exists() ) {
561
+ $notification->content = $content;
562
+ $notification->save();
563
+ return;
564
+ }
565
+
566
+ // Create a new one if it doesn't exist.
567
+ Models\Notification::addNotification( [
568
+ 'slug' => uniqid(),
569
+ 'notification_name' => 'deprecated-filters',
570
+ 'title' => __( 'Deprecated Filters Detected', 'all-in-one-seo-pack' ),
571
+ 'content' => $content,
572
+ 'type' => 'error',
573
+ 'level' => [ 'all' ],
574
+ 'button1_label' => __( 'Remind Me Later', 'all-in-one-seo-pack' ),
575
+ 'button1_action' => 'http://action#notification/deprecated-filters-reminder',
576
+ 'start' => gmdate( 'Y-m-d H:i:s' )
577
+ ] );
578
+ }
579
+ }
app/Common/Admin/Notices/Review.php ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Admin\Notices;
3
+
4
+ /**
5
+ * Review Plugin Notice.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class Review {
10
+ /**
11
+ * Class Constructor.
12
+ *
13
+ * @since 4.0.0
14
+ */
15
+ public function __construct() {
16
+ add_action( 'wp_ajax_aioseo-dismiss-review-plugin-cta', [ $this, 'dismissNotice' ] );
17
+ }
18
+
19
+ /**
20
+ * Go through all the checks to see if we should show the notice.
21
+ *
22
+ * @since 4.0.0
23
+ *
24
+ * @return void
25
+ */
26
+ public function maybeShowNotice() {
27
+ $dismissed = get_user_meta( get_current_user_id(), '_aioseo_plugin_review_dismissed', true );
28
+ if ( '1' === $dismissed ) {
29
+ return;
30
+ }
31
+
32
+ if ( ! empty( $dismissed ) && $dismissed > time() ) {
33
+ return;
34
+ }
35
+
36
+ // Only show if plugin has been active for over two weeks.
37
+ if ( ! aioseo()->internalOptions->internal->firstActivated ) {
38
+ aioseo()->internalOptions->internal->firstActivated = time();
39
+ }
40
+
41
+ $activated = aioseo()->internalOptions->internal->firstActivated( time() );
42
+ if ( $activated > strtotime( '-2 weeks' ) ) {
43
+ return;
44
+ }
45
+
46
+ $this->showNotice();
47
+ }
48
+
49
+ /**
50
+ * Actually show the review plugin.
51
+ *
52
+ * @since 4.0.0
53
+ *
54
+ * @return void
55
+ */
56
+ public function showNotice() {
57
+ $feedbackUrl = add_query_arg(
58
+ [
59
+ 'wpf7528_24' => untrailingslashit( home_url() ),
60
+ 'wpf7528_26' => aioseo()->options->has( 'general' ) && aioseo()->options->general->has( 'licenseKey' )
61
+ ? aioseo()->options->general->licenseKey
62
+ : '',
63
+ 'wpf7528_27' => aioseo()->pro ? 'pro' : 'lite',
64
+ 'wpf7528_28' => AIOSEO_VERSION,
65
+ 'utm_source' => aioseo()->pro ? 'proplugin' : 'liteplugin',
66
+ 'utm_medium' => 'review-notice',
67
+ 'utm_campaign' => 'feedback',
68
+ 'utm_content' => AIOSEO_VERSION,
69
+ ],
70
+ 'https://aioseo.com/plugin-feedback/'
71
+ );
72
+
73
+ // Translators: 1 - The plugin name ("All in One SEO").
74
+ $string1 = sprintf( __( 'Are you enjoying %1$s?', 'all-in-one-seo-pack' ), AIOSEO_PLUGIN_NAME );
75
+ $string2 = __( 'Yes I love it', 'all-in-one-seo-pack' );
76
+ $string3 = __( 'Not Really...', 'all-in-one-seo-pack' );
77
+ // Translators: The plugin name ("All in One SEO").
78
+ $string4 = sprintf( __( 'We\'re sorry to hear you aren\'t enjoying %1$s. We would love a chance to improve. Could you take a minute and let us know what we can do better?', 'all-in-one-seo-pack' ), AIOSEO_PLUGIN_NAME ); // phpcs:ignore Generic.Files.LineLength.MaxExceeded
79
+ $string5 = __( 'Give feedback', 'all-in-one-seo-pack' );
80
+ $string6 = __( 'No thanks', 'all-in-one-seo-pack' );
81
+ $string7 = __( 'That\'s awesome! Could you please do me a BIG favor and give it a 5-star rating on WordPress to help us spread the word and boost our motivation?', 'all-in-one-seo-pack' );
82
+ // Translators: The plugin name ("All in One SEO").
83
+ $string8 = sprintf( __( 'CEO of %1$s', 'all-in-one-seo-pack' ), AIOSEO_PLUGIN_NAME );
84
+ $string9 = __( 'Ok, you deserve it', 'all-in-one-seo-pack' );
85
+ $string10 = __( 'Nope, maybe later', 'all-in-one-seo-pack' );
86
+ $string11 = __( 'I already did', 'all-in-one-seo-pack' );
87
+
88
+ $nonce = wp_create_nonce( 'aioseo-dismiss-review' );
89
+ ?>
90
+ <div class="notice notice-info aioseo-review-plugin-cta is-dismissible">
91
+ <div class="step-1">
92
+ <p><?php echo esc_html( $string1 ); ?></p>
93
+ <p>
94
+ <a href="#" class="aioseo-review-switch-step-3" data-step="3"><?php echo esc_html( $string2 ); ?></a> 🙂 |
95
+ <a href="#" class="aioseo-review-switch-step-2" data-step="2"><?php echo esc_html( $string3 ); ?></a>
96
+ </p>
97
+ </div>
98
+ <div class="step-2" style="display:none;">
99
+ <p><?php echo esc_html( $string4 ); ?></p>
100
+ <p>
101
+ <a href="<?php echo esc_url( $feedbackUrl ); ?>" class="aioseo-dismiss-review-notice" target="_blank" rel="noopener noreferrer"><?php echo esc_html( $string5 ); ?></a>&nbsp;&nbsp;
102
+ <a href="#" class="aioseo-dismiss-review-notice" target="_blank" rel="noopener noreferrer"><?php echo esc_html( $string6 ); ?></a>
103
+ </p>
104
+ </div>
105
+ <div class="step-3" style="display:none;">
106
+ <p><?php echo esc_html( $string7 ); ?></p>
107
+ <p><strong>~ Syed Balkhi<br><?php echo esc_html( $string8 ); ?></strong></p>
108
+ <p>
109
+ <a href="https://wordpress.org/support/plugin/all-in-one-seo-pack/reviews/?filter=5#new-post" class="aioseo-dismiss-review-notice" target="_blank" rel="noopener noreferrer">
110
+ <?php echo esc_html( $string9 ); ?>
111
+ </a>&nbsp;&nbsp;
112
+ <a href="#" class="aioseo-dismiss-review-notice-delay" target="_blank" rel="noopener noreferrer">
113
+ <?php echo esc_html( $string10 ); ?>
114
+ </a>&nbsp;&nbsp;
115
+ <a href="#" class="aioseo-dismiss-review-notice" target="_blank" rel="noopener noreferrer">
116
+ <?php echo esc_html( $string11 ); ?>
117
+ </a>
118
+ </p>
119
+ </div>
120
+ </div>
121
+ <style>
122
+ .aioseop-notice-review_plugin_cta .aioseo-action-buttons {
123
+ display: none;
124
+ }
125
+ </style>
126
+ <script type="text/javascript">
127
+ // @TODO: [V4+] Move this into vue or ES6 app.
128
+ jQuery(document).ready(function() {
129
+ var aioseoSetupButton = function (button) {
130
+ var delay = false;
131
+ var aioseoReviewPluginDismiss = function () {
132
+ jQuery.post(window.ajaxurl, {
133
+ delay : delay,
134
+ nonce : '<?php echo esc_attr( $nonce ); ?>',
135
+ action : 'aioseo-dismiss-review-plugin-cta'
136
+ });
137
+ };
138
+ button.addEventListener('click', function (event) {
139
+ // Dismiss notice here.
140
+ event.preventDefault();
141
+ aioseoReviewPluginDismiss();
142
+ });
143
+
144
+ jQuery(document).on('click', '.aioseo-review-plugin-cta .aioseo-review-switch-step-3', function(event) {
145
+ event.preventDefault();
146
+ jQuery('.aioseo-review-plugin-cta .step-1, .aioseo-review-plugin-cta .step-2').hide();
147
+ jQuery('.aioseo-review-plugin-cta .step-3').show();
148
+ });
149
+ jQuery(document).on('click', '.aioseo-review-plugin-cta .aioseo-review-switch-step-2', function(event) {
150
+ event.preventDefault();
151
+ jQuery('.aioseo-review-plugin-cta .step-1, .aioseo-review-plugin-cta .step-3').hide();
152
+ jQuery('.aioseo-review-plugin-cta .step-2').show();
153
+ });
154
+ jQuery(document).on('click', '.aioseo-review-plugin-cta .aioseo-dismiss-review-notice-delay', function(event) {
155
+ event.preventDefault();
156
+ delay = true;
157
+ button.click();
158
+ });
159
+ jQuery(document).on('click', '.aioseo-review-plugin-cta .aioseo-dismiss-review-notice', function(event) {
160
+ if ('#' === jQuery(this).attr('href')) {
161
+ event.preventDefault();
162
+ }
163
+ button.click();
164
+ });
165
+ }
166
+
167
+ var notice = document.querySelector('.notice.aioseo-review-plugin-cta');
168
+ var button = notice.querySelector('button.notice-dismiss');
169
+ if (!button) {
170
+ var interval = window.setInterval(function() {
171
+ button = notice.querySelector('button.notice-dismiss');
172
+ if (button) {
173
+ aioseoSetupButton(button);
174
+ window.clearInterval(interval)
175
+ }
176
+ }, 50);
177
+ }
178
+ });
179
+ </script>
180
+ <?php
181
+ }
182
+
183
+ /**
184
+ * Dismiss the review plugin CTA.
185
+ *
186
+ * @since 4.0.0
187
+ *
188
+ * @return WP_Response The successful response.
189
+ */
190
+ public function dismissNotice() {
191
+ check_ajax_referer( 'aioseo-dismiss-review', 'nonce' );
192
+ $delay = isset( $_POST['delay'] ) ? 'true' === wp_unslash( $_POST['delay'] ) : false; // phpcs:ignore HM.Security.ValidatedSanitizedInput.InputNotSanitized
193
+
194
+ if ( ! $delay ) {
195
+ update_user_meta( get_current_user_id(), '_aioseo_plugin_review_dismissed', true );
196
+ return wp_send_json_success();
197
+ }
198
+
199
+ update_user_meta( get_current_user_id(), '_aioseo_plugin_review_dismissed', strtotime( '+1 week' ) );
200
+
201
+ return wp_send_json_success();
202
+ }
203
+ }
app/Common/Admin/PostSettings.php ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Admin;
3
+
4
+ use AIOSEO\Plugin\Common\Models;
5
+
6
+ /**
7
+ * Abstract class that Pro and Lite both extend.
8
+ *
9
+ * @since 4.0.0
10
+ */
11
+ class PostSettings {
12
+ /**
13
+ * Initialize the admin.
14
+ *
15
+ * @since 4.0.0
16
+ *
17
+ * @return void
18
+ */
19
+ public function __construct() {
20
+ if ( is_admin() ) {
21
+ // Load Vue APP
22
+ add_action( 'admin_enqueue_scripts', [ $this, 'enqueuePostSettingsAssets' ] );
23
+
24
+ // Add metabox
25
+ add_action( 'add_meta_boxes', [ $this, 'addPostSettingsMetabox' ] );
26
+
27
+ // Add metabox to terms on init hook
28
+ add_action( 'init', [ $this, 'init' ], 1000 );
29
+
30
+ // Save metabox
31
+ add_action( 'save_post', [ $this, 'saveSettingsMetabox' ] );
32
+ add_action( 'edit_attachment', [ $this, 'saveSettingsMetabox' ] );
33
+ add_action( 'add_attachment', [ $this, 'saveSettingsMetabox' ] );
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Enqueues the JS/CSS for the on page/posts settings.
39
+ *
40
+ * @since 4.0.0
41
+ *
42
+ * @return void
43
+ */
44
+ public function enqueuePostSettingsAssets() {
45
+ $screen = get_current_screen();
46
+ if ( 'post' === $screen->base || 'term' === $screen->base || 'edit-tags' === $screen->base ) {
47
+ aioseo()->helpers->enqueueScript(
48
+ 'aioseo-post-settings-metabox',
49
+ 'js/post-settings.js'
50
+ );
51
+ wp_localize_script(
52
+ 'aioseo-post-settings-metabox',
53
+ 'aioseo',
54
+ aioseo()->helpers->getVueData()
55
+ );
56
+
57
+ $rtl = is_rtl() ? '.rtl' : '';
58
+ aioseo()->helpers->enqueueStyle(
59
+ 'aioseo-post-settings-metabox',
60
+ "css/post-settings$rtl.css"
61
+ );
62
+ }
63
+
64
+ if ( 'attachment' === $screen->id ) {
65
+ wp_enqueue_media();
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Adds a meta box to page/posts screens.
71
+ *
72
+ * @since 4.0.0
73
+ *
74
+ * @return void
75
+ */
76
+ public function addPostSettingsMetabox() {
77
+ $options = aioseo()->options->noConflict();
78
+ $screen = get_current_screen();
79
+ $postType = $screen->post_type;
80
+
81
+ $pageAnalysisSettingsCapability = aioseo()->access->hasCapability( 'aioseo_page_analysis' );
82
+ $generalSettingsCapability = aioseo()->access->hasCapability( 'aioseo_page_general_settings' );
83
+ $socialSettingsCapability = aioseo()->access->hasCapability( 'aioseo_page_social_settings' );
84
+ $schemaSettingsCapability = aioseo()->access->hasCapability( 'aioseo_page_schema_settings' );
85
+ $advancedSettingsCapability = aioseo()->access->hasCapability( 'aioseo_page_advanced_settings' );
86
+
87
+ if (
88
+ $options->searchAppearance->dynamic->postTypes->has( $postType ) &&
89
+ $options->searchAppearance->dynamic->postTypes->$postType->advanced->showMetaBox &&
90
+ ! (
91
+ empty( $pageAnalysisSettingsCapability ) &&
92
+ empty( $generalSettingsCapability ) &&
93
+ empty( $socialSettingsCapability ) &&
94
+ empty( $schemaSettingsCapability ) &&
95
+ empty( $advancedSettingsCapability )
96
+ )
97
+ ) {
98
+ // Translators: 1 - The plugin short name ("AIOSEO").
99
+ $aioseoMetaboxTitle = sprintf( esc_html__( '%1$s Settings', 'all-in-one-seo-pack' ), AIOSEO_PLUGIN_SHORT_NAME );
100
+
101
+ add_meta_box(
102
+ 'aioseo-settings',
103
+ $aioseoMetaboxTitle,
104
+ [ $this, 'postSettingsMetabox' ],
105
+ [ $postType ],
106
+ 'normal',
107
+ 'high'
108
+ );
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Render the on page/posts settings metabox with Vue App wrapper.
114
+ *
115
+ * @since 4.0.0
116
+ *
117
+ * @param WP_Post $post The current post.
118
+ * @return string
119
+ */
120
+ public function postSettingsMetabox() {
121
+ ?>
122
+ <div id="aioseo-post-settings-field">
123
+ <input type="hidden" name="aioseo-post-settings" id="aioseo-post-settings" value="" />
124
+ <?php wp_nonce_field( 'aioseoPostSettingsNonce', 'PostSettingsNonce' ); ?>
125
+ </div>
126
+ <div id="aioseo-post-settings-metabox">
127
+ <div style="height:50px">
128
+ <div class="aioseo-loading-spinner dark" style="top:calc( 50% - 17px);left:calc( 50% - 17px);">
129
+ <div class="double-bounce1"></div>
130
+ <div class="double-bounce2"></div>
131
+ </div>
132
+ </div>
133
+ </div>
134
+ <?php
135
+ }
136
+
137
+ /**
138
+ * Handles metabox saving.
139
+ *
140
+ * @since 4.0.3
141
+ *
142
+ * @param int $postId Post ID.
143
+ * @return void
144
+ */
145
+ public function saveSettingsMetabox( $postId ) {
146
+ // Ignore auto saving
147
+ if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
148
+ return;
149
+ }
150
+
151
+ // Security check
152
+ if ( ! isset( $_POST['PostSettingsNonce'] ) || ! wp_verify_nonce( $_POST['PostSettingsNonce'], 'aioseoPostSettingsNonce' ) ) {
153
+ return;
154
+ }
155
+
156
+ // If we don't have our post settings input, we can safely skip.
157
+ if ( ! isset( $_POST['aioseo-post-settings'] ) ) {
158
+ return;
159
+ }
160
+
161
+ // Check user permissions
162
+ if ( ! current_user_can( 'edit_post', $postId ) ) {
163
+ return;
164
+ }
165
+
166
+ $currentPost = json_decode( stripslashes( $_POST['aioseo-post-settings'] ), true ); // phpcs:ignore HM.Security.ValidatedSanitizedInput
167
+
168
+ Models\Post::savePost( $postId, $currentPost );
169
+
170
+ }
171
+ }
app/Common/Admin/SetupWizard.php ADDED
@@ -0,0 +1,228 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Admin;
3
+
4
+ /**
5
+ * Class that holds our setup wizard.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class SetupWizard {
10
+ /**
11
+ * Class Constructor.
12
+ *
13
+ * @since 4.0.0
14
+ */
15
+ public function __construct() {
16
+ add_action( 'admin_menu', [ $this, 'addDashboardPage' ] );
17
+ add_action( 'admin_init', [ $this, 'maybeLoadOnboardingWizard' ] );
18
+ add_action( 'admin_init', [ $this, 'redirect' ], 9999 );
19
+ }
20
+
21
+ /**
22
+ * Onboarding Wizard redirect.
23
+ *
24
+ * This function checks if a new install or update has just occurred. If so,
25
+ * then we redirect the user to the appropriate page.
26
+ *
27
+ * @since 4.0.0
28
+ *
29
+ * @return void
30
+ */
31
+ public function redirect() {
32
+ // Check if we should consider redirection.
33
+ if ( ! get_transient( 'aioseo_activation_redirect' ) ) {
34
+ return;
35
+ }
36
+
37
+ // If we are redirecting, clear the transient so it only happens once.
38
+ delete_transient( 'aioseo_activation_redirect' );
39
+
40
+ // Check option to disable welcome redirect.
41
+ if ( get_option( 'aioseo_activation_redirect', false ) ) {
42
+ return;
43
+ }
44
+
45
+ // Only do this for single site installs.
46
+ if ( isset( $_GET['activate-multi'] ) || is_network_admin() ) {
47
+ return;
48
+ }
49
+
50
+ wp_safe_redirect( admin_url( 'index.php?page=aioseo-setup-wizard' ) );
51
+ exit;
52
+ }
53
+
54
+ /**
55
+ * Adds a dashboard page for our setup wizard.
56
+ *
57
+ * @since 4.0.0
58
+ *
59
+ * @return void
60
+ */
61
+ public function addDashboardPage() {
62
+ add_dashboard_page( '', '', 'aioseo_setup_wizard', 'aioseo-setup-wizard', '' );
63
+ remove_submenu_page( 'index.php', 'aioseo-setup-wizard' );
64
+ }
65
+
66
+ /**
67
+ * Checks to see if we should load the setup wizard.
68
+ *
69
+ * @since 4.0.0
70
+ *
71
+ * @return void
72
+ */
73
+ public function maybeLoadOnboardingWizard() {
74
+ // Don't load the interface if doing an ajax call.
75
+ if ( wp_doing_ajax() || wp_doing_cron() ) {
76
+ return;
77
+ }
78
+
79
+ // Check for wizard-specific parameter
80
+ // Allow plugins to disable the setup wizard
81
+ // Check if current user is allowed to save settings.
82
+ if (
83
+ ! isset( $_GET['page'] ) ||
84
+ 'aioseo-setup-wizard' !== wp_unslash( $_GET['page'] ) || // phpcs:ignore HM.Security.ValidatedSanitizedInput.InputNotSanitized
85
+ ! current_user_can( 'aioseo_setup_wizard' )
86
+ ) {
87
+ return;
88
+ }
89
+
90
+ set_current_screen();
91
+
92
+ // Remove an action in the Gutenberg plugin ( not core Gutenberg ) which throws an error.
93
+ remove_action( 'admin_print_styles', 'gutenberg_block_editor_admin_print_styles' );
94
+
95
+ $this->loadOnboardingWizard();
96
+ }
97
+
98
+ /**
99
+ * Load the Onboarding Wizard template.
100
+ *
101
+ * @since 4.0.0
102
+ *
103
+ * @return void
104
+ */
105
+ private function loadOnboardingWizard() {
106
+ $this->enqueueScripts();
107
+ $this->setupWizardHeader();
108
+ $this->setupWizardContent();
109
+ $this->setupWizardFooter();
110
+ exit;
111
+ }
112
+
113
+ /**
114
+ * Enqueue's scripts for the setup wizard.
115
+ *
116
+ * @since 4.0.0
117
+ *
118
+ * @return void
119
+ */
120
+ public function enqueueScripts() {
121
+ // We don't want any plugin adding notices to our screens. Let's clear them out here.
122
+ remove_all_actions( 'admin_notices' );
123
+ remove_all_actions( 'all_admin_notices' );
124
+
125
+ // Scripts.
126
+ aioseo()->helpers->enqueueScript(
127
+ 'aioseo-vendors',
128
+ 'js/chunk-vendors.js'
129
+ );
130
+ aioseo()->helpers->enqueueScript(
131
+ 'aioseo-common',
132
+ 'js/chunk-common.js'
133
+ );
134
+ aioseo()->helpers->enqueueScript(
135
+ 'aioseo-setup-wizard-script',
136
+ 'js/setup-wizard.js'
137
+ );
138
+
139
+ // Styles.
140
+ $rtl = is_rtl() ? '.rtl' : '';
141
+ aioseo()->helpers->enqueueStyle(
142
+ 'aioseo-vendors',
143
+ "css/chunk-vendors$rtl.css"
144
+ );
145
+ aioseo()->helpers->enqueueStyle(
146
+ 'aioseo-common',
147
+ "css/chunk-common$rtl.css"
148
+ );
149
+ // aioseo()->helpers->enqueueStyle(
150
+ // 'aioseo-setup-wizard-style',
151
+ // "css/setup-wizard$rtl.css"
152
+ // );
153
+ // aioseo()->helpers->enqueueStyle(
154
+ // 'aioseo-setup-wizard-vendors-style',
155
+ // "css/chunk-setup-wizard-vendors$rtl.css"
156
+ // );
157
+
158
+ wp_localize_script(
159
+ 'aioseo-setup-wizard-script',
160
+ 'aioseo',
161
+ aioseo()->helpers->getVueData( 'setup-wizard' )
162
+ );
163
+
164
+ wp_enqueue_style( 'common' );
165
+ wp_enqueue_media();
166
+ }
167
+
168
+ /**
169
+ * Outputs the simplified header used for the Onboarding Wizard.
170
+ *
171
+ * @since 4.0.0
172
+ *
173
+ * @return void
174
+ */
175
+ public function setupWizardHeader() {
176
+ ?>
177
+ <!DOCTYPE html>
178
+ <html <?php language_attributes(); ?>>
179
+ <head>
180
+ <meta name="viewport" content="width=device-width"/>
181
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
182
+ <title>
183
+ <?php
184
+ // Translators: 1 - The plugin name ("All in One SEO").
185
+ echo sprintf( esc_html__( '%1$s &rsaquo; Onboarding Wizard', 'all-in-one-seo-pack' ), esc_html( AIOSEO_PLUGIN_SHORT_NAME ) );
186
+ ?>
187
+ </title>
188
+ <?php do_action( 'admin_print_scripts' ); ?>
189
+ <?php do_action( 'admin_print_styles' ); ?>
190
+ <?php do_action( 'admin_head' ); ?>
191
+ </head>
192
+ <body class="aioseo-setup-wizard">
193
+ <?php
194
+ }
195
+
196
+ /**
197
+ * Outputs the content of the current step.
198
+ *
199
+ * @since 4.0.0
200
+ *
201
+ * @return void
202
+ */
203
+ public function setupWizardContent() {
204
+ echo '<div id="aioseo-app"></div>';
205
+ }
206
+
207
+ /**
208
+ * Outputs the simplified footer used for the Onboarding Wizard.
209
+ *
210
+ * @since 4.0.0
211
+ *
212
+ * @return void
213
+ */
214
+ public function setupWizardFooter() {
215
+ ?>
216
+ <?php
217
+ wp_print_scripts( 'aioseo-vendors' );
218
+ wp_print_scripts( 'aioseo-common' );
219
+ wp_print_scripts( 'aioseo-setup-wizard-script' );
220
+ do_action( 'admin_footer', '' );
221
+ do_action( 'admin_print_footer_scripts' );
222
+ // do_action( 'customize_controls_print_footer_scripts' );
223
+ ?>
224
+ </body>
225
+ </html>
226
+ <?php
227
+ }
228
+ }
app/Common/Admin/SiteHealth.php ADDED
@@ -0,0 +1,560 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Admin;
3
+
4
+ /**
5
+ * WP Site Health class.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class SiteHealth {
10
+ /**
11
+ * Class Constructor.
12
+ *
13
+ * @since 4.0.0
14
+ */
15
+ public function __construct() {
16
+ add_filter( 'site_status_tests', [ $this, 'registerTests' ], 0, 1 );
17
+ add_filter( 'debug_information', [ $this, 'addDebugInfo' ], 0, 1 );
18
+ }
19
+
20
+ /**
21
+ * Add AIOSEO WP Site Health tests.
22
+ *
23
+ * @since 4.0.0
24
+ *
25
+ * @param array $tests The current filters array.
26
+ * @return array
27
+ */
28
+ public function registerTests( $tests ) {
29
+ $tests['direct']['aioseo_automatic_updates'] = [
30
+ // Translators: 1 - The plugin short name ("AIOSEO").
31
+ 'label' => sprintf( __( '%1$s Automatic Updates', 'all-in-one-seo-pack' ), AIOSEO_PLUGIN_SHORT_NAME ),
32
+ 'test' => [ $this, 'testCheckAutoUpdates' ],
33
+ ];
34
+ $tests['direct']['aioseo_site_public'] = [
35
+ // Translators: 1 - The plugin short name ("AIOSEO").
36
+ 'label' => sprintf( __( '%1$s Site Public', 'all-in-one-seo-pack' ), AIOSEO_PLUGIN_SHORT_NAME ),
37
+ 'test' => [ $this, 'testCheckSitePublic' ],
38
+ ];
39
+ $tests['direct']['aioseo_site_info'] = [
40
+ // Translators: 1 - The plugin short name ("AIOSEO").
41
+ 'label' => sprintf( __( '%1$s Site Info', 'all-in-one-seo-pack' ), AIOSEO_PLUGIN_SHORT_NAME ),
42
+ 'test' => [ $this, 'testCheckSiteInfo' ],
43
+ ];
44
+ $tests['direct']['aioseo_plugin_update'] = [
45
+ // Translators: 1 - The plugin short name ("AIOSEO").
46
+ 'label' => sprintf( __( '%1$s Plugin Update', 'all-in-one-seo-pack' ), AIOSEO_PLUGIN_SHORT_NAME ),
47
+ 'test' => [ $this, 'testCheckPluginUpdate' ],
48
+ ];
49
+
50
+ $tests['direct']['aioseo_schema_markup'] = [
51
+ // Translators: 1 - The plugin short name ("AIOSEO").
52
+ 'label' => sprintf( __( '%1$s Schema Markup', 'all-in-one-seo-pack' ), AIOSEO_PLUGIN_SHORT_NAME ),
53
+ 'test' => [ $this, 'testCheckSchemaMarkup' ],
54
+ ];
55
+
56
+ return $tests;
57
+ }
58
+
59
+ /**
60
+ * Adds our site health debug info.
61
+ *
62
+ * @since 4.0.0
63
+ *
64
+ * @param array $debugInfo The debug info.
65
+ * @return array $debugInfo The debug info.
66
+ */
67
+ public function addDebugInfo( $debugInfo ) {
68
+ $fields = [];
69
+
70
+ $noindexed = $this->noindexed();
71
+ if ( $noindexed ) {
72
+ $fields['noindexed'] = $this->field(
73
+ __( 'Noindexed content', 'all-in-one-seo-pack' ),
74
+ implode( ', ', $noindexed )
75
+ );
76
+ }
77
+
78
+ $nofollowed = $this->nofollowed();
79
+ if ( $nofollowed ) {
80
+ $fields['nofollowed'] = $this->field(
81
+ __( 'Nofollowed content', 'all-in-one-seo-pack' ),
82
+ implode( ', ', $nofollowed )
83
+ );
84
+ }
85
+
86
+ if ( ! count( $fields ) ) {
87
+ return $debugInfo;
88
+ }
89
+
90
+ $debugInfo['aioseo'] = [
91
+ 'label' => __( 'SEO', 'all-in-one-seo-pack' ),
92
+ 'description' => sprintf(
93
+ // Translators: 1 - The plugin short name ("AIOSEO").
94
+ __( 'The fields below contain important SEO information from %1$s that may effect your site.', 'all-in-one-seo-pack' ),
95
+ AIOSEO_PLUGIN_SHORT_NAME
96
+ ),
97
+ 'private' => false,
98
+ 'show_count' => true,
99
+ 'fields' => $fields,
100
+ ];
101
+ return $debugInfo;
102
+ }
103
+
104
+ /**
105
+ * Tests that run to check if autoupdates are enabled.
106
+ *
107
+ * @since 4.0.0
108
+ *
109
+ * @return array A results array for the test.
110
+ */
111
+ public function testCheckAutoUpdates() {
112
+ $label = __( 'Your website is receiving automatic updates', 'all-in-one-seo-pack' );
113
+ $status = 'good';
114
+ $actions = '';
115
+ $description = sprintf(
116
+ // Translators: 1 - The plugin short name ("AIOSEO").
117
+ __( '%1$s automatic updates are enabled and you are getting the latest features, bugfixes, and security updates as they are released.', 'all-in-one-seo-pack' ),
118
+ AIOSEO_PLUGIN_SHORT_NAME
119
+ );
120
+
121
+ $updatesOption = aioseo()->options->advanced->autoUpdates;
122
+
123
+ if ( 'minor' === $updatesOption ) {
124
+ $label = __( 'Your website is receiving minor updates', 'all-in-one-seo-pack' );
125
+ $description = sprintf(
126
+ // Translators: 1 - The plugin short name ("AIOSEO").
127
+ __( '%1$s minor updates are enabled and you are getting the latest bugfixes and security updates, but not major features.', 'all-in-one-seo-pack' ),
128
+ AIOSEO_PLUGIN_SHORT_NAME
129
+ );
130
+ }
131
+ if ( 'none' === $updatesOption ) {
132
+ $status = 'recommended';
133
+ $label = __( 'Automatic updates are disabled', 'all-in-one-seo-pack' );
134
+ $description = sprintf(
135
+ // Translators: 1 - The plugin short name ("AIOSEO").
136
+ __(
137
+ '%1$s automatic updates are disabled. We recommend enabling automatic updates so you can get access to the latest features, bugfixes, and security updates as they are released.',
138
+ 'all-in-one-seo-pack'
139
+ ),
140
+ AIOSEO_PLUGIN_SHORT_NAME
141
+ );
142
+ $actions = $this->actionLink( add_query_arg( 'page', 'aioseo-settings#/advanced', admin_url( 'admin.php' ) ), __( 'Update Settings', 'all-in-one-seo-pack' ) );
143
+ }
144
+
145
+ return [
146
+ 'label' => $label,
147
+ 'status' => $status,
148
+ 'badge' => [
149
+ 'label' => AIOSEO_PLUGIN_SHORT_NAME,
150
+ 'color' => 'blue',
151
+ ],
152
+ 'description' => $description,
153
+ 'test' => 'aioseo_automatic_updates',
154
+ 'actions' => $actions
155
+ ];
156
+ return $this->result(
157
+ 'aioseo_automatic_updates',
158
+ $status,
159
+ $label,
160
+ $description,
161
+ $actions
162
+ );
163
+ }
164
+
165
+ /**
166
+ * Checks whether the site is public.
167
+ *
168
+ * @since 4.0.0
169
+ *
170
+ * @return array The test result.
171
+ */
172
+ public function testCheckSitePublic() {
173
+ $test = 'aioseo_site_public';
174
+
175
+ if ( ! get_option( 'blog_public' ) ) {
176
+ return $this->result(
177
+ $test,
178
+ 'critical',
179
+ __( 'Your site does not appear in search results', 'all-in-one-seo-pack' ),
180
+ __( 'Your site is set to private. This means WordPress asks search engines to exclude your website from search results.', 'all-in-one-seo-pack' ),
181
+ $this->actionLink( admin_url( 'options-reading.php' ), __( 'Go to Settings > Reading', 'all-in-one-seo-pack' ) )
182
+ );
183
+ }
184
+ return $this->result(
185
+ $test,
186
+ 'good',
187
+ __( 'Your site appears in search results', 'all-in-one-seo-pack' ),
188
+ __( 'Your site is set to public. Search engines will index your website and it will appear in search results.', 'all-in-one-seo-pack' )
189
+ );
190
+ }
191
+
192
+ /**
193
+ * Checks whether the site title and tagline are set.
194
+ *
195
+ * @since 4.0.0
196
+ *
197
+ * @return array The test result.
198
+ */
199
+ public function testCheckSiteInfo() {
200
+ $siteTitle = get_bloginfo( 'name' );
201
+ $siteTagline = get_bloginfo( 'description' );
202
+
203
+ if ( ! $siteTitle || ! $siteTagline ) {
204
+ return $this->result(
205
+ 'aioseo_site_info',
206
+ 'recommended',
207
+ __( 'Your Site Title and/or Tagline are blank', 'all-in-one-seo-pack' ),
208
+ sprintf(
209
+ // Translators: 1 - The plugin short name ("AIOSEO").
210
+ __(
211
+ 'Your Site Title and/or Tagline are blank. We recommend setting both of these values as %1$s requires these for various features, including our schema markup',
212
+ 'all-in-one-seo-pack'
213
+ ),
214
+ AIOSEO_PLUGIN_SHORT_NAME
215
+ ),
216
+ $this->actionLink( admin_url( 'options-general.php' ), __( 'Go to Settings > General', 'all-in-one-seo-pack' ) )
217
+ );
218
+ }
219
+ return $this->result(
220
+ 'aioseo_site_info',
221
+ 'good',
222
+ __( 'Your Site Title and Tagline are set', 'all-in-one-seo-pack' ),
223
+ sprintf(
224
+ // Translators: 1 - The plugin short name ("AIOSEO").
225
+ __( 'Great! These are required for %1$s\'s schema markup and are often used as fallback values for various other features.', 'all-in-one-seo-pack' ),
226
+ AIOSEO_PLUGIN_SHORT_NAME
227
+ )
228
+ );
229
+ }
230
+
231
+ /**
232
+ * Checks whether the required settings for our schema markup are set.
233
+ *
234
+ * @since 4.0.0
235
+ *
236
+ * @return array The test result.
237
+ */
238
+ public function testCheckSchemaMarkup() {
239
+ $menuPath = admin_url( 'admin.php?page=aioseo-search-appearance' );
240
+
241
+ if ( 'organization' === aioseo()->options->searchAppearance->global->schema->siteRepresents ) {
242
+ if (
243
+ ! aioseo()->options->searchAppearance->global->schema->organizationName ||
244
+ (
245
+ ! aioseo()->options->searchAppearance->global->schema->organizationLogo &&
246
+ ! aioseo()->helpers->getSiteLogoUrl()
247
+ )
248
+ ) {
249
+ return $this->result(
250
+ 'aioseo_schema_markup',
251
+ 'recommended',
252
+ __( 'Your Organization Name and/or Logo are blank', 'all-in-one-seo-pack' ),
253
+ sprintf(
254
+ // Translators: 1 - The plugin short name ("AIOSEO").
255
+ __( 'Your Organization Name and/or Logo are blank. These values are required for %1$s\'s Organization schema markup.', 'all-in-one-seo-pack' ),
256
+ AIOSEO_PLUGIN_SHORT_NAME
257
+ ),
258
+ $this->actionLink( $menuPath, __( 'Go to Schema Settings', 'all-in-one-seo-pack' ) )
259
+ );
260
+ }
261
+ return $this->result(
262
+ 'aioseo_schema_markup',
263
+ 'good',
264
+ __( 'Your Organization Name and Logo are set', 'all-in-one-seo-pack' ),
265
+ sprintf(
266
+ // Translators: 1 - The plugin short name ("AIOSEO").
267
+ __( 'Awesome! These are required for %1$s\'s Organization schema markup.', 'all-in-one-seo-pack' ),
268
+ AIOSEO_PLUGIN_SHORT_NAME
269
+ )
270
+ );
271
+ }
272
+
273
+ if (
274
+ ! aioseo()->options->searchAppearance->global->schema->person ||
275
+ (
276
+ 'manual' === aioseo()->options->searchAppearance->global->schema->person &&
277
+ (
278
+ ! aioseo()->options->searchAppearance->global->schema->personName ||
279
+ ! aioseo()->options->searchAppearance->global->schema->personLogo
280
+ )
281
+ )
282
+ ) {
283
+ return $this->result(
284
+ 'aioseo_schema_markup',
285
+ 'recommended',
286
+ __( 'Your Person Name and/or Image are blank', 'all-in-one-seo-pack' ),
287
+ sprintf(
288
+ // Translators: 1 - The plugin short name ("AIOSEO").
289
+ __( 'Your Person Name and/or Image are blank. These values are required for %1$s\'s Person schema markup.', 'all-in-one-seo-pack' ),
290
+ AIOSEO_PLUGIN_SHORT_NAME
291
+ ),
292
+ $this->actionLink( $menuPath, __( 'Go to Schema Settings', 'all-in-one-seo-pack' ) )
293
+ );
294
+ }
295
+ return $this->result(
296
+ 'aioseo_schema_markup',
297
+ 'good',
298
+ __( 'Your Person Name and Image are set', 'all-in-one-seo-pack' ),
299
+ sprintf(
300
+ // Translators: 1 - The plugin short name ("AIOSEO").
301
+ __( 'Awesome! These are required for %1$s\'s Person schema markup.', 'all-in-one-seo-pack' ),
302
+ AIOSEO_PLUGIN_SHORT_NAME
303
+ )
304
+ );
305
+ }
306
+
307
+ /**
308
+ * Checks whether the required settings for our schema markup are set.
309
+ *
310
+ * @since 4.0.0
311
+ *
312
+ * @return array The test result.
313
+ */
314
+ public function testCheckPluginUpdate() {
315
+ $response = wp_remote_get( 'https://api.wordpress.org/plugins/info/1.0/all-in-one-seo-pack.json' );
316
+ $body = wp_remote_retrieve_body( $response );
317
+ if ( ! $body ) {
318
+ // Something went wrong.
319
+ return;
320
+ }
321
+
322
+ $pluginData = json_decode( $body );
323
+ $shouldUpdate = version_compare( AIOSEO_VERSION, $pluginData->version, '<' );
324
+
325
+ if ( $shouldUpdate ) {
326
+ return $this->result(
327
+ 'aioseo_plugin_update',
328
+ 'critical',
329
+ sprintf(
330
+ // Translators: 1 - The plugin short name ("AIOSEO").
331
+ __( '%1$s needs to be updated', 'all-in-one-seo-pack' ),
332
+ AIOSEO_PLUGIN_SHORT_NAME
333
+ ),
334
+ sprintf(
335
+ // Translators: 1 - The plugin short name ("AIOSEO").
336
+ __( 'An update is available for %1$s. Upgrade to the latest version to receive all the latest features, bug fixes and security improvements.', 'all-in-one-seo-pack' ),
337
+ AIOSEO_PLUGIN_SHORT_NAME
338
+ ),
339
+ $this->actionLink( admin_url( 'plugins.php' ), __( 'Go to Plugins', 'all-in-one-seo-pack' ) )
340
+ );
341
+ }
342
+ return $this->result(
343
+ 'aioseo_plugin_update',
344
+ 'good',
345
+ sprintf(
346
+ // Translators: 1 - The plugin short name ("AIOSEO").
347
+ __( '%1$s is updated to the latest version', 'all-in-one-seo-pack' ),
348
+ AIOSEO_PLUGIN_SHORT_NAME
349
+ ),
350
+ __( 'Fantastic! By updating to the latest version, you have access to all the latest features, bug fixes and security improvements.', 'all-in-one-seo-pack' )
351
+ );
352
+ }
353
+
354
+ /**
355
+ * Returns a list of noindexed content.
356
+ *
357
+ * @since 4.0.0
358
+ *
359
+ * @return array $noindexed A list of noindexed content.
360
+ */
361
+ protected function noindexed() {
362
+ $globalDefault = aioseo()->options->searchAppearance->advanced->globalRobotsMeta->default;
363
+ if (
364
+ ! $globalDefault &&
365
+ aioseo()->options->searchAppearance->advanced->globalRobotsMeta->noindex
366
+ ) {
367
+ return [
368
+ __( 'Your entire site is set to globally noindex content.', 'all-in-one-seo-pack' )
369
+ ];
370
+ }
371
+
372
+ $noindexed = [];
373
+
374
+ if (
375
+ ! $globalDefault &&
376
+ aioseo()->options->searchAppearance->advanced->globalRobotsMeta->noindexPaginated
377
+ ) {
378
+ $noindexed[] = __( 'Paginated Content', 'all-in-one-seo-pack' );
379
+ }
380
+
381
+ $archives = [
382
+ 'author' => __( 'Author Archives', 'all-in-one-seo-pack' ),
383
+ 'date' => __( 'Date Archives', 'all-in-one-seo-pack' ),
384
+ 'search' => __( 'Search Page', 'all-in-one-seo-pack' )
385
+ ];
386
+
387
+ // Archives.
388
+ foreach ( $archives as $name => $type ) {
389
+ if (
390
+ ! aioseo()->options->searchAppearance->archives->{ $name }->advanced->robotsMeta->default &&
391
+ aioseo()->options->searchAppearance->archives->{ $name }->advanced->robotsMeta->noindex
392
+ ) {
393
+ $noindexed[] = $type;
394
+ }
395
+ }
396
+
397
+ foreach ( aioseo()->helpers->getPublicPostTypes() as $postType ) {
398
+ if (
399
+ aioseo()->options->searchAppearance->dynamic->postTypes->has( $postType['name'] ) &&
400
+ ! aioseo()->options->searchAppearance->dynamic->postTypes->{ $postType['name'] }->advanced->robotsMeta->default &&
401
+ aioseo()->options->searchAppearance->dynamic->postTypes->{ $postType['name'] }->advanced->robotsMeta->noindex
402
+ ) {
403
+ $noindexed[] = $postType['label'] . ' (' . $postType['name'] . ')';
404
+ }
405
+ }
406
+
407
+ foreach ( aioseo()->helpers->getPublicTaxonomies() as $taxonomy ) {
408
+ if (
409
+ aioseo()->options->searchAppearance->dynamic->taxonomies->has( $taxonomy['name'] ) &&
410
+ ! aioseo()->options->searchAppearance->dynamic->taxonomies->{ $taxonomy['name'] }->advanced->robotsMeta->default &&
411
+ aioseo()->options->searchAppearance->dynamic->taxonomies->{ $taxonomy['name'] }->advanced->robotsMeta->noindex
412
+ ) {
413
+ $noindexed[] = $taxonomy['label'] . ' (' . $taxonomy['name'] . ')';
414
+ }
415
+ }
416
+
417
+ return $noindexed;
418
+ }
419
+
420
+ /**
421
+ * Returns a list of nofollowed content.
422
+ *
423
+ * @since 4.0.0
424
+ *
425
+ * @return array $nofollowed A list of nofollowed content.
426
+ */
427
+ protected function nofollowed() {
428
+ $globalDefault = aioseo()->options->searchAppearance->advanced->globalRobotsMeta->default;
429
+ if (
430
+ ! $globalDefault &&
431
+ aioseo()->options->searchAppearance->advanced->globalRobotsMeta->nofollow
432
+ ) {
433
+ return [
434
+ __( 'Your entire site is set to globally nofollow content.', 'all-in-one-seo-pack' )
435
+ ];
436
+ }
437
+
438
+ $nofollowed = [];
439
+
440
+ if (
441
+ ! $globalDefault &&
442
+ aioseo()->options->searchAppearance->advanced->globalRobotsMeta->nofollowPaginated
443
+ ) {
444
+ $nofollowed[] = __( 'Paginated Content', 'all-in-one-seo-pack' );
445
+ }
446
+
447
+ $archives = [
448
+ 'author' => __( 'Author Archives', 'all-in-one-seo-pack' ),
449
+ 'date' => __( 'Date Archives', 'all-in-one-seo-pack' ),
450
+ 'search' => __( 'Search Page', 'all-in-one-seo-pack' )
451
+ ];
452
+
453
+ // Archives.
454
+ foreach ( $archives as $name => $type ) {
455
+ if (
456
+ ! aioseo()->options->searchAppearance->archives->{ $name }->advanced->robotsMeta->default &&
457
+ aioseo()->options->searchAppearance->archives->{ $name }->advanced->robotsMeta->nofollow
458
+ ) {
459
+ $nofollowed[] = $type;
460
+ }
461
+ }
462
+
463
+ foreach ( aioseo()->helpers->getPublicPostTypes() as $postType ) {
464
+ if (
465
+ aioseo()->options->searchAppearance->dynamic->postTypes->has( $postType['name'] ) &&
466
+ ! aioseo()->options->searchAppearance->dynamic->postTypes->{ $postType['name'] }->advanced->robotsMeta->default &&
467
+ aioseo()->options->searchAppearance->dynamic->postTypes->{ $postType['name'] }->advanced->robotsMeta->nofollow
468
+ ) {
469
+ $nofollowed[] = $postType['label'] . ' (' . $postType['name'] . ')';
470
+ }
471
+ }
472
+
473
+ foreach ( aioseo()->helpers->getPublicTaxonomies() as $taxonomy ) {
474
+ if (
475
+ aioseo()->options->searchAppearance->dynamic->taxonomies->has( $taxonomy['name'] ) &&
476
+ ! aioseo()->options->searchAppearance->dynamic->taxonomies->{ $taxonomy['name'] }->advanced->robotsMeta->default &&
477
+ aioseo()->options->searchAppearance->dynamic->taxonomies->{ $taxonomy['name'] }->advanced->robotsMeta->nofollow
478
+ ) {
479
+ $nofollowed[] = $taxonomy['label'] . ' (' . $taxonomy['name'] . ')';
480
+ }
481
+ }
482
+
483
+ return $nofollowed;
484
+ }
485
+
486
+ /**
487
+ * Returns a debug info data field.
488
+ *
489
+ * @since 4.0.0
490
+ *
491
+ * @param string $label The field label.
492
+ * @param string $value The field value.
493
+ * @param boolean $private Whether the field shouldn't be included if the debug info is copied.
494
+ * @return array The debug info data field.
495
+ */
496
+ private function field( $label, $value, $private = false ) {
497
+ return [
498
+ 'label' => $label,
499
+ 'value' => $value,
500
+ 'private' => $private,
501
+ ];
502
+ }
503
+
504
+ /**
505
+ * Returns the test result.
506
+ *
507
+ * @since 4.0.0
508
+ *
509
+ * @param string $name The test name.
510
+ * @param string $status The result status.
511
+ * @param string $header The test header.
512
+ * @param string $description The result description.
513
+ * @param string $actions The result actions.
514
+ * @return array The test result.
515
+ */
516
+ protected function result( $name, $status, $header, $description, $actions = '' ) {
517
+ $color = 'blue';
518
+ switch ( $status ) {
519
+ case 'good':
520
+ break;
521
+ case 'recommended':
522
+ $color = 'orange';
523
+ break;
524
+ case 'critical':
525
+ $color = 'red';
526
+ break;
527
+ default:
528
+ break;
529
+ }
530
+
531
+ return [
532
+ 'test' => $name,
533
+ 'status' => $status,
534
+ 'label' => $header,
535
+ 'description' => $description,
536
+ 'actions' => $actions,
537
+ 'badge' => [
538
+ 'label' => AIOSEO_PLUGIN_SHORT_NAME,
539
+ 'color' => $color,
540
+ ],
541
+ ];
542
+ }
543
+
544
+ /**
545
+ * Returns an action link.
546
+ *
547
+ * @since 4.0.0
548
+ *
549
+ * @param string $path The path.
550
+ * @param string $anchor The anchor text.
551
+ * @return string The action link.
552
+ */
553
+ protected function actionLink( $path, $anchor ) {
554
+ return sprintf(
555
+ '<p><a href="%1$s">%2$s</a></p>',
556
+ $path,
557
+ $anchor
558
+ );
559
+ }
560
+ }
app/Common/Admin/Usage.php ADDED
@@ -0,0 +1,217 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Admin;
3
+
4
+ /**
5
+ * Usage tracking class.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class Usage {
10
+ /**
11
+ * Source of notifications content.
12
+ *
13
+ * @since 4.0.0
14
+ *
15
+ * @var string
16
+ */
17
+ private $url = 'https://aiousage.com/v1/track';
18
+
19
+ /**
20
+ * Whether or not usage tracking is enabled.
21
+ *
22
+ * @since 4.0.0
23
+ *
24
+ * @var bool
25
+ */
26
+ protected $enabled = false;
27
+
28
+ /**
29
+ * Class Constructor.
30
+ *
31
+ * @since 4.0.0
32
+ */
33
+ public function __construct() {
34
+ add_action( 'init', [ $this, 'init' ], 2 );
35
+ }
36
+
37
+ /**
38
+ * Runs on the init action.
39
+ *
40
+ * @since 4.0.0
41
+ *
42
+ * @return void
43
+ */
44
+ public function init() {
45
+ try {
46
+ $action = 'aioseo_send_usage_data';
47
+ if ( ! $this->enabled ) {
48
+ if ( as_next_scheduled_action( $action ) ) {
49
+ as_unschedule_action( $action, [], 'aioseo' );
50
+ }
51
+ return;
52
+ }
53
+
54
+ // Register the action handler.
55
+ add_action( $action, [ $this, 'process' ] );
56
+
57
+ if ( ! as_next_scheduled_action( $action ) ) {
58
+ as_schedule_recurring_action( $this->generateStartDate(), WEEK_IN_SECONDS, $action, [], 'aioseo' );
59
+
60
+ // Run the task immediately using an async action.
61
+ as_enqueue_async_action( $action, [], 'aioseo' );
62
+ }
63
+ } catch ( \Exception $e ) {
64
+ // Do nothing.
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Processes the usage tracking.
70
+ *
71
+ * @since 4.0.0
72
+ *
73
+ * @return void
74
+ */
75
+ public function process() {
76
+ if ( ! $this->enabled ) {
77
+ return;
78
+ }
79
+
80
+ wp_remote_post(
81
+ $this->getUrl(),
82
+ [
83
+ 'timeout' => 5,
84
+ 'redirection' => 5,
85
+ 'httpversion' => '1.1',
86
+ 'blocking' => true,
87
+ 'headers' => [ 'Content-Type' => 'application/json; charset=utf-8' ],
88
+ 'body' => wp_json_encode( $this->getData() ),
89
+ 'user-agent' => 'AIOSEO/' . AIOSEO_VERSION . '; ' . get_bloginfo( 'url' ),
90
+ ]
91
+ );
92
+ }
93
+
94
+ /**
95
+ * Gets the URL for the notifications api.
96
+ *
97
+ * @since 4.0.0
98
+ *
99
+ * @return string The URL to use for the api requests.
100
+ */
101
+ private function getUrl() {
102
+ if ( defined( 'AIOSEO_USAGE_TRACKING_URL' ) ) {
103
+ return AIOSEO_USAGE_TRACKING_URL;
104
+ }
105
+
106
+ return $this->url;
107
+ }
108
+
109
+ /**
110
+ * Retrieves the data to send in the usage tracking.
111
+ *
112
+ * @since 4.0.0
113
+ *
114
+ * @return array An array of data to send.
115
+ */
116
+ private function getData() {
117
+ $themeData = wp_get_theme();
118
+ $type = $this->getType();
119
+
120
+ return [
121
+ // Generic data (environment).
122
+ 'url' => home_url(),
123
+ 'php_version' => PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION,
124
+ 'wp_version' => get_bloginfo( 'version' ),
125
+ 'mysql_version' => aioseo()->db->db->db_version(),
126
+ 'server_version' => isset( $_SERVER['SERVER_SOFTWARE'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) ) : '',
127
+ 'is_ssl' => is_ssl(),
128
+ 'is_multisite' => is_multisite(),
129
+ 'sites_count' => function_exists( 'get_blog_count' ) ? (int) get_blog_count() : 1,
130
+ 'active_plugins' => $this->getActivePlugins(),
131
+ 'theme_name' => $themeData->name,
132
+ 'theme_version' => $themeData->version,
133
+ 'user_count' => function_exists( 'get_user_count' ) ? get_user_count() : null,
134
+ 'locale' => get_locale(),
135
+ 'timezone_offset' => aioseo()->helpers->getTimeZoneOffset(),
136
+ 'email' => get_bloginfo( 'admin_email' ),
137
+ // AIOSEO specific data.
138
+ 'aioseo_version' => AIOSEO_VERSION,
139
+ 'aioseo_license_key' => null,
140
+ 'aioseo_license_type' => null,
141
+ 'aioseo_is_pro' => false,
142
+ "aioseo_${type}_installed_date" => aioseo()->internalOptions->internal->installed,
143
+ 'aioseo_settings' => $this->getSettings()
144
+ ];
145
+ }
146
+
147
+ /**
148
+ * Get the settings and escape the quotes so it can be JSON encoded.
149
+ *
150
+ * @since 4.0.0
151
+ *
152
+ * @return array An array of settings data.
153
+ */
154
+ private function getSettings() {
155
+ $settings = aioseo()->options->all();
156
+ array_walk_recursive( $settings, function( &$v ) {
157
+ if ( is_string( $v ) && strpos( $v, '&quot' ) !== false ) {
158
+ $v = str_replace( '&quot', '&#x5c;&quot', $v );
159
+ }
160
+ });
161
+
162
+ $internal = aioseo()->internalOptions->all();
163
+ array_walk_recursive( $internal, function( &$v ) {
164
+ if ( is_string( $v ) && strpos( $v, '&quot' ) !== false ) {
165
+ $v = str_replace( '&quot', '&#x5c;&quot', $v );
166
+ }
167
+ });
168
+
169
+ return [
170
+ 'options' => $settings,
171
+ 'internal' => $internal
172
+ ];
173
+ }
174
+
175
+ /**
176
+ * Return a list of active plugins.
177
+ *
178
+ * @since 4.0.0
179
+ *
180
+ * @return array An array of active plugin data.
181
+ */
182
+ private function getActivePlugins() {
183
+ if ( ! function_exists( 'get_plugins' ) ) {
184
+ include ABSPATH . '/wp-admin/includes/plugin.php';
185
+ }
186
+ $active = get_option( 'active_plugins', [] );
187
+ $plugins = array_intersect_key( get_plugins(), array_flip( $active ) );
188
+
189
+ return array_map(
190
+ static function ( $plugin ) {
191
+ if ( isset( $plugin['Version'] ) ) {
192
+ return $plugin['Version'];
193
+ }
194
+ return 'Not Set';
195
+ },
196
+ $plugins
197
+ );
198
+ }
199
+
200
+ /**
201
+ * Generate a random start date for usage tracking.
202
+ *
203
+ * @since 4.0.0
204
+ *
205
+ * @return integer The randomized start date.
206
+ */
207
+ private function generateStartDate() {
208
+ $tracking = [
209
+ 'days' => wp_rand( 0, 6 ) * DAY_IN_SECONDS,
210
+ 'hours' => wp_rand( 0, 23 ) * HOUR_IN_SECONDS,
211
+ 'minutes' => wp_rand( 0, 23 ) * HOUR_IN_SECONDS,
212
+ 'seconds' => wp_rand( 0, 59 )
213
+ ];
214
+
215
+ return strtotime( 'next sunday' ) + array_sum( $tracking );
216
+ }
217
+ }
app/Common/Api/Analyze.php ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Api;
3
+
4
+ /**
5
+ * Route class for the API.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class Analyze {
10
+ /**
11
+ * Analyzes the site for SEO.
12
+ *
13
+ * @since 4.0.0
14
+ *
15
+ * @param \WP_REST_Request $request The REST Request
16
+ * @return \WP_REST_Response The response.
17
+ */
18
+ public static function analyzeSite( $request ) {
19
+ $body = $request->get_json_params();
20
+ $analyzeUrl = ! empty( $body['url'] ) ? esc_url_raw( urldecode( $body['url'] ) ) : null;
21
+ $refreshResults = ! empty( $body['refresh'] ) ? (bool) $body['refresh'] : false;
22
+ $analyzeOrHomeUrl = ! empty( $analyzeUrl ) ? $analyzeUrl : home_url();
23
+ $responseCode = false === get_transient( 'aioseo_analyze_site_code' ) ? [] : get_transient( 'aioseo_analyze_site_code' );
24
+ $responseBody = false === get_transient( 'aioseo_analyze_site_body' ) ? [] : get_transient( 'aioseo_analyze_site_body' );
25
+ if (
26
+ empty( $responseCode ) ||
27
+ empty( $responseCode[ $analyzeOrHomeUrl ] ) ||
28
+ empty( $responseBody ) ||
29
+ empty( $responseBody[ $analyzeOrHomeUrl ] ) ||
30
+ $refreshResults
31
+ ) {
32
+ $token = aioseo()->internalOptions->internal->siteAnalysis->connectToken;
33
+ $license = aioseo()->options->has( 'general' ) && aioseo()->options->general->has( 'licenseKey' )
34
+ ? aioseo()->options->general->licenseKey
35
+ : '';
36
+ $url = defined( 'AIOSEO_ANALYZE_URL' ) ? AIOSEO_ANALYZE_URL : 'https://analyze.aioseo.com';
37
+ $response = wp_remote_post( $url . '/v1/analyze/', [
38
+ 'headers' => [
39
+ 'X-AIOSEO-Key' => $token,
40
+ 'X-AIOSEO-License' => $license,
41
+ 'Content-Type' => 'application/json'
42
+ ],
43
+ 'body' => wp_json_encode( [
44
+ 'url' => $analyzeOrHomeUrl
45
+ ] ),
46
+ 'timeout' => 30
47
+ ] );
48
+
49
+ $responseCode[ $analyzeOrHomeUrl ] = wp_remote_retrieve_response_code( $response );
50
+ $responseBody[ $analyzeOrHomeUrl ] = json_decode( wp_remote_retrieve_body( $response ) );
51
+
52
+ set_transient( 'aioseo_analyze_site_code', $responseCode, 10 * MINUTE_IN_SECONDS );
53
+ set_transient( 'aioseo_analyze_site_body', $responseBody, 10 * MINUTE_IN_SECONDS );
54
+ }
55
+
56
+ if ( 200 !== $responseCode[ $analyzeOrHomeUrl ] || empty( $responseBody[ $analyzeOrHomeUrl ]->success ) || ! empty( $responseBody[ $analyzeOrHomeUrl ]->error ) ) {
57
+ if ( ! empty( $responseBody[ $analyzeOrHomeUrl ]->error ) && 'invalid-token' === $responseBody[ $analyzeOrHomeUrl ]->error ) {
58
+ aioseo()->internalOptions->internal->siteAnalysis->reset();
59
+ }
60
+ return new \WP_REST_Response( [
61
+ 'success' => false,
62
+ 'response' => $responseBody[ $analyzeOrHomeUrl ]
63
+ ], 400 );
64
+ }
65
+
66
+ if ( $analyzeUrl ) {
67
+ $competitors = aioseo()->internalOptions->internal->siteAnalysis->competitors;
68
+ $competitors = array_reverse( $competitors, true );
69
+
70
+ if ( empty( $competitors[ $analyzeUrl ] ) ) {
71
+ $competitors[ $analyzeUrl ] = '';
72
+ }
73
+
74
+ $competitors[ $analyzeUrl ] = wp_json_encode( $responseBody[ $analyzeOrHomeUrl ] );
75
+
76
+ $competitors = array_reverse( $competitors, true );
77
+
78
+ // Reset the competitors.
79
+ aioseo()->internalOptions->internal->siteAnalysis->competitors = $competitors;
80
+
81
+ return new \WP_REST_Response( $competitors, 200 );
82
+ }
83
+
84
+ aioseo()->internalOptions->internal->siteAnalysis->score = $responseBody[ $analyzeOrHomeUrl ]->score;
85
+ aioseo()->internalOptions->internal->siteAnalysis->results = wp_json_encode( $responseBody[ $analyzeOrHomeUrl ]->results );
86
+
87
+ return new \WP_REST_Response( $responseBody[ $analyzeOrHomeUrl ], 200 );
88
+ }
89
+
90
+ /**
91
+ * Deletes the analyzed site for SEO.
92
+ *
93
+ * @since 4.0.0
94
+ *
95
+ * @param \WP_REST_Request $request The REST Request
96
+ * @return \WP_REST_Response The response.
97
+ */
98
+ public static function deleteSite( $request ) {
99
+ $body = $request->get_json_params();
100
+ $analyzeUrl = ! empty( $body['url'] ) ? esc_url_raw( urldecode( $body['url'] ) ) : null;
101
+
102
+ $competitors = aioseo()->internalOptions->internal->siteAnalysis->competitors;
103
+
104
+ unset( $competitors[ $analyzeUrl ] );
105
+
106
+ // Reset the competitors.
107
+ aioseo()->internalOptions->internal->siteAnalysis->competitors = $competitors;
108
+
109
+ return new \WP_REST_Response( $competitors, 200 );
110
+ }
111
+ }
app/Common/Api/Api.php ADDED
@@ -0,0 +1,217 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Api;
3
+
4
+ /**
5
+ * Api class for the admin.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class Api {
10
+ /**
11
+ * The REST API Namespace
12
+ *
13
+ * @since 4.0.0
14
+ *
15
+ * @var string
16
+ */
17
+ public $namespace = 'aioseo/v1';
18
+
19
+ /**
20
+ * The routes we use in the rest API.
21
+ *
22
+ * @since 4.0.0
23
+ *
24
+ * @var array
25
+ */
26
+ protected $routes = [
27
+ // phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
28
+ 'GET' => [
29
+ 'options' => [ 'callback' => [ 'Settings', 'getOptions' ] ],
30
+ 'ping' => [ 'callback' => [ 'Ping', 'ping' ] ],
31
+ 'post' => [ 'callback' => [ 'PostsTerms', 'getPostData' ] ],
32
+ 'tags' => [ 'callback' => [ 'Tags', 'getTags' ] ]
33
+ ],
34
+ 'POST' => [
35
+ 'htaccess' => [ 'callback' => [ 'Tools', 'saveHtaccess' ], 'access' => 'aioseo_tools_settings' ],
36
+ 'post' => [
37
+ 'callback' => [ 'PostsTerms', 'updatePosts' ],
38
+ 'access' => [
39
+ 'aioseo_page_analysis',
40
+ 'aioseo_page_general_settings',
41
+ 'aioseo_page_advanced_settings',
42
+ 'aioseo_page_schema_settings',
43
+ 'aioseo_page_social_settings'
44
+ ]
45
+ ],
46
+ 'postscreen' => [ 'callback' => [ 'PostsTerms', 'updatePostFromScreen' ], 'access' => 'aioseo_page_general_settings' ],
47
+ 'termscreen' => [ 'callback' => [ 'PostsTerms', 'updateTermFromScreen' ], 'access' => 'aioseo_page_general_settings' ],
48
+ 'keyphrases' => [ 'callback' => [ 'PostsTerms', 'updatePostKeyphrases' ], 'access' => 'aioseo_page_analysis' ],
49
+ 'analyze' => [ 'callback' => [ 'Analyze', 'analyzeSite' ] ],
50
+ 'analyze/delete-site' => [ 'callback' => [ 'Analyze', 'deleteSite' ], 'access' => 'aioseo_seo_analysis_settings' ],
51
+ 'clear-log' => [ 'callback' => [ 'Tools', 'clearLog' ], 'access' => 'aioseo_tools_settings' ],
52
+ 'connect' => [ 'callback' => [ 'Connect', 'saveConnectToken' ], 'access' => [ 'aioseo_general_settings', 'aioseo_setup_wizard' ] ],
53
+ 'connect-pro' => [ 'callback' => [ 'Connect', 'processConnect' ], 'access' => [ 'aioseo_general_settings', 'aioseo_setup_wizard' ] ],
54
+ 'connect-url' => [ 'callback' => [ 'Connect', 'getConnectUrl' ], 'access' => [ 'aioseo_general_settings', 'aioseo_setup_wizard' ] ],
55
+ 'backup' => [ 'callback' => [ 'Tools', 'createBackup' ], 'access' => 'aioseo_tools_settings' ],
56
+ 'backup/restore' => [ 'callback' => [ 'Tools', 'restoreBackup' ], 'access' => 'aioseo_tools_settings' ],
57
+ 'email-debug-info' => [ 'callback' => [ 'Tools', 'emailDebugInfo' ], 'access' => 'aioseo_tools_settings' ],
58
+ 'migration/fix-blank-formats' => [ 'callback' => [ 'Migration', 'fixBlankFormats' ] ],
59
+
60
+ 'notification/blog-visibility-reminder' => [ 'callback' => [ 'Notifications', 'blogVisibilityReminder' ] ],
61
+ 'notification/description-format-reminder' => [ 'callback' => [ 'Notifications', 'descriptionFormatReminder' ] ],
62
+ 'notification/conflicting-plugins-reminder' => [ 'callback' => [ 'Notifications', 'conflictingPluginsReminder' ] ],
63
+ 'notification/deprecated-filters-reminder' => [ 'callback' => [ 'Notifications', 'deprecatedFiltersReminder' ] ],
64
+ 'notification/install-addons-reminder' => [ 'callback' => [ 'Notifications', 'installAddonsReminder' ] ],
65
+ 'notification/install-aioseo-image-seo-reminder' => [ 'callback' => [ 'Notifications', 'installImageSeoReminder' ] ],
66
+ 'notification/install-aioseo-local-business-reminder' => [ 'callback' => [ 'Notifications', 'installLocalBusinessReminder' ] ],
67
+ 'notification/install-aioseo-news-sitemap-reminder' => [ 'callback' => [ 'Notifications', 'installNewsSitemapReminder' ] ],
68
+ 'notification/install-aioseo-video-sitemap-reminder' => [ 'callback' => [ 'Notifications', 'installVideoSitemapReminder' ] ],
69
+ 'notification/install-mi-reminder' => [ 'callback' => [ 'Notifications', 'installMiReminder' ] ],
70
+ 'notification/v3-migration-custom-field-reminder' => [ 'callback' => [ 'Notifications', 'migrationCustomFieldReminder' ] ],
71
+ 'notification/v3-migration-schema-number-reminder' => [ 'callback' => [ 'Notifications', 'migrationSchemaNumberReminder' ] ],
72
+ 'notifications/dismiss' => [ 'callback' => [ 'Notifications', 'dismissNotifications' ] ],
73
+ 'objects' => [ 'callback' => [ 'PostsTerms', 'searchForObjects' ], 'access' => [ 'aioseo_search_appearance_settings', 'aioseo_sitemap_settings' ] ], // phpcs:ignore Generic.Files.LineLength.MaxExceeded
74
+ 'options' => [
75
+ 'callback' => [ 'Settings', 'saveChanges' ],
76
+ 'access' =>
77
+ [
78
+ 'aioseo_general_settings',
79
+ 'aioseo_search_appearance_settings',
80
+ 'aioseo_social_networks_settings',
81
+ 'aioseo_sitemap_settings',
82
+ 'aioseo_internal_links_settings',
83
+ 'aioseo_redirects_settings',
84
+ 'aioseo_seo_analysis_settings',
85
+ 'aioseo_tools_settings',
86
+ 'aioseo_feature_manager_settings',
87
+ 'aioseo_local_seo_settings'
88
+ ]
89
+ ],
90
+ 'plugins/deactivate' => [ 'callback' => [ 'Plugins', 'deactivatePlugins' ], 'access' => 'aioseo_feature_manager_settings' ],
91
+ 'plugins/install' => [ 'callback' => [ 'Plugins', 'installPlugins' ], 'access' => [ 'install_plugins', 'aioseo_feature_manager_settings' ] ],
92
+ 'reset-settings' => [ 'callback' => [ 'Settings', 'resetSettings' ], 'access' => 'aioseo_tools_settings' ],
93
+ 'settings/export' => [ 'callback' => [ 'Settings', 'exportSettings' ], 'access' => 'aioseo_tools_settings' ],
94
+ 'settings/hide-setup-wizard' => [ 'callback' => [ 'Settings', 'hideSetupWizard' ] ],
95
+ 'settings/hide-upgrade-bar' => [ 'callback' => [ 'Settings', 'hideUpgradeBar' ] ],
96
+ 'settings/import' => [ 'callback' => [ 'Settings', 'importSettings' ], 'access' => 'aioseo_tools_settings' ],
97
+ 'settings/import-plugins' => [ 'callback' => [ 'Settings', 'importPlugins' ], 'access' => 'aioseo_tools_settings' ],
98
+ 'settings/toggle-card' => [ 'callback' => [ 'Settings', 'toggleCard' ] ],
99
+ 'settings/toggle-radio' => [ 'callback' => [ 'Settings', 'toggleRadio' ] ],
100
+ 'sitemap/deactivate-conflicting-plugins' => [ 'callback' => [ 'Sitemaps', 'deactivateConflictingPlugins' ] ],
101
+ 'sitemap/delete-static-files' => [ 'callback' => [ 'Sitemaps', 'deleteStaticFiles' ] ],
102
+ 'tools/delete-robots-txt' => [ 'callback' => [ 'Tools', 'deleteRobotsTxt' ], 'access' => 'aioseo_tools_settings' ],
103
+ 'tools/import-robots-txt' => [ 'callback' => [ 'Tools', 'importRobotsTxt' ], 'access' => 'aioseo_tools_settings' ],
104
+ 'wizard' => [ 'callback' => [ 'Wizard', 'saveWizard' ], 'access' => 'aioseo_setup_wizard' ]
105
+ ],
106
+ 'DELETE' => [
107
+ 'backup' => [ 'callback' => [ 'Tools', 'deleteBackup' ], 'access' => 'aioseo_tools_settings' ]
108
+ ]
109
+ // phpcs:enable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
110
+ ];
111
+
112
+ /**
113
+ * Class contructor.
114
+ *
115
+ * @since 4.0.0
116
+ */
117
+ public function __construct() {
118
+ add_filter( 'rest_pre_serve_request', [ $this, 'allowHeaders' ] );
119
+ add_action( 'rest_api_init', [ $this, 'registerRoutes' ] );
120
+ }
121
+
122
+ /**
123
+ * Get all the routes to register.
124
+ *
125
+ * @since 4.0.0
126
+ *
127
+ * @return array An array of routes.
128
+ */
129
+ protected function getRoutes() {
130
+ return $this->routes;
131
+ }
132
+
133
+ /**
134
+ * Registers the API routes.
135
+ *
136
+ * @since 4.0.0
137
+ *
138
+ * @return void
139
+ */
140
+ public function registerRoutes() {
141
+ $class = new \ReflectionClass( get_called_class() );
142
+ foreach ( $this->getRoutes() as $method => $data ) {
143
+ foreach ( $data as $route => $options ) {
144
+ register_rest_route(
145
+ $this->namespace,
146
+ $route,
147
+ [
148
+ 'methods' => $method,
149
+ 'permission_callback' => empty( $options['permissions'] ) ? [ $this, 'validRequest' ] : [ $this, $options['permissions'] ],
150
+ 'callback' => is_array( $options['callback'] )
151
+ ? [
152
+ (
153
+ class_exists( $class->getNamespaceName() . '\\' . $options['callback'][0] )
154
+ ? $class->getNamespaceName() . '\\' . $options['callback'][0]
155
+ : __NAMESPACE__ . '\\' . $options['callback'][0]
156
+ ),
157
+ $options['callback'][1]
158
+ ]
159
+ : [ $this, $options['callback'] ]
160
+ ]
161
+ );
162
+ }
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Sets headers that are allowed for our API routes.
168
+ *
169
+ * @since 4.0.0
170
+ *
171
+ * @return void
172
+ */
173
+ public function allowHeaders() {
174
+ header( 'Access-Control-Allow-Headers: X-WP-Nonce' );
175
+ }
176
+
177
+ /**
178
+ * Determine if logged in or has the proper permissions.
179
+ *
180
+ * @since 4.0.0
181
+ *
182
+ * @param \WP_REST_Request $request The REST Request.
183
+ * @return bool True if validated, false if not.
184
+ */
185
+ public function validRequest( $request ) {
186
+ return is_user_logged_in() && $this->validateAccess( $request );
187
+ }
188
+
189
+ /**
190
+ * Validates access from the routes array.
191
+ *
192
+ * @since 4.0.0
193
+ *
194
+ * @param \WP_REST_Request $request The REST Request.
195
+ * @return bool True if validated, false if not.
196
+ */
197
+ private function validateAccess( $request ) {
198
+ $route = str_replace( '/' . $this->namespace . '/', '', $request->get_route() );
199
+ $routeData = $this->getRoutes()[ $request->get_method() ][ $route ];
200
+
201
+ if ( empty( $routeData['access'] ) ) {
202
+ return true;
203
+ }
204
+
205
+ // We validate with any of the access options.
206
+ if ( ! is_array( $routeData['access'] ) ) {
207
+ $routeData['access'] = [ $routeData['access'] ];
208
+ }
209
+ foreach ( $routeData['access'] as $access ) {
210
+ if ( current_user_can( $access ) ) {
211
+ return true;
212
+ }
213
+ }
214
+
215
+ return false;
216
+ }
217
+ }
app/Common/Api/Connect.php ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Api;
3
+
4
+ /**
5
+ * Route class for the API.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class Connect {
10
+ /**
11
+ * Get the connect URL.
12
+ *
13
+ * @since 4.0.0
14
+ *
15
+ * @param \WP_REST_Request $request The REST Request
16
+ * @return \WP_REST_Response The response.
17
+ */
18
+ public static function getConnectUrl( $request ) {
19
+ $body = $request->get_json_params();
20
+ $key = ! empty( $body['licenseKey'] ) ? sanitize_text_field( $body['licenseKey'] ) : null;
21
+ $wizard = ! empty( $body['wizard'] ) ? (bool) $body['wizard'] : false;
22
+ $success = true;
23
+ $urlData = aioseo()->admin->connect->generateConnectUrl( $key, $wizard ? admin_url( 'index.php?page=aioseo-setup-wizard#/success' ) : null );
24
+ $url = '';
25
+ $message = '';
26
+
27
+ if ( ! empty( $urlData['error'] ) ) {
28
+ $success = false;
29
+ $message = $urlData['error'];
30
+ }
31
+
32
+ $url = $urlData['url'];
33
+
34
+ return new \WP_REST_Response( [
35
+ 'success' => $success,
36
+ 'url' => $url,
37
+ 'message' => $message,
38
+ 'popup' => ! isset( $urlData['popup'] ) ? true : $urlData['popup']
39
+ ], 200 );
40
+ }
41
+
42
+ /**
43
+ * Process the connection.
44
+ *
45
+ * @since 4.0.0
46
+ *
47
+ * @param \WP_REST_Request $request The REST Request
48
+ * @return \WP_REST_Response The response.
49
+ */
50
+ public static function processConnect( $request ) {
51
+ $body = $request->get_json_params();
52
+ $downloadUrl = ! empty( $body['downloadUrl'] ) ? esc_url_raw( urldecode( $body['downloadUrl'] ) ) : null;
53
+ $token = ! empty( $body['token'] ) ? sanitize_text_field( $body['token'] ) : null;
54
+ $wizard = ! empty( $body['wizard'] ) ? sanitize_text_field( $body['wizard'] ) : null;
55
+ $success = true;
56
+ $message = '';
57
+
58
+ if ( $wizard ) {
59
+ aioseo()->internalOptions->internal->wizard = $wizard;
60
+ }
61
+
62
+ $response = aioseo()->admin->connect->process( $downloadUrl, $token );
63
+ if ( ! empty( $response['error'] ) ) {
64
+ $message = $response['error'];
65
+ } else {
66
+ $message = $response['success'];
67
+ }
68
+
69
+ return new \WP_REST_Response( [
70
+ 'success' => $success,
71
+ 'message' => $message
72
+ ], 200 );
73
+ }
74
+
75
+ /**
76
+ * Saves the connect token.
77
+ *
78
+ * @since 4.0.0
79
+ *
80
+ * @param \WP_REST_Request $request The REST Request
81
+ * @return \WP_REST_Response The response.
82
+ */
83
+ public static function saveConnectToken( $request ) {
84
+ $body = $request->get_json_params();
85
+ $token = ! empty( $body['token'] ) ? sanitize_text_field( $body['token'] ) : null;
86
+ $success = true;
87
+ $message = 'token-saved';
88
+
89
+ aioseo()->internalOptions->internal->siteAnalysis->connectToken = $token;
90
+
91
+ return new \WP_REST_Response( [
92
+ 'success' => $success,
93
+ 'message' => $message
94
+ ], 200 );
95
+ }
96
+ }
app/Common/Api/Migration.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Api;
3
+
4
+ use AIOSEO\Plugin\Common\Migration as CommonMigration;
5
+ use AIOSEO\Plugin\Common\Models;
6
+
7
+ /**
8
+ * Route class for the API.
9
+ *
10
+ * @since 4.0.6
11
+ */
12
+ class Migration {
13
+
14
+ /**
15
+ * Resets blank title formats and retriggers the post/term meta migration.
16
+ *
17
+ * @since 4.0.6
18
+ *
19
+ * @return \WP_REST_Response The response.
20
+ */
21
+ public static function fixBlankFormats() {
22
+ $oldOptions = ( new CommonMigration\OldOptions() )->oldOptions;
23
+ if ( ! $oldOptions ) {
24
+ return new \WP_REST_Response( [
25
+ 'success' => true,
26
+ 'message' => 'Could not load v3 options.'
27
+ ], 400 );
28
+ }
29
+
30
+ $postTypes = aioseo()->helpers->getPublicPostTypes( true );
31
+ $taxonomies = aioseo()->helpers->getPublicTaxonomies( true );
32
+ foreach ( $oldOptions as $k => $v ) {
33
+ if ( ! preg_match( '/^aiosp_([a-zA-Z]*)_title_format$/', $k, $match ) || ! empty( $v ) ) {
34
+ continue;
35
+ }
36
+
37
+ $objectName = $match[1];
38
+ if ( in_array( $objectName, $postTypes, true ) && aioseo()->options->searchAppearance->dynamic->postTypes->has( $objectName ) ) {
39
+ aioseo()->options->searchAppearance->dynamic->postTypes->$objectName->title = '#post_title #separator_sa #site_title';
40
+ continue;
41
+ }
42
+
43
+ if ( in_array( $objectName, $taxonomies, true ) && aioseo()->options->searchAppearance->dynamic->taxonomies->has( $objectName ) ) {
44
+ aioseo()->options->searchAppearance->dynamic->taxonomies->$objectName->title = '#taxonomy_title #separator_sa #site_title';
45
+ }
46
+ }
47
+
48
+ aioseo()->migration->redoMetaMigration();
49
+
50
+ Models\Notification::deleteNotificationByName( 'v3-migration-title-formats-blank' );
51
+
52
+ return new \WP_REST_Response( [
53
+ 'success' => true,
54
+ 'message' => 'Title formats have been reset; post/term migration has been scheduled.',
55
+ 'notifications' => [
56
+ 'active' => Models\Notification::getAllActiveNotifications(),
57
+ 'dismissed' => Models\Notification::getAllDismissedNotifications()
58
+ ]
59
+ ], 200 );
60
+ }
61
+ }
app/Common/Api/Notifications.php ADDED
@@ -0,0 +1,206 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Api;
3
+
4
+ use AIOSEO\Plugin\Common\Models;
5
+
6
+ /**
7
+ * Route class for the API.
8
+ *
9
+ * @since 4.0.0
10
+ */
11
+ class Notifications {
12
+ /**
13
+ * Extend the start date of a notice.
14
+ *
15
+ * @since 4.0.0
16
+ *
17
+ * @param \WP_REST_Request $request The REST Request
18
+ * @return \WP_REST_Response The response.
19
+ */
20
+ public static function blogVisibilityReminder() {
21
+ return self::reminder( 'blog-visibility' );
22
+ }
23
+
24
+ /**
25
+ * Extend the start date of a notice.
26
+ *
27
+ * @since 4.0.5
28
+ *
29
+ * @param \WP_REST_Request $request The REST Request
30
+ * @return \WP_REST_Response The response.
31
+ */
32
+ public static function descriptionFormatReminder() {
33
+ return self::reminder( 'description-format' );
34
+ }
35
+
36
+ /**
37
+ * Extend the start date of a notice.
38
+ *
39
+ * @since 4.0.0
40
+ *
41
+ * @param \WP_REST_Request $request The REST Request
42
+ * @return \WP_REST_Response The response.
43
+ */
44
+ public static function installMiReminder() {
45
+ return self::reminder( 'install-mi' );
46
+ }
47
+
48
+ /**
49
+ * Extend the start date of a notice.
50
+ *
51
+ * @since 4.0.0
52
+ *
53
+ * @param \WP_REST_Request $request The REST Request
54
+ * @return \WP_REST_Response The response.
55
+ */
56
+ public static function installAddonsReminder() {
57
+ return self::reminder( 'install-addons' );
58
+ }
59
+
60
+ /**
61
+ * Extend the start date of a notice.
62
+ *
63
+ * @since 4.0.0
64
+ *
65
+ * @param \WP_REST_Request $request The REST Request
66
+ * @return \WP_REST_Response The response.
67
+ */
68
+ public static function installImageSeoReminder() {
69
+ return self::reminder( 'install-aioseo-image-seo' );
70
+ }
71
+
72
+ /**
73
+ * Extend the start date of a notice.
74
+ *
75
+ * @since 4.0.0
76
+ *
77
+ * @param \WP_REST_Request $request The REST Request
78
+ * @return \WP_REST_Response The response.
79
+ */
80
+ public static function installLocalBusinessReminder() {
81
+ return self::reminder( 'install-aioseo-local-business' );
82
+ }
83
+
84
+ /**
85
+ * Extend the start date of a notice.
86
+ *
87
+ * @since 4.0.0
88
+ *
89
+ * @param \WP_REST_Request $request The REST Request
90
+ * @return \WP_REST_Response The response.
91
+ */
92
+ public static function installNewsSitemapReminder() {
93
+ return self::reminder( 'install-aioseo-news-sitemap' );
94
+ }
95
+
96
+ /**
97
+ * Extend the start date of a notice.
98
+ *
99
+ * @since 4.0.0
100
+ *
101
+ * @param \WP_REST_Request $request The REST Request
102
+ * @return \WP_REST_Response The response.
103
+ */
104
+ public static function installVideoSitemapReminder() {
105
+ return self::reminder( 'install-aioseo-video-sitemap' );
106
+ }
107
+
108
+ /**
109
+ * Extend the start date of a notice.
110
+ *
111
+ * @since 4.0.0
112
+ *
113
+ * @param \WP_REST_Request $request The REST Request
114
+ * @return \WP_REST_Response The response.
115
+ */
116
+ public static function conflictingPluginsReminder() {
117
+ return self::reminder( 'conflicting-plugins' );
118
+ }
119
+
120
+ /**
121
+ * Extend the start date of a notice.
122
+ *
123
+ * @since 4.0.0
124
+ *
125
+ * @param \WP_REST_Request $request The REST Request
126
+ * @return \WP_REST_Response The response.
127
+ */
128
+ public static function deprecatedFiltersReminder() {
129
+ return self::reminder( 'deprecated-filters' );
130
+ }
131
+
132
+ /**
133
+ * Extend the start date of a notice.
134
+ *
135
+ * @since 4.0.0
136
+ *
137
+ * @param \WP_REST_Request $request The REST Request
138
+ * @return \WP_REST_Response The response.
139
+ */
140
+ public static function migrationCustomFieldReminder() {
141
+ return self::reminder( 'v3-migration-custom-field' );
142
+ }
143
+
144
+ /**
145
+ * Extend the start date of a notice.
146
+ *
147
+ * @since 4.0.0
148
+ *
149
+ * @param \WP_REST_Request $request The REST Request
150
+ * @return \WP_REST_Response The response.
151
+ */
152
+ public static function migrationSchemaNumberReminder() {
153
+ return self::reminder( 'v3-migration-schema-number' );
154
+ }
155
+
156
+ /**
157
+ * This allows us to not repeat code over and over.
158
+ *
159
+ * @since 4.0.0
160
+ *
161
+ * @param string $slug The slug of the reminder.
162
+ * @return @return \WP_REST_Response The response.
163
+ */
164
+ protected static function reminder( $slug ) {
165
+ aioseo()->notices->remindMeLater( $slug );
166
+
167
+ return new \WP_REST_Response( [
168
+ 'success' => true,
169
+ 'notifications' => [
170
+ 'active' => Models\Notification::getAllActiveNotifications(),
171
+ 'dismissed' => Models\Notification::getAllDismissedNotifications()
172
+ ]
173
+ ], 200 );
174
+ }
175
+
176
+ /**
177
+ * Dismiss notifications.
178
+ *
179
+ * @since 4.0.0
180
+ *
181
+ * @param \WP_REST_Request $request The REST Request
182
+ * @return \WP_REST_Response The response.
183
+ */
184
+ public static function dismissNotifications( $request ) {
185
+ $slugs = $request->get_json_params();
186
+
187
+ $notifications = aioseo()->db
188
+ ->start( 'aioseo_notifications' )
189
+ ->whereIn( 'slug', $slugs )
190
+ ->run()
191
+ ->models( 'AIOSEO\\Plugin\\Common\\Models\\Notification' );
192
+
193
+ foreach ( $notifications as $notification ) {
194
+ $notification->dismissed = 1;
195
+ $notification->save();
196
+ }
197
+
198
+ return new \WP_REST_Response( [
199
+ 'success' => true,
200
+ 'notifications' => [
201
+ 'active' => Models\Notification::getAllActiveNotifications(),
202
+ 'dismissed' => Models\Notification::getAllDismissedNotifications()
203
+ ]
204
+ ], 200 );
205
+ }
206
+ }
app/Common/Api/Ping.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Api;
3
+
4
+ /**
5
+ * Route class for the API.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class Ping {
10
+ /**
11
+ * Returns a success if the API is alive.
12
+ *
13
+ * @since 4.0.0
14
+ *
15
+ * @param \WP_REST_Request $request The REST Request
16
+ * @return \WP_REST_Response The response.
17
+ */
18
+ public static function ping() {
19
+ return new \WP_REST_Response( [
20
+ 'success' => true
21
+ ], 200 );
22
+ }
23
+ }
app/Common/Api/Plugins.php ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Api;
3
+
4
+ /**
5
+ * Route class for the API.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class Plugins {
10
+ /**
11
+ * Installs plugins from vue.
12
+ *
13
+ * @since 4.0.0
14
+ *
15
+ * @param \WP_REST_Request $request The REST Request
16
+ * @return \WP_REST_Response The response.
17
+ */
18
+ public static function installPlugins( $request ) {
19
+ $error = esc_html__( 'Installation failed. Please check permissions and try again.', 'all-in-one-seo-pack' );
20
+ $body = $request->get_json_params();
21
+
22
+ if ( ! is_array( $body ) ) {
23
+ return new \WP_REST_Response( [
24
+ 'success' => false,
25
+ 'message' => $error
26
+ ], 400 );
27
+ }
28
+
29
+ if ( ! current_user_can( 'install_plugins' ) ) {
30
+ return new \WP_REST_Response( [
31
+ 'success' => false,
32
+ 'message' => $error
33
+ ], 400 );
34
+ }
35
+
36
+ $failed = [];
37
+ $completed = [];
38
+ foreach ( $body as $plugin ) {
39
+ if ( empty( $plugin['plugin'] ) ) {
40
+ return new \WP_REST_Response( [
41
+ 'success' => false,
42
+ 'message' => $error
43
+ ], 400 );
44
+ }
45
+
46
+ $result = aioseo()->addons->installAddon( $plugin['plugin'] );
47
+ if ( ! $result ) {
48
+ $failed[] = $plugin['plugin'];
49
+ } else {
50
+ $completed[ $plugin['plugin'] ] = $result;
51
+ }
52
+ }
53
+
54
+ return new \WP_REST_Response( [
55
+ 'success' => true,
56
+ 'completed' => $completed,
57
+ 'failed' => $failed
58
+ ], 200 );
59
+ }
60
+
61
+ /**
62
+ * Deactivates plugins from vue.
63
+ *
64
+ * @since 4.0.0
65
+ *
66
+ * @param \WP_REST_Request $request The REST Request
67
+ * @return \WP_REST_Response The response.
68
+ */
69
+ public static function deactivatePlugins( $request ) {
70
+ $error = esc_html__( 'Deactivation failed. Please check permissions and try again.', 'all-in-one-seo-pack' );
71
+ $body = $request->get_json_params();
72
+
73
+ if ( ! is_array( $body ) ) {
74
+ return new \WP_REST_Response( [
75
+ 'success' => false,
76
+ 'message' => $error
77
+ ], 400 );
78
+ }
79
+
80
+ if ( ! current_user_can( 'install_plugins' ) ) {
81
+ return new \WP_REST_Response( [
82
+ 'success' => false,
83
+ 'message' => $error
84
+ ], 400 );
85
+ }
86
+
87
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
88
+
89
+ $failed = [];
90
+ $completed = [];
91
+ foreach ( $body as $plugin ) {
92
+ if ( empty( $plugin['plugin'] ) ) {
93
+ return new \WP_REST_Response( [
94
+ 'success' => false,
95
+ 'message' => $error
96
+ ], 400 );
97
+ }
98
+
99
+ // Activate the plugin silently.
100
+ $activated = deactivate_plugins( $plugin['plugin'] );
101
+
102
+ if ( is_wp_error( $activated ) ) {
103
+ $failed[] = $plugin['plugin'];
104
+ }
105
+
106
+ $completed[] = $plugin['plugin'];
107
+ }
108
+
109
+ return new \WP_REST_Response( [
110
+ 'success' => true,
111
+ 'completed' => $completed,
112
+ 'failed' => $failed
113
+ ], 200 );
114
+ }
115
+ }
app/Common/Api/PostsTerms.php ADDED
@@ -0,0 +1,320 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Api;
3
+
4
+ use AIOSEO\Plugin\Common\Models;
5
+
6
+ /**
7
+ * Route class for the API.
8
+ *
9
+ * @since 4.0.0
10
+ */
11
+ class PostsTerms {
12
+ /**
13
+ * Searches for posts or terms by ID/name.
14
+ *
15
+ * @since 4.0.0
16
+ *
17
+ * @param \WP_REST_Request $request The REST Request
18
+ * @return \WP_REST_Response The response.
19
+ */
20
+ public static function searchForObjects( $request ) {
21
+ $body = $request->get_json_params();
22
+
23
+ if ( empty( $body['query'] ) ) {
24
+ return new \WP_REST_Response( [
25
+ 'success' => false,
26
+ 'message' => 'No search term was provided.'
27
+ ], 400 );
28
+ }
29
+ if ( empty( $body['type'] ) ) {
30
+ return new \WP_REST_Response( [
31
+ 'success' => false,
32
+ 'message' => 'No type was provided.'
33
+ ], 400 );
34
+ }
35
+
36
+ $objects = [];
37
+ $options = aioseo()->options->noConflict();
38
+ if ( 'posts' === $body['type'] ) {
39
+
40
+ $postTypes = aioseo()->helpers->getPublicPostTypes( true );
41
+ foreach ( $postTypes as $postType ) {
42
+ // Check if post type isn't noindexed.
43
+ if ( $options->searchAppearance->dynamic->postTypes->has( $postType ) && ! $options->searchAppearance->dynamic->postTypes->$postType->show ) {
44
+ $postTypes = aioseo()->helpers->unsetValue( $postTypes, $postType );
45
+ }
46
+ }
47
+
48
+ $objects = get_posts( [
49
+ 's' => $body['query'],
50
+ 'numberposts' => 20,
51
+ 'post_status' => [ 'publish', 'draft' ],
52
+ 'post_type' => $postTypes,
53
+ 'orderby' => 'post_title'
54
+ ] );
55
+ } elseif ( 'terms' === $body['type'] ) {
56
+
57
+ $taxonomies = aioseo()->helpers->getPublicTaxonomies( true );
58
+ foreach ( $taxonomies as $taxonomy ) {
59
+ // Check if taxonomy isn't noindexed.
60
+ if ( $options->searchAppearance->dynamic->taxonomies->has( $taxonomy ) && ! $options->searchAppearance->dynamic->taxonomies->$taxonomy->show ) {
61
+ $taxonomies = aioseo()->helpers->unsetValue( $taxonomies, $taxonomy );
62
+ }
63
+ }
64
+
65
+ $objects = get_terms( [
66
+ 'name__like' => $body['query'],
67
+ 'number' => 20,
68
+ 'taxonomy' => $taxonomies,
69
+ 'orderby' => 'name'
70
+ ] );
71
+ }
72
+
73
+ if ( empty( $objects ) ) {
74
+ return new \WP_REST_Response( [
75
+ 'success' => true,
76
+ 'objects' => []
77
+ ], 200 );
78
+ }
79
+
80
+ $parsed = [];
81
+ foreach ( $objects as $object ) {
82
+ if ( 'posts' === $body['type'] ) {
83
+ $parsed[] = [
84
+ 'type' => $object->post_type,
85
+ 'value' => $object->ID,
86
+ 'label' => $object->post_title,
87
+ 'link' => get_permalink( $object->ID )
88
+ ];
89
+ } elseif ( 'terms' === $body['type'] ) {
90
+ $parsed[] = [
91
+ 'type' => $object->taxonomy,
92
+ 'value' => $object->term_id,
93
+ 'label' => $object->name,
94
+ 'link' => get_term_link( $object->term_id )
95
+ ];
96
+ }
97
+ }
98
+
99
+ return new \WP_REST_Response( [
100
+ 'success' => true,
101
+ 'objects' => $parsed
102
+ ], 200 );
103
+ }
104
+
105
+ /**
106
+ * Get post data for fetch requests
107
+ *
108
+ * @since 4.0.0
109
+ *
110
+ * @param \WP_REST_Request $request The REST Request
111
+ * @return \WP_REST_Response The response.
112
+ */
113
+ public static function getPostData( $request ) {
114
+ $args = $request->get_query_params();
115
+
116
+ if ( empty( $args['postId'] ) ) {
117
+ return new \WP_REST_Response( [
118
+ 'success' => false,
119
+ 'message' => __( 'No post ID was provided.', 'all-in-one-seo-pack' )
120
+ ], 400 );
121
+ }
122
+
123
+ $thePost = aioseo()->db
124
+ ->start( 'aioseo_posts' )
125
+ ->where( 'post_id', $args['postId'] )
126
+ ->run()
127
+ ->model( 'AIOSEO\\Plugin\\Common\\Models\\Post' );
128
+
129
+ return new \WP_REST_Response( [
130
+ 'success' => true,
131
+ 'post' => $thePost,
132
+ 'postData' => [
133
+ 'parsedTitle' => aioseo()->tags->replaceTags( $thePost->title, $args['postId'] ),
134
+ 'parsedDescription' => aioseo()->tags->replaceTags( $thePost->description, $args['postId'] ),
135
+ 'content' => aioseo()->helpers->doShortcodes( aioseo()->helpers->getAnalysisContent( $args['postId'] ) ),
136
+ 'slug' => get_post_field( 'post_name', $args['postId'] )
137
+ ]
138
+ ], 200 );
139
+ }
140
+
141
+ /**
142
+ * Update post settings.
143
+ *
144
+ * @since 4.0.0
145
+ *
146
+ * @param \WP_REST_Request $request The REST Request
147
+ * @return \WP_REST_Response The response.
148
+ */
149
+ public static function updatePosts( $request ) {
150
+ $body = $request->get_json_params();
151
+ $postId = ! empty( $body['id'] ) ? intval( $body['id'] ) : null;
152
+
153
+ if ( ! $postId ) {
154
+ return new \WP_REST_Response( [
155
+ 'success' => false,
156
+ 'message' => __( 'Post ID is missing.', 'all-in-one-seo-pack' )
157
+ ], 400 );
158
+ }
159
+
160
+ $body['id'] = $postId;
161
+ $body['title'] = ! empty( $body['title'] ) ? sanitize_text_field( $body['title'] ) : null;
162
+ $body['description'] = ! empty( $body['description'] ) ? sanitize_text_field( $body['description'] ) : null;
163
+ $body['keywords'] = ! empty( $body['keywords'] ) ? sanitize_text_field( $body['keywords'] ) : null;
164
+ $body['og_title'] = ! empty( $body['og_title'] ) ? sanitize_text_field( $body['og_title'] ) : null;
165
+ $body['og_description'] = ! empty( $body['og_description'] ) ? sanitize_text_field( $body['og_description'] ) : null;
166
+ $body['og_article_section'] = ! empty( $body['og_article_section'] ) ? sanitize_text_field( $body['og_article_section'] ) : null;
167
+ $body['og_article_tags'] = ! empty( $body['og_article_tags'] ) ? sanitize_text_field( $body['og_article_tags'] ) : null;
168
+ $body['twitter_title'] = ! empty( $body['twitter_title'] ) ? sanitize_text_field( $body['twitter_title'] ) : null;
169
+ $body['twitter_description'] = ! empty( $body['twitter_description'] ) ? sanitize_text_field( $body['twitter_description'] ) : null;
170
+
171
+ $saveStatus = Models\Post::savePost( $postId, $body );
172
+
173
+ if ( ! empty( $saveStatus ) ) {
174
+ return new \WP_REST_Response( [
175
+ 'success' => false,
176
+ 'message' => 'Failed update query: ' . $saveStatus
177
+ ], 401 );
178
+ }
179
+
180
+ return new \WP_REST_Response( [
181
+ 'success' => true,
182
+ 'posts' => $postId
183
+ ], 200 );
184
+ }
185
+
186
+ /**
187
+ * Update post settings from Post screen.
188
+ *
189
+ * @since 4.0.0
190
+ *
191
+ * @param \WP_REST_Request $request The REST Request
192
+ * @return \WP_REST_Response The response.
193
+ */
194
+ public static function updatePostFromScreen( $request ) {
195
+ $body = $request->get_json_params();
196
+ $postId = ! empty( $body['postId'] ) ? intval( $body['postId'] ) : null;
197
+ $isMedia = isset( $body['isMedia'] ) ? true : false;
198
+ $post = aioseo()->helpers->getPost( $postId );
199
+
200
+ if ( ! $postId ) {
201
+ return new \WP_REST_Response( [
202
+ 'success' => false,
203
+ 'message' => 'Post ID is missing.'
204
+ ], 400 );
205
+ }
206
+
207
+ $thePost = aioseo()->db
208
+ ->start( 'aioseo_posts' )
209
+ ->where( 'post_id', $postId )
210
+ ->run()
211
+ ->model( 'AIOSEO\\Plugin\\Common\\Models\\Post' );
212
+
213
+ if ( $thePost->exists() ) {
214
+ $metaTitle = aioseo()->meta->title->getPostTypeTitle( $post->post_type );
215
+ if ( empty( $thePost->title ) && ! empty( $body['title'] ) && trim( $body['title'] ) === trim( $metaTitle ) ) {
216
+ $body['title'] = null;
217
+ }
218
+ $thePost->title = ! empty( $body['title'] ) ? sanitize_text_field( $body['title'] ) : null;
219
+
220
+ $metaDescription = aioseo()->meta->description->getPostTypeDescription( $post->post_type );
221
+ if ( empty( $thePost->description ) && ! empty( $body['description'] ) && trim( $body['description'] ) === trim( $metaDescription ) ) {
222
+ $body['description'] = null;
223
+ }
224
+ $thePost->description = ! empty( $body['description'] ) ? sanitize_text_field( $body['description'] ) : '';
225
+ $thePost->updated = gmdate( 'Y-m-d H:i:s' );
226
+ if ( $isMedia ) {
227
+ wp_update_post(
228
+ [
229
+ 'ID' => $postId,
230
+ 'post_title' => sanitize_text_field( $body['imageTitle'] ),
231
+ ]
232
+ );
233
+ update_post_meta( $postId, '_wp_attachment_image_alt', sanitize_text_field( $body['imageAltTag'] ) );
234
+ }
235
+ } else {
236
+ $thePost->post_id = $postId;
237
+ $thePost->title = ! empty( $body['title'] ) ? sanitize_text_field( $body['title'] ) : '';
238
+ $thePost->description = ! empty( $body['description'] ) ? sanitize_text_field( $body['description'] ) : null;
239
+ $thePost->created = gmdate( 'Y-m-d H:i:s' );
240
+ $thePost->updated = gmdate( 'Y-m-d H:i:s' );
241
+ if ( $isMedia ) {
242
+ wp_update_post(
243
+ [
244
+ 'ID' => $postId,
245
+ 'post_title' => sanitize_text_field( $body['imageTitle'] ),
246
+ ]
247
+ );
248
+ update_post_meta( $postId, '_wp_attachment_image_alt', sanitize_text_field( $body['imageAltTag'] ) );
249
+ }
250
+ }
251
+ $thePost->save();
252
+
253
+ $lastError = aioseo()->db->lastError();
254
+ if ( ! empty( $lastError ) ) {
255
+ return new \WP_REST_Response( [
256
+ 'success' => false,
257
+ 'message' => 'Failed update query: ' . $lastError
258
+ ], 401 );
259
+ }
260
+
261
+ return new \WP_REST_Response( [
262
+ 'success' => true,
263
+ 'posts' => $postId,
264
+ 'title' => aioseo()->meta->title->getPostTitle( $postId ),
265
+ 'description' => aioseo()->meta->description->getPostDescription( $postId )
266
+ ], 200 );
267
+ }
268
+
269
+ /**
270
+ * Update post keyphrases.
271
+ *
272
+ * @since 4.0.0
273
+ *
274
+ * @param \WP_REST_Request $request The REST Request
275
+ * @return \WP_REST_Response The response.
276
+ */
277
+ public static function updatePostKeyphrases( $request ) {
278
+ $body = $request->get_json_params();
279
+ $postId = ! empty( $body['postId'] ) ? intval( $body['postId'] ) : null;
280
+
281
+ if ( ! $postId ) {
282
+ return new \WP_REST_Response( [
283
+ 'success' => false,
284
+ 'message' => 'Post ID is missing.'
285
+ ], 400 );
286
+ }
287
+
288
+ $thePost = aioseo()->db
289
+ ->start( 'aioseo_posts' )
290
+ ->where( 'post_id', $postId )
291
+ ->run()
292
+ ->model( 'AIOSEO\\Plugin\\Common\\Models\\Post' );
293
+
294
+ $thePost->post_id = $postId;
295
+ if ( ! empty( $body['keyphrases'] ) ) {
296
+ $thePost->keyphrases = wp_json_encode( $body['keyphrases'] );
297
+ }
298
+ if ( ! empty( $body['page_analysis'] ) ) {
299
+ $thePost->page_analysis = wp_json_encode( $body['page_analysis'] );
300
+ }
301
+ if ( ! empty( $body['seo_score'] ) ) {
302
+ $thePost->seo_score = sanitize_text_field( $body['seo_score'] );
303
+ }
304
+ $thePost->updated = gmdate( 'Y-m-d H:i:s' );
305
+ $thePost->save();
306
+
307
+ $lastError = aioseo()->db->lastError();
308
+ if ( ! empty( $lastError ) ) {
309
+ return new \WP_REST_Response( [
310
+ 'success' => false,
311
+ 'message' => 'Failed update query: ' . $lastError
312
+ ], 401 );
313
+ }
314
+
315
+ return new \WP_REST_Response( [
316
+ 'success' => true,
317
+ 'post' => $postId
318
+ ], 200 );
319
+ }
320
+ }
app/Common/Api/Settings.php ADDED
@@ -0,0 +1,322 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Api;
3
+
4
+ use AIOSEO\Plugin\Common\Models;
5
+
6
+ /**
7
+ * Route class for the API.
8
+ *
9
+ * @since 4.0.0
10
+ */
11
+ class Settings {
12
+ /**
13
+ * Update the settings.
14
+ *
15
+ * @since 4.0.0
16
+ *
17
+ * @param \WP_REST_Request $request The REST Request
18
+ * @return \WP_REST_Response The response.
19
+ */
20
+ public static function getOptions() {
21
+ return new \WP_REST_Response( [
22
+ 'options' => aioseo()->options->all(),
23
+ 'settings' => aioseo()->settings->all()
24
+ ], 200 );
25
+ }
26
+
27
+ /**
28
+ * Toggles a card in the settings.
29
+ *
30
+ * @since 4.0.0
31
+ *
32
+ * @param \WP_REST_Request $request The REST Request
33
+ * @return \WP_REST_Response The response.
34
+ */
35
+ public static function toggleCard( $request ) {
36
+ $body = $request->get_json_params();
37
+ $card = ! empty( $body['card'] ) ? sanitize_text_field( $body['card'] ) : null;
38
+ $cards = aioseo()->settings->toggledCards;
39
+ if ( array_key_exists( $card, $cards ) ) {
40
+ $cards[ $card ] = ! $cards[ $card ];
41
+ aioseo()->settings->toggledCards = $cards;
42
+ }
43
+
44
+ return new \WP_REST_Response( [
45
+ 'success' => true
46
+ ], 200 );
47
+ }
48
+
49
+ /**
50
+ * Toggles a radio in the settings.
51
+ *
52
+ * @since 4.0.0
53
+ *
54
+ * @param \WP_REST_Request $request The REST Request
55
+ * @return \WP_REST_Response The response.
56
+ */
57
+ public static function toggleRadio( $request ) {
58
+ $body = $request->get_json_params();
59
+ $radio = ! empty( $body['radio'] ) ? sanitize_text_field( $body['radio'] ) : null;
60
+ $value = ! empty( $body['value'] ) ? sanitize_text_field( $body['value'] ) : null;
61
+ $radios = aioseo()->settings->toggledRadio;
62
+ if ( array_key_exists( $radio, $radios ) ) {
63
+ $radios[ $radio ] = $value;
64
+ aioseo()->settings->toggledRadio = $radios;
65
+ }
66
+
67
+ return new \WP_REST_Response( [
68
+ 'success' => true
69
+ ], 200 );
70
+ }
71
+
72
+ /**
73
+ * Dismisses the upgrade bar.
74
+ *
75
+ * @since 4.0.0
76
+ *
77
+ * @param \WP_REST_Request $request The REST Request
78
+ * @return \WP_REST_Response The response.
79
+ */
80
+ public static function hideUpgradeBar() {
81
+ aioseo()->settings->showUpgradeBar = false;
82
+
83
+ return new \WP_REST_Response( [
84
+ 'success' => true
85
+ ], 200 );
86
+ }
87
+
88
+ /**
89
+ * Hides the Setup Wizard CTA.
90
+ *
91
+ * @since 4.0.0
92
+ *
93
+ * @param \WP_REST_Request $request The REST Request
94
+ * @return \WP_REST_Response The response.
95
+ */
96
+ public static function hideSetupWizard() {
97
+ aioseo()->settings->showSetupWizard = false;
98
+
99
+ return new \WP_REST_Response( [
100
+ 'success' => true
101
+ ], 200 );
102
+ }
103
+
104
+ /**
105
+ * Save options from the front end.
106
+ *
107
+ * @since 4.0.0
108
+ *
109
+ * @param \WP_REST_Request $request The REST Request
110
+ * @return \WP_REST_Response The response.
111
+ */
112
+ public static function saveChanges( $request ) {
113
+ $body = $request->get_json_params();
114
+ $options = ! empty( $body['options'] ) ? $body['options'] : [];
115
+ $network = ! empty( $body['network'] ) ? (bool) $body['network'] : false;
116
+
117
+ // If this is the network admin, reset the options.
118
+ if ( $network ) {
119
+ aioseo()->options->initNetwork();
120
+ }
121
+
122
+ aioseo()->options->sanitizeAndSave( $options );
123
+
124
+ // Re-initialize notices.
125
+ aioseo()->notices->init();
126
+
127
+ return new \WP_REST_Response( [
128
+ 'success' => true,
129
+ 'notifications' => [
130
+ 'active' => Models\Notification::getAllActiveNotifications(),
131
+ 'dismissed' => Models\Notification::getAllDismissedNotifications()
132
+ ]
133
+ ], 200 );
134
+ }
135
+
136
+ /**
137
+ * Reset settings.
138
+ *
139
+ * @since 4.0.0
140
+ *
141
+ * @param \WP_REST_Request $request The REST Request
142
+ * @return \WP_REST_Response The response.
143
+ */
144
+ public static function resetSettings( $request ) {
145
+ $body = $request->get_json_params();
146
+ $settings = ! empty( $body['settings'] ) ? $body['settings'] : [];
147
+
148
+ foreach ( $settings as $setting ) {
149
+ switch ( $setting ) {
150
+ case 'webmaster-tools':
151
+ aioseo()->options->webmasterTools->reset();
152
+ break;
153
+ case 'rss-content':
154
+ aioseo()->options->rssContent->reset();
155
+ break;
156
+ case 'search-appearance':
157
+ aioseo()->options->searchAppearance->reset();
158
+ break;
159
+ case 'access-control':
160
+ aioseo()->options->accessControl->reset();
161
+ aioseo()->access->addCapabilities();
162
+ break;
163
+ case 'advanced':
164
+ aioseo()->options->advanced->reset();
165
+ break;
166
+ case 'social-networks':
167
+ aioseo()->options->social->reset();
168
+ break;
169
+ case 'sitemaps':
170
+ aioseo()->options->sitemap->reset();
171
+ break;
172
+ case 'local-business-seo':
173
+ aioseo()->options->localBusiness->reset();
174
+ break;
175
+ case 'robots-txt':
176
+ aioseo()->options->tools->robots->reset();
177
+ break;
178
+ case 'bad-bot-blocker':
179
+ aioseo()->options->deprecated->tools->blocker->reset();
180
+ break;
181
+ case 'image-seo':
182
+ aioseo()->options->image->reset();
183
+ break;
184
+ }
185
+ }
186
+
187
+ return new \WP_REST_Response( [
188
+ 'success' => true,
189
+ 'options' => aioseo()->options->all()
190
+ ], 200 );
191
+ }
192
+
193
+ /**
194
+ * Import settings from external file.
195
+ *
196
+ * @since 4.0.0
197
+ *
198
+ * @param \WP_REST_Request $request The REST Request
199
+ * @return \WP_REST_Response The response.
200
+ */
201
+ public static function importSettings( $request ) {
202
+ $file = $request->get_file_params()['file'];
203
+ $wpfs = aioseo()->helpers->wpfs();
204
+ $contents = $wpfs->get_contents( $file['tmp_name'] );
205
+ if ( ! empty( $file['type'] ) && 'application/json' === $file['type'] ) {
206
+ // Since this could be any file, we need to pretend like every variable here is missing.
207
+ $contents = json_decode( $contents, true );
208
+ if ( empty( $contents ) ) {
209
+ return new \WP_REST_Response( [
210
+ 'success' => false
211
+ ], 400 );
212
+ }
213
+
214
+ $imported = false;
215
+ if ( ! empty( $contents['settings'] ) ) {
216
+ $imported = true;
217
+ aioseo()->options->sanitizeAndSave( $contents['settings'] );
218
+ }
219
+
220
+ if ( ! empty( $contents['postOptions'] ) ) {
221
+ $imported = true;
222
+ foreach ( $contents['postOptions'] as $postType => $postData ) {
223
+ // Posts.
224
+ if ( ! empty( $postData['posts'] ) ) {
225
+ foreach ( $postData['posts'] as $post ) {
226
+ unset( $post['id'] );
227
+ $thePost = Models\Post::getPost( $post['post_id'] );
228
+ $thePost->set( $post );
229
+ $thePost->save();
230
+ }
231
+ }
232
+ }
233
+ }
234
+
235
+ if ( ! $imported ) {
236
+ return new \WP_REST_Response( [
237
+ 'success' => false
238
+ ], 400 );
239
+ }
240
+ }
241
+
242
+ if ( ! empty( $file['type'] ) && 'application/octet-stream' === $file['type'] ) {
243
+ $response = aioseo()->importExport->importIniData( $contents );
244
+ if ( ! $response ) {
245
+ return new \WP_REST_Response( [
246
+ 'success' => false
247
+ ], 400 );
248
+ }
249
+ }
250
+
251
+ return new \WP_REST_Response( [
252
+ 'success' => true,
253
+ 'options' => aioseo()->options->all()
254
+ ], 200 );
255
+ }
256
+
257
+ /**
258
+ * Export settings.
259
+ *
260
+ * @since 4.0.0
261
+ *
262
+ * @param \WP_REST_Request $request The REST Request
263
+ * @return \WP_REST_Response The response.
264
+ */
265
+ public static function exportSettings( $request ) {
266
+ $body = $request->get_json_params();
267
+ $settings = ! empty( $body['settings'] ) ? $body['settings'] : [];
268
+ $postOptions = ! empty( $body['postOptions'] ) ? $body['postOptions'] : [];
269
+ $allSettings = [
270
+ 'settings' => [],
271
+ 'postOptions' => []
272
+ ];
273
+
274
+ if ( ! empty( $settings ) ) {
275
+ $options = aioseo()->options->noConflict();
276
+ foreach ( $settings as $setting ) {
277
+ if ( $options->has( $setting ) ) {
278
+ $allSettings['settings'][ $setting ] = $options->$setting->all();
279
+ }
280
+ }
281
+ }
282
+
283
+ if ( ! empty( $postOptions ) ) {
284
+ foreach ( $postOptions as $postType ) {
285
+ $allSettings['postOptions'][ $postType ] = [
286
+ 'posts' => aioseo()->db->start( 'aioseo_posts as ap' )
287
+ ->select( 'ap.*' )
288
+ ->join( 'posts as p', 'ap.post_id = p.ID' )
289
+ ->where( 'p.post_type', $postType )
290
+ ->run()
291
+ ->result()
292
+ ];
293
+ }
294
+ }
295
+
296
+ return new \WP_REST_Response( [
297
+ 'success' => true,
298
+ 'settings' => $allSettings
299
+ ], 200 );
300
+ }
301
+
302
+ /**
303
+ * Import other plugin settings.
304
+ *
305
+ * @since 4.0.0
306
+ *
307
+ * @param \WP_REST_Request $request The REST Request
308
+ * @return \WP_REST_Response The response.
309
+ */
310
+ public static function importPlugins( $request ) {
311
+ $body = $request->get_json_params();
312
+ $plugins = ! empty( $body['plugins'] ) ? $body['plugins'] : [];
313
+
314
+ foreach ( $plugins as $plugin ) {
315
+ aioseo()->importExport->startImport( $plugin['plugin'], $plugin['settings'] );
316
+ }
317
+
318
+ return new \WP_REST_Response( [
319
+ 'success' => true
320
+ ], 200 );
321
+ }
322
+ }
app/Common/Api/Sitemaps.php ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Api;
3
+
4
+ use AIOSEO\Plugin\Common\Models;
5
+
6
+ /**
7
+ * Route class for the API.
8
+ *
9
+ * @since 4.0.0
10
+ */
11
+ class Sitemaps {
12
+ /**
13
+ * Delete all static sitemap files.
14
+ *
15
+ * @since 4.0.0
16
+ *
17
+ * @return \WP_REST_Response The response.
18
+ */
19
+ public static function deleteStaticFiles() {
20
+ require_once( ABSPATH . 'wp-admin/includes/file.php' );
21
+ $files = list_files( get_home_path(), 1 );
22
+ if ( ! count( $files ) ) {
23
+ return;
24
+ }
25
+
26
+ $isGeneralSitemapStatic = aioseo()->options->sitemap->general->advancedSettings->enable &&
27
+ in_array( 'staticSitemap', aioseo()->internalOptions->internal->deprecatedOptions, true ) &&
28
+ ! aioseo()->options->deprecated->sitemap->general->advancedSettings->dynamic;
29
+
30
+ $detectedFiles = [];
31
+ if ( ! $isGeneralSitemapStatic ) {
32
+ foreach ( $files as $index => $filename ) {
33
+ if ( preg_match( '#.*sitemap.*#', $filename ) ) {
34
+ // We don't want to delete the video sitemap here at all.
35
+ $isVideoSitemap = preg_match( '#.*video.*#', $filename ) ? true : false;
36
+ if ( ! $isVideoSitemap ) {
37
+ $detectedFiles[] = $filename;
38
+ }
39
+ }
40
+ }
41
+ }
42
+
43
+ if ( ! count( $detectedFiles ) ) {
44
+ return new \WP_REST_Response( [
45
+ 'success' => false,
46
+ 'message' => 'No sitemap files found.'
47
+ ], 400 );
48
+ }
49
+
50
+ $wpfs = aioseo()->helpers->wpfs();
51
+ if ( ! is_object( $wpfs ) ) {
52
+ return new \WP_REST_Response( [
53
+ 'success' => false,
54
+ 'message' => 'No access to filesystem.'
55
+ ], 400 );
56
+ }
57
+
58
+ foreach ( $detectedFiles as $file ) {
59
+ $wpfs->delete( $file, false, 'f' );
60
+ }
61
+
62
+ Models\Notification::deleteNotificationByName( 'sitemap-static-files' );
63
+
64
+ return new \WP_REST_Response( [
65
+ 'success' => true,
66
+ 'notifications' => [
67
+ 'active' => Models\Notification::getAllActiveNotifications(),
68
+ 'dismissed' => Models\Notification::getAllDismissedNotifications()
69
+ ]
70
+ ], 200 );
71
+ }
72
+
73
+ /**
74
+ * Deactivates conflicting plugins.
75
+ *
76
+ * @since 4.0.0
77
+ *
78
+ * @param \WP_REST_Request $request The REST Request
79
+ * @return \WP_REST_Response The response.
80
+ */
81
+ public static function deactivateConflictingPlugins() {
82
+ $error = esc_html__( 'Deactivation failed. Please check permissions and try again.', 'all-in-one-seo-pack' );
83
+ if ( ! current_user_can( 'install_plugins' ) ) {
84
+ return new \WP_REST_Response( [
85
+ 'success' => false,
86
+ 'message' => $error
87
+ ], 400 );
88
+ }
89
+
90
+ $plugins = array_merge(
91
+ aioseo()->conflictingPlugins->getConflictingPlugins( 'seo' ),
92
+ aioseo()->conflictingPlugins->getConflictingPlugins( 'sitemap' )
93
+ );
94
+
95
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
96
+
97
+ foreach ( $plugins as $pluginPath ) {
98
+ if ( is_plugin_active( $pluginPath ) ) {
99
+ deactivate_plugins( $pluginPath );
100
+ }
101
+ }
102
+
103
+ Models\Notification::deleteNotificationByName( 'conflicting-plugins' );
104
+
105
+ return new \WP_REST_Response( [
106
+ 'success' => true,
107
+ 'notifications' => [
108
+ 'active' => Models\Notification::getAllActiveNotifications(),
109
+ 'dismissed' => Models\Notification::getAllDismissedNotifications()
110
+ ]
111
+ ], 200 );
112
+ }
113
+ }
app/Common/Api/Tags.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Api;
3
+
4
+ /**
5
+ * Route class for the API.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class Tags {
10
+ /**
11
+ * Get all Tags.
12
+ *
13
+ * @since 4.0.0
14
+ *
15
+ * @param \WP_REST_Request $request The REST Request
16
+ * @return \WP_REST_Response The response.
17
+ */
18
+ public static function getTags() {
19
+ return new \WP_REST_Response( [
20
+ 'tags' => aioseo()->tags->all( true )
21
+ ], 200 );
22
+ }
23
+ }
app/Common/Api/Tools.php ADDED
@@ -0,0 +1,251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Api;
3
+
4
+ use AIOSEO\Plugin\Common\Models;
5
+ use AIOSEO\Plugin\Common\Tools as CommonTools;
6
+
7
+ /**
8
+ * Route class for the API.
9
+ *
10
+ * @since 4.0.0
11
+ */
12
+ class Tools {
13
+ /**
14
+ * Import and delete the static robots.txt.
15
+ *
16
+ * @since 4.0.0
17
+ *
18
+ * @param \WP_REST_Request $request The REST Request
19
+ * @return \WP_REST_Response The response.
20
+ */
21
+ public static function importRobotsTxt( $request ) {
22
+ $body = $request->get_json_params();
23
+ $network = ! empty( $body['network'] ) ? (bool) $body['network'] : false;
24
+
25
+ if ( ! aioseo()->robotsTxt->importPhysicalRobotsTxt( $network ) ) {
26
+ return new \WP_REST_Response( [
27
+ 'success' => false,
28
+ 'message' => __( 'There was an error importing the physical robots.txt file.', 'all-in-one-seo-pack' )
29
+ ], 400 );
30
+ }
31
+
32
+ aioseo()->options->tools->robots->enable = true;
33
+
34
+ if ( ! aioseo()->robotsTxt->deletePhysicalRobotsTxt() ) {
35
+ return new \WP_REST_Response( [
36
+ 'success' => false,
37
+ 'message' => __( 'There was an error deleting the physical robots.txt file.', 'all-in-one-seo-pack' )
38
+ ], 400 );
39
+ }
40
+
41
+ Models\Notification::deleteNotificationByName( 'robots-physical-file' );
42
+
43
+ return new \WP_REST_Response( [
44
+ 'success' => true,
45
+ 'notifications' => [
46
+ 'active' => Models\Notification::getAllActiveNotifications(),
47
+ 'dismissed' => Models\Notification::getAllDismissedNotifications()
48
+ ]
49
+ ], 200 );
50
+ }
51
+
52
+ /**
53
+ * Delete the static robots.txt.
54
+ *
55
+ * @since 4.0.0
56
+ *
57
+ * @param \WP_REST_Request $request The REST Request
58
+ * @return \WP_REST_Response The response.
59
+ */
60
+ public static function deleteRobotsTxt() {
61
+ if ( ! aioseo()->robotsTxt->deletePhysicalRobotsTxt() ) {
62
+ return new \WP_REST_Response( [
63
+ 'success' => false,
64
+ 'message' => __( 'There was an error deleting the physical robots.txt file.', 'all-in-one-seo-pack' )
65
+ ], 400 );
66
+ }
67
+
68
+ Models\Notification::deleteNotificationByName( 'robots-physical-file' );
69
+
70
+ return new \WP_REST_Response( [
71
+ 'success' => true,
72
+ 'notifications' => [
73
+ 'active' => Models\Notification::getAllActiveNotifications(),
74
+ 'dismissed' => Models\Notification::getAllDismissedNotifications()
75
+ ]
76
+ ], 200 );
77
+ }
78
+
79
+ /**
80
+ * Email debug info.
81
+ *
82
+ * @since 4.0.0
83
+ *
84
+ * @param \WP_REST_Request $request The REST Request
85
+ * @return \WP_REST_Response The response.
86
+ */
87
+ public static function emailDebugInfo( $request ) {
88
+ $body = $request->get_json_params();
89
+ $email = ! empty( $body['email'] ) ? $body['email'] : null;
90
+
91
+ if ( ! filter_var( $email, FILTER_VALIDATE_EMAIL ) ) {
92
+ return new \WP_REST_Response( [
93
+ 'success' => false,
94
+ 'message' => 'invalid-email-address'
95
+ ], 400 );
96
+ }
97
+
98
+ require_once ABSPATH . 'wp-admin/includes/update.php';
99
+
100
+ // Translators: 1 - The plugin name ("All in One SEO"), 2 - The Site URL.
101
+ $html = sprintf( __( '%1$s Debug Info from %2$s', 'all-in-one-seo-pack' ), AIOSEO_PLUGIN_NAME, aioseo()->helpers->getSiteDomain() ) . "\r\n------------------\r\n\r\n";
102
+ $info = CommonTools\SystemStatus::getSystemStatusInfo();
103
+ foreach ( $info as $key => $group ) {
104
+ if ( empty( $group['results'] ) ) {
105
+ continue;
106
+ }
107
+
108
+ $html .= "\r\n\r\n{$group['label']}\r\n";
109
+ foreach ( $group['results'] as $data ) {
110
+ $html .= "{$data['header']}: {$data['value']}\r\n";
111
+ }
112
+ }
113
+
114
+ if ( ! wp_mail(
115
+ $email,
116
+ // Translators: 1 - The plugin name ("All in One SEO).
117
+ sprintf( __( '%1$s Debug Info', 'all-in-one-seo-pack' ), AIOSEO_PLUGIN_NAME ),
118
+ $html
119
+ ) ) {
120
+ return new \WP_REST_Response( [
121
+ 'success' => false,
122
+ 'message' => __( 'Unable to send debug email, please check your email send settings and try again.', 'all-in-one-seo-pack' )
123
+ ], 400 );
124
+ }
125
+
126
+ return new \WP_REST_Response( [
127
+ 'success' => true
128
+ ], 200 );
129
+ }
130
+
131
+ /**
132
+ * Create a settings backup.
133
+ *
134
+ * @since 4.0.0
135
+ *
136
+ * @param \WP_REST_Request $request The REST Request
137
+ * @return \WP_REST_Response The response.
138
+ */
139
+ public static function createBackup() {
140
+ aioseo()->backup->create();
141
+
142
+ return new \WP_REST_Response( [
143
+ 'success' => true,
144
+ 'backups' => array_reverse( aioseo()->backup->all() )
145
+ ], 200 );
146
+ }
147
+
148
+ /**
149
+ * Restore a settings backup.
150
+ *
151
+ * @since 4.0.0
152
+ *
153
+ * @param \WP_REST_Request $request The REST Request
154
+ * @return \WP_REST_Response The response.
155
+ */
156
+ public static function restoreBackup( $request ) {
157
+ $body = $request->get_json_params();
158
+ $backup = ! empty( $body['backup'] ) ? (int) $body['backup'] : null;
159
+ if ( empty( $backup ) ) {
160
+ return new \WP_REST_Response( [
161
+ 'success' => false,
162
+ 'backups' => array_reverse( aioseo()->backup->all() )
163
+ ], 400 );
164
+ }
165
+
166
+ aioseo()->backup->restore( $backup );
167
+
168
+ return new \WP_REST_Response( [
169
+ 'success' => true,
170
+ 'backups' => array_reverse( aioseo()->backup->all() ),
171
+ 'options' => aioseo()->options->all(),
172
+ 'internalOptions' => aioseo()->internalOptions->all()
173
+ ], 200 );
174
+ }
175
+
176
+ /**
177
+ * Delete a settings backup.
178
+ *
179
+ * @since 4.0.0
180
+ *
181
+ * @param \WP_REST_Request $request The REST Request
182
+ * @return \WP_REST_Response The response.
183
+ */
184
+ public static function deleteBackup( $request ) {
185
+ $body = $request->get_json_params();
186
+ $backup = ! empty( $body['backup'] ) ? (int) $body['backup'] : null;
187
+ if ( empty( $backup ) ) {
188
+ return new \WP_REST_Response( [
189
+ 'success' => false,
190
+ 'backups' => array_reverse( aioseo()->backup->all() )
191
+ ], 400 );
192
+ }
193
+
194
+ aioseo()->backup->delete( $backup );
195
+
196
+ return new \WP_REST_Response( [
197
+ 'success' => true,
198
+ 'backups' => array_reverse( aioseo()->backup->all() )
199
+ ], 200 );
200
+ }
201
+
202
+ /**
203
+ * Save the .htaccess file.
204
+ *
205
+ * @since 4.0.0
206
+ *
207
+ * @param \WP_REST_Request $request The REST Request
208
+ * @return \WP_REST_Response The response.
209
+ */
210
+ public static function saveHtaccess( $request ) {
211
+ $body = $request->get_json_params();
212
+ $htaccess = ! empty( $body['htaccess'] ) ? sanitize_textarea_field( $body['htaccess'] ) : '';
213
+
214
+ if ( ! aioseo()->htaccess->saveContents( $htaccess ) ) {
215
+ return new \WP_REST_Response( [
216
+ 'success' => false,
217
+ 'message' => __( 'An error occurred while trying to write to the .htaccess file. Please try again later.', 'all-in-one-seo-pack' )
218
+ ], 400 );
219
+ }
220
+
221
+ return new \WP_REST_Response( [
222
+ 'success' => true
223
+ ], 200 );
224
+ }
225
+
226
+ /**
227
+ * Clear the passed in log.
228
+ *
229
+ * @since 4.0.0
230
+ *
231
+ * @param \WP_REST_Request $request The REST Request
232
+ * @return \WP_REST_Response The response.
233
+ */
234
+ public static function clearLog( $request ) {
235
+ $body = $request->get_json_params();
236
+ $log = ! empty( $body['log'] ) ? $body['log'] : null;
237
+
238
+ $logSize = 0;
239
+ switch ( $log ) {
240
+ case 'badBotBlockerLog':
241
+ aioseo()->badBotBlocker->clearLog();
242
+ break;
243
+
244
+ }
245
+
246
+ return new \WP_REST_Response( [
247
+ 'success' => true,
248
+ 'logSize' => aioseo()->helpers->convertFileSize( $logSize )
249
+ ], 200 );
250
+ }
251
+ }
app/Common/Api/Wizard.php ADDED
@@ -0,0 +1,360 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Api;
3
+
4
+ use AIOSEO\Plugin\Common\Models;
5
+
6
+ /**
7
+ * Route class for the API.
8
+ *
9
+ * @since 4.0.0
10
+ */
11
+ class Wizard {
12
+ /**
13
+ * Save the wizard information.
14
+ *
15
+ * @since 4.0.0
16
+ *
17
+ * @param \WP_REST_Request $request The REST Request
18
+ * @return \WP_REST_Response The response.
19
+ */
20
+ public static function saveWizard( $request ) {
21
+ $body = $request->get_json_params();
22
+ $section = ! empty( $body['section'] ) ? sanitize_text_field( $body['section'] ) : null;
23
+ $wizard = ! empty( $body['wizard'] ) ? $body['wizard'] : null;
24
+
25
+ aioseo()->internalOptions->internal->wizard = wp_json_encode( $wizard );
26
+
27
+ // Process the importers.
28
+ if ( 'importers' === $section && ! empty( $wizard['importers'] ) ) {
29
+ $importers = $wizard['importers'];
30
+
31
+ try {
32
+ foreach ( $importers as $plugin ) {
33
+ aioseo()->importExport->startImport( $plugin, [
34
+ 'settings',
35
+ 'postMeta',
36
+ 'termMeta'
37
+ ] );
38
+ }
39
+ } catch ( \Exception $e ) {
40
+ // Import failed. Let's create a notification but move on.
41
+ $notification = Models\Notification::getNotificationByName( 'import-failed' );
42
+ if ( ! $notification->exists() ) {
43
+ Models\Notification::addNotification( [
44
+ 'slug' => uniqid(),
45
+ 'notification_name' => 'install-mi',
46
+ 'title' => __( 'SEO Plugin Import Failed', 'all-in-one-seo-pack' ),
47
+ 'content' => __( 'Unfortunately, there was an error importing your SEO plugin settings. This could be due to an incompatibility in the version installed. Make sure you are on the latest version of the plugin and try again.', 'all-in-one-seo-pack' ), // phpcs:ignore Generic.Files.LineLength.MaxExceeded
48
+ 'type' => 'error',
49
+ 'level' => [ 'all' ],
50
+ 'button1_label' => __( 'Try Again', 'all-in-one-seo-pack' ),
51
+ 'button1_action' => 'http://route#aioseo-tools&aioseo-scroll=aioseo-import-others&aioseo-highlight=aioseo-import-others:import-export',
52
+ 'start' => gmdate( 'Y-m-d H:i:s' )
53
+ ] );
54
+ }
55
+ }
56
+ }
57
+
58
+ // Save the category section.
59
+ if (
60
+ ( 'category' === $section || 'searchAppearance' === $section ) && // We allow the user to update the site title/description in search appearance.
61
+ ! empty( $wizard['category'] )
62
+ ) {
63
+ $category = $wizard['category'];
64
+ if ( ! empty( $category['category'] ) ) {
65
+ aioseo()->internalOptions->internal->category = $category['category'];
66
+ }
67
+
68
+ if ( ! empty( $category['categoryOther'] ) ) {
69
+ aioseo()->internalOptions->internal->categoryOther = $category['categoryOther'];
70
+ }
71
+
72
+ // If the home page is a static page, let's find and set that,
73
+ // otherwise set our home page settings.
74
+ $staticHomePage = 'page' === get_option( 'show_on_front' ) ? get_post( get_option( 'page_on_front' ) ) : null;
75
+ if ( ! empty( $category['siteTitle'] ) ) {
76
+ if ( $staticHomePage ) {
77
+ $page = Models\Post::getPost( $staticHomePage->ID );
78
+ $page->title = $category['siteTitle'];
79
+ } else {
80
+ aioseo()->options->searchAppearance->global->siteTitle = $category['siteTitle'];
81
+ }
82
+ }
83
+
84
+ if ( ! empty( $category['metaDescription'] ) ) {
85
+ if ( $staticHomePage ) {
86
+ $page = Models\Post::getPost( $staticHomePage->ID );
87
+ $page->description = $category['metaDescription'];
88
+ } else {
89
+ aioseo()->options->searchAppearance->global->metaDescription = $category['metaDescription'];
90
+ }
91
+ }
92
+ }
93
+
94
+ // Save the additional information section.
95
+ if ( 'additionalInformation' === $section && ! empty( $wizard['additionalInformation'] ) ) {
96
+ $additionalInformation = $wizard['additionalInformation'];
97
+ if ( ! empty( $additionalInformation['siteRepresents'] ) ) {
98
+ aioseo()->options->searchAppearance->global->schema->siteRepresents = $additionalInformation['siteRepresents'];
99
+ }
100
+
101
+ if ( ! empty( $additionalInformation['person'] ) ) {
102
+ aioseo()->options->searchAppearance->global->schema->person = $additionalInformation['person'];
103
+ }
104
+
105
+ if ( ! empty( $additionalInformation['organizationName'] ) ) {
106
+ aioseo()->options->searchAppearance->global->schema->organizationName = $additionalInformation['organizationName'];
107
+ }
108
+
109
+ if ( ! empty( $additionalInformation['phone'] ) ) {
110
+ aioseo()->options->searchAppearance->global->schema->phone = $additionalInformation['phone'];
111
+ }
112
+
113
+ if ( ! empty( $additionalInformation['organizationLogo'] ) ) {
114
+ aioseo()->options->searchAppearance->global->schema->organizationLogo = $additionalInformation['organizationLogo'];
115
+ }
116
+
117
+ if ( ! empty( $additionalInformation['personName'] ) ) {
118
+ aioseo()->options->searchAppearance->global->schema->personName = $additionalInformation['personName'];
119
+ }
120
+
121
+ if ( ! empty( $additionalInformation['personLogo'] ) ) {
122
+ aioseo()->options->searchAppearance->global->schema->personLogo = $additionalInformation['personLogo'];
123
+ }
124
+
125
+ if ( ! empty( $additionalInformation['contactType'] ) ) {
126
+ aioseo()->options->searchAppearance->global->schema->contactType = $additionalInformation['contactType'];
127
+ }
128
+
129
+ if ( ! empty( $additionalInformation['contactManual'] ) ) {
130
+ aioseo()->options->searchAppearance->global->schema->contactManual = $additionalInformation['contactManual'];
131
+ }
132
+
133
+ if ( ! empty( $additionalInformation['socialShareImage'] ) ) {
134
+ aioseo()->options->social->facebook->general->defaultImagePosts = $additionalInformation['socialShareImage'];
135
+ aioseo()->options->social->twitter->general->defaultImagePosts = $additionalInformation['socialShareImage'];
136
+ }
137
+
138
+ if ( ! empty( $additionalInformation['social'] ) && ! empty( $additionalInformation['social']['profiles'] ) ) {
139
+ $profiles = $additionalInformation['social']['profiles'];
140
+ if ( ! empty( $profiles['sameUsername'] ) ) {
141
+ $sameUsername = $profiles['sameUsername'];
142
+ if ( isset( $sameUsername['enable'] ) ) {
143
+ aioseo()->options->social->profiles->sameUsername->enable = $sameUsername['enable'];
144
+ }
145
+
146
+ if ( ! empty( $sameUsername['username'] ) ) {
147
+ aioseo()->options->social->profiles->sameUsername->username = $sameUsername['username'];
148
+ }
149
+
150
+ if ( ! empty( $sameUsername['included'] ) ) {
151
+ aioseo()->options->social->profiles->sameUsername->included = $sameUsername['included'];
152
+ }
153
+ }
154
+
155
+ if ( ! empty( $profiles['urls'] ) ) {
156
+ $urls = $profiles['urls'];
157
+ if ( ! empty( $urls['facebookPageUrl'] ) ) {
158
+ aioseo()->options->social->profiles->urls->facebookPageUrl = $urls['facebookPageUrl'];
159
+ }
160
+
161
+ if ( ! empty( $urls['twitterUrl'] ) ) {
162
+ aioseo()->options->social->profiles->urls->twitterUrl = $urls['twitterUrl'];
163
+ }
164
+
165
+ if ( ! empty( $urls['instagramUrl'] ) ) {
166
+ aioseo()->options->social->profiles->urls->instagramUrl = $urls['instagramUrl'];
167
+ }
168
+
169
+ if ( ! empty( $urls['pinterestUrl'] ) ) {
170
+ aioseo()->options->social->profiles->urls->pinterestUrl = $urls['pinterestUrl'];
171
+ }
172
+
173
+ if ( ! empty( $urls['youtubeUrl'] ) ) {
174
+ aioseo()->options->social->profiles->urls->youtubeUrl = $urls['youtubeUrl'];
175
+ }
176
+
177
+ if ( ! empty( $urls['linkedinUrl'] ) ) {
178
+ aioseo()->options->social->profiles->urls->linkedinUrl = $urls['linkedinUrl'];
179
+ }
180
+
181
+ if ( ! empty( $urls['tumblrUrl'] ) ) {
182
+ aioseo()->options->social->profiles->urls->tumblrUrl = $urls['tumblrUrl'];
183
+ }
184
+
185
+ if ( ! empty( $urls['yelpPageUrl'] ) ) {
186
+ aioseo()->options->social->profiles->urls->yelpPageUrl = $urls['yelpPageUrl'];
187
+ }
188
+
189
+ if ( ! empty( $urls['soundCloudUrl'] ) ) {
190
+ aioseo()->options->social->profiles->urls->soundCloudUrl = $urls['soundCloudUrl'];
191
+ }
192
+
193
+ if ( ! empty( $urls['wikipediaUrl'] ) ) {
194
+ aioseo()->options->social->profiles->urls->wikipediaUrl = $urls['wikipediaUrl'];
195
+ }
196
+
197
+ if ( ! empty( $urls['myspaceUrl'] ) ) {
198
+ aioseo()->options->social->profiles->urls->myspaceUrl = $urls['myspaceUrl'];
199
+ }
200
+
201
+ if ( ! empty( $urls['googlePlacesUrl'] ) ) {
202
+ aioseo()->options->social->profiles->urls->googlePlacesUrl = $urls['googlePlacesUrl'];
203
+ }
204
+ }
205
+ }
206
+
207
+ return new \WP_REST_Response( [
208
+ 'success' => true
209
+ ], 200 );
210
+ }
211
+
212
+ // Save the features section.
213
+ if ( 'features' === $section && ! empty( $wizard['features'] ) ) {
214
+ $features = $wizard['features'];
215
+
216
+ // Install MI.
217
+ if ( in_array( 'analytics', $features, true ) ) {
218
+ $cantInstall = false;
219
+ $pluginData = aioseo()->helpers->getPluginData();
220
+ if ( ! $pluginData['miPro']['activated'] && ! $pluginData['miLite']['activated'] ) {
221
+ if ( $pluginData['miPro']['installed'] ) {
222
+ aioseo()->addons->installAddon( 'miPro' );
223
+
224
+ // Stop the redirect from happening.
225
+ delete_transient( '_monsterinsights_activation_redirect' );
226
+ } else {
227
+ if ( $pluginData['miPro']['installed'] || aioseo()->addons->canInstall() ) {
228
+ aioseo()->addons->installAddon( 'miLite' );
229
+
230
+ // Stop the redirect from happening.
231
+ delete_transient( '_monsterinsights_activation_redirect' );
232
+ } else {
233
+ $cantInstall = true;
234
+ }
235
+ }
236
+ }
237
+
238
+ if ( $cantInstall ) {
239
+ $notification = Models\Notification::getNotificationByName( 'install-mi' );
240
+ if ( ! $notification->exists() ) {
241
+ Models\Notification::addNotification( [
242
+ 'slug' => uniqid(),
243
+ 'notification_name' => 'install-mi',
244
+ 'title' => __( 'Install MonsterInsights', 'all-in-one-seo-pack' ),
245
+ 'content' => sprintf(
246
+ // Translators: 1 - The plugin short name ("AIOSEO").
247
+ __( 'You selected to install the free MonsterInsights Analytics plugin during the setup of %1$s, but there was an issue during installation. Click below to manually install.', 'all-in-one-seo-pack' ), // phpcs:ignore Generic.Files.LineLength.MaxExceeded
248
+ AIOSEO_PLUGIN_SHORT_NAME
249
+ ),
250
+ 'type' => 'info',
251
+ 'level' => [ 'all' ],
252
+ 'button1_label' => __( 'Install MonsterInsights', 'all-in-one-seo-pack' ),
253
+ 'button1_action' => $pluginData['miLite']['wpLink'],
254
+ 'button2_label' => __( 'Remind Me Later', 'all-in-one-seo-pack' ),
255
+ 'button2_action' => 'http://action#notification/install-mi-reminder',
256
+ 'start' => gmdate( 'Y-m-d H:i:s' )
257
+ ] );
258
+ }
259
+ }
260
+ }
261
+ }
262
+
263
+ // Save the search appearance section.
264
+ if ( 'searchAppearance' === $section && ! empty( $wizard['searchAppearance'] ) ) {
265
+ $searchAppearance = $wizard['searchAppearance'];
266
+
267
+ if ( isset( $searchAppearance['underConstruction'] ) ) {
268
+ update_option( 'blog_public', ! $searchAppearance['underConstruction'] );
269
+ }
270
+
271
+ if (
272
+ ! empty( $searchAppearance['postTypes'] ) &&
273
+ ! empty( $searchAppearance['postTypes']['postTypes'] )
274
+ ) {
275
+ // Robots.
276
+ if ( ! empty( $searchAppearance['postTypes']['postTypes']['all'] ) ) {
277
+ $options = aioseo()->options->noConflict();
278
+ foreach ( aioseo()->helpers->getPublicPostTypes( true ) as $postType ) {
279
+ if ( $options->searchAppearance->dynamic->postTypes->has( $postType ) ) {
280
+ $options->searchAppearance->dynamic->postTypes->$postType->show = true;
281
+ $options->searchAppearance->dynamic->postTypes->$postType->advanced->robotsMeta->default = true;
282
+ $options->searchAppearance->dynamic->postTypes->$postType->advanced->robotsMeta->noindex = false;
283
+ }
284
+ }
285
+
286
+ aioseo()->options->refresh();
287
+ } else {
288
+ $options = aioseo()->options->noConflict();
289
+ foreach ( aioseo()->helpers->getPublicPostTypes( true ) as $postType ) {
290
+ if ( $options->searchAppearance->dynamic->postTypes->has( $postType ) ) {
291
+ if ( in_array( $postType, (array) $searchAppearance['postTypes']['postTypes']['included'], true ) ) {
292
+ $options->searchAppearance->dynamic->postTypes->$postType->show = true;
293
+ $options->searchAppearance->dynamic->postTypes->$postType->advanced->robotsMeta->default = true;
294
+ $options->searchAppearance->dynamic->postTypes->$postType->advanced->robotsMeta->noindex = false;
295
+ } else {
296
+ $options->searchAppearance->dynamic->postTypes->$postType->show = false;
297
+ $options->searchAppearance->dynamic->postTypes->$postType->advanced->robotsMeta->default = false;
298
+ $options->searchAppearance->dynamic->postTypes->$postType->advanced->robotsMeta->noindex = true;
299
+ }
300
+ }
301
+ }
302
+
303
+ aioseo()->options->refresh();
304
+ }
305
+
306
+ // Sitemaps.
307
+ if ( isset( $searchAppearance['postTypes']['postTypes']['all'] ) ) {
308
+ aioseo()->options->sitemap->general->postTypes->all = $searchAppearance['postTypes']['postTypes']['all'];
309
+ }
310
+
311
+ if ( isset( $searchAppearance['postTypes']['postTypes']['included'] ) ) {
312
+ aioseo()->options->sitemap->general->postTypes->included = $searchAppearance['postTypes']['postTypes']['included'];
313
+ }
314
+ }
315
+
316
+ if ( isset( $searchAppearance['multipleAuthors'] ) ) {
317
+ aioseo()->options->searchAppearance->archives->author->show = $searchAppearance['multipleAuthors'];
318
+ aioseo()->options->searchAppearance->archives->author->advanced->robotsMeta->default = $searchAppearance['multipleAuthors'];
319
+ aioseo()->options->searchAppearance->archives->author->advanced->robotsMeta->noindex = ! $searchAppearance['multipleAuthors'];
320
+ }
321
+
322
+ $options = aioseo()->options->noConflict();
323
+ if ( isset( $searchAppearance['redirectAttachmentPages'] ) && $options->searchAppearance->dynamic->postTypes->has( 'attachment' ) ) {
324
+ $options->searchAppearance->dynamic->postTypes->attachment->redirectAttachmentUrls = $searchAppearance['redirectAttachmentPages'] ? 'attachment' : 'disabled';
325
+ aioseo()->options->refresh();
326
+ }
327
+ }
328
+
329
+ // Save the smart recommendations section.
330
+ if ( 'smartRecommendations' === $section && ! empty( $wizard['smartRecommendations'] ) ) {
331
+ $smartRecommendations = $wizard['smartRecommendations'];
332
+ if ( isset( $smartRecommendations['automaticUpdates'] ) ) {
333
+ aioseo()->options->advanced->autoUpdates = $smartRecommendations['automaticUpdates'] ? 'all' : 'none';
334
+ }
335
+
336
+ if ( ! empty( $smartRecommendations['accountInfo'] ) && ! aioseo()->internalOptions->internal->siteAnalysis->connectToken ) {
337
+ $url = defined( 'AIOSEO_CONNECT_DIRECT_URL' ) ? AIOSEO_CONNECT_DIRECT_URL : 'https://aioseo.com/wp-json/aioseo-lite-connect/v1/connect/';
338
+ $response = wp_remote_post( $url, [
339
+ 'headers' => [
340
+ 'Content-Type' => 'application/json'
341
+ ],
342
+ 'body' => wp_json_encode( [
343
+ 'accountInfo' => $smartRecommendations['accountInfo'],
344
+ 'homeurl' => home_url()
345
+ ] )
346
+ ] );
347
+
348
+ $token = json_decode( wp_remote_retrieve_body( $response ) );
349
+ if ( ! empty( $token->token ) ) {
350
+ aioseo()->internalOptions->internal->siteAnalysis->connectToken = $token->token;
351
+ }
352
+ }
353
+ }
354
+
355
+ return new \WP_REST_Response( [
356
+ 'success' => true,
357
+ 'options' => aioseo()->options->all()
358
+ ], 200 );
359
+ }
360
+ }
{public/js/vendor → app/Common/Assets/js}/autotrack.js RENAMED
File without changes
app/Common/Help/Help.php ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Help;
3
+
4
+ class Help {
5
+ /**
6
+ * Source of notifications content.
7
+ *
8
+ * @since 4.0.0
9
+ *
10
+ * @var string
11
+ */
12
+ private $url = 'https://aioseo.com/wp-content/docs.json';
13
+
14
+ /**
15
+ * Settings.
16
+ *
17
+ * @since 4.0.0
18
+ *
19
+ * @var array
20
+ */
21
+ private $settings = [
22
+ 'docsUrl' => 'https://aioseo.com/docs/',
23
+ 'supportTicketUrl' => 'https://aioseo.com/account/support/',
24
+ 'upgradeUrl' => 'https://aioseo.com/pricing/',
25
+ ];
26
+
27
+ /**
28
+ * Gets the URL for the notifications api.
29
+ *
30
+ * @since 4.0.0
31
+ *
32
+ * @return string The URL to use for the api requests.
33
+ */
34
+ private function getUrl() {
35
+ if ( defined( 'AIOSEO_DOCS_FEED_URL' ) ) {
36
+ return AIOSEO_DOCS_FEED_URL;
37
+ }
38
+
39
+ return $this->url;
40
+ }
41
+
42
+ /**
43
+ * Get docs from the cache.
44
+ *
45
+ * @since 4.0.0
46
+ *
47
+ * @return array Docs data.
48
+ */
49
+ public function getDocs() {
50
+ $aioseoAdminHelpDocs = get_transient( 'aioseo_admin_help_docs' );
51
+ $aioseoAdminHelpDocsCacheTime = WEEK_IN_SECONDS;
52
+ if ( false === $aioseoAdminHelpDocs ) {
53
+ $request = wp_remote_get( $this->getUrl() );
54
+
55
+ if ( is_wp_error( $request ) ) {
56
+ return false;
57
+ }
58
+
59
+ $response = $request['response'];
60
+
61
+ if ( ( $response['code'] <= 200 ) && ( $response['code'] > 299 ) ) {
62
+ $aioseoAdminHelpDocsCacheTime = 10 * MINUTE_IN_SECONDS;
63
+ }
64
+ $docs = wp_remote_retrieve_body( $request );
65
+ set_transient( 'aioseo_admin_help_docs', $docs, $aioseoAdminHelpDocsCacheTime );
66
+ }
67
+ return $aioseoAdminHelpDocs;
68
+ }
69
+ }
app/Common/ImportExport/Helpers.php ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\ImportExport;
3
+
4
+ /**
5
+ * Contains helper methods for the import from other plugins.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ abstract class Helpers {
10
+
11
+ /**
12
+ * Maps a list of old settings from V3 to their counterparts in V4.
13
+ *
14
+ * @since 4.0.0
15
+ *
16
+ * @param array $mappings The old settings, mapped to their new settings.
17
+ * @param array $group The old settings group.
18
+ * @param bool $convertMacros Whether to convert the old V3 macros to V4 smart tags.
19
+ * @return void
20
+ */
21
+ public function mapOldToNew( $mappings, $group, $convertMacros = false ) {
22
+ if (
23
+ ! is_array( $mappings ) ||
24
+ ! is_array( $group ) ||
25
+ ! count( $mappings ) ||
26
+ ! count( $group )
27
+ ) {
28
+ return;
29
+ }
30
+
31
+ foreach ( $mappings as $name => $values ) {
32
+ if ( ! isset( $group[ $name ] ) ) {
33
+ continue;
34
+ }
35
+
36
+ $error = false;
37
+ $options = aioseo()->options->noConflict();
38
+ $lastOption = '';
39
+ for ( $i = 0; $i < count( $values['newOption'] ); $i++ ) {
40
+ $lastOption = $values['newOption'][ $i ];
41
+ if ( ! $options->has( $lastOption, false ) ) {
42
+ $error = true;
43
+ break;
44
+ };
45
+ if ( count( $values['newOption'] ) - 1 !== $i ) {
46
+ $options = $options->$lastOption;
47
+ }
48
+ }
49
+
50
+ if ( $error ) {
51
+ continue;
52
+ }
53
+
54
+ switch ( $values['type'] ) {
55
+ case 'boolean':
56
+ if ( ! empty( $group[ $name ] ) ) {
57
+ $options->$lastOption = true;
58
+ break;
59
+ }
60
+ $options->$lastOption = false;
61
+ break;
62
+ case 'integer':
63
+ case 'float':
64
+ $value = aioseo()->helpers->sanitizeOption( $group[ $name ] );
65
+ if ( $value ) {
66
+ $options->$lastOption = $value;
67
+ }
68
+ break;
69
+ default:
70
+ $value = $group[ $name ];
71
+ if ( $convertMacros ) {
72
+ $value = $this->macrosToSmartTags( $value );
73
+ }
74
+ $options->$lastOption = aioseo()->helpers->sanitizeOption( $value );
75
+ break;
76
+ }
77
+ }
78
+
79
+ aioseo()->options->refresh();
80
+ }
81
+ }
app/Common/ImportExport/ImportExport.php ADDED
@@ -0,0 +1,358 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\ImportExport;
3
+
4
+ use AIOSEO\Plugin\Common\Models;
5
+
6
+ /**
7
+ * Handles the importing/exporting of settings and SEO data.
8
+ *
9
+ * @since 4.0.0
10
+ */
11
+ class ImportExport {
12
+ /**
13
+ * Set up an array of plugins for importing.
14
+ *
15
+ * @since 4.0.0
16
+ *
17
+ * @var array
18
+ */
19
+ private $plugins = [];
20
+
21
+ /**
22
+ * Class constructor.
23
+ *
24
+ * @since 4.0.0
25
+ */
26
+ public function __construct() {
27
+ $this->yoastSeo = new YoastSeo\YoastSeo( $this );
28
+ $this->rankMath = new RankMath\RankMath( $this );
29
+ }
30
+
31
+ /**
32
+ * Converts the content of a given V3 .ini settings file to an array of settings.
33
+ *
34
+ * @since 4.0.0
35
+ *
36
+ * @param string $content The .ini file contents.
37
+ * @return array The settings.
38
+ */
39
+ public function importIniData( $contents ) {
40
+ $lines = array_filter( preg_split( '/\r\n|\r|\n/', $contents ) );
41
+
42
+ $sections = [];
43
+ $sectionLabel = '';
44
+ $sectionCount = 0;
45
+
46
+ foreach ( $lines as $lineNumber => $line ) {
47
+ $line = trim( $line );
48
+ // Ignore comments.
49
+ if ( preg_match( '#^;.*#', $line ) || preg_match( '#\<(\?php|script)#', $line ) ) {
50
+ continue;
51
+ }
52
+
53
+ $matches = [];
54
+ if ( preg_match( '#^\[(\S+)\]$#', $line, $label ) ) {
55
+ $sectionLabel = strval( $label[1] );
56
+ if ( 'post_data' === $sectionLabel ) {
57
+ $sectionCount++;
58
+ }
59
+ if ( ! isset( $sections[ $sectionLabel ] ) ) {
60
+ $sections[ $sectionLabel ] = [];
61
+ }
62
+ } elseif ( preg_match( "#^(\S+)\s*=\s*'(.*)'$#", $line, $matches ) ) {
63
+ if ( 'post_data' === $sectionLabel ) {
64
+ $sections[ $sectionLabel ][ $sectionCount ][ $matches[1] ] = $matches[2];
65
+ } else {
66
+ $sections[ $sectionLabel ][ $matches[1] ] = $matches[2];
67
+ }
68
+ } elseif ( preg_match( '#^(\S+)\s*=\s*NULL$#', $line, $matches ) ) {
69
+ if ( 'post_data' === $sectionLabel ) {
70
+ $sections[ $sectionLabel ][ $sectionCount ][ $matches[1] ] = '';
71
+ } else {
72
+ $sections[ $sectionLabel ][ $matches[1] ] = '';
73
+ }
74
+ } else {
75
+ return false;
76
+ }
77
+ }
78
+
79
+ $sanitizedSections = [];
80
+ foreach ( $sections as $section => $options ) {
81
+ $sanitizedSection = [];
82
+ foreach ( $options as $option => $value ) {
83
+ $sanitizedSection[ $option ] = $this->convertAndSanitize( $value );
84
+ }
85
+ $sanitizedSections[ $section ] = $sanitizedSection;
86
+ }
87
+
88
+ $oldOptions = [];
89
+ $postData = [];
90
+ foreach ( $sanitizedSections as $label => $data ) {
91
+ switch ( $label ) {
92
+ case 'aioseop_options':
93
+ $oldOptions = array_merge( $oldOptions, $data );
94
+ break;
95
+ case 'aiosp_feature_manager_options':
96
+ case 'aiosp_opengraph_options':
97
+ case 'aiosp_sitemap_options':
98
+ case 'aiosp_video_sitemap_options':
99
+ case 'aiosp_schema_local_business_options':
100
+ case 'aiosp_image_seo_options':
101
+ case 'aiosp_robots_options':
102
+ case 'aiosp_bad_robots_options':
103
+ $oldOptions['modules'][ $label ] = $data;
104
+ break;
105
+ case 'post_data':
106
+ $postData = $data;
107
+ break;
108
+ default:
109
+ break;
110
+ }
111
+ }
112
+
113
+ if ( ! empty( $oldOptions ) ) {
114
+ aioseo()->migration->doMigration( $oldOptions );
115
+ }
116
+
117
+ if ( ! empty( $postData ) ) {
118
+ $this->importOldPostMeta( $postData );
119
+ }
120
+ return true;
121
+ }
122
+
123
+ /**
124
+ * Imports the post meta from V3.
125
+ *
126
+ * @since 4.0.0
127
+ *
128
+ * @param array $postData The post data.
129
+ * @return void
130
+ */
131
+ private function importOldPostMeta( $postData ) {
132
+ $mappedMeta = [
133
+ '_aioseop_title' => 'title',
134
+ '_aioseop_description' => 'description',
135
+ '_aioseop_custom_link' => 'canonical_url',
136
+ '_aioseop_sitemap_exclude' => '',
137
+ '_aioseop_disable' => '',
138
+ '_aioseop_noindex' => 'robots_noindex',
139
+ '_aioseop_nofollow' => 'robots_nofollow',
140
+ '_aioseop_sitemap_priority' => 'priority',
141
+ '_aioseop_sitemap_frequency' => 'frequency',
142
+ '_aioseop_keywords' => 'keywords',
143
+ '_aioseop_opengraph_settings' => ''
144
+ ];
145
+
146
+ $excludedPosts = [];
147
+ $sitemapExcludedPosts = [];
148
+
149
+ require_once( ABSPATH . 'wp-admin/includes/post.php' );
150
+ foreach ( $postData as $post => $values ) {
151
+ $postId = \post_exists( $values['post_title'], '', $values['post_date'] );
152
+ if ( ! $postId ) {
153
+ continue;
154
+ }
155
+
156
+ $meta = [
157
+ 'post_id' => $postId,
158
+ ];
159
+
160
+ foreach ( $values as $name => $value ) {
161
+ if ( ! in_array( $name, array_keys( $mappedMeta ), true ) ) {
162
+ continue;
163
+ }
164
+
165
+ switch ( $name ) {
166
+ case '_aioseop_sitemap_exclude':
167
+ if ( empty( $value ) ) {
168
+ break;
169
+ }
170
+ $sitemapExcludedPosts[] = $postId;
171
+ break;
172
+ case '_aioseop_disable':
173
+ if ( empty( $value ) ) {
174
+ break;
175
+ }
176
+ $excludedPosts[] = $postId;
177
+ break;
178
+ case '_aioseop_noindex':
179
+ case '_aioseop_nofollow':
180
+ $meta[ $mappedMeta[ $name ] ] = ! empty( $value );
181
+ if ( ! empty( $value ) ) {
182
+ $meta['robots_default'] = false;
183
+ }
184
+ break;
185
+ case '_aioseop_keywords':
186
+ $meta[ $mappedMeta[ $name ] ] = aioseo()->migration->helpers->oldKeywordsToNewKeywords( $value );
187
+ break;
188
+ case '_aioseop_opengraph_settings':
189
+ $class = new \AIOSEO\Plugin\Common\Migration\Meta();
190
+ $meta += $class->convertOpenGraphMeta( $value );
191
+ break;
192
+ default:
193
+ $meta[ $mappedMeta[ $name ] ] = esc_html( wp_strip_all_tags( strval( $value ) ) );
194
+ break;
195
+ }
196
+ }
197
+ $post = Models\Post::getPost( $postId );
198
+ $post->set( $meta );
199
+ $post->save();
200
+ }
201
+
202
+ if ( count( $excludedPosts ) ) {
203
+ $deprecatedOptions = aioseo()->internalOptions->internal->deprecatedOptions;
204
+ if ( ! in_array( 'excludePosts', $deprecatedOptions, true ) ) {
205
+ array_push( $deprecatedOptions, 'excludePosts' );
206
+ aioseo()->internalOptions->internal->deprecatedOptions = $deprecatedOptions;
207
+ }
208
+
209
+ $posts = aioseo()->options->deprecated->searchAppearance->advanced->excludePosts;
210
+
211
+ foreach ( $excludedPosts as $id ) {
212
+ if ( ! intval( $id ) ) {
213
+ continue;
214
+ }
215
+ $post = get_post( $id );
216
+ if ( ! is_object( $post ) ) {
217
+ continue;
218
+ }
219
+ $excludedPost = new \stdClass();
220
+ $excludedPost->type = $post->post_type;
221
+ $excludedPost->value = $post->ID;
222
+ $excludedPost->label = $post->post_title;
223
+ $excludedPost->link = get_permalink( $post );
224
+
225
+ $posts[] = wp_json_encode( $excludedPost );
226
+ }
227
+ aioseo()->options->deprecated->searchAppearance->advanced->excludePosts = $posts;
228
+ }
229
+
230
+ if ( count( $sitemapExcludedPosts ) ) {
231
+ aioseo()->options->sitemap->general->advancedSettings->enable = true;
232
+
233
+ $posts = aioseo()->options->sitemap->general->advancedSettings->excludePosts;
234
+ foreach ( $sitemapExcludedPosts as $id ) {
235
+ if ( ! intval( $id ) ) {
236
+ continue;
237
+ }
238
+ $post = get_post( $id );
239
+ if ( ! is_object( $post ) ) {
240
+ continue;
241
+ }
242
+ $excludedPost = new \stdClass();
243
+ $excludedPost->type = $post->post_type;
244
+ $excludedPost->value = $post->ID;
245
+ $excludedPost->label = $post->post_title;
246
+ $excludedPost->link = get_permalink( $post );
247
+
248
+ $posts[] = wp_json_encode( $excludedPost );
249
+ }
250
+ aioseo()->options->sitemap->general->advancedSettings->excludePosts = $posts;
251
+ }
252
+ }
253
+
254
+ /**
255
+ * Unserializes an option value if needed and then sanitizes it.
256
+ *
257
+ * @since 4.0.0
258
+ *
259
+ * @param string $value The option value.
260
+ * @return mixed The sanitized, converted option value.
261
+ */
262
+ private function convertAndSanitize( $value ) {
263
+ $value = maybe_unserialize( $value );
264
+
265
+ switch ( gettype( $value ) ) {
266
+ case 'boolean':
267
+ return (bool) $value;
268
+ case 'string':
269
+ return esc_html( wp_strip_all_tags( wp_check_invalid_utf8( trim( $value ) ) ) );
270
+ case 'integer':
271
+ return intval( $value );
272
+ case 'double':
273
+ return floatval( $value );
274
+ case 'array':
275
+ $sanitized = [];
276
+ foreach ( (array) $value as $k => $v ) {
277
+ $sanitized[ $k ] = $this->convertAndSanitize( $v );
278
+ }
279
+ return $sanitized;
280
+ default:
281
+ return '';
282
+ }
283
+ }
284
+
285
+ /**
286
+ * Starts an import.
287
+ *
288
+ * @since 4.0.0
289
+ *
290
+ * @param string $plugin The slug of the plugin to import.
291
+ * @param array $settings Which settings to import.
292
+ * @return void
293
+ */
294
+ public function startImport( $plugin, $settings ) {
295
+ foreach ( $this->plugins as $pluginData ) {
296
+ if ( $pluginData['slug'] === $plugin ) {
297
+ $pluginData['class']->doImport( $settings );
298
+ return;
299
+ }
300
+ }
301
+ }
302
+
303
+ /**
304
+ * Adds plugins to the import/export.
305
+ *
306
+ * @since 4.0.0
307
+ *
308
+ * @param array $plugins The plugins to add.
309
+ * @return void
310
+ */
311
+ public function addPlugins( $plugins ) {
312
+ $this->plugins = array_merge( $this->plugins, $plugins );
313
+ }
314
+
315
+ /**
316
+ * Get the plugins we allow importing from.
317
+ *
318
+ * @since 4.0.0
319
+ *
320
+ * @return void
321
+ */
322
+ public function plugins() {
323
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
324
+ $plugins = [];
325
+ $installedPlugins = array_keys( get_plugins() );
326
+ foreach ( $this->plugins as $importerPlugin ) {
327
+ $data = [
328
+ 'slug' => $importerPlugin['slug'],
329
+ 'name' => $importerPlugin['name'],
330
+ 'version' => null,
331
+ 'canImport' => false,
332
+ 'basename' => $importerPlugin['basename'],
333
+ 'installed' => false
334
+ ];
335
+
336
+ if ( in_array( $importerPlugin['basename'], $installedPlugins, true ) ) {
337
+ $pluginData = get_file_data( trailingslashit( WP_PLUGIN_DIR ) . $importerPlugin['basename'], [
338
+ 'name' => 'Plugin Name',
339
+ 'version' => 'Version',
340
+ ] );
341
+
342
+ $canImport = false;
343
+ if ( version_compare( $importerPlugin['version'], $pluginData['version'], '<=' ) ) {
344
+ $canImport = true;
345
+ }
346
+
347
+ $data['name'] = $pluginData['name'];
348
+ $data['version'] = $pluginData['version'];
349
+ $data['canImport'] = $canImport;
350
+ $data['installed'] = true;
351
+ }
352
+
353
+ $plugins[] = $data;
354
+ }
355
+
356
+ return $plugins;
357
+ }
358
+ }
app/Common/ImportExport/Importer.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\ImportExport;
3
+
4
+ /**
5
+ * Imports the settings and meta data from other plugins.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ abstract class Importer {
10
+
11
+ /**
12
+ * Starts the import.
13
+ *
14
+ * @since 4.0.0
15
+ *
16
+ * @param array $options What the user wants to import.
17
+ * @return void
18
+ */
19
+ public function doImport( $options = [] ) {
20
+ if ( empty( $options ) ) {
21
+ $this->importSettings();
22
+ $this->importPostMeta();
23
+ $this->importTermMeta();
24
+ return;
25
+ }
26
+
27
+ foreach ( $options as $optionName ) {
28
+ switch ( $optionName ) {
29
+ case 'settings':
30
+ $this->importSettings();
31
+ break;
32
+ case 'postMeta':
33
+ $this->postMeta->scheduleImport();
34
+ break;
35
+ default:
36
+ break;
37
+ }
38
+ }
39
+ }
40
+ }
app/Common/ImportExport/RankMath/GeneralSettings.php ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\ImportExport\RankMath;
3
+
4
+ // phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
5
+
6
+ /**
7
+ * Migrates the General Settings.
8
+ *
9
+ * @since 4.0.0
10
+ */
11
+ class GeneralSettings {
12
+
13
+ /**
14
+ * Class constructor.
15
+ *
16
+ * @since 4.0.0
17
+ */
18
+ public function __construct() {
19
+ $this->options = get_option( 'rank-math-options-general' );
20
+ if ( empty( $this->options ) ) {
21
+ return;
22
+ }
23
+
24
+ $this->isTruSeoDisabled();
25
+ $this->migrateRedirectAttachments();
26
+ $this->migrateRssContentSettings();
27
+
28
+ $settings = [
29
+ 'google_verify' => [ 'type' => 'string', 'newOption' => [ 'webmasterTools', 'google' ] ],
30
+ 'bing_verify' => [ 'type' => 'string', 'newOption' => [ 'webmasterTools', 'bing' ] ],
31
+ 'yandex_verify' => [ 'type' => 'string', 'newOption' => [ 'webmasterTools', 'yandex' ] ],
32
+ 'baidu_verify' => [ 'type' => 'string', 'newOption' => [ 'webmasterTools', 'baidu' ] ],
33
+ 'pinterest_verify' => [ 'type' => 'string', 'newOption' => [ 'webmasterTools', 'pinterest' ] ],
34
+ ];
35
+
36
+ aioseo()->importExport->rankMath->helpers->mapOldToNew( $settings, $this->options );
37
+ }
38
+
39
+ /**
40
+ * Checks whether TruSEO should be disabled.
41
+ *
42
+ * @since 4.0.0
43
+ *
44
+ * @return void
45
+ */
46
+ private function isTruSeoDisabled() {
47
+ if ( ! empty( $this->options['frontend_seo_score'] ) ) {
48
+ aioseo()->options->advanced->truSeo = 'on' === $this->options['frontend_seo_score'];
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Migrates the Redirect Attachments setting.
54
+ *
55
+ * @since 4.0.0
56
+ *
57
+ * @return void
58
+ */
59
+ private function migrateRedirectAttachments() {
60
+ if ( isset( $this->options['attachment_redirect_urls'] ) ) {
61
+ if ( 'on' === $this->options['attachment_redirect_urls'] ) {
62
+ aioseo()->options->searchAppearance->dynamic->postTypes->attachment->redirectAttachmentUrls = 'attachment_parent';
63
+ } else {
64
+ aioseo()->options->searchAppearance->dynamic->postTypes->attachment->redirectAttachmentUrls = 'disabled';
65
+ }
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Migrates the RSS content settings.
71
+ *
72
+ * @since 4.0.0
73
+ *
74
+ * @return void
75
+ */
76
+ private function migrateRssContentSettings() {
77
+ if ( isset( $this->options['rss_before_content'] ) ) {
78
+ aioseo()->options->rssContent->before = esc_html( aioseo()->importExport->rankMath->helpers->macrosToSmartTags( $this->options['rss_before_content'] ) );
79
+ }
80
+
81
+ if ( isset( $this->options['rss_after_content'] ) ) {
82
+ aioseo()->options->rssContent->after = esc_html( aioseo()->importExport->rankMath->helpers->macrosToSmartTags( $this->options['rss_after_content'] ) );
83
+ }
84
+ }
85
+ }
app/Common/ImportExport/RankMath/Helpers.php ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\ImportExport\RankMath;
3
+
4
+ use AIOSEO\Plugin\Common\ImportExport;
5
+
6
+ // phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
7
+
8
+ /**
9
+ * Contains helper methods for the import from Rank Math.
10
+ *
11
+ * @since 4.0.0
12
+ */
13
+ class Helpers extends ImportExport\Helpers {
14
+
15
+ /**
16
+ * Converts the macros from Rank Math to our own smart tags.
17
+ *
18
+ * @since 4.0.0
19
+ *
20
+ * @param string $string The string with macros.
21
+ * @return string $string The string with smart tags.
22
+ */
23
+ public function macrosToSmartTags( $string ) {
24
+ $macros = [
25
+ '%sitename%' => '#site_title',
26
+ '%blog_title%' => '#site_title',
27
+ '%blog_description%' => '#tagline',
28
+ '%sitedesc%' => '#tagline',
29
+ '%sep%' => '#separator_sa',
30
+ '%post_title%' => '#post_title',
31
+ '%page_title%' => '#post_title',
32
+ '%postname%' => '#post_title',
33
+ '%title%' => '#post_title',
34
+ '%seo_title%' => '#post_title',
35
+ '%excerpt%' => '#post_excerpt',
36
+ '%wc_shortdesc%' => '#post_excerpt',
37
+ '%category%' => '#taxonomy_title',
38
+ '%term%' => '#taxonomy_title',
39
+ '%term_description%' => '#taxonomy_description',
40
+ '%currentdate%' => '#current_date',
41
+ '%currentday%' => '#current_day',
42
+ '%currentmonth%' => '#current_month',
43
+ '%name%' => '#author_first_name #author_last_name',
44
+ '%author%' => '#author_first_name #author_last_name',
45
+ '%date%' => '#post_date',
46
+ '%year%' => '#current_year',
47
+ '%user_description%' => '',
48
+ '%search_query%' => '#search_term',
49
+ '%AUTHORLINK%' => '#author_link',
50
+ '%POSTLINK%' => '#post_link',
51
+ '%BLOGLINK%' => '#site_link',
52
+ /* '%seo_description%' => '',
53
+ '%wc_price%' => '',
54
+ '%page%' => '',
55
+ '%FEATUREDIMAGE%' => '',
56
+ '%filename%' => '',*/
57
+ ];
58
+
59
+ if ( preg_match( '#%BLOGDESCLINK%#', $string ) ) {
60
+ $blogDescriptionLink = '<a href="' .
61
+ aioseo()->helpers->decodeHtmlEntities( get_bloginfo( 'url' ) ) . '">' .
62
+ aioseo()->helpers->decodeHtmlEntities( get_bloginfo( 'name' ) ) . ' - ' .
63
+ aioseo()->helpers->decodeHtmlEntities( get_bloginfo( 'description' ) ) . '</a>';
64
+
65
+ $string = str_replace( '%BLOGDESCLINK%', $blogDescriptionLink, $string );
66
+ }
67
+
68
+ if ( preg_match_all( '#%customfield\(([^%\s]*)\)%#', $string, $matches ) && ! empty( $matches[1] ) ) {
69
+ foreach ( $matches[1] as $name ) {
70
+ $string = preg_replace( "#%customfield\($name\)%#", "#custom_field-$name", $string );
71
+ }
72
+ }
73
+
74
+ if ( preg_match_all( '#%customterm\(([^%\s]*)\)%#', $string, $matches ) && ! empty( $matches[1] ) ) {
75
+ foreach ( $matches[1] as $name ) {
76
+ $string = preg_replace( "#%customterm\($name\)%#", "#tax_name-$name", $string );
77
+ }
78
+ }
79
+
80
+ foreach ( $macros as $macro => $tag ) {
81
+ $string = preg_replace( "#$macro(?![a-zA-Z0-9_])#im", $tag, $string );
82
+ }
83
+
84
+ // Strip out all remaining tags.
85
+ $string = preg_replace( '/%[^\%\s]*\([^\%]*\)%/i', '', preg_replace( '/%[^\%\s]*%/i', '', $string ) );
86
+ return trim( $string );
87
+ }
88
+ }
app/Common/ImportExport/RankMath/PostMeta.php ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\ImportExport\RankMath;
3
+
4
+ use AIOSEO\Plugin\Common\Models;
5
+
6
+ // phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
7
+
8
+ /**
9
+ * Imports the post meta from Rank Math.
10
+ *
11
+ * @since 4.0.0
12
+ */
13
+ class PostMeta {
14
+
15
+ /**
16
+ * Schedules the post meta import.
17
+ *
18
+ * @since 4.0.0
19
+ *
20
+ * @return void
21
+ */
22
+ public function scheduleImport() {
23
+ try {
24
+ if ( as_next_scheduled_action( aioseo()->importExport->rankMath->postActionName ) ) {
25
+ return;
26
+ }
27
+
28
+ if ( ! get_transient( 'aioseo_import_post_meta_rank_math' ) ) {
29
+ set_transient( 'aioseo_import_post_meta_rank_math', time(), WEEK_IN_SECONDS );
30
+ }
31
+
32
+ as_schedule_single_action( time(), aioseo()->importExport->rankMath->postActionName, [], 'aioseo' );
33
+ } catch ( \Exception $e ) {
34
+ // Do nothing.
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Imports the post meta.
40
+ *
41
+ * @since 4.0.0
42
+ *
43
+ * @return void
44
+ */
45
+ public function importPostMeta() {
46
+ $postsPerAction = 100;
47
+ $publicPostTypes = implode( "', '", aioseo()->helpers->getPublicPostTypes( true ) );
48
+ $timeStarted = gmdate( 'Y-m-d H:i:s', get_transient( 'aioseo_import_post_meta_rank_math' ) );
49
+
50
+ $posts = aioseo()->db
51
+ ->start( 'posts' . ' as p' )
52
+ ->select( 'p.ID, p.post_type' )
53
+ ->join( 'postmeta as pm', '`p`.`ID` = `pm`.`post_id`' )
54
+ ->leftJoin( 'aioseo_posts as ap', '`p`.`ID` = `ap`.`post_id`' )
55
+ ->whereRaw( "pm.meta_key LIKE 'rank_math_%'" )
56
+ ->whereRaw( "( p.post_type IN ( '$publicPostTypes' ) )" )
57
+ ->whereRaw( "( ap.post_id IS NULL OR ap.updated < '$timeStarted' )" )
58
+ ->orderBy( 'p.ID DESC' )
59
+ ->limit( $postsPerAction )
60
+ ->run()
61
+ ->result();
62
+
63
+ if ( ! $posts || ! count( $posts ) ) {
64
+ delete_transient( 'aioseo_import_post_meta_rank_math' );
65
+ return;
66
+ }
67
+
68
+ $mappedMeta = [
69
+ 'rank_math_title' => 'title',
70
+ 'rank_math_description' => 'description',
71
+ 'rank_math_canonical_url' => 'canonical_url',
72
+ 'rank_math_focus_keyword' => 'keyphrases',
73
+ 'rank_math_robots' => '',
74
+ 'rank_math_advanced_robots' => '',
75
+ 'rank_math_facebook_title' => 'og_title',
76
+ 'rank_math_facebook_description' => 'og_description',
77
+ 'rank_math_facebook_image' => 'og_image_custom_url',
78
+ 'rank_math_twitter_use_facebook' => 'twitter_use_og',
79
+ 'rank_math_twitter_title' => 'twitter_title',
80
+ 'rank_math_twitter_description' => 'twitter_description',
81
+ 'rank_math_twitter_image' => 'twitter_image_custom_url',
82
+ 'rank_math_twitter_card_type' => 'twitter_card'
83
+ ];
84
+
85
+ foreach ( $posts as $post ) {
86
+ $postMeta = aioseo()->db
87
+ ->start( 'postmeta' . ' as pm' )
88
+ ->select( 'pm.meta_key, pm.meta_value' )
89
+ ->where( 'pm.post_id', $post->ID )
90
+ ->whereRaw( "`pm`.`meta_key` LIKE 'rank_math_%'" )
91
+ ->run()
92
+ ->result();
93
+
94
+ if ( ! $postMeta || ! count( $postMeta ) ) {
95
+ continue;
96
+ }
97
+
98
+ $meta = [
99
+ 'post_id' => $post->ID,
100
+ ];
101
+
102
+ foreach ( $postMeta as $record ) {
103
+ $name = $record->meta_key;
104
+ $value = $record->meta_value;
105
+
106
+ if (
107
+ ! in_array( $post->post_type, [ 'page', 'attachment' ], true ) &&
108
+ preg_match( '#^rank_math_schema_([^\s]*)$#', $name, $match ) && ! empty( $match[1] )
109
+ ) {
110
+ switch ( $match[1] ) {
111
+ case 'Article':
112
+ case 'NewsArticle':
113
+ case 'BlogPosting':
114
+ $meta['schema_type'] = 'Article';
115
+ $meta['schema_type_options'] = wp_json_encode(
116
+ [ 'article' => [ 'articleType' => $match[1] ] ]
117
+ );
118
+ break;
119
+ default:
120
+ break;
121
+ }
122
+ }
123
+
124
+ if ( ! in_array( $name, array_keys( $mappedMeta ), true ) ) {
125
+ continue;
126
+ }
127
+
128
+ switch ( $name ) {
129
+ case 'rank_math_focus_keyword':
130
+ $keyphrase = [
131
+ 'focus' => [ 'keyphrase' => aioseo()->helpers->sanitizeOption( $value ) ],
132
+ 'additional' => []
133
+ ];
134
+ $meta['keyphrases'] = wp_json_encode( $keyphrase );
135
+ break;
136
+ case 'rank_math_robots':
137
+ $value = maybe_unserialize( $value );
138
+ if ( ! empty( $value ) ) {
139
+ $meta['robots_default'] = false;
140
+ foreach ( $value as $robotsName ) {
141
+ $meta[ "robots_$robotsName" ] = true;
142
+ }
143
+ }
144
+ break;
145
+ case 'rank_math_advanced_robots':
146
+ $value = maybe_unserialize( $value );
147
+ if ( ! empty( $value['max-snippet'] ) && intval( $value['max-snippet'] ) ) {
148
+ $meta['robots_max_snippet'] = intval( $value['max-snippet'] );
149
+ }
150
+ if ( ! empty( $value['max-video-preview'] ) && intval( $value['max-video-preview'] ) ) {
151
+ $meta['robots_max_videopreview'] = intval( $value['max-video-preview'] );
152
+ }
153
+ if ( ! empty( $value['max-image-preview'] ) ) {
154
+ $meta['robots_max_imagepreview'] = aioseo()->helpers->sanitizeOption( lcfirst( $value['max-image-preview'] ) );
155
+ }
156
+ break;
157
+ case 'rank_math_facebook_image':
158
+ $meta['og_image_type'] = 'custom_image';
159
+ $meta[ $mappedMeta[ $name ] ] = esc_url( $value );
160
+ break;
161
+ case 'rank_math_twitter_image':
162
+ $meta['twitter_image_type'] = 'custom_image';
163
+ $meta[ $mappedMeta[ $name ] ] = esc_url( $value );
164
+ break;
165
+ case 'rank_math_twitter_card_type':
166
+ preg_match( '#large#', $value, $match );
167
+ $meta[ $mappedMeta[ $name ] ] = ! empty( $match ) ? 'summary_large_image' : 'summary';
168
+ break;
169
+ case 'rank_math_twitter_use_facebook':
170
+ $meta[ $mappedMeta[ $name ] ] = 'on' === $value;
171
+ break;
172
+ case 'rank_math_title':
173
+ case 'rank_math_description':
174
+ if ( 'page' === $post->post_type ) {
175
+ $value = preg_replace( '#%category%#', '', $value );
176
+ $value = preg_replace( '#%excerpt%#', '', $value );
177
+ }
178
+ $value = aioseo()->importExport->rankMath->helpers->macrosToSmartTags( $value );
179
+ default:
180
+ $meta[ $mappedMeta[ $name ] ] = esc_html( wp_strip_all_tags( strval( $value ) ) );
181
+ break;
182
+ }
183
+ }
184
+
185
+ $aioseoPost = Models\Post::getPost( $post->ID );
186
+ $aioseoPost->set( $meta );
187
+ $aioseoPost->save();
188
+ }
189
+
190
+ if ( count( $posts ) === $postsPerAction ) {
191
+ try {
192
+ as_schedule_single_action( time() + 5, aioseo()->importExport->rankMath->postActionName, [], 'aioseo' );
193
+ } catch ( \Exception $e ) {
194
+ // Do nothing.
195
+ }
196
+ } else {
197
+ delete_transient( 'aioseo_import_post_meta_rank_math' );
198
+ }
199
+ }
200
+ }
app/Common/ImportExport/RankMath/RankMath.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\ImportExport\RankMath;
3
+
4
+ use AIOSEO\Plugin\Common\ImportExport;
5
+
6
+ class RankMath extends ImportExport\Importer {
7
+
8
+ /**
9
+ * A list of plugins to look for to import.
10
+ *
11
+ * @since 4.0.0
12
+ *
13
+ * @var array
14
+ */
15
+ public $plugins = [
16
+ [
17
+ 'name' => 'Rank Math SEO',
18
+ 'version' => '1.0',
19
+ 'basename' => 'seo-by-rank-math/rank-math.php',
20
+ 'slug' => 'rank-math-seo'
21
+ ]
22
+ ];
23
+
24
+ /**
25
+ * The post action name.
26
+ *
27
+ * @since 4.0.0
28
+ *
29
+ * @var string
30
+ */
31
+ public $postActionName = 'aioseo_import_post_meta_rank_math';
32
+
33
+ /**
34
+ * Class constructor.
35
+ *
36
+ * @since 4.0.0
37
+ *
38
+ * @param ImportExport $importer the ImportExport class.
39
+ */
40
+ public function __construct( $importer ) {
41
+ $this->helpers = new Helpers();
42
+ $this->postMeta = new PostMeta();
43
+ add_action( $this->postActionName, [ $this->postMeta, 'importPostMeta' ] );
44
+
45
+ $plugins = $this->plugins;
46
+ foreach ( $plugins as $key => $plugin ) {
47
+ $plugins[ $key ]['class'] = $this;
48
+ }
49
+ $importer->addPlugins( $plugins );
50
+ }
51
+
52
+
53
+ /**
54
+ * Imports the settings.
55
+ *
56
+ * @since 4.0.0
57
+ *
58
+ * @return void
59
+ */
60
+ protected function importSettings() {
61
+ new GeneralSettings();
62
+ new TitleMeta();
63
+ new Sitemap();
64
+ }
65
+ }
app/Common/ImportExport/RankMath/Sitemap.php ADDED
@@ -0,0 +1,185 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\ImportExport\RankMath;
3
+
4
+ // phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
5
+
6
+ /**
7
+ * Migrates the sitemap settings.
8
+ *
9
+ * @since 4.0.0
10
+ */
11
+ class Sitemap {
12
+
13
+ /**
14
+ * Class constructor.
15
+ *
16
+ * @since 4.0.0
17
+ */
18
+ public function __construct() {
19
+ $this->options = get_option( 'rank-math-options-sitemap' );
20
+ if ( empty( $this->options ) ) {
21
+ return;
22
+ }
23
+
24
+ $this->migrateIncludedObjects();
25
+ $this->migrateIncludeImages();
26
+ $this->migrateExcludedPosts();
27
+ $this->migrateExcludedTerms();
28
+ $this->migrateExcludedRoles();
29
+
30
+ $settings = [
31
+ 'items_per_page' => [ 'type' => 'string', 'newOption' => [ 'sitemap', 'general', 'linksPerIndex' ] ],
32
+ ];
33
+
34
+ aioseo()->options->sitemap->general->indexes = true;
35
+ aioseo()->importExport->rankMath->helpers->mapOldToNew( $settings, $this->options );
36
+ }
37
+
38
+ /**
39
+ * Migrates the included post types and taxonomies.
40
+ *
41
+ * @since 4.0.0
42
+ *
43
+ * @return void
44
+ */
45
+ private function migrateIncludedObjects() {
46
+ $includedPostTypes = [];
47
+ $includedTaxonomies = [];
48
+
49
+ $allowedPostTypes = array_values( array_diff( aioseo()->helpers->getPublicPostTypes( true ), aioseo()->helpers->getNoindexedPostTypes() ) );
50
+ foreach ( $allowedPostTypes as $postType ) {
51
+ foreach ( $this->options as $name => $value ) {
52
+ if ( preg_match( "#pt_${postType}_sitemap$#", $name, $match ) && 'on' === $this->options[ $name ] ) {
53
+ $includedPostTypes[] = $postType;
54
+ }
55
+ }
56
+ }
57
+
58
+ $allowedTaxonomies = array_values( array_diff( aioseo()->helpers->getPublicTaxonomies( true ), aioseo()->helpers->getNoindexedTaxonomies() ) );
59
+ foreach ( $allowedTaxonomies as $taxonomy ) {
60
+ foreach ( $this->options as $name => $value ) {
61
+ if ( preg_match( "#tax_${taxonomy}_sitemap$#", $name, $match ) && 'on' === $this->options[ $name ] ) {
62
+ $includedTaxonomies[] = $taxonomy;
63
+ }
64
+ }
65
+ }
66
+
67
+ aioseo()->options->sitemap->general->postTypes->included = $includedPostTypes;
68
+ if ( count( $allowedPostTypes ) !== count( $includedPostTypes ) ) {
69
+ aioseo()->options->sitemap->general->postTypes->all = false;
70
+ }
71
+
72
+ aioseo()->options->sitemap->general->taxonomies->included = $includedTaxonomies;
73
+ if ( count( $allowedTaxonomies ) !== count( $includedTaxonomies ) ) {
74
+ aioseo()->options->sitemap->general->taxonomies->all = false;
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Migrates the Redirect Attachments setting.
80
+ *
81
+ * @since 4.0.0
82
+ *
83
+ * @return void
84
+ */
85
+ private function migrateIncludeImages() {
86
+ if ( ! empty( $this->options['include_images'] ) ) {
87
+ if ( 'off' === $this->options['include_images'] ) {
88
+ aioseo()->options->sitemap->general->advancedSettings->enable = true;
89
+ aioseo()->options->sitemap->general->advancedSettings->excludeImages = true;
90
+ }
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Migrates the posts that are excluded from the sitemap.
96
+ *
97
+ * @since 4.0.0
98
+ *
99
+ * @return void
100
+ */
101
+ private function migrateExcludedPosts() {
102
+ if ( empty( $this->options['exclude_posts'] ) ) {
103
+ return;
104
+ }
105
+
106
+ $rmExcludedPosts = array_filter( explode( ',', $this->options['exclude_posts'] ) );
107
+ $excludedPosts = aioseo()->options->sitemap->general->advancedSettings->excludePosts;
108
+
109
+ if ( count( $rmExcludedPosts ) ) {
110
+ foreach ( $rmExcludedPosts as $rmExcludedPost ) {
111
+ $post = get_post( trim( $rmExcludedPost ) );
112
+ if ( ! is_object( $post ) ) {
113
+ continue;
114
+ }
115
+
116
+ $excludedPost = new \stdClass();
117
+ $excludedPost->value = $post->ID;
118
+ $excludedPost->type = $post->post_type;
119
+ $excludedPost->label = $post->post_title;
120
+ $excludedPost->link = get_permalink( $post->ID );
121
+
122
+ array_push( $excludedPosts, wp_json_encode( $excludedPost ) );
123
+ }
124
+ aioseo()->options->sitemap->general->advancedSettings->enable = true;
125
+ }
126
+ aioseo()->options->sitemap->general->advancedSettings->excludePosts = $excludedPosts;
127
+ }
128
+
129
+ /**
130
+ * Migrates the terms that are excluded from the sitemap.
131
+ *
132
+ * @since 4.0.0
133
+ *
134
+ * @return void
135
+ */
136
+ private function migrateExcludedTerms() {
137
+ if ( empty( $this->options['exclude_terms'] ) ) {
138
+ return;
139
+ }
140
+
141
+ $rmExcludedTerms = array_filter( explode( ',', $this->options['exclude_terms'] ) );
142
+ $excludedTerms = aioseo()->options->sitemap->general->advancedSettings->excludeTerms;
143
+
144
+ if ( count( $rmExcludedTerms ) ) {
145
+ foreach ( $rmExcludedTerms as $rmExcludedTerm ) {
146
+ $term = get_term( trim( $rmExcludedTerm ) );
147
+ if ( ! is_object( $term ) ) {
148
+ continue;
149
+ }
150
+
151
+ $excludedTerm = new \stdClass();
152
+ $excludedTerm->value = $term->term_id;
153
+ $excludedTerm->type = $term->taxonomy;
154
+ $excludedTerm->label = $term->name;
155
+ $excludedTerm->link = get_term_link( $term );
156
+
157
+ array_push( $excludedTerms, wp_json_encode( $excludedTerm ) );
158
+ }
159
+ aioseo()->options->sitemap->general->advancedSettings->enable = true;
160
+ }
161
+ aioseo()->options->sitemap->general->advancedSettings->excludeTerms = $excludedTerms;
162
+ }
163
+
164
+ /**
165
+ * Migrates the roles that are excluded from GA tracking.
166
+ *
167
+ * For some reason, Rank Math stores these in the sitemap settings.
168
+ *
169
+ * @since 4.0.0
170
+ *
171
+ * @return void
172
+ */
173
+ private function migrateExcludedRoles() {
174
+ if ( empty( $this->options['exclude_users'] ) ) {
175
+ return;
176
+ }
177
+
178
+ $excludedRoles = [];
179
+ foreach ( $this->options['exclude_users'] as $k => $v ) {
180
+ $excludedRoles[] = $k;
181
+ }
182
+
183
+ aioseo()->options->deprecated->webmasterTools->googleAnalytics->excludeUsers = $excludedRoles;
184
+ }
185
+ }
app/Common/ImportExport/RankMath/TitleMeta.php ADDED
@@ -0,0 +1,438 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\ImportExport\RankMath;
3
+
4
+ use AIOSEO\Plugin\Common\ImportExport;
5
+ use AIOSEO\Plugin\Common\Models;
6
+
7
+ // phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
8
+
9
+ /**
10
+ * Migrates the Search Appearance settings.
11
+ *
12
+ * @since 4.0.0
13
+ */
14
+ class TitleMeta extends ImportExport\SearchAppearance {
15
+
16
+ /**
17
+ * Our robot meta settings.
18
+ *
19
+ * @since 4.0.0
20
+ */
21
+ private $robotMetaSettings = [
22
+ 'noindex',
23
+ 'nofollow',
24
+ 'noarchive',
25
+ 'noimageindex',
26
+ 'nosnippet'
27
+ ];
28
+
29
+ /**
30
+ * Class constructor.
31
+ *
32
+ * @since 4.0.0
33
+ */
34
+ public function __construct() {
35
+ $this->options = get_option( 'rank-math-options-titles' );
36
+ if ( empty( $this->options ) ) {
37
+ return;
38
+ }
39
+
40
+ $this->migrateHomePageSettings();
41
+ $this->migratePostTypeSettings();
42
+ $this->migrateArchiveSettings();
43
+ $this->migrateRobotMetaSettings();
44
+ $this->migrateKnowledgeGraphSettings();
45
+ $this->migrateSocialMetaSettings();
46
+
47
+ $settings = [
48
+ 'title_separator' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'global', 'separator' ] ],
49
+ ];
50
+
51
+ aioseo()->importExport->rankMath->helpers->mapOldToNew( $settings, $this->options );
52
+ }
53
+
54
+ /**
55
+ * Migrates the homepage settings.
56
+ *
57
+ * @since 4.0.0
58
+ *
59
+ * @return void
60
+ */
61
+ private function migrateHomePageSettings() {
62
+ if ( isset( $this->options['homepage_title'] ) ) {
63
+ aioseo()->options->searchAppearance->global->siteTitle =
64
+ aioseo()->helpers->sanitizeOption( aioseo()->importExport->rankMath->helpers->macrosToSmartTags( $this->options['homepage_title'] ) );
65
+ }
66
+
67
+ if ( isset( $this->options['homepage_description'] ) ) {
68
+ aioseo()->options->searchAppearance->global->metaDescription =
69
+ aioseo()->helpers->sanitizeOption( aioseo()->importExport->rankMath->helpers->macrosToSmartTags( $this->options['homepage_description'] ) );
70
+ }
71
+
72
+ if ( isset( $this->options['homepage_facebook_title'] ) ) {
73
+ aioseo()->options->social->facebook->homePage->title = aioseo()->helpers->sanitizeOption( $this->options['homepage_facebook_title'] );
74
+ }
75
+
76
+ if ( isset( $this->options['homepage_facebook_description'] ) ) {
77
+ aioseo()->options->social->facebook->homePage->description = aioseo()->helpers->sanitizeOption( $this->options['homepage_facebook_description'] );
78
+ }
79
+
80
+ if ( isset( $this->options['homepage_facebook_image'] ) ) {
81
+ aioseo()->options->social->facebook->homePage->image = esc_url( $this->options['homepage_facebook_image'] );
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Migrates the archive settings.
87
+ *
88
+ * @since 4.0.0
89
+ *
90
+ * @return void
91
+ */
92
+ private function migrateArchiveSettings() {
93
+ $archives = [
94
+ 'author',
95
+ 'date',
96
+ ];
97
+
98
+ foreach ( $archives as $archive ) {
99
+ // Reset existing values first.
100
+ foreach ( $this->robotMetaSettings as $robotsMetaName ) {
101
+ aioseo()->options->searchAppearance->archives->$archive->advanced->robotsMeta->$robotsMetaName = false;
102
+ }
103
+
104
+ if ( isset( $this->options[ "disable_${archive}_archives" ] ) ) {
105
+ aioseo()->options->searchAppearance->archives->$archive->show = 'off' === $this->options[ "disable_${archive}_archives" ];
106
+ aioseo()->options->searchAppearance->archives->$archive->advanced->robotsMeta->default = 'off' === $this->options[ "disable_${archive}_archives" ];
107
+ aioseo()->options->searchAppearance->archives->$archive->advanced->robotsMeta->noindex = 'off' === $this->options[ "disable_${archive}_archives" ];
108
+ }
109
+
110
+ if ( isset( $this->options[ "${archive}_archive_title" ] ) ) {
111
+ aioseo()->options->searchAppearance->archives->$archive->title =
112
+ aioseo()->helpers->sanitizeOption( aioseo()->importExport->rankMath->helpers->macrosToSmartTags( $this->options[ "${archive}_archive_title" ] ) );
113
+ }
114
+
115
+ if ( isset( $this->options[ "${archive}_archive_description" ] ) ) {
116
+ aioseo()->options->searchAppearance->archives->$archive->metaDescription =
117
+ aioseo()->helpers->sanitizeOption( aioseo()->importExport->rankMath->helpers->macrosToSmartTags( $this->options[ "${archive}_archive_description" ] ) );
118
+ }
119
+
120
+ if ( ! empty( $this->options[ "${archive}_custom_robots" ] ) ) {
121
+ aioseo()->options->searchAppearance->archives->$archive->advanced->robotsMeta->default = 'off' === $this->options[ "${archive}_custom_robots" ];
122
+ }
123
+
124
+ if ( ! empty( $this->options[ "${archive}_robots" ] ) ) {
125
+ foreach ( $this->options[ "${archive}_robots" ] as $robotsName ) {
126
+ if ( 'index' === $robotsName ) {
127
+ continue;
128
+ }
129
+ aioseo()->options->searchAppearance->archives->author->advanced->robotsMeta->$robotsName = true;
130
+ }
131
+ }
132
+
133
+ if ( ! empty( $this->options[ "${archive}_advanced_robots" ] ) ) {
134
+ if ( ! empty( $this->options[ "${archive}_advanced_robots" ]['max-snippet'] ) ) {
135
+ aioseo()->options->searchAppearance->archives->$archive->advanced->robotsMeta->maxSnippet = intval( $this->options[ "${archive}_advanced_robots" ]['max-snippet'] );
136
+ }
137
+ if ( ! empty( $this->options[ "${archive}_advanced_robots" ]['max-video-preview'] ) ) {
138
+ aioseo()->options->searchAppearance->archives->$archive->advanced->robotsMeta->maxVideoPreview = intval( $this->options[ "${archive}_advanced_robots" ]['max-video-preview'] );
139
+ }
140
+ if ( ! empty( $this->options[ "${archive}_advanced_robots" ]['max-image-preview'] ) ) {
141
+ aioseo()->options->searchAppearance->archives->$archive->advanced->robotsMeta->maxImagePreview =
142
+ aioseo()->helpers->sanitizeOption( lcfirst( $this->options[ "${archive}_advanced_robots" ]['max-image-preview'] ) );
143
+ }
144
+ }
145
+ }
146
+
147
+ if ( isset( $this->options['search_title'] ) ) {
148
+ aioseo()->options->searchAppearance->archives->search->title =
149
+ aioseo()->helpers->sanitizeOption( aioseo()->importExport->rankMath->helpers->macrosToSmartTags( $this->options['search_title'] ) );
150
+ }
151
+
152
+ if ( ! empty( $this->options['noindex_search'] ) ) {
153
+ aioseo()->options->searchAppearance->archives->search->show = 'off' === $this->options['noindex_search'];
154
+ aioseo()->options->searchAppearance->archives->search->advanced->robotsMeta->default = 'off' === $this->options['noindex_search'];
155
+ aioseo()->options->searchAppearance->archives->search->advanced->robotsMeta->noindex = 'off' === $this->options['noindex_search'];
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Migrates the post type settings.
161
+ *
162
+ * @since 4.0.0
163
+ *
164
+ * @return void
165
+ */
166
+ private function migratePostTypeSettings() {
167
+ $supportedSettings = [
168
+ 'title',
169
+ 'description',
170
+ 'custom_robots',
171
+ 'robots',
172
+ 'advanced_robots',
173
+ 'default_rich_snippet',
174
+ 'default_article_type',
175
+ 'add_meta_box'
176
+ ];
177
+
178
+ foreach ( aioseo()->helpers->getPublicPostTypes( true ) as $postType ) {
179
+ // Reset existing values first.
180
+ foreach ( $this->robotMetaSettings as $robotsMetaName ) {
181
+ aioseo()->options->searchAppearance->dynamic->postTypes->$postType->advanced->robotsMeta->$robotsMetaName = false;
182
+ }
183
+
184
+ foreach ( $this->options as $name => $value ) {
185
+ if ( ! preg_match( "#^pt_${postType}_(.*)$#", $name, $match ) || ! in_array( $match[1], $supportedSettings, true ) ) {
186
+ continue;
187
+ }
188
+
189
+ switch ( $match[1] ) {
190
+ case 'title':
191
+ if ( 'page' === $postType ) {
192
+ $value = preg_replace( '#%category%#', '', $value );
193
+ $value = preg_replace( '#%excerpt%#', '', $value );
194
+ }
195
+ aioseo()->options->searchAppearance->dynamic->postTypes->$postType->title =
196
+ aioseo()->helpers->sanitizeOption( aioseo()->importExport->rankMath->helpers->macrosToSmartTags( $value ) );
197
+ break;
198
+ case 'description':
199
+ if ( 'page' === $postType ) {
200
+ $value = preg_replace( '#%category%#', '', $value );
201
+ $value = preg_replace( '#%excerpt%#', '', $value );
202
+ }
203
+ aioseo()->options->searchAppearance->dynamic->postTypes->$postType->metaDescription =
204
+ aioseo()->helpers->sanitizeOption( aioseo()->importExport->rankMath->helpers->macrosToSmartTags( $value ) );
205
+ break;
206
+ case 'custom_robots':
207
+ aioseo()->options->searchAppearance->dynamic->postTypes->$postType->advanced->robotsMeta->default = 'off' === $value;
208
+ break;
209
+ case 'robots':
210
+ if ( ! empty( $value ) ) {
211
+ foreach ( $value as $robotsName ) {
212
+ if ( 'index' === $robotsName ) {
213
+ continue;
214
+ }
215
+ aioseo()->options->searchAppearance->dynamic->postTypes->$postType->advanced->robotsMeta->$robotsName = true;
216
+ }
217
+ }
218
+ break;
219
+ case 'advanced_robots':
220
+ if ( ! empty( $value['max-snippet'] ) ) {
221
+ aioseo()->options->searchAppearance->dynamic->postTypes->$postType->advanced->robotsMeta->maxSnippet = intval( $value['max-snippet'] );
222
+ }
223
+ if ( ! empty( $value['max-video-preview'] ) ) {
224
+ aioseo()->options->searchAppearance->dynamic->postTypes->$postType->advanced->robotsMeta->maxVideoPreview = intval( $value['max-video-preview'] );
225
+ }
226
+ if ( ! empty( $value['max-image-preview'] ) ) {
227
+ aioseo()->options->searchAppearance->dynamic->postTypes->$postType->advanced->robotsMeta->maxImagePreview =
228
+ aioseo()->helpers->sanitizeOption( $value['max-image-preview'] );
229
+ }
230
+ break;
231
+ case 'add_meta_box':
232
+ aioseo()->options->searchAppearance->dynamic->postTypes->$postType->advanced->showMetaBox = 'on' === $value;
233
+ break;
234
+ case 'default_rich_snippet':
235
+ $value = preg_replace( '#\s#', '', $value );
236
+ if ( 'off' === lcfirst( $value ) || in_array( $postType, [ 'page', 'attachment' ], true ) ) {
237
+ aioseo()->options->searchAppearance->dynamic->postTypes->$postType->schemaType = 'none';
238
+ break;
239
+ }
240
+ if ( in_array( ucfirst( $value ), ImportExport\SearchAppearance::$supportedSchemaGraphs, true ) ) {
241
+ aioseo()->options->searchAppearance->dynamic->postTypes->$postType->schemaType = ucfirst( $value );
242
+ }
243
+ break;
244
+ case 'default_article_type':
245
+ if ( in_array( $postType, [ 'page', 'attachment' ], true ) ) {
246
+ break;
247
+ }
248
+ $value = preg_replace( '#\s#', '', $value );
249
+ if ( in_array( ucfirst( $value ), ImportExport\SearchAppearance::$supportedArticleGraphs, true ) ) {
250
+ aioseo()->options->searchAppearance->dynamic->postTypes->$postType->articleType = ucfirst( $value );
251
+ } else {
252
+ aioseo()->options->searchAppearance->dynamic->postTypes->$postType->articleType = 'BlogPosting';
253
+ }
254
+ break;
255
+ default:
256
+ break;
257
+ }
258
+ }
259
+ }
260
+ }
261
+
262
+ /**
263
+ * Migrates the robots meta settings.
264
+ *
265
+ * @since 4.0.0
266
+ *
267
+ * @return void
268
+ */
269
+ private function migrateRobotMetaSettings() {
270
+ // Reset existing values first.
271
+ foreach ( $this->robotMetaSettings as $robotsMetaName ) {
272
+ aioseo()->options->searchAppearance->advanced->globalRobotsMeta->$robotsMetaName = false;
273
+ }
274
+
275
+ if ( ! empty( $this->options['robots_global'] ) ) {
276
+ foreach ( $this->options['robots_global'] as $robotsName ) {
277
+ if ( 'index' === $robotsName ) {
278
+ continue;
279
+ }
280
+ aioseo()->options->searchAppearance->advanced->globalRobotsMeta->default = false;
281
+ aioseo()->options->searchAppearance->advanced->globalRobotsMeta->$robotsName = true;
282
+ }
283
+ }
284
+
285
+ if ( ! empty( $this->options['advanced_robots_global'] ) ) {
286
+ aioseo()->options->searchAppearance->advanced->globalRobotsMeta->default = false;
287
+
288
+ if ( ! empty( $this->options['robots_global']['max-snippet'] ) ) {
289
+ aioseo()->options->searchAppearance->advanced->globalRobotsMeta->maxSnippet = intval( $this->options['robots_global']['max-snippet'] );
290
+ }
291
+ if ( ! empty( $this->options['robots_global']['max-video-preview'] ) ) {
292
+ aioseo()->options->searchAppearance->advanced->globalRobotsMeta->maxVideoPreview = intval( $this->options['robots_global']['max-video-preview'] );
293
+ }
294
+ if ( ! empty( $this->options['robots_global']['max-image-preview'] ) ) {
295
+ aioseo()->options->searchAppearance->advanced->globalRobotsMeta->maxImagePreview =
296
+ aioseo()->helpers->sanitizeOption( $this->options['robots_global']['max-image-preview'] );
297
+ }
298
+ }
299
+
300
+ if ( ! empty( $this->options['noindex_paginated_pages'] ) ) {
301
+ aioseo()->options->searchAppearance->advanced->globalRobotsMeta->default = false;
302
+ aioseo()->options->searchAppearance->advanced->globalRobotsMeta->noindexPaginated = 'on' === $this->options['noindex_paginated_pages'];
303
+ }
304
+ }
305
+
306
+ /**
307
+ * Migrates the Knowledge Graph settings.
308
+ *
309
+ * @since 4.0.0
310
+ *
311
+ * @return void
312
+ */
313
+ private function migrateKnowledgeGraphSettings() {
314
+ if ( empty( $this->options['knowledgegraph_type'] ) ) {
315
+ return;
316
+ }
317
+
318
+ aioseo()->options->searchAppearance->global->schema->siteRepresents =
319
+ 'company' === $this->options['knowledgegraph_type'] ? 'organization' : 'person';
320
+
321
+ if ( ! empty( $this->options['knowledgegraph_name'] ) && 'company' === $this->options['knowledgegraph_type'] ) {
322
+ aioseo()->options->searchAppearance->global->schema->organizationName = aioseo()->helpers->sanitizeOption( $this->options['knowledgegraph_name'] );
323
+ } elseif ( ! empty( $this->options['knowledgegraph_logo'] ) ) {
324
+ aioseo()->options->searchAppearance->global->schema->person = 'manual';
325
+ aioseo()->options->searchAppearance->global->schema->personName = aioseo()->helpers->sanitizeOption( $this->options['knowledgegraph_name'] );
326
+ }
327
+
328
+ if ( ! empty( $this->options['knowledgegraph_logo'] ) && 'company' === $this->options['knowledgegraph_type'] ) {
329
+ aioseo()->options->searchAppearance->global->schema->organizationLogo = esc_url( $this->options['knowledgegraph_logo'] );
330
+ } elseif ( ! empty( $this->options['knowledgegraph_logo'] ) ) {
331
+ aioseo()->options->searchAppearance->global->schema->person = 'manual';
332
+ aioseo()->options->searchAppearance->global->schema->personLogo = esc_url( $this->options['knowledgegraph_logo'] );
333
+ }
334
+
335
+ $this->migrateKnowledgeGraphPhoneNumber();
336
+ }
337
+
338
+ /**
339
+ * Migrates the Knowledge Graph phone number.
340
+ *
341
+ * @since 4.0.0
342
+ *
343
+ * @return void
344
+ */
345
+ private function migrateKnowledgeGraphPhoneNumber() {
346
+ if ( empty( $this->options['phone'] ) ) {
347
+ return;
348
+ }
349
+
350
+ $phoneNumber = aioseo()->helpers->sanitizeOption( $this->options['phone'] );
351
+ if ( ! preg_match( '#\+\d+#', $phoneNumber ) ) {
352
+ $notification = Models\Notification::getNotificationByName( 'v3-migration-schema-number' );
353
+ if ( $notification->notification_name ) {
354
+ return;
355
+ }
356
+
357
+ Models\Notification::addNotification( [
358
+ 'slug' => uniqid(),
359
+ 'notification_name' => 'v3-migration-schema-number',
360
+ 'title' => __( 'Invalid Phone Number for Knowledge Graph', 'all-in-one-seo-pack' ),
361
+ 'content' => sprintf(
362
+ // Translators: 1 - The phone number.
363
+ __( 'We were unable to import the phone number that you previously entered for your Knowledge Graph schema markup.
364
+ As it needs to be internationally formatted, please enter it (%1$s) with the country code, e.g. +1 (555) 555-1234.', 'all-in-one-seo-pack' ),
365
+ "<strong>$phoneNumber</strong>"
366
+ ),
367
+ 'type' => 'warning',
368
+ 'level' => [ 'all' ],
369
+ 'button1_label' => __( 'Fix Now', 'all-in-one-seo-pack' ),
370
+ 'button1_action' => 'http://route#aioseo-search-appearance:schema-markup',
371
+ 'button2_label' => __( 'Remind Me Later', 'all-in-one-seo-pack' ),
372
+ 'button2_action' => 'http://action#notification/v3-migration-schema-number-reminder',
373
+ 'start' => gmdate( 'Y-m-d H:i:s' )
374
+ ] );
375
+ return;
376
+ }
377
+ aioseo()->options->searchAppearance->global->schema->phone = $phoneNumber;
378
+ }
379
+
380
+ /**
381
+ * Migrates the Social Meta settings.
382
+ *
383
+ * @since 4.0.0
384
+ *
385
+ * @return void
386
+ */
387
+ private function migrateSocialMetaSettings() {
388
+ if ( ! empty( $this->options['open_graph_image'] ) ) {
389
+ $defaultImage = esc_url( $this->options['open_graph_image'] );
390
+ aioseo()->options->social->facebook->general->defaultImagePosts = $defaultImage;
391
+ aioseo()->options->social->twitter->general->defaultImagePosts = $defaultImage;
392
+ }
393
+
394
+ if ( ! empty( $this->options['social_url_facebook'] ) ) {
395
+ aioseo()->options->social->profiles->urls->facebookPageUrl = esc_url( $this->options['social_url_facebook'] );
396
+ }
397
+
398
+ if ( ! empty( $this->options['facebook_author_urls'] ) ) {
399
+ aioseo()->options->social->facebook->advanced->enable = true;
400
+ aioseo()->options->social->facebook->advanced->authorUrl = esc_url( $this->options['facebook_author_urls'] );
401
+ }
402
+
403
+ if ( ! empty( $this->options['facebook_admin_id'] ) ) {
404
+ aioseo()->options->social->facebook->advanced->enable = true;
405
+ aioseo()->options->social->facebook->advanced->adminId = aioseo()->helpers->sanitizeOption( $this->options['facebook_admin_id'] );
406
+ }
407
+
408
+ if ( ! empty( $this->options['facebook_app_id'] ) ) {
409
+ aioseo()->options->social->facebook->advanced->enable = true;
410
+ aioseo()->options->social->facebook->advanced->appId = aioseo()->helpers->sanitizeOption( $this->options['facebook_app_id'] );
411
+ }
412
+
413
+ if ( ! empty( $this->options['twitter_author_names'] ) ) {
414
+ aioseo()->options->social->profiles->urls->twitterUrl =
415
+ 'https://twitter.com/' . aioseo()->helpers->sanitizeOption( $this->options['twitter_author_names'] );
416
+ }
417
+
418
+ if ( ! empty( $this->options['twitter_card_type'] ) ) {
419
+ preg_match( '#large#', $this->options['twitter_card_type'], $match );
420
+ aioseo()->options->social->twitter->general->defaultCardType = ! empty( $match ) ? 'summary_large_image' : 'summary';
421
+ }
422
+ }
423
+
424
+ /**
425
+ * Migrates the default social image for posts.
426
+ *
427
+ * @since 4.0.0
428
+ *
429
+ * @return void
430
+ */
431
+ private function migrateDefaultPostSocialImage() {
432
+ if ( ! empty( $this->options['open_graph_image'] ) ) {
433
+ $defaultImage = esc_url( $this->options['open_graph_image'] );
434
+ aioseo()->options->social->facebook->general->defaultImagePosts = $defaultImage;
435
+ aioseo()->options->social->twitter->general->defaultImagePosts = $defaultImage;
436
+ }
437
+ }
438
+ }
app/Common/ImportExport/SearchAppearance.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\ImportExport;
3
+
4
+ /**
5
+ * Migrates the Search Appearance settings.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ abstract class SearchAppearance {
10
+
11
+ /**
12
+ * The schema graphs we support.
13
+ *
14
+ * @since 4.0.0
15
+ *
16
+ * @var array
17
+ */
18
+ public static $supportedSchemaGraphs = [
19
+ 'none',
20
+ 'WebPage',
21
+ 'Article'
22
+ ];
23
+
24
+ /**
25
+ * The WebPage graphs we support.
26
+ *
27
+ * @since 4.0.0
28
+ *
29
+ * @var array
30
+ */
31
+ public static $supportedWebPageGraphs = [
32
+ 'AboutPage',
33
+ 'CollectionPage',
34
+ 'ContactPage',
35
+ 'FAQPage',
36
+ 'ItemPage',
37
+ 'ProfilePage',
38
+ 'QAPage',
39
+ 'RealEstateListing',
40
+ 'SearchResultsPage',
41
+ 'WebPage'
42
+ ];
43
+
44
+ /**
45
+ * The Article graphs we support.
46
+ *
47
+ * @since 4.0.0
48
+ *
49
+ * @var array
50
+ */
51
+ public static $supportedArticleGraphs = [
52
+ 'Article',
53
+ 'BlogPosting',
54
+ 'NewsArticle'
55
+ ];
56
+ }
app/Common/ImportExport/SeoPress/Helpers.php ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\ImportExport\SeoPress;
3
+
4
+ use AIOSEO\Plugin\Common\ImportExport;
5
+
6
+ // phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
7
+
8
+ /**
9
+ * Contains helper methods for the import from SEOPress.
10
+ *
11
+ * @since 4.0.0
12
+ */
13
+ class Helpers extends ImportExport\Helpers {
14
+
15
+ /**
16
+ * Converts the macros from SEOPress to our own smart tags.
17
+ *
18
+ * @since 4.0.0
19
+ *
20
+ * @param string $string The string with macros.
21
+ * @return string $string The string with smart tags.
22
+ */
23
+ public function macrosToSmartTags( $string ) {
24
+ $macros = [
25
+ '%%tagline%%' => '#tagline',
26
+ ];
27
+
28
+ foreach ( $macros as $macro => $tag ) {
29
+ $string = preg_replace( "#$macro(?![a-zA-Z0-9_])#im", $tag, $string );
30
+ }
31
+ return $string;
32
+ }
33
+ }
app/Common/ImportExport/SeoPress/SearchAppearance.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\ImportExport\SeoPress;
3
+
4
+ // phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
5
+
6
+ /**
7
+ * Migrates the Search Appearance settings.
8
+ *
9
+ * @since 4.0.0
10
+ */
11
+ class SearchAppearance {
12
+
13
+ /**
14
+ * Class constructor.
15
+ *
16
+ * @since 4.0.0
17
+ */
18
+ public function __construct() {
19
+ $this->options = get_option( 'seopress_titles_option_name' );
20
+ if ( empty( $this->options ) ) {
21
+ return;
22
+ }
23
+
24
+ $this->migrateTitleFormats();
25
+
26
+ }
27
+
28
+ /**
29
+ * Migrates the title format settings.
30
+ *
31
+ * @since 4.0.0
32
+ *
33
+ * @return void
34
+ */
35
+ private function migrateTitleFormats() {
36
+ if ( isset( $this->options['seopress_titles_home_site_title'] ) ) {
37
+ aioseo()->options->searchAppearance->global->siteTitle =
38
+ aioseo()->importExport->seoPress->helpers->macrosToSmartTags( $this->options['seopress_titles_home_site_title'] );
39
+ }
40
+ }
41
+
42
+ }
app/Common/ImportExport/SeoPress/SeoPress.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\ImportExport\SeoPress;
3
+
4
+ class SeoPress {
5
+
6
+ /**
7
+ * Starts the import.
8
+ *
9
+ * @since 4.0.0
10
+ *
11
+ * @return void
12
+ */
13
+ public function doImport() {
14
+ // @TODO: [V4+] Write this once SEOPress is going in.
15
+ }
16
+
17
+ /**
18
+ * Imports the settings.
19
+ *
20
+ * @since 4.0.0
21
+ *
22
+ * @return void
23
+ */
24
+ public function migrateSettings() {
25
+ $this->helpers = new Helpers();
26
+
27
+ new SearchAppearance();
28
+ }
29
+ }
app/Common/ImportExport/YoastSeo/GeneralSettings.php ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\ImportExport\YoastSeo;
3
+
4
+ // phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
5
+
6
+ /**
7
+ * Migrates the General Settings.
8
+ *
9
+ * @since 4.0.0
10
+ */
11
+ class GeneralSettings {
12
+
13
+ /**
14
+ * Class constructor.
15
+ *
16
+ * @since 4.0.0
17
+ */
18
+ public function __construct() {
19
+ $this->options = get_option( 'wpseo' );
20
+ if ( empty( $this->options ) ) {
21
+ return;
22
+ }
23
+
24
+ $this->checkIfTrueSeoIsDisabled();
25
+
26
+ $settings = [
27
+ 'googleverify' => [ 'type' => 'string', 'newOption' => [ 'webmasterTools', 'google' ] ],
28
+ 'msverify' => [ 'type' => 'string', 'newOption' => [ 'webmasterTools', 'bing' ] ],
29
+ 'yandexverify' => [ 'type' => 'string', 'newOption' => [ 'webmasterTools', 'yandex' ] ],
30
+ 'baiduverify' => [ 'type' => 'string', 'newOption' => [ 'webmasterTools', 'baidu' ] ],
31
+ 'enable_xml_sitemap' => [ 'type' => 'boolean', 'newOption' => [ 'sitemap', 'general', 'enable' ] ],
32
+ ];
33
+
34
+ aioseo()->importExport->yoastSeo->helpers->mapOldToNew( $settings, $this->options );
35
+ }
36
+
37
+ /**
38
+ * Checks if TrueSEO should be disabled.
39
+ *
40
+ * @since 4.0.0
41
+ *
42
+ * @return void
43
+ */
44
+ private function checkIfTrueSeoIsDisabled() {
45
+ if ( ! isset( $this->options['content_analysis_active'] ) || ! isset( $this->options['keyword_analysis_active'] ) ) {
46
+ return;
47
+ }
48
+
49
+ if ( ! $this->options['content_analysis_active'] && ! $this->options['keyword_analysis_active'] ) {
50
+ aioseo()->options->advanced->truSeo = false;
51
+ }
52
+ }
53
+ }
app/Common/ImportExport/YoastSeo/Helpers.php ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\ImportExport\YoastSeo;
3
+
4
+ use AIOSEO\Plugin\Common\ImportExport;
5
+
6
+ // phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
7
+
8
+ /**
9
+ * Contains helper methods for the import from Rank Math.
10
+ *
11
+ * @since 4.0.0
12
+ */
13
+ class Helpers extends ImportExport\Helpers {
14
+
15
+ /**
16
+ * Converts the macros from Yoast SEO to our own smart tags.
17
+ *
18
+ * @since 4.0.0
19
+ *
20
+ * @param string $string The string with macros.
21
+ * @return string $string The string with smart tags.
22
+ */
23
+ public function macrosToSmartTags( $string ) {
24
+ $macros = [
25
+ '%%sitename%%' => '#site_title',
26
+ '%%sitedesc%%' => '#tagline',
27
+ '%%sep%%' => '#separator_sa',
28
+ '%%title%%' => '#post_title',
29
+ '%%term_title%%' => '#taxonomy_title',
30
+ '%%term_description%%' => '#taxonomy_description',
31
+ '%%category_description%%' => '#taxonomy_description',
32
+ '%%tag_description%%' => '#taxonomy_description',
33
+ '%%primary_category%%' => '#taxonomy_title',
34
+ '%%archive_title%%' => '#archive_title',
35
+ '%%pagenumber%%' => '#page_number',
36
+ '%%caption%%' => '#attachment_caption',
37
+ '%%name%%' => '#author_first_name #author_last_name',
38
+ '%%user_description%%' => '#author_bio',
39
+ '%%date%%' => '#archive_date',
40
+ '%%currentday%%' => '#current_day',
41
+ '%%currentmonth%%' => '#current_month',
42
+ '%%currentyear%%' => '#current_year',
43
+ '%%searchphrase%%' => '#search_term',
44
+ '%%AUTHORLINK%%' => '#author_link',
45
+ '%%POSTLINK%%' => '#post_link',
46
+ '%%BLOGLINK%%' => '#site_link',
47
+ '%%category%%' => '#categories',
48
+ '%%parent_title%%' => '#parent_title',
49
+ '%%wc_sku%%' => '#woocommerce_sku',
50
+ '%%wc_price%%' => '#woocommerce_price',
51
+ '%%wc_brand%%' => '#woocommerce_brand',
52
+ '%%tag%%' => '',
53
+ '%%excerpt%%' => '#post_excerpt',
54
+ '%%excerpt_only%%' => '',
55
+ '%%id%%' => '',
56
+ '%%parent_title%%' => '',
57
+ '%%page%%' => '',
58
+ '%%modified%%' => '',
59
+ '%%pt_single%%' => '',
60
+ '%%pt_plural%%' => '',
61
+ '%%pagetotal%%' => '',
62
+ '%%focuskw%%' => '',
63
+ '%%term404%%' => '',
64
+ '%%ct_desc_[^%]*%%' => '',
65
+ '%%[^%]*%%' => ''
66
+ ];
67
+
68
+ if ( preg_match( '#%%BLOGDESCLINK%%#', $string ) ) {
69
+ $blogDescriptionLink = '<a href="' .
70
+ aioseo()->helpers->decodeHtmlEntities( get_bloginfo( 'url' ) ) . '">' .
71
+ aioseo()->helpers->decodeHtmlEntities( get_bloginfo( 'name' ) ) . ' - ' .
72
+ aioseo()->helpers->decodeHtmlEntities( get_bloginfo( 'description' ) ) . '</a>';
73
+
74
+ $string = str_replace( '%%BLOGDESCLINK%%', $blogDescriptionLink, $string );
75
+ }
76
+
77
+ if ( preg_match_all( '#%%cf_([^%]*)%%#', $string, $matches ) && ! empty( $matches[1] ) ) {
78
+ foreach ( $matches[1] as $name ) {
79
+ if ( ! preg_match( '#\s#', $name ) ) {
80
+ $string = preg_replace( "#%%cf_$name%%#", "#custom_field-$name", $string );
81
+ }
82
+ }
83
+ }
84
+
85
+ if ( preg_match_all( '#%%tax_([^%]*)%%#', $string, $matches ) && ! empty( $matches[1] ) ) {
86
+ foreach ( $matches[1] as $name ) {
87
+ if ( ! preg_match( '#\s#', $name ) ) {
88
+ $string = preg_replace( "#%%tax_$name%%#", "#tax_name-$name", $string );
89
+ }
90
+ }
91
+ }
92
+
93
+ foreach ( $macros as $macro => $tag ) {
94
+ $string = preg_replace( "#$macro(?![a-zA-Z0-9_])#im", $tag, $string );
95
+ }
96
+
97
+ // Strip out all remaining tags.
98
+ $string = preg_replace( '/%[^\%\s]*\([^\%]*\)%/i', '', preg_replace( '/%[^\%\s]*%/i', '', $string ) );
99
+ return trim( $string );
100
+ }
101
+ }
app/Common/ImportExport/YoastSeo/PostMeta.php ADDED
@@ -0,0 +1,230 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\ImportExport\YoastSeo;
3
+
4
+ use AIOSEO\Plugin\Common\ImportExport;
5
+ use AIOSEO\Plugin\Common\Models;
6
+
7
+ // phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
8
+
9
+ /**
10
+ * Imports the post meta from Yoast SEO.
11
+ *
12
+ * @since 4.0.0
13
+ */
14
+ class PostMeta {
15
+
16
+ /**
17
+ * Class constructor.
18
+ *
19
+ * @since 4.0.0
20
+ */
21
+ public function scheduleImport() {
22
+ try {
23
+ if ( as_next_scheduled_action( aioseo()->importExport->yoastSeo->postActionName ) ) {
24
+ return;
25
+ }
26
+
27
+ if ( ! get_transient( 'aioseo_import_post_meta_yoast_seo' ) ) {
28
+ set_transient( 'aioseo_import_post_meta_yoast_seo', time(), WEEK_IN_SECONDS );
29
+ }
30
+
31
+ as_schedule_single_action( time(), aioseo()->importExport->yoastSeo->postActionName, [], 'aioseo' );
32
+ } catch ( \Exception $e ) {
33
+ // Do nothing.
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Imports the post meta.
39
+ *
40
+ * @since 4.0.0
41
+ *
42
+ * @return void
43
+ */
44
+ public function importPostMeta() {
45
+ $postsPerAction = 100;
46
+ $publicPostTypes = implode( "', '", aioseo()->helpers->getPublicPostTypes( true ) );
47
+ $timeStarted = gmdate( 'Y-m-d H:i:s', get_transient( 'aioseo_import_post_meta_yoast_seo' ) );
48
+
49
+ $posts = aioseo()->db
50
+ ->start( 'posts' . ' as p' )
51
+ ->select( 'p.ID, p.post_type' )
52
+ ->join( 'postmeta as pm', '`p`.`ID` = `pm`.`post_id`' )
53
+ ->leftJoin( 'aioseo_posts as ap', '`p`.`ID` = `ap`.`post_id`' )
54
+ ->whereRaw( "pm.meta_key LIKE '_yoast_wpseo_%'" )
55
+ ->whereRaw( "( p.post_type IN ( '$publicPostTypes' ) )" )
56
+ ->whereRaw( "( ap.post_id IS NULL OR ap.updated < '$timeStarted' )" )
57
+ ->orderBy( 'p.ID DESC' )
58
+ ->limit( $postsPerAction )
59
+ ->run()
60
+ ->result();
61
+
62
+ if ( ! $posts || ! count( $posts ) ) {
63
+ delete_transient( 'aioseo_import_post_meta_yoast_seo' );
64
+ return;
65
+ }
66
+
67
+ $mappedMeta = [
68
+ '_yoast_wpseo_title' => 'title',
69
+ '_yoast_wpseo_metadesc' => 'description',
70
+ '_yoast_wpseo_canonical' => 'canonical_url',
71
+ '_yoast_wpseo_meta-robots-noindex' => 'robots_noindex',
72
+ '_yoast_wpseo_meta-robots-nofollow' => 'robots_nofollow',
73
+ '_yoast_wpseo_meta-robots-adv' => '',
74
+ '_yoast_wpseo_focuskw' => '',
75
+ '_yoast_wpseo_focuskeywords' => '',
76
+ '_yoast_wpseo_opengraph-title' => 'og_title',
77
+ '_yoast_wpseo_opengraph-description' => 'og_description',
78
+ '_yoast_wpseo_opengraph-image' => 'og_image_custom_url',
79
+ '_yoast_wpseo_twitter-title' => 'twitter_title',
80
+ '_yoast_wpseo_twitter-description' => 'twitter_description',
81
+ '_yoast_wpseo_twitter-image' => 'twitter_image_custom_url',
82
+ '_yoast_wpseo_schema_page_type' => '',
83
+ '_yoast_wpseo_schema_article_type' => ''
84
+ ];
85
+
86
+ foreach ( $posts as $post ) {
87
+ $postMeta = aioseo()->db
88
+ ->start( 'postmeta' . ' as pm' )
89
+ ->select( 'pm.meta_key, pm.meta_value' )
90
+ ->where( 'pm.post_id', $post->ID )
91
+ ->whereRaw( "`pm`.`meta_key` LIKE '_yoast_wpseo_%'" )
92
+ ->run()
93
+ ->result();
94
+
95
+ if ( ! $postMeta || ! count( $postMeta ) ) {
96
+ continue;
97
+ }
98
+
99
+ $meta = [
100
+ 'post_id' => (int) $post->ID,
101
+ ];
102
+
103
+ foreach ( $postMeta as $record ) {
104
+ $name = $record->meta_key;
105
+ $value = $record->meta_value;
106
+
107
+ if ( ! in_array( $name, array_keys( $mappedMeta ), true ) ) {
108
+ continue;
109
+ }
110
+
111
+ switch ( $name ) {
112
+ case '_yoast_wpseo_meta-robots-noindex':
113
+ case '_yoast_wpseo_meta-robots-nofollow':
114
+ if ( (bool) $value ) {
115
+ $meta[ $mappedMeta[ $name ] ] = ! empty( $value );
116
+ $meta['robots_default'] = false;
117
+ }
118
+ break;
119
+ case '_yoast_wpseo_meta-robots-adv':
120
+ $values = explode( ',', $value );
121
+ if ( $values ) {
122
+ foreach ( $values as $value ) {
123
+ $meta[ "robots_$value" ] = true;
124
+ }
125
+ }
126
+ break;
127
+ case '_yoast_wpseo_canonical':
128
+ $meta[ $mappedMeta[ $name ] ] = esc_url( $value );
129
+ break;
130
+ case '_yoast_wpseo_opengraph-image':
131
+ $meta['og_image_type'] = 'custom_image';
132
+ $meta[ $mappedMeta[ $name ] ] = esc_url( $value );
133
+ break;
134
+ case '_yoast_wpseo_twitter-image':
135
+ $meta['twitter_use_og'] = false;
136
+ $meta['twitter_image_type'] = 'custom_image';
137
+ $meta[ $mappedMeta[ $name ] ] = esc_url( $value );
138
+ break;
139
+ case '_yoast_wpseo_schema_page_type':
140
+ $value = preg_replace( '#\s#', '', $value );
141
+ if ( in_array( $post->post_type, [ 'post', 'page', 'attachment' ], true ) ) {
142
+ break;
143
+ }
144
+ if ( in_array( $value, ImportExport\SearchAppearance::$supportedWebPageGraphs, true ) ) {
145
+ $meta[ $mappedMeta[ $name ] ] = 'WebPage';
146
+ $options = new \stdClass();
147
+ $options->webPage = [ 'webPageType' => $value ];
148
+ }
149
+ $meta['schema_type_options'] = wp_json_encode( $options );
150
+ break;
151
+ case '_yoast_wpseo_schema_article_type':
152
+ $value = preg_replace( '#\s#', '', $value );
153
+ if ( 'none' === lcfirst( $value ) ) {
154
+ $meta[ $mappedMeta[ $name ] ] = 'None';
155
+ break;
156
+ }
157
+
158
+ if ( in_array( $post->post_type, [ 'page', 'attachment' ], true ) ) {
159
+ break;
160
+ }
161
+
162
+ $options = new \stdClass();
163
+ if ( isset( $meta['schema_type_options'] ) ) {
164
+ $options = json_decode( $meta['schema_type_options'] );
165
+ }
166
+
167
+ $options->article = [ 'articleType' => 'Article' ];
168
+ if ( in_array( $value, ImportExport\SearchAppearance::$supportedArticleGraphs, true ) ) {
169
+ $options->article = [ 'articleType' => $value ];
170
+ } else {
171
+ $options->article = [ 'articleType' => 'BlogPosting' ];
172
+ }
173
+
174
+ $meta['schema_type'] = 'Article';
175
+ $meta['schema_type_options'] = wp_json_encode( $options );
176
+ break;
177
+ case '_yoast_wpseo_focuskw':
178
+ $keyphrase = [
179
+ 'focus' => [ 'keyphrase' => aioseo()->helpers->sanitizeOption( $value ) ],
180
+ 'additional' => []
181
+ ];
182
+ $meta['keyphrases'] = wp_json_encode( $keyphrase );
183
+ break;
184
+ case '_yoast_wpseo_focuskeywords':
185
+ $keyphrases = [];
186
+ if ( ! empty( $meta[ $mappedMeta[ $name ] ] ) ) {
187
+ $keyphrases = (array) json_decode( $meta[ $mappedMeta[ $name ] ] );
188
+ }
189
+
190
+ $yoastKeyphrases = json_decode( $value );
191
+ for ( $i = 0; $i < count( $yoastKeyphrases ); $i++ ) {
192
+ $keyphrase = [ 'keyphrase' => aioseo()->helpers->sanitizeOption( $yoastKeyphrases[ $i ]->keyword ) ];
193
+ $keyphrases['additional'][ $i ] = $keyphrase;
194
+ }
195
+
196
+ if ( ! empty( $keyphrases ) ) {
197
+ $meta['keyphrases'] = wp_json_encode( $keyphrases );
198
+ }
199
+ break;
200
+ case '_yoast_wpseo_title':
201
+ case '_yoast_wpseo_metadesc':
202
+ case '_yoast_wpseo_opengraph-title':
203
+ case '_yoast_wpseo_opengraph-description':
204
+ if ( 'page' === $post->post_type ) {
205
+ $value = preg_replace( '#%%primary_category%%#', '', $value );
206
+ $value = preg_replace( '#%%excerpt%%#', '', $value );
207
+ }
208
+ $value = aioseo()->importExport->yoastSeo->helpers->macrosToSmartTags( $value );
209
+ default:
210
+ $meta[ $mappedMeta[ $name ] ] = esc_html( wp_strip_all_tags( strval( $value ) ) );
211
+ break;
212
+ }
213
+ }
214
+
215
+ $aioseoPost = Models\Post::getPost( (int) $post->ID );
216
+ $aioseoPost->set( $meta );
217
+ $aioseoPost->save();
218
+ }
219
+
220
+ if ( count( $posts ) === $postsPerAction ) {
221
+ try {
222
+ as_schedule_single_action( time() + 5, aioseo()->importExport->yoastSeo->postActionName, [], 'aioseo' );
223
+ } catch ( \Exception $e ) {
224
+ // Do nothing.
225
+ }
226
+ } else {
227
+ delete_transient( 'aioseo_import_post_meta_yoast_seo' );
228
+ }
229
+ }
230
+ }
app/Common/ImportExport/YoastSeo/SearchAppearance.php ADDED
@@ -0,0 +1,260 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\ImportExport\YoastSeo;
3
+
4
+ use AIOSEO\Plugin\Common\ImportExport;
5
+
6
+ // phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
7
+
8
+ /**
9
+ * Migrates the Search Appearance settings.
10
+ *
11
+ * @since 4.0.0
12
+ */
13
+ class SearchAppearance {
14
+
15
+ /**
16
+ * Class constructor.
17
+ *
18
+ * @since 4.0.0
19
+ */
20
+ public function __construct() {
21
+ $this->options = get_option( 'wpseo_titles' );
22
+ if ( empty( $this->options ) ) {
23
+ return;
24
+ }
25
+
26
+ $this->migrateSeparator();
27
+ $this->migrateTitleFormats();
28
+ $this->migrateDescriptionFormats();
29
+ $this->migrateNoindexSettings();
30
+ $this->migratePostTypeSettings();
31
+ $this->migrateRedirectAttachments();
32
+ $this->migrateKnowledgeGraphSettings();
33
+ $this->migrateRssContentSettings();
34
+ }
35
+
36
+ /**
37
+ * Migrates the title/description separator.
38
+ *
39
+ * @since 4.0.0
40
+ *
41
+ * @return void
42
+ */
43
+ private function migrateSeparator() {
44
+ $separators = [
45
+ 'sc-dash' => '-',
46
+ 'sc-ndash' => '&ndash;',
47
+ 'sc-mdash' => '&mdash;',
48
+ 'sc-colon' => ':',
49
+ 'sc-middot' => '&middot;',
50
+ 'sc-bull' => '&bull;',
51
+ 'sc-star' => '*',
52
+ 'sc-smstar' => '&#8902;',
53
+ 'sc-pipe' => '|',
54
+ 'sc-tilde' => '~',
55
+ 'sc-laquo' => '&laquo;',
56
+ 'sc-raquo' => '&raquo;',
57
+ 'sc-lt' => '&lt;',
58
+ 'sc-gt' => '&gt;',
59
+ ];
60
+
61
+ if ( ! empty( $this->options['separator'] ) && in_array( $this->options['separator'], array_keys( $separators ), true ) ) {
62
+ aioseo()->options->searchAppearance->global->separator = $separators[ $this->options['separator'] ];
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Migrates the title formats.
68
+ *
69
+ * @since 4.0.0
70
+ *
71
+ * @return void
72
+ */
73
+ private function migrateTitleFormats() {
74
+ $settings = [
75
+ 'title-home-wpseo' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'global', 'siteTitle' ] ],
76
+ 'title-author-wpseo' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'archives', 'author', 'title' ] ],
77
+ 'title-archive-wpseo' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'archives', 'date', 'title' ] ],
78
+ 'title-search-wpseo' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'archives', 'search', 'title' ] ],
79
+ ];
80
+
81
+ aioseo()->importExport->yoastSeo->helpers->mapOldToNew( $settings, $this->options, true );
82
+ }
83
+
84
+ /**
85
+ * Migrates the description formats.
86
+ *
87
+ * @since 4.0.0
88
+ *
89
+ * @return void
90
+ */
91
+ private function migrateDescriptionFormats() {
92
+ $settings = [
93
+ 'metadesc-home-wpseo' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'global', 'metaDescription' ] ],
94
+ 'metadesc-author-wpseo' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'archives', 'author', 'metaDescription' ] ],
95
+ 'metadesc-archive-wpseo' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'archives', 'date', 'metaDescription' ] ],
96
+ ];
97
+
98
+ aioseo()->importExport->yoastSeo->helpers->mapOldToNew( $settings, $this->options, true );
99
+ }
100
+
101
+ /**
102
+ * Migrates the noindex settings.
103
+ *
104
+ * @since 4.0.0
105
+ *
106
+ * @return void
107
+ */
108
+ private function migrateNoindexSettings() {
109
+ if ( ! empty( $this->options['noindex-author-wpseo'] ) ) {
110
+ aioseo()->options->searchAppearance->archives->author->show = false;
111
+ aioseo()->options->searchAppearance->archives->author->advanced->robotsMeta->default = false;
112
+ aioseo()->options->searchAppearance->archives->author->advanced->robotsMeta->noindex = true;
113
+ } else {
114
+ aioseo()->options->searchAppearance->archives->author->show = true;
115
+ }
116
+
117
+ if ( ! empty( $this->options['noindex-archive-wpseo'] ) ) {
118
+ aioseo()->options->searchAppearance->archives->date->show = false;
119
+ aioseo()->options->searchAppearance->archives->date->advanced->robotsMeta->default = false;
120
+ aioseo()->options->searchAppearance->archives->date->advanced->robotsMeta->noindex = true;
121
+ } else {
122
+ aioseo()->options->searchAppearance->archives->date->show = true;
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Migrates the post type settings.
128
+ *
129
+ * @since 4.0.0
130
+ *
131
+ * @return void
132
+ */
133
+ private function migratePostTypeSettings() {
134
+ $supportedSettings = [
135
+ 'title',
136
+ 'metadesc',
137
+ 'noindex',
138
+ 'display-metabox-pt',
139
+ 'schema-page-type',
140
+ 'schema-article-type'
141
+ ];
142
+
143
+ foreach ( aioseo()->helpers->getPublicPostTypes( true ) as $postType ) {
144
+ foreach ( $this->options as $name => $value ) {
145
+ if ( ! preg_match( "#(.*)-$postType$#", $name, $match ) || ! in_array( $match[1], $supportedSettings, true ) ) {
146
+ continue;
147
+ }
148
+
149
+ switch ( $match[1] ) {
150
+ case 'title':
151
+ if ( 'page' === $postType ) {
152
+ $value = preg_replace( '#%%primary_category%%#', '', $value );
153
+ $value = preg_replace( '#%%excerpt%%#', '', $value );
154
+ }
155
+ aioseo()->options->searchAppearance->dynamic->postTypes->$postType->title =
156
+ aioseo()->helpers->sanitizeOption( aioseo()->importExport->yoastSeo->helpers->macrosToSmartTags( $value ) );
157
+ break;
158
+ case 'metadesc':
159
+ if ( 'page' === $postType ) {
160
+ $value = preg_replace( '#%%primary_category%%#', '', $value );
161
+ $value = preg_replace( '#%%excerpt%%#', '', $value );
162
+ }
163
+ aioseo()->options->searchAppearance->dynamic->postTypes->$postType->metaDescription =
164
+ aioseo()->helpers->sanitizeOption( aioseo()->importExport->yoastSeo->helpers->macrosToSmartTags( $value ) );
165
+ break;
166
+ case 'noindex':
167
+ aioseo()->options->searchAppearance->dynamic->postTypes->$postType->show = empty( $value ) ? true : false;
168
+ aioseo()->options->searchAppearance->dynamic->postTypes->$postType->advanced->robotsMeta->default = empty( $value ) ? true : false;
169
+ aioseo()->options->searchAppearance->dynamic->postTypes->$postType->advanced->robotsMeta->noindex = empty( $value ) ? false : true;
170
+ break;
171
+ case 'display-metabox-pt':
172
+ if ( empty( $value ) ) {
173
+ aioseo()->options->searchAppearance->dynamic->postTypes->$postType->advanced->showMetaBox = false;
174
+ }
175
+ break;
176
+ case 'schema-page-type':
177
+ $value = preg_replace( '#\s#', '', $value );
178
+ if ( in_array( $postType, [ 'post', 'page', 'attachment' ], true ) ) {
179
+ break;
180
+ }
181
+ aioseo()->options->searchAppearance->dynamic->postTypes->$postType->schemaType = 'WebPage';
182
+ if ( in_array( $value, ImportExport\SearchAppearance::$supportedWebPageGraphs, true ) ) {
183
+ aioseo()->options->searchAppearance->dynamic->postTypes->$postType->webPageType = $value;
184
+ }
185
+ break;
186
+ case 'schema-article-type':
187
+ $value = preg_replace( '#\s#', '', $value );
188
+ if ( 'none' === lcfirst( $value ) ) {
189
+ aioseo()->options->searchAppearance->dynamic->postTypes->$postType->articleType = 'none';
190
+ break;
191
+ }
192
+
193
+ aioseo()->options->searchAppearance->dynamic->postTypes->$postType->articleType = 'Article';
194
+ if ( in_array( $value, ImportExport\SearchAppearance::$supportedArticleGraphs, true ) ) {
195
+ if ( ! in_array( $postType, [ 'page', 'attachment' ], true ) ) {
196
+ aioseo()->options->searchAppearance->dynamic->postTypes->$postType->articleType = $value;
197
+ }
198
+ } else {
199
+ aioseo()->options->searchAppearance->dynamic->postTypes->$postType->articleType = 'BlogPosting';
200
+ }
201
+ break;
202
+ default:
203
+ break;
204
+ }
205
+ }
206
+ }
207
+ }
208
+
209
+ /**
210
+ * Migrates the Knowledge Graph settings.
211
+ *
212
+ * @since 4.0.0
213
+ *
214
+ * @return void
215
+ */
216
+ private function migrateKnowledgeGraphSettings() {
217
+ if ( ! empty( $this->options['company_or_person'] ) ) {
218
+ aioseo()->options->searchAppearance->global->schema->siteRepresents =
219
+ 'company' === $this->options['company_or_person'] ? 'organization' : 'person';
220
+ }
221
+
222
+ $settings = [
223
+ 'company_or_person_user_id' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'global', 'schema', 'person' ] ],
224
+ 'person_logo' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'global', 'schema', 'personLogo' ] ],
225
+ 'person_name' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'global', 'schema', 'personName' ] ],
226
+ 'company_name' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'global', 'schema', 'organizationName' ] ],
227
+ 'company_logo' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'global', 'schema', 'organizationLogo' ] ],
228
+ ];
229
+
230
+ aioseo()->importExport->yoastSeo->helpers->mapOldToNew( $settings, $this->options );
231
+ }
232
+
233
+ /**
234
+ * Migrates the RSS content settings.
235
+ *
236
+ * @since 4.0.0
237
+ *
238
+ * @return void
239
+ */
240
+ private function migrateRssContentSettings() {
241
+ if ( isset( $this->options['rssbefore'] ) ) {
242
+ aioseo()->options->rssContent->before = esc_html( aioseo()->importExport->yoastSeo->helpers->macrosToSmartTags( $this->options['rssbefore'] ) );
243
+ }
244
+
245
+ if ( isset( $this->options['rssafter'] ) ) {
246
+ aioseo()->options->rssContent->after = esc_html( aioseo()->importExport->yoastSeo->helpers->macrosToSmartTags( $this->options['rssafter'] ) );
247
+ }
248
+ }
249
+
250
+ /**
251
+ * Migrates the Redirect Attachments setting.
252
+ *
253
+ * @since 4.0.0
254
+ *
255
+ * @return void
256
+ */
257
+ private function migrateRedirectAttachments() {
258
+ aioseo()->options->searchAppearance->dynamic->postTypes->attachment->redirectAttachmentUrls = empty( $this->options['disable-attachment'] ) ? 'disabled' : 'attachment';
259
+ }
260
+ }
app/Common/ImportExport/YoastSeo/SocialMeta.php ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\ImportExport\YoastSeo;
3
+
4
+ // phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
5
+
6
+ /**
7
+ * Migrates the Social Meta.
8
+ *
9
+ * @since 4.0.0
10
+ */
11
+ class SocialMeta {
12
+
13
+ /**
14
+ * Class constructor.
15
+ *
16
+ * @since 4.0.0
17
+ */
18
+ public function __construct() {
19
+ $this->options = get_option( 'wpseo_social' );
20
+
21
+ if ( empty( $this->options ) ) {
22
+ return;
23
+ }
24
+
25
+ $this->migrateSocialUrls();
26
+ $this->migrateFacebookSettings();
27
+ $this->migrateTwitterSettings();
28
+ $this->migrateFacebookAdminId();
29
+
30
+ $settings = [
31
+ 'pinterestverify' => [ 'type' => 'string', 'newOption' => [ 'webmasterTools', 'pinterest' ] ]
32
+ ];
33
+
34
+ aioseo()->importExport->yoastSeo->helpers->mapOldToNew( $settings, $this->options );
35
+ }
36
+
37
+ private function migrateSocialUrls() {
38
+ $settings = [
39
+ 'facebook_site' => [ 'type' => 'string', 'newOption' => [ 'social', 'profiles', 'urls', 'facebookPageUrl' ] ],
40
+ 'instagram_url' => [ 'type' => 'string', 'newOption' => [ 'social', 'profiles', 'urls', 'instagramUrl' ] ],
41
+ 'linkedin_url' => [ 'type' => 'string', 'newOption' => [ 'social', 'profiles', 'urls', 'linkedinUrl' ] ],
42
+ 'myspace_url' => [ 'type' => 'string', 'newOption' => [ 'social', 'profiles', 'urls', 'myspaceUrl' ] ],
43
+ 'pinterest_url' => [ 'type' => 'string', 'newOption' => [ 'social', 'profiles', 'urls', 'pinterestUrl' ] ],
44
+ 'youtube_url' => [ 'type' => 'string', 'newOption' => [ 'social', 'profiles', 'urls', 'youtubeUrl' ] ],
45
+ 'wikipedia_url' => [ 'type' => 'string', 'newOption' => [ 'social', 'profiles', 'urls', 'wikipediaUrl' ] ]
46
+ ];
47
+
48
+ if ( ! empty( $this->options['twitter_site'] ) ) {
49
+ aioseo()->options->social->profiles->urls->twitterUrl =
50
+ 'https://twitter.com/' . aioseo()->helpers->sanitizeOption( $this->options['twitter_site'] );
51
+ }
52
+
53
+ aioseo()->importExport->yoastSeo->helpers->mapOldToNew( $settings, $this->options );
54
+ }
55
+
56
+ /**
57
+ * Migrates the Facebook settings.
58
+ *
59
+ * @since 4.0.0
60
+ *
61
+ * @return void
62
+ */
63
+ private function migrateFacebookSettings() {
64
+ if ( ! empty( $this->options['og_default_image'] ) ) {
65
+ $defaultImage = esc_url( $this->options['og_default_image'] );
66
+ aioseo()->options->social->facebook->general->defaultImagePosts = $defaultImage;
67
+ aioseo()->options->social->facebook->general->defaultImageSourcePosts = 'default';
68
+
69
+ aioseo()->options->social->twitter->general->defaultImagePosts = $defaultImage;
70
+ aioseo()->options->social->twitter->general->defaultImageSourcePosts = 'default';
71
+ }
72
+
73
+ $settings = [
74
+ 'opengraph' => [ 'type' => 'boolean', 'newOption' => [ 'social', 'facebook', 'general', 'enable' ] ],
75
+ 'og_frontpage_title' => [ 'type' => 'string', 'newOption' => [ 'social', 'facebook', 'homePage', 'title' ] ],
76
+ 'og_frontpage_desc' => [ 'type' => 'string', 'newOption' => [ 'social', 'facebook', 'homePage', 'description' ] ],
77
+ 'og_frontpage_image' => [ 'type' => 'string', 'newOption' => [ 'social', 'facebook', 'homePage', 'image' ] ],
78
+ ];
79
+
80
+ aioseo()->importExport->yoastSeo->helpers->mapOldToNew( $settings, $this->options, true );
81
+ }
82
+
83
+ /**
84
+ * Migrates the Twitter settings.
85
+ *
86
+ * @since 4.0.0
87
+ *
88
+ * @return void
89
+ */
90
+ private function migrateTwitterSettings() {
91
+ $settings = [
92
+ 'twitter' => [ 'type' => 'boolean', 'newOption' => [ 'social', 'twitter', 'general', 'enable' ] ],
93
+ 'twitter_card_type' => [ 'type' => 'string', 'newOption' => [ 'social', 'twitter', 'general', 'defaultCardType' ] ],
94
+ ];
95
+
96
+ aioseo()->importExport->yoastSeo->helpers->mapOldToNew( $settings, $this->options );
97
+ }
98
+
99
+ /**
100
+ * Migrates the Facebook admin ID.
101
+ *
102
+ * @since 4.0.0
103
+ *
104
+ * @return void
105
+ */
106
+ private function migrateFacebookAdminId() {
107
+ if ( ! empty( $this->options['fbadminapp'] ) ) {
108
+ aioseo()->options->social->facebook->advanced->enable = true;
109
+ aioseo()->options->social->facebook->advanced->adminId = aioseo()->helpers->sanitizeOption( $this->options['fbadminapp'] );
110
+ }
111
+ }
112
+ }
app/Common/ImportExport/YoastSeo/YoastSeo.php ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\ImportExport\YoastSeo;
3
+
4
+ use AIOSEO\Plugin\Common\ImportExport;
5
+
6
+ class YoastSeo extends ImportExport\Importer {
7
+
8
+ /**
9
+ * A list of plugins to look for to import.
10
+ *
11
+ * @since 4.0.0
12
+ *
13
+ * @var array
14
+ */
15
+ public $plugins = [
16
+ [
17
+ 'name' => 'Yoast SEO',
18
+ 'version' => '14.0',
19
+ 'basename' => 'wordpress-seo/wp-seo.php',
20
+ 'slug' => 'yoast-seo'
21
+ ],
22
+ [
23
+ 'name' => 'Yoast SEO Premium',
24
+ 'version' => '14.0',
25
+ 'basename' => 'wordpress-seo-premium/wp-seo-premium.php',
26
+ 'slug' => 'yoast-seo-premium'
27
+ ],
28
+ ];
29
+
30
+ /**
31
+ * The post action name.
32
+ *
33
+ * @since 4.0.0
34
+ *
35
+ * @var string
36
+ */
37
+ public $postActionName = 'aioseo_import_post_meta_yoast_seo';
38
+
39
+ /**
40
+ * The post action name.
41
+ *
42
+ * @since 4.0.0
43
+ *
44
+ * @param ImportExport $importer The main importer class.
45
+ */
46
+ public function __construct( $importer ) {
47
+ $this->helpers = new Helpers();
48
+ $this->postMeta = new PostMeta();
49
+ add_action( $this->postActionName, [ $this->postMeta, 'importPostMeta' ] );
50
+
51
+ $plugins = $this->plugins;
52
+ foreach ( $plugins as $key => $plugin ) {
53
+ $plugins[ $key ]['class'] = $this;
54
+ }
55
+ $importer->addPlugins( $plugins );
56
+ }
57
+
58
+ /**
59
+ * Imports the settings.
60
+ *
61
+ * @since 4.0.0
62
+ *
63
+ * @return void
64
+ */
65
+ protected function importSettings() {
66
+ new GeneralSettings();
67
+ new SearchAppearance();
68
+ new SocialMeta();
69
+ }
70
+ }
app/Common/Main/Activate.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Main;
3
+
4
+ /**
5
+ * Abstract class that Pro and Lite both extend.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class Activate {
10
+
11
+ /**
12
+ * Construct method.
13
+ *
14
+ * @since 4.0.0
15
+ */
16
+ public function __construct() {
17
+ register_activation_hook( AIOSEO_FILE, [ $this, 'activate' ] );
18
+ register_deactivation_hook( AIOSEO_FILE, [ $this, 'deactivate' ] );
19
+
20
+ // If Pro just deactivated the lite version, we need to manually run the activation hook, because it doesn't run here.
21
+ $proDeactivatedLite = (bool) get_transient( 'aioseo_pro_just_deactivated_lite' );
22
+ if ( $proDeactivatedLite ) {
23
+ delete_transient( 'aioseo_pro_just_deactivated_lite', true );
24
+ $this->activate( false );
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Runs on deactivation.
30
+ *
31
+ * @since 4.0.0
32
+ *
33
+ * @return void
34
+ */
35
+ public function deactivate() {
36
+ aioseo()->access->removeCapabilities();
37
+ \AIOSEO\Plugin\Common\Sitemap\Rewrite::delete( true );
38
+ }
39
+ }
app/Common/Main/Filters.php ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Main;
3
+
4
+ /**
5
+ * Abstract class that Pro and Lite both extend.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ abstract class Filters {
10
+ /**
11
+ * The plugin we are checking.
12
+ *
13
+ * @since 4.0.0
14
+ *
15
+ * @var string
16
+ */
17
+ private $plugin;
18
+
19
+ /**
20
+ * Construct method.
21
+ *
22
+ * @since 4.0.0
23
+ */
24
+ public function __construct() {
25
+ add_filter( 'plugin_row_meta', [ $this, 'pluginRowMeta' ], 10, 2 );
26
+ add_filter( 'plugin_action_links_' . AIOSEO_PLUGIN_BASENAME, [ $this, 'pluginActionLinks' ], 10, 2 );
27
+
28
+ // Genesis theme compatibility.
29
+ add_filter( 'genesis_detect_seo_plugins', [ $this, 'genesisTheme' ] );
30
+
31
+ // WeGlot compatibility.
32
+ if ( preg_match( '#(/default\.xsl)$#i', $_SERVER['REQUEST_URI'] ) ) {
33
+ add_filter( 'weglot_active_translation_before_treat_page', '__return_false' );
34
+ }
35
+
36
+ // GoDaddy CDN compatibility.
37
+ add_filter( 'wpaas_cdn_file_ext', [ $this, 'goDaddySitemapXml' ] );
38
+ }
39
+
40
+ /**
41
+ * Disable SEO inside the Genesis theme if it's running.
42
+ *
43
+ * @since 4.0.3
44
+ *
45
+ * @param array $array An array of checks.
46
+ * @return array An array with our function added.
47
+ */
48
+ public function genesisTheme( $array ) {
49
+ if ( empty( $array ) || ! isset( $array['functions'] ) ) {
50
+ return $array;
51
+ }
52
+
53
+ $array['functions'][] = 'aioseo';
54
+
55
+ return $array;
56
+ }
57
+
58
+ /**
59
+ * Remove XML from the GoDaddy CDN so our urls remain intact.
60
+ *
61
+ * @since 4.0.5
62
+ *
63
+ * @param array $extensions The original extensions list.
64
+ * @return array The extensions list without xml.
65
+ */
66
+ public function goDaddySitemapXml( $extensions ) {
67
+ $key = array_search( 'xml', $extensions, true );
68
+ unset( $extensions[ $key ] );
69
+ return $extensions;
70
+ }
71
+
72
+ /**
73
+ * Action links for the plugins page.
74
+ *
75
+ * @since 4.0.0
76
+ *
77
+ * @return array The array of actions.
78
+ */
79
+ abstract public function pluginRowMeta( $actions, $pluginFile );
80
+
81
+ /**
82
+ * Action links for the plugins page.
83
+ *
84
+ * @since 4.0.0
85
+ *
86
+ * @return array The array of actions.
87
+ */
88
+ abstract public function pluginActionLinks( $actions, $pluginFile );
89
+
90
+ /**
91
+ * Parse the action links.
92
+ *
93
+ * @since 4.0.0
94
+ *
95
+ * @param array $actions
96
+ * @param string $pluginFile
97
+ * @param
98
+ * @return array
99
+ */
100
+ protected function parseActionLinks( $actions, $pluginFile, $actionLinks = [], $position = 'after' ) {
101
+ if ( empty( $this->plugin ) ) {
102
+ $this->plugin = AIOSEO_PLUGIN_BASENAME;
103
+ }
104
+
105
+ if ( $this->plugin === $pluginFile && ! empty( $actionLinks ) ) {
106
+ foreach ( $actionLinks as $key => $value ) {
107
+ $link = [
108
+ $key => '<a href="' . $value['url'] . '">' . $value['label'] . '</a>'
109
+ ];
110
+
111
+ $actions = 'after' === $position ? array_merge( $actions, $link ) : array_merge( $link, $actions );
112
+ }
113
+ }
114
+ return $actions;
115
+ }
116
+ }
app/Common/Main/GoogleAnalytics.php ADDED
@@ -0,0 +1,233 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Main;
3
+
4
+ use AIOSEO\Plugin\Common\Utils;
5
+
6
+ /**
7
+ * Outputs the Google Analytics to the head.
8
+ *
9
+ * @since 4.0.0
10
+ */
11
+ class GoogleAnalytics {
12
+ /**
13
+ * Retrieves the script to output.
14
+ *
15
+ * @since 4.0.0
16
+ */
17
+ public function canShowScript() {
18
+ $pluginUpgrader = new Utils\PluginUpgraderSilentAjax();
19
+ $miLite = $pluginUpgrader->pluginSlugs['miLite'];
20
+ $miPro = $pluginUpgrader->pluginSlugs['miPro'];
21
+ $emLite = $pluginUpgrader->pluginSlugs['emLite'];
22
+ $emPro = $pluginUpgrader->pluginSlugs['emPro'];
23
+ $activePlugins = get_option( 'active_plugins' );
24
+
25
+ if (
26
+ in_array( $miLite, $activePlugins, true ) ||
27
+ in_array( $miPro, $activePlugins, true ) ||
28
+ in_array( $emLite, $activePlugins, true ) ||
29
+ in_array( $emPro, $activePlugins, true )
30
+ ) {
31
+ return false;
32
+ }
33
+
34
+ $googleAnalyticsId = aioseo()->options->deprecated->webmasterTools->googleAnalytics->id;
35
+ $gtmContainerId = aioseo()->options->deprecated->webmasterTools->googleAnalytics->gtmContainerId;
36
+
37
+ if (
38
+ in_array( 'googleAnalytics', aioseo()->internalOptions->internal->deprecatedOptions, true ) &&
39
+ ! $googleAnalyticsId &&
40
+ ! $gtmContainerId
41
+ ) {
42
+ return false;
43
+ }
44
+
45
+ return ! $this->userIsExcluded();
46
+ }
47
+
48
+ /**
49
+ * Checks if the user is excluded from tracking.
50
+ *
51
+ * @since 4.0.0
52
+ *
53
+ * @return boolean True if the user is excluded from tracking.
54
+ */
55
+ public function userIsExcluded() {
56
+ // Check whether we should exclude tracking for specific user roles.
57
+ $excludeUsers = aioseo()->options->deprecated->webmasterTools->googleAnalytics->excludeUsers;
58
+ if (
59
+ aioseo()->options->deprecated->webmasterTools->googleAnalytics->advanced &&
60
+ ! empty( $excludeUsers ) &&
61
+ is_user_logged_in()
62
+ ) {
63
+ $currentUser = wp_get_current_user();
64
+ if ( ! empty( $currentUser ) ) {
65
+ $intersect = array_intersect( $excludeUsers, $currentUser->roles );
66
+ if ( ! empty( $intersect ) ) {
67
+ return true;
68
+ }
69
+ }
70
+ }
71
+
72
+ return false;
73
+ }
74
+
75
+ /**
76
+ * Get analytics options.
77
+ *
78
+ * @since 4.0.0
79
+ *
80
+ * @return array An array of options.
81
+ */
82
+ public function getOptions() {
83
+ $allowLinker = '';
84
+ $cookieDomain = '';
85
+ $domain = '';
86
+ $additionalDomains = '';
87
+ $domainList = [];
88
+ $advancedOptions = aioseo()->options->deprecated->webmasterTools->googleAnalytics->advanced;
89
+ if ( $advancedOptions ) {
90
+ $trackingDomain = aioseo()->options->deprecated->webmasterTools->googleAnalytics->trackingDomain;
91
+ $cookieDomain = ! empty( $trackingDomain ) ? aioseo()->helpers->sanitizeDomain( $trackingDomain ) : '';
92
+
93
+ if ( aioseo()->options->deprecated->webmasterTools->googleAnalytics->multipleDomains ) {
94
+ $allowLinker = '\'allowLinker\': true';
95
+ $optionDomains = aioseo()->options->deprecated->webmasterTools->googleAnalytics->additionalDomains;
96
+ if ( ! empty( $optionDomains ) ) {
97
+ $additionalDomains = trim( $optionDomains );
98
+ $additionalDomains = preg_split( '/[\s,]+/', $additionalDomains );
99
+ if ( ! empty( $additionalDomains ) ) {
100
+ foreach ( $additionalDomains as $d ) {
101
+ $d = aioseo()->helpers->sanitizeDomain( $d );
102
+ if ( ! empty( $d ) ) {
103
+ $domainList[] = $d;
104
+ }
105
+ }
106
+ }
107
+ }
108
+ }
109
+ }
110
+
111
+ if ( ! empty( $cookieDomain ) ) {
112
+ $cookieDomain = esc_js( $cookieDomain );
113
+ $cookieDomain = '\'cookieDomain\': \'' . $cookieDomain . '\'';
114
+ }
115
+ if ( empty( $cookieDomain ) ) {
116
+ $domain = ', \'auto\'';
117
+ }
118
+
119
+ $options = [];
120
+ if ( ! empty( $domainList ) ) {
121
+ $options[] = [
122
+ 'require',
123
+ 'linker'
124
+ ];
125
+ $options[] = [
126
+ 'linker:autoLink',
127
+ $domainList
128
+ ];
129
+ }
130
+ if ( $advancedOptions ) {
131
+ if ( aioseo()->options->deprecated->webmasterTools->googleAnalytics->displayAdvertiserTracking ) {
132
+ $options[] = [
133
+ 'require',
134
+ 'displayfeatures'
135
+ ];
136
+ }
137
+ if ( aioseo()->options->deprecated->webmasterTools->googleAnalytics->enhancedEcommerce ) {
138
+ $options[] = [
139
+ 'require',
140
+ 'ec'
141
+ ];
142
+ }
143
+ if ( aioseo()->options->deprecated->webmasterTools->googleAnalytics->enhancedLinkAttribution ) {
144
+ $options[] = [
145
+ 'require',
146
+ 'linkid',
147
+ 'linkid.js'
148
+ ];
149
+ }
150
+ if ( aioseo()->options->deprecated->webmasterTools->googleAnalytics->anonymizeIp ) {
151
+ $options[] = [
152
+ 'set',
153
+ 'anonymizeIp',
154
+ true
155
+ ];
156
+ }
157
+ if ( aioseo()->options->deprecated->webmasterTools->googleAnalytics->trackOutboundLinks ) {
158
+ $options[] = [
159
+ 'require',
160
+ 'outboundLinkTracker'
161
+ ];
162
+ }
163
+ }
164
+
165
+ $jsOptions = [];
166
+ if ( ! empty( $cookieDomain ) ) {
167
+ $jsOptions[] = $cookieDomain;
168
+ }
169
+ if ( ! empty( $allowLinker ) ) {
170
+ $jsOptions[] = $allowLinker;
171
+ }
172
+
173
+ $jsOptions = empty( $jsOptions )
174
+ ? ''
175
+ : ', { ' . implode( ',', $jsOptions ) . ' } ';
176
+
177
+ return [
178
+ 'options' => $options,
179
+ 'domain' => $domain,
180
+ 'jsOptions' => $jsOptions
181
+ ];
182
+ }
183
+
184
+ /**
185
+ * Retrieve any attributes needed for the script tag.
186
+ *
187
+ * @since 4.0.0
188
+ *
189
+ * @return string The attributes as a string.
190
+ */
191
+ public function getScriptAttributes() {
192
+ return ' ' . trim( preg_replace( '/\s+/', ' ', apply_filters( 'aioseo_ga_attributes', '' ) ) );
193
+ }
194
+
195
+ /**
196
+ * Get the URL for autotrack.js
197
+ *
198
+ * @since 4.0.0
199
+ *
200
+ * @return string The autotrack.js URL.
201
+ */
202
+ public function autoTrackUrl() {
203
+ return apply_filters( 'aioseo_google_autotrack', plugin_dir_url( AIOSEO_FILE ) . 'app/Common/Assets/js/autotrack.js' );
204
+ }
205
+
206
+ /**
207
+ * Check if autotrack JS should be included.
208
+ *
209
+ * @since 4.0.0
210
+ *
211
+ * @return boolean True if so, false if not.
212
+ */
213
+ public function autoTrack() {
214
+ if (
215
+ in_array( 'googleAnalytics', aioseo()->internalOptions->internal->deprecatedOptions, true ) &&
216
+ ! aioseo()->options->deprecated->webmasterTools->googleAnalytics->id
217
+ ) {
218
+ return false;
219
+ }
220
+
221
+ if ( ! aioseo()->options->deprecated->webmasterTools->googleAnalytics->advanced ) {
222
+ return false;
223
+ }
224
+
225
+ if (
226
+ aioseo()->options->deprecated->webmasterTools->googleAnalytics->trackOutboundLinks
227
+ ) {
228
+ return true;
229
+ }
230
+
231
+ return false;
232
+ }
233
+ }
app/Common/Main/Head.php ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Main;
3
+
4
+ use AIOSEO\Plugin\Common\Meta;
5
+
6
+ /**
7
+ * Outputs anything we need to the head of the site.
8
+ *
9
+ * @since 4.0.0
10
+ */
11
+ class Head {
12
+
13
+ /**
14
+ * The page title.
15
+ *
16
+ * @since 4.0.5
17
+ *
18
+ * @var string
19
+ */
20
+ private static $pageTitle = null;
21
+
22
+ /**
23
+ * Class constructor.
24
+ *
25
+ * @since 4.0.0
26
+ */
27
+ public function __construct() {
28
+ add_action( 'init', [ $this, 'addAnalytics' ] );
29
+ add_action( 'after_setup_theme', [ $this, 'registerTitleHooks' ], 1000 );
30
+ add_action( 'wp_head', [ $this, 'init' ], 1 );
31
+
32
+ $this->analytics = new GoogleAnalytics();
33
+ $this->links = new Meta\Links();
34
+ $this->robots = new Meta\Robots();
35
+ $this->keywords = new Meta\Keywords();
36
+ $this->verification = new Meta\SiteVerification();
37
+ $this->views = [
38
+ 'meta' => AIOSEO_DIR . '/app/Common/Views/main/meta.php',
39
+ 'social' => AIOSEO_DIR . '/app/Common/Views/main/social.php',
40
+ 'schema' => AIOSEO_DIR . '/app/Common/Views/main/schema.php'
41
+ ];
42
+ }
43
+
44
+ /**
45
+ * Adds analytics to the views if needed.
46
+ *
47
+ * @since 4.0.5
48
+ *
49
+ * @return void
50
+ */
51
+ public function addAnalytics() {
52
+ if ( $this->analytics->canShowScript() ) {
53
+ $this->views['analytics'] = AIOSEO_DIR . '/app/Common/Views/main/analytics.php';
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Registers our title hooks.
59
+ *
60
+ * @since 4.0.5
61
+ *
62
+ * @return void
63
+ */
64
+ public function registerTitleHooks() {
65
+ add_filter( 'pre_get_document_title', [ $this, 'getTitle' ], 99999 );
66
+ add_filter( 'wp_title', [ $this, 'getTitle' ], 99999 );
67
+ if ( ! current_theme_supports( 'title-tag' ) ) {
68
+ add_action( 'template_redirect', [ $this, 'startOutputBuffering' ] );
69
+ add_action( 'wp_footer', [ $this, 'rewriteTitle' ], -2 );
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Initializes the class.
75
+ *
76
+ * @since 4.0.5
77
+ *
78
+ * @return void
79
+ */
80
+ public function init() {
81
+ $included = new Meta\Included();
82
+ if ( is_admin() || wp_doing_ajax() || wp_doing_cron() || ! $included->isIncluded() ) {
83
+ return;
84
+ }
85
+
86
+ $this->output();
87
+ }
88
+
89
+ /**
90
+ * Returns the page title.
91
+ *
92
+ * @since 4.0.5
93
+ *
94
+ * @param string $wpTitle The original page title from WordPress.
95
+ * @return string $pageTitle The page title.
96
+ */
97
+ public function getTitle( $wpTitle = '' ) {
98
+ if ( null !== self::$pageTitle ) {
99
+ return self::$pageTitle;
100
+ }
101
+ self::$pageTitle = aioseo()->meta->title->filterPageTitle( $wpTitle );
102
+ return self::$pageTitle;
103
+ }
104
+
105
+ /**
106
+ * Starts our output buffering.
107
+ *
108
+ * @since 4.0.5
109
+ *
110
+ * @return void
111
+ */
112
+ public function startOutputBuffering() {
113
+ ob_start();
114
+ }
115
+
116
+ /**
117
+ * Rewrites the page title using output buffering.
118
+ *
119
+ * @since 4.0.5
120
+ *
121
+ * @return void
122
+ */
123
+ public function rewriteTitle() {
124
+ $content = ob_get_clean();
125
+ $split = preg_split( '#</head>#', $content );
126
+ $head = $split[0];
127
+ $body = $split[1];
128
+
129
+ // Remove all existing title tags.
130
+ $head = preg_replace( '#<title.*?\/title>#', '', $head );
131
+
132
+ // Add the new title tag to our own comment block.
133
+ $pageTitle = $this->getTitle();
134
+ $head = preg_replace( '#(<!--\sAll\sin\sOne\sSEO[a-zA-Z\s0-9.]+\s-->)#', "$1\r\n\t\t<title>$pageTitle</title>", $head, 1 );
135
+
136
+ $content = $head . $body;
137
+ echo $content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
138
+ }
139
+
140
+ /**
141
+ * The output function itself.
142
+ *
143
+ * @since 4.0.0
144
+ *
145
+ * @return void
146
+ */
147
+ public function output() {
148
+ remove_action( 'wp_head', 'rel_canonical' );
149
+
150
+ echo "\n\t\t<!-- " . sprintf(
151
+ '%1$s %2$s',
152
+ esc_html( AIOSEO_PLUGIN_NAME ),
153
+ aioseo()->version // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
154
+ ) . " -->\n";
155
+
156
+ foreach ( $this->views as $view ) {
157
+ require_once( $view );
158
+ }
159
+
160
+ echo "\t\t<!-- " . esc_html( AIOSEO_PLUGIN_NAME ) . " -->\n\n";
161
+ }
162
+ }
app/Common/Main/Main.php ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Main;
3
+
4
+ /**
5
+ * Abstract class that Pro and Lite both extend.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class Main {
10
+ /**
11
+ * Construct method.
12
+ *
13
+ * @since 4.0.0
14
+ */
15
+ public function __construct() {
16
+ $this->media = new Media();
17
+
18
+ add_action( 'admin_enqueue_scripts', [ $this, 'enqueueAssets' ] );
19
+ add_action( 'wp_enqueue_scripts', [ $this, 'enqueueFrontEndAssets' ] );
20
+ add_action( 'admin_footer', [ $this, 'adminFooter' ] );
21
+ add_action( 'wp_footer', [ $this, 'adminFooter' ] );
22
+ }
23
+
24
+ /**
25
+ * Enqueue styles.
26
+ *
27
+ * @since 4.0.0
28
+ *
29
+ * @return void
30
+ */
31
+ public function enqueueAssets() {
32
+ // Scripts.
33
+ aioseo()->helpers->enqueueScript(
34
+ 'aioseo-app',
35
+ 'js/app.js'
36
+ );
37
+ aioseo()->helpers->enqueueScript(
38
+ 'aioseo-vendors',
39
+ 'js/chunk-vendors.js'
40
+ );
41
+ aioseo()->helpers->enqueueScript(
42
+ 'aioseo-common',
43
+ 'js/chunk-common.js'
44
+ );
45
+
46
+ // Styles.
47
+ $rtl = is_rtl() ? '.rtl' : '';
48
+ aioseo()->helpers->enqueueStyle(
49
+ 'aioseo-common',
50
+ "css/chunk-common$rtl.css"
51
+ );
52
+ aioseo()->helpers->enqueueStyle(
53
+ 'aioseo-vendors',
54
+ "css/chunk-vendors$rtl.css"
55
+ );
56
+ aioseo()->helpers->enqueueStyle(
57
+ 'aioseo-app-style',
58
+ "css/app$rtl.css"
59
+ );
60
+ }
61
+
62
+ /**
63
+ * Enqueue styles on the front-end.
64
+ *
65
+ * @since 4.0.0
66
+ *
67
+ * @return void
68
+ */
69
+ public function enqueueFrontEndAssets() {
70
+ if ( ! is_user_logged_in() || ! current_user_can( 'aioseo_manage_seo' ) ) {
71
+ return;
72
+ }
73
+
74
+ // Styles.
75
+ aioseo()->helpers->enqueueStyle(
76
+ 'aioseo-admin-bar',
77
+ 'css/aioseo-admin-bar.css'
78
+ );
79
+ }
80
+
81
+ /**
82
+ * Enqueue the footer file to let vue attach.
83
+ *
84
+ * @since 4.0.0
85
+ *
86
+ * @return void
87
+ */
88
+ public function adminFooter() {
89
+ echo '<div id="aioseo-admin"></div>';
90
+ }
91
+ }
app/Common/Main/Media.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Main;
3
+
4
+ /**
5
+ * Media class.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class Media {
10
+ /**
11
+ * Construct method.
12
+ *
13
+ * @since 4.0.0
14
+ */
15
+ public function __construct() {
16
+ add_action( 'template_redirect', [ $this, 'attachmentRedirect' ], 1 );
17
+ }
18
+
19
+ /**
20
+ * If the user wants to redirect attachment pages, this is where we do it.
21
+ *
22
+ * @since 4.0.0
23
+ *
24
+ * @return void
25
+ */
26
+ public function attachmentRedirect() {
27
+ if ( ! is_attachment() ) {
28
+ return;
29
+ }
30
+
31
+ if (
32
+ ! aioseo()->options->searchAppearance->dynamic->postTypes->has( 'attachment' )
33
+ ) {
34
+ return;
35
+ }
36
+
37
+ $redirect = aioseo()->options->searchAppearance->dynamic->postTypes->attachment->redirectAttachmentUrls;
38
+ if ( 'disabled' === $redirect ) {
39
+ return;
40
+ }
41
+
42
+ if ( 'attachment' === $redirect ) {
43
+ $url = wp_get_attachment_url( get_queried_object_id() );
44
+ if ( empty( $url ) ) {
45
+ return;
46
+ }
47
+
48
+ return wp_safe_redirect( $url, 301, AIOSEO_PLUGIN_SHORT_NAME );
49
+ }
50
+
51
+ global $post;
52
+ if ( ! empty( $post->post_parent ) ) {
53
+ wp_safe_redirect( urldecode( get_permalink( $post->post_parent ) ), 301 );
54
+ }
55
+ }
56
+ }
app/Common/Main/Updates.php ADDED
@@ -0,0 +1,273 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Main;
3
+
4
+ /**
5
+ * Updater class.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class Updates {
10
+
11
+ /**
12
+ * Class constructor.
13
+ *
14
+ * @since 4.0.0
15
+ */
16
+ public function __construct() {
17
+ if ( wp_doing_ajax() || wp_doing_cron() ) {
18
+ return;
19
+ }
20
+
21
+ add_action( 'init', [ $this, 'init' ], 1001 );
22
+ add_action( 'init', [ $this, 'runUpdates' ], 1002 );
23
+ add_action( 'init', [ $this, 'updateLatestVersion' ], 3000 );
24
+ }
25
+
26
+ /**
27
+ * Sets the latest active version if it is not set yet.
28
+ *
29
+ * @since 4.0.0
30
+ *
31
+ * @return void
32
+ */
33
+ public function init() {
34
+ if ( '0.0' !== aioseo()->internalOptions->internal->lastActiveVersion ) {
35
+ return;
36
+ }
37
+
38
+ // It's possible the user may not have capabilities. Let's add them now.
39
+ aioseo()->access->addCapabilities();
40
+
41
+ $oldOptions = get_option( 'aioseop_options' );
42
+ if ( empty( $oldOptions ) && ( ! is_network_admin() || ! isset( $_GET['activate-multi'] ) ) ) {
43
+ // Sets 30 second transient for welcome screen redirect on activation.
44
+ set_transient( 'aioseo_activation_redirect', true, 30 );
45
+ }
46
+
47
+ if ( ! empty( $oldOptions['last_active_version'] ) ) {
48
+ aioseo()->internalOptions->internal->lastActiveVersion = $oldOptions['last_active_version'];
49
+ }
50
+
51
+ $this->addInitialCustomTablesForV4();
52
+ add_action( 'wp_loaded', [ $this, 'setDefaultSocialImages' ], 1001 );
53
+ }
54
+
55
+ /**
56
+ * Runs our migrations.
57
+ *
58
+ * @since 4.0.0
59
+ *
60
+ * @return void
61
+ */
62
+ public function runUpdates() {
63
+ $lastActiveVersion = aioseo()->internalOptions->internal->lastActiveVersion;
64
+ if ( version_compare( $lastActiveVersion, '4.0.5', '<' ) ) {
65
+ $this->addImageScanDateColumn();
66
+ }
67
+
68
+ if ( version_compare( $lastActiveVersion, '4.0.6', '<' ) ) {
69
+ $this->disableTwitterUseOgDefault();
70
+ $this->updateMaxImagePreviewDefault();
71
+ }
72
+
73
+ if ( ! aioseo()->pro && version_compare( $lastActiveVersion, '4.0.6', '=' ) && 'posts' !== get_option( 'show_on_front' ) ) {
74
+ aioseo()->migration->helpers->redoMigration();
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Updates the latest version after all migrations and updates have run.
80
+ *
81
+ * @since 4.0.3
82
+ *
83
+ * @return void
84
+ */
85
+ public function updateLatestVersion() {
86
+ aioseo()->internalOptions->internal->lastActiveVersion = aioseo()->version;
87
+ }
88
+
89
+ /**
90
+ * Adds our custom tables for V4.
91
+ *
92
+ * @since 4.0.0
93
+ *
94
+ * @return void
95
+ */
96
+ public function addInitialCustomTablesForV4() {
97
+ $db = aioseo()->db->db;
98
+ $charsetCollate = '';
99
+
100
+ if ( ! empty( $db->charset ) ) {
101
+ $charsetCollate .= "DEFAULT CHARACTER SET {$db->charset}";
102
+ }
103
+ if ( ! empty( $db->collate ) ) {
104
+ $charsetCollate .= " COLLATE {$db->collate}";
105
+ }
106
+
107
+ // Check for notifications table.
108
+ if ( ! aioseo()->db->tableExists( 'aioseo_notifications' ) ) {
109
+ $tableName = $db->prefix . 'aioseo_notifications';
110
+
111
+ aioseo()->db->execute(
112
+ "CREATE TABLE {$tableName} (
113
+ id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
114
+ slug varchar(13) NOT NULL,
115
+ title text NOT NULL,
116
+ content longtext NOT NULL,
117
+ type varchar(64) NOT NULL,
118
+ level text NOT NULL,
119
+ notification_id bigint(20) unsigned DEFAULT NULL,
120
+ notification_name varchar(255) DEFAULT NULL,
121
+ start datetime DEFAULT NULL,
122
+ end datetime DEFAULT NULL,
123
+ button1_label varchar(255) DEFAULT NULL,
124
+ button1_action varchar(255) DEFAULT NULL,
125
+ button2_label varchar(255) DEFAULT NULL,
126
+ button2_action varchar(255) DEFAULT NULL,
127
+ dismissed tinyint(1) NOT NULL DEFAULT 0,
128
+ created datetime NOT NULL,
129
+ updated datetime NOT NULL,
130
+ PRIMARY KEY (id),
131
+ UNIQUE KEY ndx_aioseo_notifications_slug (slug),
132
+ KEY ndx_aioseo_notifications_dates (start, end),
133
+ KEY ndx_aioseo_notifications_type (type),
134
+ KEY ndx_aioseo_notifications_dismissed (dismissed)
135
+ ) {$charsetCollate};"
136
+ );
137
+ }
138
+
139
+ if ( ! aioseo()->db->tableExists( 'aioseo_posts' ) ) {
140
+ $tableName = $db->prefix . 'aioseo_posts';
141
+
142
+ aioseo()->db->execute(
143
+ "CREATE TABLE {$tableName} (
144
+ id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
145
+ post_id bigint(20) unsigned NOT NULL,
146
+ title text DEFAULT NULL,
147
+ description text DEFAULT NULL,
148
+ keywords mediumtext DEFAULT NULL,
149
+ keyphrases longtext DEFAULT NULL,
150
+ page_analysis longtext DEFAULT NULL,
151
+ canonical_url text DEFAULT NULL,
152
+ og_title text DEFAULT NULL,
153
+ og_description text DEFAULT NULL,
154
+ og_object_type varchar(64) DEFAULT 'default',
155
+ og_image_type varchar(64) DEFAULT 'default',
156
+ og_image_custom_url text DEFAULT NULL,
157
+ og_image_custom_fields text DEFAULT NULL,
158
+ og_custom_image_width int(11) DEFAULT NULL,
159
+ og_custom_image_height int(11) DEFAULT NULL,
160
+ og_video varchar(255) DEFAULT NULL,
161
+ og_custom_url text DEFAULT NULL,
162
+ og_article_section text DEFAULT NULL,
163
+ og_article_tags text DEFAULT NULL,
164
+ twitter_use_og tinyint(1) DEFAULT 1,
165
+ twitter_card varchar(64) DEFAULT 'default',
166
+ twitter_image_type varchar(64) DEFAULT 'default',
167
+ twitter_image_custom_url text DEFAULT NULL,
168
+ twitter_image_custom_fields text DEFAULT NULL,
169
+ twitter_title text DEFAULT NULL,
170
+ twitter_description text DEFAULT NULL,
171
+ seo_score int(11) DEFAULT 0 NOT NULL,
172
+ schema_type varchar(20) DEFAULT NULL,
173
+ schema_type_options longtext DEFAULT NULL,
174
+ pillar_content tinyint(1) DEFAULT NULL,
175
+ robots_default tinyint(1) DEFAULT 1 NOT NULL,
176
+ robots_noindex tinyint(1) DEFAULT 0 NOT NULL,
177
+ robots_noarchive tinyint(1) DEFAULT 0 NOT NULL,
178
+ robots_nosnippet tinyint(1) DEFAULT 0 NOT NULL,
179
+ robots_nofollow tinyint(1) DEFAULT 0 NOT NULL,
180
+ robots_noimageindex tinyint(1) DEFAULT 0 NOT NULL,
181
+ robots_noodp tinyint(1) DEFAULT 0 NOT NULL,
182
+ robots_notranslate tinyint(1) DEFAULT 0 NOT NULL,
183
+ robots_max_snippet int(11) DEFAULT NULL,
184
+ robots_max_videopreview int(11) DEFAULT NULL,
185
+ robots_max_imagepreview varchar(20) DEFAULT 'none',
186
+ tabs mediumtext DEFAULT NULL,
187
+ images longtext DEFAULT NULL,
188
+ priority tinytext DEFAULT NULL,
189
+ frequency tinytext DEFAULT NULL,
190
+ videos longtext DEFAULT NULL,
191
+ video_thumbnail text DEFAULT NULL,
192
+ video_scan_date datetime DEFAULT NULL,
193
+ location text DEFAULT NULL,
194
+ local_seo longtext DEFAULT NULL,
195
+ created datetime NOT NULL,
196
+ updated datetime NOT NULL,
197
+ PRIMARY KEY (id),
198
+ KEY ndx_aioseo_posts_post_id (post_id)
199
+ ) {$charsetCollate};"
200
+ );
201
+ }
202
+ }
203
+
204
+ /**
205
+ * Sets the default social images.
206
+ *
207
+ * @since 4.0.0
208
+ *
209
+ * @return void
210
+ */
211
+ public function setDefaultSocialImages() {
212
+ $siteLogo = aioseo()->helpers->getSiteLogoUrl();
213
+ if ( $siteLogo && ! aioseo()->internalOptions->internal->migratedVersion ) {
214
+ if ( ! aioseo()->options->social->facebook->general->defaultImagePosts ) {
215
+ aioseo()->options->social->facebook->general->defaultImagePosts = $siteLogo;
216
+ }
217
+ if ( ! aioseo()->options->social->twitter->general->defaultImagePosts ) {
218
+ aioseo()->options->social->twitter->general->defaultImagePosts = $siteLogo;
219
+ }
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Adds the image scan date column to our posts table.
225
+ *
226
+ * @since 4.0.5
227
+ *
228
+ * @return void
229
+ */
230
+ public function addImageScanDateColumn() {
231
+ if ( ! aioseo()->db->columnExists( 'aioseo_posts', 'image_scan_date' ) ) {
232
+ $tableName = aioseo()->db->db->prefix . 'aioseo_posts';
233
+ aioseo()->db->execute(
234
+ "ALTER TABLE {$tableName}
235
+ ADD image_scan_date datetime DEFAULT NULL AFTER images"
236
+ );
237
+ }
238
+ }
239
+
240
+ /**
241
+ * Modifes the default value of the twitter_use_og column.
242
+ *
243
+ * @since 4.0.6
244
+ *
245
+ * @return void
246
+ */
247
+ public function disableTwitterUseOgDefault() {
248
+ if ( aioseo()->db->tableExists( 'aioseo_posts' ) ) {
249
+ $tableName = aioseo()->db->db->prefix . 'aioseo_posts';
250
+ aioseo()->db->execute(
251
+ "ALTER TABLE {$tableName}
252
+ MODIFY twitter_use_og tinyint(1) DEFAULT 0"
253
+ );
254
+ }
255
+ }
256
+
257
+ /**
258
+ * Modifes the default value of the robots_max_imagepreview column.
259
+ *
260
+ * @since 4.0.6
261
+ *
262
+ * @return void
263
+ */
264
+ public function updateMaxImagePreviewDefault() {
265
+ if ( aioseo()->db->tableExists( 'aioseo_posts' ) ) {
266
+ $tableName = aioseo()->db->db->prefix . 'aioseo_posts';
267
+ aioseo()->db->execute(
268
+ "ALTER TABLE {$tableName}
269
+ MODIFY robots_max_imagepreview varchar(20) DEFAULT 'large'"
270
+ );
271
+ }
272
+ }
273
+ }
app/Common/Meta/Amp.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Meta;
3
+
4
+ /**
5
+ * Adds support for Google AMP.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class Amp {
10
+ /**
11
+ * Class constructor.
12
+ *
13
+ * @since 4.0.0
14
+ */
15
+ public function __construct() {
16
+ add_action( 'init', [ $this, 'runAmp' ] );
17
+ }
18
+
19
+ /**
20
+ * Run the AMP hooks.
21
+ *
22
+ * @since 4.0.0
23
+ *
24
+ * @return void
25
+ */
26
+ public function runAmp() {
27
+ if ( is_admin() || wp_doing_ajax() || wp_doing_cron() ) {
28
+ return;
29
+ }
30
+
31
+ // Add social meta to AMP plugin.
32
+ $enableAmp = apply_filters( 'aioseo_enable_amp_social_meta', true );
33
+
34
+ if ( $enableAmp ) {
35
+ $useSchema = apply_filters( 'aioseo_amp_schema', true );
36
+
37
+ if ( $useSchema ) {
38
+ add_action( 'amp_post_template_head', [ $this, 'removeHooksAmpSchema' ], 9 );
39
+ }
40
+
41
+ add_action( 'amp_post_template_head', [ aioseo()->head, 'output' ], 11 );
42
+ add_action( 'amp_post_template_head', [ aioseo()->social->output, 'getMeta' ], 12 );
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Remove Hooks with AMP's Schema.
48
+ *
49
+ * @since 4.0.0
50
+ *
51
+ * @return void
52
+ */
53
+ public function removeHooksAmpSchema() {
54
+ // Remove AMP Schema hook used for outputting data.
55
+ remove_action( 'amp_post_template_head', 'amp_print_schemaorg_metadata' );
56
+ }
57
+ }
app/Common/Meta/Description.php ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Meta;
3
+
4
+ /**
5
+ * Handles the (Open Graph) description.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class Description {
10
+
11
+ /**
12
+ * Returns the homepage description.
13
+ *
14
+ * @since 4.0.0
15
+ *
16
+ * @return string The homepage description.
17
+ */
18
+ public function getHomePageDescription() {
19
+ if ( 'page' === get_option( 'show_on_front' ) ) {
20
+ $description = $this->getPostDescription( (int) get_option( 'page_on_front' ) );
21
+ return $description ? $description : aioseo()->helpers->decodeHtmlEntities( get_bloginfo( 'description' ) );
22
+ }
23
+
24
+ $description = $this->prepareDescription( aioseo()->options->searchAppearance->global->metaDescription );
25
+ return $description ? $description : aioseo()->helpers->decodeHtmlEntities( get_bloginfo( 'description' ) );
26
+ }
27
+
28
+ /**
29
+ * Returns the description for the current page.
30
+ *
31
+ * @since 4.0.0
32
+ *
33
+ * @param WP_Post $post The post object (optional).
34
+ * @param boolean $default Whether we want the default value, not the post one.
35
+ * @return string The page description.
36
+ */
37
+ public function getDescription( $post = null, $default = false ) {
38
+ if ( is_home() && 'posts' === get_option( 'show_on_front' ) ) {
39
+ return $this->getHomePageDescription();
40
+ }
41
+
42
+ if ( $post || is_singular() || aioseo()->helpers->isStaticPage() ) {
43
+ $description = $this->getPostDescription( $post, $default );
44
+ if ( $description ) {
45
+ return $description;
46
+ }
47
+
48
+ if ( is_attachment() ) {
49
+ $post = empty( $post ) ? aioseo()->helpers->getPost() : $post;
50
+ $caption = wp_get_attachment_caption( $post->ID );
51
+ return $caption ? $this->prepareDescription( $caption ) : $this->prepareDescription( $post->post_content );
52
+ }
53
+ }
54
+
55
+ if ( is_category() || is_tag() || is_tax() ) {
56
+ $term = $post ? $post : get_queried_object();
57
+ return $this->getTermDescription( $term, $default );
58
+ }
59
+
60
+ if ( is_author() ) {
61
+ $description = $this->prepareDescription( aioseo()->options->searchAppearance->archives->author->metaDescription );
62
+ if ( $description ) {
63
+ return $description;
64
+ }
65
+
66
+ $author = get_queried_object();
67
+ return $author ? $this->prepareDescription( get_the_author_meta( 'description', $author->ID ) ) : '';
68
+ }
69
+
70
+ if ( is_date() ) {
71
+ return $this->prepareDescription( aioseo()->options->searchAppearance->archives->date->metaDescription );
72
+ }
73
+
74
+ if ( is_search() ) {
75
+ return $this->prepareDescription( aioseo()->options->searchAppearance->archives->search->metaDescription );
76
+ }
77
+
78
+ if ( is_archive() ) {
79
+ $postType = get_queried_object();
80
+ $options = aioseo()->options->noConflict();
81
+ if ( $options->searchAppearance->dynamic->archives->has( $postType->name ) ) {
82
+ return $this->prepareDescription( aioseo()->options->searchAppearance->dynamic->archives->{ $postType->name }->metaDescription );
83
+ }
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Returns the description for a given post.
89
+ *
90
+ * @since 4.0.0
91
+ *
92
+ * @param WP_Post|int $post The post object or ID.
93
+ * @param boolean $default Whether we want the default value, not the post one.
94
+ * @return string The post description.
95
+ */
96
+ public function getPostDescription( $post, $default = false ) {
97
+ $post = $post && is_object( $post ) ? $post : aioseo()->helpers->getPost( $post );
98
+ $metaData = aioseo()->meta->metaData->getMetaData( $post );
99
+
100
+ $description = '';
101
+ if ( ! empty( $metaData->description ) && ! $default ) {
102
+ $description = $this->prepareDescription( $metaData->description, $post->ID );
103
+ }
104
+
105
+ if (
106
+ ! $description &&
107
+ in_array( 'autogenerateDescriptions', aioseo()->internalOptions->deprecatedOptions, true ) &&
108
+ ! aioseo()->options->deprecated->searchAppearance->advanced->autogenerateDescriptions
109
+ ) {
110
+ return $description;
111
+ }
112
+
113
+ if ( ! $description ) {
114
+ $description = $this->prepareDescription( $this->getPostTypeDescription( $post->post_type ), $post->ID, $default );
115
+ }
116
+
117
+ $generateDescriptions = apply_filters( 'aioseo_generate_descriptions_from_content', true, [ $post ] );
118
+ if ( ! $description && ! post_password_required( $post ) ) {
119
+ $description = $post->post_excerpt;
120
+ if (
121
+ $generateDescriptions &&
122
+ in_array( 'useContentForAutogeneratedDescriptions', aioseo()->internalOptions->deprecatedOptions, true ) &&
123
+ aioseo()->options->deprecated->searchAppearance->advanced->useContentForAutogeneratedDescriptions
124
+ ) {
125
+ $description = aioseo()->helpers->getContent( $post );
126
+ }
127
+
128
+ $description = $this->prepareDescription( $description, $post->ID, $default );
129
+
130
+ if ( ! $description && $generateDescriptions && $post->post_content ) {
131
+ $description = $this->prepareDescription( aioseo()->helpers->getContent( $post ), $post->ID, $default );
132
+ }
133
+ }
134
+
135
+ if ( ! is_paged() ) {
136
+ if ( in_array( 'descriptionFormat', aioseo()->internalOptions->deprecatedOptions, true ) ) {
137
+ $descriptionFormat = aioseo()->options->deprecated->searchAppearance->global->descriptionFormat;
138
+ if ( $descriptionFormat ) {
139
+ $description = preg_replace( '/#description/', $description, $descriptionFormat );
140
+ }
141
+ }
142
+
143
+ $description = $this->prepareDescription( $description, $post->ID, $default );
144
+ }
145
+
146
+ return $description ? $description : $this->prepareDescription( term_description( '' ), $post->ID, $default );
147
+ }
148
+
149
+ /**
150
+ * Retrieve the default description for the post type.
151
+ *
152
+ * @since 4.0.6
153
+ *
154
+ * @param string $postType The post type.
155
+ * @return string The description.
156
+ */
157
+ public function getPostTypeDescription( $postType ) {
158
+ $options = aioseo()->options->noConflict();
159
+ if ( $options->searchAppearance->dynamic->postTypes->has( $postType ) ) {
160
+ return aioseo()->options->searchAppearance->dynamic->postTypes->{$postType}->metaDescription;
161
+ }
162
+
163
+ return '';
164
+ }
165
+
166
+ /**
167
+ * Returns the term description.
168
+ *
169
+ * @since 4.0.6
170
+ *
171
+ * @param WP_Term $term The term object.
172
+ * @param boolean $default Whether we want the default value, not the post one.
173
+ * @return string The term description.
174
+ */
175
+ public function getTermDescription( $term, $default = false ) {
176
+ $description = '';
177
+ if (
178
+ ! $description &&
179
+ in_array( 'autogenerateDescriptions', aioseo()->internalOptions->deprecatedOptions, true ) &&
180
+ ! aioseo()->options->deprecated->searchAppearance->advanced->autogenerateDescriptions
181
+ ) {
182
+ return $description;
183
+ }
184
+
185
+ $options = aioseo()->options->noConflict();
186
+ if ( ! $description && $options->searchAppearance->dynamic->taxonomies->has( $term->taxonomy ) ) {
187
+ $description = $this->prepareDescription( aioseo()->options->searchAppearance->dynamic->taxonomies->{$term->taxonomy}->metaDescription, false, $default );
188
+ }
189
+ return $description ? $description : $this->prepareDescription( wp_strip_all_tags( term_description( $term->term_id ) ), false, $default );
190
+ }
191
+
192
+ /**
193
+ * Prepares and sanitizes the description.
194
+ *
195
+ * @since 4.0.0
196
+ *
197
+ * @param string $description The description.
198
+ * @param int $id The ID of the page or post.
199
+ * @param boolean $default Whether we want the default value, not the post one.
200
+ * @return string $description The sanitized description.
201
+ */
202
+ public function prepareDescription( $description, $id = false, $default = false ) {
203
+ if ( ! is_admin() && 1 < aioseo()->helpers->getPageNumber() ) {
204
+ $description .= aioseo()->options->searchAppearance->advanced->pagedFormat;
205
+ }
206
+
207
+ $description = $default ? $description : aioseo()->tags->replaceTags( $description, $id );
208
+ $description = apply_filters( 'aioseo_description', $description );
209
+
210
+ $description = aioseo()->helpers->decodeHtmlEntities( $description );
211
+ $description = wp_strip_all_tags( strip_shortcodes( $description ) );
212
+ // Trim both internal and external whitespace.
213
+ $description = preg_replace( '/[\s]+/u', ' ', trim( $description ) );
214
+ return aioseo()->helpers->internationalize( $description );
215
+ }
216
+ }
app/Common/Meta/Included.php ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Meta;
3
+
4
+ /**
5
+ * To check whether SEO is enabled for the queried object.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class Included {
10
+ /**
11
+ * Checks whether the queried object is included.
12
+ *
13
+ * @since 4.0.0
14
+ *
15
+ * @return bool
16
+ */
17
+ public function isIncluded() {
18
+ if ( is_admin() || is_feed() ) {
19
+ return false;
20
+ }
21
+
22
+ if ( apply_filters( 'aioseo_disable', false ) || $this->isExcludedGlobal() ) {
23
+ return false;
24
+ }
25
+
26
+ return true;
27
+ }
28
+
29
+ /**
30
+ * Checks whether the queried object has been excluded globally.
31
+ *
32
+ * @since 4.0.0
33
+ *
34
+ * @return bool
35
+ */
36
+ protected function isExcludedGlobal() {
37
+ if ( is_category() || is_tag() || is_tax() ) {
38
+ return $this->isTaxExcludedGlobal();
39
+ }
40
+
41
+ if ( ! in_array( 'excludePosts', aioseo()->internalOptions->deprecatedOptions, true ) ) {
42
+ return false;
43
+ }
44
+
45
+ $excludedPosts = aioseo()->options->deprecated->searchAppearance->advanced->excludePosts;
46
+
47
+ if ( empty( $excludedPosts ) ) {
48
+ return false;
49
+ }
50
+
51
+ $ids = [];
52
+ foreach ( $excludedPosts as $object ) {
53
+ $object = json_decode( $object );
54
+ if ( is_int( $object->value ) ) {
55
+ $ids[] = (int) $object->value;
56
+ }
57
+ }
58
+
59
+ $post = aioseo()->helpers->getPost();
60
+ if ( empty( $post ) ) {
61
+ return false;
62
+ }
63
+
64
+ if ( in_array( (int) $post->ID, $ids, true ) ) {
65
+ return true;
66
+ }
67
+
68
+ return false;
69
+ }
70
+
71
+ /**
72
+ * Checks whether the queried object has been excluded globally.
73
+ *
74
+ * @since 4.0.0
75
+ *
76
+ * @return bool
77
+ */
78
+ protected function isTaxExcludedGlobal() {
79
+ if ( ! in_array( 'excludeTerms', aioseo()->internalOptions->deprecatedOptions, true ) ) {
80
+ return false;
81
+ }
82
+
83
+ $excludedTerms = aioseo()->options->deprecated->searchAppearance->advanced->excludeTerms;
84
+
85
+ if ( empty( $excludedTerms ) ) {
86
+ return false;
87
+ }
88
+
89
+ $ids = [];
90
+ foreach ( $excludedTerms as $object ) {
91
+ $object = json_decode( $object );
92
+ if ( is_int( $object->value ) ) {
93
+ $ids[] = (int) $object->value;
94
+ }
95
+ }
96
+
97
+ $term = get_queried_object();
98
+ if ( in_array( (int) $term->term_id, $ids, true ) ) {
99
+ return true;
100
+ }
101
+ return false;
102
+ }
103
+ }
app/Common/Meta/Keywords.php ADDED
@@ -0,0 +1,229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Meta;
3
+
4
+ /**
5
+ * Handles the keywords.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class Keywords {
10
+ /**
11
+ * Get the keywords for the meta output.
12
+ *
13
+ * @since 4.0.0
14
+ *
15
+ * @return string The keywords as a string.
16
+ */
17
+ public function getKeywords() {
18
+ if ( ! aioseo()->options->searchAppearance->advanced->useKeywords ) {
19
+ return '';
20
+ }
21
+
22
+ $isStaticArchive = aioseo()->helpers->isWooCommerceShopPage() || aioseo()->helpers->isStaticPostsPage();
23
+ $dynamicContent = is_archive() || is_post_type_archive() || is_home() || aioseo()->helpers->isWooCommerceShopPage() || is_category() || is_tag() || is_tax();
24
+ $generate = aioseo()->options->searchAppearance->advanced->dynamicallyGenerateKeywords;
25
+ if ( $dynamicContent && $generate ) {
26
+ return $this->prepareKeywords( $this->getGeneratedKeywords() );
27
+ }
28
+
29
+ if ( is_front_page() && ! aioseo()->helpers->isStaticHomePage() ) {
30
+ $keywords = $this->extractMetaKeywords( aioseo()->options->searchAppearance->global->keywords );
31
+ return $this->prepareKeywords( $keywords );
32
+ }
33
+
34
+ if ( $dynamicContent && ! $isStaticArchive ) {
35
+ if ( is_date() ) {
36
+ $keywords = $this->extractMetaKeywords( aioseo()->options->searchAppearance->archives->date->advanced->keywords );
37
+ return $this->prepareKeywords( $keywords );
38
+ }
39
+
40
+ if ( is_author() ) {
41
+ $keywords = $this->extractMetaKeywords( aioseo()->options->searchAppearance->archives->author->advanced->keywords );
42
+ return $this->prepareKeywords( $keywords );
43
+ }
44
+
45
+ if ( is_search() ) {
46
+ $keywords = $this->extractMetaKeywords( aioseo()->options->searchAppearance->archives->search->advanced->keywords );
47
+ return $this->prepareKeywords( $keywords );
48
+ }
49
+
50
+ $postType = get_queried_object();
51
+ $options = aioseo()->options->noConflict();
52
+ if ( $postType && $options->searchAppearance->dynamic->archives->has( $postType->name ) ) {
53
+ $keywords = $this->extractMetaKeywords( aioseo()->options->searchAppearance->dynamic->archives->{ $postType->name }->advanced->keywords );
54
+ return $this->prepareKeywords( $keywords );
55
+ }
56
+
57
+ return '';
58
+ }
59
+
60
+ return $this->prepareKeywords( $this->getAllKeywords() );
61
+ }
62
+
63
+ /**
64
+ * Get generated keywords for an archive page.
65
+ *
66
+ * @since 4.0.0
67
+ *
68
+ * @return array An array of generated keywords.
69
+ */
70
+ private function getGeneratedKeywords() {
71
+ global $posts, $wp_query;
72
+
73
+ $keywords = [];
74
+ $isStaticArchive = aioseo()->helpers->isWooCommerceShopPage() || aioseo()->helpers->isStaticPostsPage();
75
+ if ( $isStaticArchive ) {
76
+ $keywords = $this->getAllKeywords();
77
+ } elseif ( is_front_page() && ! aioseo()->helpers->isStaticHomePage() ) {
78
+ $keywords = $this->extractMetaKeywords( aioseo()->options->searchAppearance->global->keywords );
79
+ } elseif ( is_category() || is_tag() || is_tax() ) {
80
+ $metaData = aioseo()->meta->metaData->getMetaData();
81
+ if ( ! empty( $metaData->keywords ) ) {
82
+ $keywords = $this->extractMetaKeywords( $metaData->keywords );
83
+ }
84
+ }
85
+
86
+ $wpPosts = $posts;
87
+ if ( empty( $posts ) ) {
88
+ $wpPosts = array_filter( [ aioseo()->helpers->getPost() ] );
89
+ }
90
+
91
+ // Turn off current query so we can get specific post data.
92
+ $originalTag = $wp_query->is_tag;
93
+ $originalTax = $wp_query->is_tax;
94
+ $originalCategory = $wp_query->is_category;
95
+
96
+ $wp_query->is_tag = false;
97
+ $wp_query->is_tax = false;
98
+ $wp_query->is_category = false;
99
+
100
+ foreach ( $wpPosts as $post ) {
101
+ $metaData = aioseo()->meta->metaData->getMetaData( $post );
102
+ $tmpKeywords = $this->extractMetaKeywords( $metaData->keywords );
103
+ if ( count( $tmpKeywords ) ) {
104
+ foreach ( $tmpKeywords as $keyword ) {
105
+ $keywords[] = $keyword;
106
+ }
107
+ }
108
+ }
109
+
110
+ $wp_query->is_tag = $originalTag;
111
+ $wp_query->is_tax = $originalTax;
112
+ $wp_query->is_category = $originalCategory;
113
+
114
+ return $keywords;
115
+ }
116
+
117
+ /**
118
+ * Returns the keywords.
119
+ *
120
+ * @since 4.0.0
121
+ *
122
+ * @return string A comma-separated list of unique keywords.
123
+ */
124
+ public function getAllKeywords() {
125
+ $keywords = [];
126
+ $post = aioseo()->helpers->getPost();
127
+ $metaData = aioseo()->meta->metaData->getMetaData();
128
+ if ( ! empty( $metaData->keywords ) ) {
129
+ $keywords = $this->extractMetaKeywords( $metaData->keywords );
130
+ }
131
+
132
+ if ( $post ) {
133
+ if ( aioseo()->options->searchAppearance->advanced->useTagsForMetaKeywords ) {
134
+ $keywords = array_merge( $keywords, aioseo()->social->helpers->getAllTags( $post->ID ) );
135
+ }
136
+
137
+ if ( aioseo()->options->searchAppearance->advanced->useCategoriesForMetaKeywords && ! is_page() ) {
138
+ $keywords = array_merge( $keywords, aioseo()->social->helpers->getAllCategories( $post->ID ) );
139
+ }
140
+ }
141
+
142
+ return $keywords;
143
+ }
144
+
145
+ /**
146
+ * Prepares the keywords for display.
147
+ *
148
+ * @since 4.0.0
149
+ *
150
+ * @param array $keywords Raw keywords.
151
+ * @return string A list of prepared keywords, comma-separated.
152
+ */
153
+ protected function prepareKeywords( $keywords ) {
154
+ $keywords = $this->getUniqueKeywords( $keywords );
155
+ $keywords = trim( $keywords );
156
+ $keywords = aioseo()->helpers->internationalize( $keywords );
157
+ $keywords = stripslashes( $keywords );
158
+ $keywords = str_replace( '"', '', $keywords );
159
+ $keywords = wp_filter_nohtml_kses( $keywords );
160
+ return apply_filters( 'aioseo_keywords', $keywords );
161
+ }
162
+
163
+ /**
164
+ * Returns an array of keywords, based on a stringified list separated by commas.
165
+ *
166
+ * @since 4.0.0
167
+ *
168
+ * @param string $keywords The keywords string.
169
+ * @return array The keywords.
170
+ */
171
+ public function keywordStringToList( $keywords ) {
172
+ $keywords = str_replace( '"', '', $keywords );
173
+ return ! empty( $keywords ) ? explode( ',', $keywords ) : [];
174
+ }
175
+
176
+ /**
177
+ * Returns a stringified list of unique keywords, separated by commas.
178
+ *
179
+ * @since 4.0.0
180
+ *
181
+ * @param array $keywords The keywords.
182
+ * @param boolean $toString Whether or not to turn it into a comma separated string.
183
+ * @return string The keywords string.
184
+ */
185
+ public function getUniqueKeywords( $keywords, $toString = true ) {
186
+ $keywords = $this->keywordsToLowerCase( $keywords );
187
+ return $toString ? implode( ',', $keywords ) : $keywords;
188
+ }
189
+
190
+ /**
191
+ * Returns the keywords in lowercase.
192
+ *
193
+ * @since 4.0.0
194
+ *
195
+ * @param array $keywords The keywords.
196
+ * @return array The formatted keywords.
197
+ */
198
+ private function keywordsToLowerCase( $keywords ) {
199
+ $smallKeywords = [];
200
+ if ( ! is_array( $keywords ) ) {
201
+ $keywords = $this->keywordStringToList( $keywords );
202
+ }
203
+ if ( ! empty( $keywords ) ) {
204
+ foreach ( $keywords as $keyword ) {
205
+ $smallKeywords[] = trim( strtolower( $keyword ) );
206
+ }
207
+ }
208
+ return array_unique( $smallKeywords );
209
+ }
210
+
211
+ /**
212
+ * Extract keywords and then return as a string.
213
+ *
214
+ * @param string $keywords A json encoded string of keywords.
215
+ * @return string A string of keywords that were extracted.
216
+ */
217
+ public function extractMetaKeywords( $keywords ) {
218
+ $extracted = [];
219
+ $keywords = ! empty( $keywords ) ? json_decode( $keywords ) : null;
220
+
221
+ if ( ! empty( $keywords ) ) {
222
+ foreach ( $keywords as $keyword ) {
223
+ $extracted[] = trim( $keyword->value );
224
+ }
225
+ }
226
+
227
+ return $extracted;
228
+ }
229
+ }
app/Common/Meta/Links.php ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Meta;
3
+
4
+ /**
5
+ * Instantiates the meta links "next" and "prev".
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class Links {
10
+ /**
11
+ * Get the prev/next links for the current page.
12
+ *
13
+ * @since 4.0.0
14
+ *
15
+ * @return array An array of link data.
16
+ */
17
+ public function getLinks() {
18
+ $links = [
19
+ 'prev' => '',
20
+ 'next' => '',
21
+ ];
22
+ if ( is_home() || is_archive() || is_paged() || is_search() ) {
23
+ $links = $this->getHomeLinks();
24
+ }
25
+
26
+ if ( is_page() || is_single() ) {
27
+ global $post;
28
+ $links = $this->getPostLinks( $post );
29
+ }
30
+
31
+ $links['prev'] = apply_filters( 'aioseo_prev_link', $links['prev'] );
32
+ $links['next'] = apply_filters( 'aioseo_next_link', $links['next'] );
33
+
34
+ return $links;
35
+ }
36
+
37
+ /**
38
+ * Get the prev/next links for the current page (home/archive, etc.).
39
+ *
40
+ * @since 4.0.0
41
+ *
42
+ * @return array An array of link data.
43
+ */
44
+ private function getHomeLinks() {
45
+ $prev = '';
46
+ $next = '';
47
+ $page = aioseo()->helpers->getPageNumber();
48
+
49
+ global $wp_query;
50
+ $maxPage = $wp_query->max_num_pages;
51
+ if ( $page > 1 ) {
52
+ $prev = get_previous_posts_page_link();
53
+ }
54
+ if ( $page < $maxPage ) {
55
+ $next = get_next_posts_page_link();
56
+ $paged = is_paged();
57
+ if ( ! is_single() ) {
58
+ if ( ! $paged ) {
59
+ $paged = 1;
60
+ }
61
+ $nextpage = intval( $paged ) + 1;
62
+ if ( ! $maxPage || $maxPage >= $nextpage ) {
63
+ $next = get_pagenum_link( $nextpage );
64
+ }
65
+ }
66
+ }
67
+
68
+ // Remove trailing slashes if not set in the permalink structure.
69
+ $prev = aioseo()->helpers->maybeRemoveTrailingSlash( $prev );
70
+ $next = aioseo()->helpers->maybeRemoveTrailingSlash( $next );
71
+
72
+ return [
73
+ 'prev' => $prev,
74
+ 'next' => $next,
75
+ ];
76
+ }
77
+
78
+ /**
79
+ * Get the prev/next links for the current post.
80
+ *
81
+ * @since 4.0.0
82
+ *
83
+ * @param WP_Post $post The post.
84
+ * @return array An array of link data.
85
+ */
86
+ private function getPostLinks( $post ) {
87
+ $prev = '';
88
+ $next = '';
89
+ $page = aioseo()->helpers->getPageNumber();
90
+ $numpages = 1;
91
+ $content = $post->post_content;
92
+ if ( false !== strpos( $content, '<!--nextpage-->', 0 ) ) {
93
+ $content = str_replace( "\n<!--nextpage-->\n", '<!--nextpage-->', $content );
94
+ $content = str_replace( "\n<!--nextpage-->", '<!--nextpage-->', $content );
95
+ $content = str_replace( "<!--nextpage-->\n", '<!--nextpage-->', $content );
96
+ // Ignore nextpage at the beginning of the content.
97
+ if ( 0 === strpos( $content, '<!--nextpage-->', 0 ) ) {
98
+ $content = substr( $content, 15 );
99
+ }
100
+ $pages = explode( '<!--nextpage-->', $content );
101
+ $numpages = count( $pages );
102
+ } else {
103
+ $page = null;
104
+ }
105
+ if ( ! empty( $page ) ) {
106
+ if ( $page > 1 ) {
107
+ $prev = $this->getLinkPage( $page - 1 );
108
+ }
109
+ if ( $page + 1 <= $numpages ) {
110
+ $next = $this->getLinkPage( $page + 1 );
111
+ }
112
+ }
113
+
114
+ return [
115
+ 'prev' => $prev,
116
+ 'next' => $next,
117
+ ];
118
+ }
119
+
120
+ /**
121
+ * This is a clone of _wp_link_page, except that we don't output HTML.
122
+ *
123
+ * @since 4.0.0
124
+ *
125
+ * @param integer $number The page number.
126
+ * @return string The URL.
127
+ */
128
+ private function getLinkPage( $number ) {
129
+ global $wp_rewrite;
130
+ $post = get_post();
131
+ $queryArgs = [];
132
+
133
+ if ( 1 === (int) $number ) {
134
+ $url = get_permalink();
135
+ } else {
136
+ if ( ! get_option( 'permalink_structure' ) || in_array( $post->post_status, [ 'draft', 'pending' ], true ) ) {
137
+ $url = add_query_arg( 'page', $number, get_permalink() );
138
+ } elseif ( 'page' === get_option( 'show_on_front' ) && get_option( 'page_on_front' ) === $post->ID ) {
139
+ $url = trailingslashit( get_permalink() ) . user_trailingslashit( "$wp_rewrite->pagination_base/" . $number, 'single_paged' );
140
+ } else {
141
+ $url = trailingslashit( get_permalink() ) . user_trailingslashit( $number, 'single_paged' );
142
+ }
143
+ }
144
+
145
+ if ( is_preview() ) {
146
+
147
+ if ( ( 'draft' !== $post->post_status ) && isset( $_GET['preview_id'], $_GET['preview_nonce'] ) ) {
148
+ $queryArgs['preview_id'] = sanitize_text_field( wp_unslash( $_GET['preview_id'] ) );
149
+ $queryArgs['preview_nonce'] = sanitize_text_field( wp_unslash( $_GET['preview_nonce'] ) );
150
+ }
151
+
152
+ $url = get_preview_post_link( $post, $queryArgs, $url );
153
+ }
154
+
155
+ return esc_url( $url );
156
+ }
157
+ }
app/Common/Meta/Meta.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Meta;
3
+
4
+ use AIOSEO\Plugin\Common\Models;
5
+
6
+ /**
7
+ * Instantiates the Meta classes.
8
+ *
9
+ * @since 4.0.0
10
+ */
11
+ class Meta {
12
+
13
+ /**
14
+ * Class constructor.
15
+ *
16
+ * @since 4.0.0
17
+ */
18
+ public function __construct() {
19
+ $this->metaData = new MetaData();
20
+ $this->title = new Title();
21
+ $this->description = new Description();
22
+ $this->keywords = new Keywords();
23
+ $this->amp = new Amp();
24
+ $this->links = new Links();
25
+
26
+ add_action( 'delete_post', [ $this, 'deletePostMeta' ], 1000, 2 );
27
+ }
28
+
29
+ /**
30
+ * When we delete the meta, we want to delete our post model.
31
+ *
32
+ * @since 4.0.1
33
+ *
34
+ * @param integer $postId The ID of the post.
35
+ * @param WP_Post $post The post object.
36
+ * @return void
37
+ */
38
+ public function deletePostMeta( $postId ) {
39
+ $aioseoPost = Models\Post::getPost( $postId );
40
+ if ( $aioseoPost->exists() ) {
41
+ $aioseoPost->delete();
42
+ }
43
+ }
44
+ }
app/Common/Meta/MetaData.php ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Meta;
3
+
4
+ use AIOSEO\Plugin\Common\Models;
5
+
6
+ /**
7
+ * Handles fetching metadata for the current object.
8
+ *
9
+ * @since 4.0.0
10
+ */
11
+ class MetaData {
12
+ /**
13
+ * Class constructor.
14
+ *
15
+ * @since 4.0.0
16
+ */
17
+ public function __construct() {
18
+ add_action( 'wpml_pro_translation_completed', [ $this, 'updateWpmlLocalization' ], 1000, 3 );
19
+ }
20
+
21
+ /**
22
+ * Update the localized data in our posts table.
23
+ *
24
+ * @since 4.0.0
25
+ *
26
+ * @param integer $postId The post ID.
27
+ * @param array $fields An array of fields to update.
28
+ * @return void
29
+ */
30
+ public function updateWpmlLocalization( $postId, $fields, $job ) {
31
+ $aioseoFields = [
32
+ '_aioseo_title',
33
+ '_aioseo_description',
34
+ '_aioseo_keywords',
35
+ '_aioseo_og_title',
36
+ '_aioseo_og_description',
37
+ '_aioseo_twitter_title',
38
+ '_aioseo_twitter_description'
39
+ ];
40
+
41
+ $parentId = $job->original_doc_id;
42
+ $parentPost = Models\Post::getPost( $parentId );
43
+ $currentPost = Models\Post::getPost( $postId );
44
+ $columns = $parentPost->getColumns();
45
+ foreach ( $columns as $column => $value ) {
46
+ // Skip the ID columns.
47
+ if ( 'id' === $column || 'post_id' === $column ) {
48
+ continue;
49
+ }
50
+
51
+ $currentPost->$column = $parentPost->$column;
52
+ }
53
+
54
+ $currentPost->post_id = $postId;
55
+
56
+ foreach ( $aioseoFields as $aioseoField ) {
57
+ if ( ! empty( $fields[ 'field-' . $aioseoField . '-0' ] ) ) {
58
+ $value = $fields[ 'field-' . $aioseoField . '-0' ]['data'];
59
+ if ( '_aioseo_keywords' === $aioseoField ) {
60
+ $value = explode( ',', $value );
61
+ foreach ( $value as $k => $keyword ) {
62
+ $value[ $k ] = [
63
+ 'label' => $keyword,
64
+ 'value' => $keyword
65
+ ];
66
+ }
67
+
68
+ $value = wp_json_encode( $value );
69
+ }
70
+ $currentPost->{ str_replace( '_aioseo_', '', $aioseoField ) } = $value;
71
+ }
72
+ }
73
+
74
+ $currentPost->save();
75
+ }
76
+
77
+ /**
78
+ * Returns the metadata for the current object.
79
+ *
80
+ * @since 4.0.0
81
+ *
82
+ * @param WP_Post $post The post object (optional).
83
+ * @return array|bool The meta data or false.
84
+ */
85
+ public function getMetaData( $post = null ) {
86
+ static $posts = [];
87
+
88
+ if ( ! $post ) {
89
+ $post = aioseo()->helpers->getPost();
90
+ }
91
+
92
+ if ( $post ) {
93
+ $post = is_object( $post ) ? $post : aioseo()->helpers->getPost( $post );
94
+ // If we still have no post, let's return false.
95
+ if ( empty( $post ) ) {
96
+ return false;
97
+ }
98
+
99
+ if ( isset( $posts[ $post->ID ] ) ) {
100
+ return $posts[ $post->ID ];
101
+ }
102
+ $posts[ $post->ID ] = Models\Post::getPost( $post->ID );
103
+
104
+ if ( ! $posts[ $post->ID ]->exists() ) {
105
+ $migratedMeta = aioseo()->migration->meta->getMigratedPostMeta( $post->ID );
106
+ if ( ! empty( $migratedMeta ) ) {
107
+ foreach ( $migratedMeta as $k => $v ) {
108
+ $posts[ $post->ID ]->{$k} = $v;
109
+ }
110
+
111
+ $posts[ $post->ID ]->save();
112
+ }
113
+ }
114
+
115
+ return $posts[ $post->ID ];
116
+ }
117
+ return false;
118
+ }
119
+ }
app/Common/Meta/Robots.php ADDED
@@ -0,0 +1,318 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Meta;
3
+
4
+ /**
5
+ * Handles the robots meta tag.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class Robots {
10
+
11
+ /**
12
+ * The robots meta tag attributes.
13
+ *
14
+ * We'll already set the keys on construction so that we always output the attributes in the same order.
15
+ *
16
+ * @since 4.0.0
17
+ *
18
+ * @var array
19
+ */
20
+ protected $attributes = [
21
+ 'noindex' => '',
22
+ 'nofollow' => '',
23
+ 'noarchive' => '',
24
+ 'nosnippet' => '',
25
+ 'noimageindex' => '',
26
+ 'noodp' => '',
27
+ 'notranslate' => '',
28
+ 'max-snippet' => '',
29
+ 'max-image-preview' => '',
30
+ 'max-video-preview' => ''
31
+ ];
32
+
33
+ /**
34
+ * Returns the robots meta tag value.
35
+ *
36
+ * @since 4.0.0
37
+ *
38
+ * @return mixed The robots meta tag value or false.
39
+ */
40
+ public function meta() {
41
+ if ( is_category() || is_tag() || is_tax() ) {
42
+ return $this->term();
43
+ }
44
+
45
+ if ( ! get_option( 'blog_public' ) || $this->isNoindexedWooCommercePage() ) {
46
+ return false;
47
+ }
48
+
49
+ if ( is_home() && 'posts' === get_option( 'show_on_front' ) ) {
50
+ $this->globalValues();
51
+ return $this->metaHelper();
52
+ }
53
+
54
+ $post = aioseo()->helpers->getPost();
55
+ if ( $post ) {
56
+ $this->post();
57
+ return $this->metaHelper();
58
+ }
59
+
60
+ if ( is_author() ) {
61
+ $this->globalValues( [ 'archives', 'author' ] );
62
+ return $this->metaHelper();
63
+ }
64
+
65
+ if ( is_date() ) {
66
+ $this->globalValues( [ 'archives', 'date' ] );
67
+ return $this->metaHelper();
68
+ }
69
+
70
+ if ( is_search() ) {
71
+ $this->globalValues( [ 'archives', 'search' ] );
72
+ return $this->metaHelper();
73
+ }
74
+
75
+ if ( is_404() ) {
76
+ return apply_filters( 'aioseo_404_robots', 'noindex' );
77
+ }
78
+
79
+ if ( is_archive() ) {
80
+ $this->archives();
81
+ return $this->metaHelper();
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Stringifies and filters the robots meta tag value.
87
+ *
88
+ * Acts as a helper for meta().
89
+ *
90
+ * @since 4.0.0
91
+ *
92
+ * @return string The robots meta tag value.
93
+ */
94
+ protected function metaHelper() {
95
+ $pageNumber = aioseo()->helpers->getPageNumber();
96
+ if ( 1 < $pageNumber ) {
97
+ if ( aioseo()->options->searchAppearance->advanced->globalRobotsMeta->noindexPaginated ) {
98
+ $this->attributes['noindex'] = 'noindex';
99
+ }
100
+
101
+ if ( aioseo()->options->searchAppearance->advanced->globalRobotsMeta->nofollowPaginated ) {
102
+ $this->attributes['nofollow'] = 'nofollow';
103
+ }
104
+ }
105
+
106
+ // Never allow users to noindex the homepage.
107
+ if ( is_front_page() ) {
108
+ $this->attributes['noindex'] = '';
109
+ }
110
+
111
+ $this->attributes = apply_filters( 'aioseo_robots_meta', $this->attributes );
112
+ return implode( ', ', array_filter( $this->attributes ) );
113
+ }
114
+
115
+ /**
116
+ * Sets the attributes for the current post.
117
+ *
118
+ * @since 4.0.0
119
+ *
120
+ * @return void
121
+ */
122
+ private function post() {
123
+ $options = aioseo()->options->noConflict();
124
+ $post = aioseo()->helpers->getPost();
125
+ $metaData = aioseo()->meta->metaData->getMetaData( $post );
126
+
127
+ if ( ! empty( $metaData ) && ! $metaData->robots_default ) {
128
+ $this->metaValues( $metaData );
129
+ return;
130
+ }
131
+
132
+ if ( $options->searchAppearance->dynamic->postTypes->has( $post->post_type ) ) {
133
+ $this->globalValues( [ 'dynamic', 'postTypes', $post->post_type ] );
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Returns the robots meta tag value for the current term.
139
+ *
140
+ * @since 4.0.6
141
+ *
142
+ * @return string The robots meta tag value.
143
+ */
144
+ private function term() {
145
+ $options = aioseo()->options->noConflict();
146
+ $term = get_queried_object();
147
+
148
+ if ( $options->searchAppearance->dynamic->taxonomies->has( $term->taxonomy ) ) {
149
+ $this->globalValues( [ 'dynamic', 'taxonomies', $term->taxonomy ] );
150
+ return$this->metaHelper();
151
+ }
152
+
153
+ $this->globalValues();
154
+ return $this->metaHelper();
155
+ }
156
+
157
+ /**
158
+ * Sets the attributes for the current archive.
159
+ *
160
+ * @since 4.0.0
161
+ *
162
+ * @return void
163
+ */
164
+ private function archives() {
165
+ $options = aioseo()->options->noConflict();
166
+ $postType = get_queried_object();
167
+
168
+ if ( $options->searchAppearance->dynamic->archives->has( $postType->name ) ) {
169
+ $this->globalValues( [ 'dynamic', 'archives', $postType->name ] );
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Sets the attributes based on the global values.
175
+ *
176
+ * @since 4.0.0
177
+ *
178
+ * @param array $optionOrder The order in which the options need to be called to get the relevant robots meta settings.
179
+ * @return void
180
+ */
181
+ protected function globalValues( $optionOrder = [] ) {
182
+ $robotsMeta = [];
183
+ if ( count( $optionOrder ) ) {
184
+ $options = aioseo()->options->noConflict()->searchAppearance;
185
+ foreach ( $optionOrder as $option ) {
186
+ $options = $options->$option;
187
+ }
188
+
189
+ $clonedOptions = clone $options;
190
+ if ( ! $clonedOptions->show ) {
191
+ $this->attributes['noindex'] = 'noindex';
192
+ }
193
+
194
+ $robotsMeta = $options->advanced->robotsMeta->all();
195
+ if ( $robotsMeta['default'] ) {
196
+ $robotsMeta = aioseo()->options->searchAppearance->advanced->globalRobotsMeta->all();
197
+ }
198
+ } else {
199
+ $robotsMeta = aioseo()->options->searchAppearance->advanced->globalRobotsMeta->all();
200
+ }
201
+
202
+ if ( $robotsMeta['default'] ) {
203
+ return;
204
+ }
205
+
206
+ if ( $robotsMeta['noindex'] ) {
207
+ $this->attributes['noindex'] = 'noindex';
208
+ }
209
+ if ( $robotsMeta['nofollow'] ) {
210
+ $this->attributes['nofollow'] = 'nofollow';
211
+ }
212
+ if ( $robotsMeta['noarchive'] ) {
213
+ $this->attributes['noarchive'] = 'noarchive';
214
+ }
215
+ $noSnippet = $robotsMeta['nosnippet'];
216
+ if ( $noSnippet ) {
217
+ $this->attributes['nosnippet'] = 'nosnippet';
218
+ }
219
+ $noImageIndex = $robotsMeta['noimageindex'];
220
+ if ( $noImageIndex ) {
221
+ $this->attributes['noimageindex'] = 'noimageindex';
222
+ }
223
+ if ( $robotsMeta['noodp'] ) {
224
+ $this->attributes['noodp'] = 'noodp';
225
+ }
226
+ if ( $robotsMeta['notranslate'] ) {
227
+ $this->attributes['notranslate'] = 'notranslate';
228
+ }
229
+ $maxSnippet = $robotsMeta['maxSnippet'];
230
+ if ( ! $noSnippet && $maxSnippet && intval( $maxSnippet ) ) {
231
+ $this->attributes['max-snippet'] = "max-snippet:$maxSnippet";
232
+ }
233
+ $maxImagePreview = $robotsMeta['maxImagePreview'];
234
+ if ( ! $noImageIndex && $maxImagePreview && in_array( $maxImagePreview, [ 'none', 'standard', 'large' ], true ) ) {
235
+ $this->attributes['max-image-preview'] = "max-image-preview:$maxImagePreview";
236
+ }
237
+ $maxVideoPreview = $robotsMeta['maxVideoPreview'];
238
+ if ( $maxVideoPreview && intval( $maxVideoPreview ) ) {
239
+ $this->attributes['max-video-preview'] = "max-video-preview:$maxVideoPreview";
240
+ }
241
+ }
242
+
243
+ /**
244
+ * Sets the attributes from the meta data.
245
+ *
246
+ * @since 4.0.0
247
+ *
248
+ * @param array $metaData The post/term meta data.
249
+ * @return void
250
+ */
251
+ protected function metaValues( $metaData ) {
252
+ if ( $metaData->robots_noindex || $this->isPasswordProtected() ) {
253
+ $this->attributes['noindex'] = 'noindex';
254
+ }
255
+ if ( $metaData->robots_nofollow ) {
256
+ $this->attributes['nofollow'] = 'nofollow';
257
+ }
258
+ if ( $metaData->robots_noarchive ) {
259
+ $this->attributes['noarchive'] = 'noarchive';
260
+ }
261
+ if ( $metaData->robots_nosnippet ) {
262
+ $this->attributes['nosnippet'] = 'nosnippet';
263
+ }
264
+ if ( $metaData->robots_noimageindex ) {
265
+ $this->attributes['noimageindex'] = 'noimageindex';
266
+ }
267
+ if ( $metaData->robots_noodp ) {
268
+ $this->attributes['noodp'] = 'noodp';
269
+ }
270
+ if ( $metaData->robots_notranslate ) {
271
+ $this->attributes['notranslate'] = 'notranslate';
272
+ }
273
+ if ( ! $metaData->robots_nosnippet && $metaData->robots_max_snippet && intval( $metaData->robots_max_snippet ) ) {
274
+ $this->attributes['max-snippet'] = "max-snippet:$metaData->robots_max_snippet";
275
+ }
276
+ if ( ! $metaData->robots_noimageindex && $metaData->robots_max_imagepreview && in_array( $metaData->robots_max_imagepreview, [ 'none', 'standard', 'large' ], true ) ) {
277
+ $this->attributes['max-image-preview'] = "max-image-preview:$metaData->robots_max_imagepreview";
278
+ }
279
+ if ( $metaData->robots_max_videopreview && intval( $metaData->robots_max_videopreview ) ) {
280
+ $this->attributes['max-video-preview'] = "max-video-preview:$metaData->robots_max_videopreview";
281
+ }
282
+ }
283
+
284
+ /**
285
+ * Checks whether the current post is password protected.
286
+ *
287
+ * @since 4.0.0
288
+ *
289
+ * @return bool Whether the post is password protected.
290
+ */
291
+ private function isPasswordProtected() {
292
+ $post = aioseo()->helpers->getPost();
293
+ return is_object( $post ) && $post->post_password;
294
+ }
295
+
296
+ /**
297
+ * Checks whether the current page is a noindexed WooCommerce page.
298
+ *
299
+ * WooCommerce noindexes the Cart, Checkout and My Account pages by default. In this case, we don't need to output another robots meta tag.
300
+ *
301
+ * @since 4.0.0
302
+ *
303
+ * @return boolean Whether the current page is an noindexed WooCommerce page.
304
+ */
305
+ private function isNoindexedWooCommercePage() {
306
+ $post = aioseo()->helpers->getPost();
307
+ if (
308
+ ! aioseo()->helpers->isWooCommerceActive() ||
309
+ ! is_object( $post ) ||
310
+ 'page' !== $post->post_type ||
311
+ ! has_action( 'wp_head', 'wc_page_noindex' )
312
+ ) {
313
+ return false;
314
+ }
315
+
316
+ return in_array( get_permalink(), aioseo()->helpers->getNoindexedWooCommercePages(), true );
317
+ }
318
+ }
app/Common/Meta/SiteVerification.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Meta;
3
+
4
+ /**
5
+ * Handles the site verification meta tags.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class SiteVerification {
10
+ /**
11
+ * An array of webmaster tools and their meta names.
12
+ *
13
+ * @since 4.0.0
14
+ *
15
+ * @var array
16
+ */
17
+ private $webmasterTools = [
18
+ 'google' => 'google-site-verification',
19
+ 'bing' => 'msvalidate.01',
20
+ 'pinterest' => 'p:domain_verify',
21
+ 'yandex' => 'yandex-verification',
22
+ 'baidu' => 'baidu-site-verification'
23
+ ];
24
+
25
+ /**
26
+ * Returns the robots meta tag value.
27
+ *
28
+ * @since 4.0.0
29
+ *
30
+ * @return mixed The robots meta tag value or false.
31
+ */
32
+ public function meta() {
33
+ $metaArray = [];
34
+ foreach ( $this->webmasterTools as $key => $metaName ) {
35
+ $value = aioseo()->options->webmasterTools->$key;
36
+ if ( $value ) {
37
+ $metaArray[ $metaName ] = $value;
38
+ }
39
+ }
40
+
41
+ return $metaArray;
42
+ }
43
+ }
app/Common/Meta/Title.php ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Meta;
3
+
4
+ /**
5
+ * Handles the title.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class Title {
10
+ /**
11
+ * Returns the filtered page title.
12
+ *
13
+ * Acts as a helper for getTitle() because we need to encode the title before sending it back to the filter.
14
+ *
15
+ * @since 4.0.0
16
+ *
17
+ * @return string The page title.
18
+ */
19
+ public function filterPageTitle() {
20
+ return aioseo()->helpers->encodeOutputHtml( $this->getTitle() );
21
+ }
22
+
23
+ /**
24
+ * Returns the homepage title.
25
+ *
26
+ * @since 4.0.0
27
+ *
28
+ * @return string The homepage title.
29
+ */
30
+ public function getHomePageTitle() {
31
+ if ( 'page' === get_option( 'show_on_front' ) ) {
32
+ $title = $this->getPostTitle( (int) get_option( 'page_on_front' ) );
33
+ return $title ? $title : aioseo()->helpers->decodeHtmlEntities( get_bloginfo( 'name' ) );
34
+ }
35
+
36
+ $title = $this->prepareTitle( aioseo()->options->searchAppearance->global->siteTitle );
37
+ return $title ? $title : aioseo()->helpers->decodeHtmlEntities( get_bloginfo( 'name' ) );
38
+ }
39
+
40
+ /**
41
+ * Returns the title for the current page.
42
+ *
43
+ * @since 4.0.0
44
+ *
45
+ * @param WP_Post $post The post object (optional).
46
+ * @param boolean $default Whether we want the default value, not the post one.
47
+ * @return string The page title.
48
+ */
49
+ public function getTitle( $post = null, $default = false ) {
50
+ if ( is_home() && 'posts' === get_option( 'show_on_front' ) ) {
51
+ return $this->getHomePageTitle();
52
+ }
53
+
54
+ if ( $post || is_singular() || aioseo()->helpers->isStaticPage() ) {
55
+ return $this->getPostTitle( $post, $default );
56
+ }
57
+
58
+ if ( is_category() || is_tag() || is_tax() ) {
59
+ $term = $post ? $post : get_queried_object();
60
+ return $this->getTermTitle( $term, $default );
61
+ }
62
+
63
+ if ( is_author() ) {
64
+ $title = $this->prepareTitle( aioseo()->options->searchAppearance->archives->author->title );
65
+ if ( $title ) {
66
+ return $title;
67
+ }
68
+ $author = get_queried_object();
69
+ $name = trim( sprintf( '%1$s %2$s', get_the_author_meta( 'first_name', $author->ID ), get_the_author_meta( 'last_name', $author->ID ) ) );
70
+ return $this->prepareTitle( $name );
71
+ }
72
+
73
+ if ( is_date() ) {
74
+ $title = $this->prepareTitle( aioseo()->options->searchAppearance->archives->date->title );
75
+ if ( $title ) {
76
+ return $title;
77
+ }
78
+
79
+ if ( is_year() ) {
80
+ $title = get_the_date( 'Y' );
81
+ } elseif ( is_month() ) {
82
+ $title = get_the_date( 'F, Y' );
83
+ } elseif ( is_day() ) {
84
+ $title = get_the_date();
85
+ }
86
+ return $this->prepareTitle( "$title &#8211; #site_title" );
87
+ }
88
+
89
+ if ( is_search() ) {
90
+ return $this->prepareTitle( aioseo()->options->searchAppearance->archives->search->title );
91
+ }
92
+
93
+ if ( is_archive() ) {
94
+ $postType = get_queried_object();
95
+ $options = aioseo()->options->noConflict();
96
+ if ( $options->searchAppearance->dynamic->archives->has( $postType->name ) ) {
97
+ return $this->prepareTitle( aioseo()->options->searchAppearance->dynamic->archives->{ $postType->name }->title );
98
+ }
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Returns the post title.
104
+ *
105
+ * @since 4.0.0
106
+ *
107
+ * @param WP_Post|int $post The post object or ID.
108
+ * @param boolean $default Whether we want the default value, not the post one.
109
+ * @return string The post title.
110
+ */
111
+ public function getPostTitle( $post, $default = false ) {
112
+ $post = $post && is_object( $post ) ? $post : aioseo()->helpers->getPost( $post );
113
+ $metaData = aioseo()->meta->metaData->getMetaData( $post );
114
+
115
+ $title = '';
116
+ if ( ! empty( $metaData->title ) && ! $default ) {
117
+ $title = $this->prepareTitle( $metaData->title, $post->ID );
118
+ }
119
+
120
+ // If this post is the static home page and we have no title, let's reset to the site name.
121
+ if ( empty( $title ) && 'page' === get_option( 'show_on_front' ) && (int) get_option( 'page_on_front' ) === $post->ID ) {
122
+ return aioseo()->helpers->decodeHtmlEntities( get_bloginfo( 'name' ) );
123
+ }
124
+
125
+ if ( ! $title ) {
126
+ $title = $this->prepareTitle( $this->getPostTypeTitle( $post->post_type ), $post->ID, $default );
127
+ }
128
+ return $title ? $title : $this->prepareTitle( $post->post_title, $post->ID, $default );
129
+ }
130
+
131
+ /**
132
+ * Retrieve the default title for the post type.
133
+ *
134
+ * @since 4.0.6
135
+ *
136
+ * @param string $postType The post type.
137
+ * @return string The title.
138
+ */
139
+ public function getPostTypeTitle( $postType ) {
140
+ $options = aioseo()->options->noConflict();
141
+ if ( $options->searchAppearance->dynamic->postTypes->has( $postType ) ) {
142
+ return aioseo()->options->searchAppearance->dynamic->postTypes->{$postType}->title;
143
+ }
144
+
145
+ return '';
146
+ }
147
+
148
+ /**
149
+ * Returns the term title.
150
+ *
151
+ * @since 4.0.6
152
+ *
153
+ * @param WP_Term $term The term object.
154
+ * @param boolean $default Whether we want the default value, not the post one.
155
+ * @return string The term title.
156
+ */
157
+ public function getTermTitle( $term, $default = false ) {
158
+ $title = '';
159
+ $options = aioseo()->options->noConflict();
160
+ if ( ! $title && $options->searchAppearance->dynamic->taxonomies->has( $term->taxonomy ) ) {
161
+ $newTitle = aioseo()->options->searchAppearance->dynamic->taxonomies->{$term->taxonomy}->title;
162
+ $newTitle = preg_replace( '/#taxonomy_title/', $term->name, $newTitle );
163
+ $title = $this->prepareTitle( $newTitle, false, $default );
164
+ }
165
+ return $title ? $title : $this->prepareTitle( single_term_title( '', false ), false, $default );
166
+ }
167
+
168
+ /**
169
+ * Prepares and sanitizes the title.
170
+ *
171
+ * @since 4.0.0
172
+ *
173
+ * @param string $title The title.
174
+ * @param int $id The page or post id.
175
+ * @param boolean $default Whether we want the default value, not the post one.
176
+ * @return string The sanitized title.
177
+ */
178
+ public function prepareTitle( $title, $id = false, $default = false ) {
179
+ if ( ! is_admin() && 1 < aioseo()->helpers->getPageNumber() ) {
180
+ $title .= aioseo()->options->searchAppearance->advanced->pagedFormat;
181
+ }
182
+
183
+ $title = $default ? $title : aioseo()->tags->replaceTags( $title, $id );
184
+ $title = apply_filters( 'aioseo_title', $title );
185
+
186
+ $title = aioseo()->helpers->decodeHtmlEntities( $title );
187
+ $title = wp_strip_all_tags( strip_shortcodes( $title ) );
188
+ // Trim both internal and external whitespace.
189
+ $title = preg_replace( '/[\s]+/u', ' ', trim( $title ) );
190
+ return aioseo()->helpers->internationalize( $title );
191
+ }
192
+ }
app/Common/Migration/BadRobots.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Migration;
3
+
4
+ // phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
5
+
6
+ /**
7
+ * Migrates the Bad Robots Blocker settings from V3.
8
+ *
9
+ * @since 4.0.0
10
+ */
11
+ class BadRobots {
12
+
13
+
14
+ /**
15
+ * Class constructor.
16
+ *
17
+ * @since 4.0.0
18
+ */
19
+ public function __construct() {
20
+ $oldOptions = aioseo()->migration->oldOptions;
21
+
22
+ $deprecatedOptions = aioseo()->internalOptions->internal->deprecatedOptions;
23
+ array_push( $deprecatedOptions, 'badBotBlocker' );
24
+ aioseo()->internalOptions->internal->deprecatedOptions = $deprecatedOptions;
25
+
26
+ if ( empty( $oldOptions['modules']['aiosp_bad_robots_options'] ) ) {
27
+ return;
28
+ }
29
+
30
+ if ( ! empty( $oldOptions['modules']['aiosp_bad_robots_options']['aiosp_bad_robots_blocklist'] ) ) {
31
+ $badBots = explode( '\r\n', $oldOptions['modules']['aiosp_bad_robots_options']['aiosp_bad_robots_blocklist'] );
32
+ if ( $badBots ) {
33
+ foreach ( $badBots as $k => $v ) {
34
+ $badBots[ $k ] = aioseo()->helpers->sanitizeOption( $v );
35
+ }
36
+ aioseo()->options->deprecated->tools->blocker->custom->bots = implode( "\r\n", $badBots );
37
+ }
38
+ }
39
+
40
+ if ( ! empty( $oldOptions['modules']['aiosp_bad_robots_options']['aiosp_bad_robots_referlist'] ) ) {
41
+ $badReferers = explode( '\r\n', $oldOptions['modules']['aiosp_bad_robots_options']['aiosp_bad_robots_referlist'] );
42
+ if ( $badReferers ) {
43
+ foreach ( $badReferers as $k => $v ) {
44
+ $badReferers[ $k ] = aioseo()->helpers->sanitizeOption( $v );
45
+ }
46
+ aioseo()->options->deprecated->tools->blocker->custom->referer = implode( "\r\n", $badReferers );
47
+ }
48
+ }
49
+
50
+ $settings = [
51
+ 'aiosp_bad_robots_block_bots' => [ 'type' => 'boolean', 'newOption' => [ 'deprecated', 'tools', 'blocker', 'blockBots' ] ],
52
+ 'aiosp_bad_robots_block_refer' => [ 'type' => 'boolean', 'newOption' => [ 'deprecated', 'tools', 'blocker', 'blockReferer' ] ],
53
+ 'aiosp_bad_robots_track_blocks' => [ 'type' => 'boolean', 'newOption' => [ 'deprecated', 'tools', 'blocker', 'track' ] ],
54
+ 'aiosp_bad_robots_edit_blocks' => [ 'type' => 'boolean', 'newOption' => [ 'deprecated', 'tools', 'blocker', 'custom', 'enable' ] ],
55
+ ];
56
+
57
+ aioseo()->migration->helpers->mapOldToNew( $settings, $oldOptions['modules']['aiosp_bad_robots_options'] );
58
+ }
59
+ }
app/Common/Migration/GeneralSettings.php ADDED
@@ -0,0 +1,892 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Migration;
3
+
4
+ use AIOSEO\Plugin\Common\Models;
5
+
6
+ // phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
7
+
8
+ /**
9
+ * Migrates the General Settings from V3.
10
+ *
11
+ * @since 4.0.0
12
+ */
13
+ class GeneralSettings {
14
+
15
+ /**
16
+ * The old V3 options.
17
+ *
18
+ * @since 4.0.0
19
+ *
20
+ * @var array
21
+ */
22
+ protected $oldOptions = [];
23
+
24
+ /**
25
+ * Class constructor.
26
+ *
27
+ * @since 4.0.0
28
+ */
29
+ public function __construct() {
30
+ $this->oldOptions = aioseo()->migration->oldOptions;
31
+
32
+ $this->migrateSeparatorCharacter();
33
+ $this->setDefaultArticleType();
34
+ $this->migrateHomePageMeta();
35
+ $this->migrateTitleFormats();
36
+ $this->migrateDescriptionFormat();
37
+ $this->migrateNoindexSettings();
38
+ $this->migrateNofollowSettings();
39
+ $this->migratePostSeoColumns();
40
+ $this->migrateSocialUrls();
41
+ $this->migrateSchemaMarkupSettings();
42
+ $this->migrateHomePageKeywords();
43
+ $this->migrateDeprecatedAdvancedOptions();
44
+ $this->migrateRssContentSettings();
45
+ $this->migrateRedirectToParent();
46
+ $this->migrateDisabledPosts();
47
+ $this->migrateGoogleAnalytics();
48
+
49
+ $settings = [
50
+ 'aiosp_no_paged_canonical_links' => [ 'type' => 'boolean', 'newOption' => [ 'searchAppearance', 'advanced', 'noPaginationForCanonical' ] ],
51
+ 'aiosp_showseonews' => [ 'type' => 'boolean', 'newOption' => [ 'advanced', 'dashboardWidget' ] ],
52
+ 'aiosp_admin_bar' => [ 'type' => 'boolean', 'newOption' => [ 'advanced', 'adminBarMenu' ] ],
53
+ 'aiosp_google_verify' => [ 'type' => 'string', 'newOption' => [ 'webmasterTools', 'google' ] ],
54
+ 'aiosp_bing_verify' => [ 'type' => 'string', 'newOption' => [ 'webmasterTools', 'bing' ] ],
55
+ 'aiosp_pinterest_verify' => [ 'type' => 'string', 'newOption' => [ 'webmasterTools', 'pinterest' ] ],
56
+ 'aiosp_yandex_verify' => [ 'type' => 'string', 'newOption' => [ 'webmasterTools', 'yandex' ] ],
57
+ 'aiosp_baidu_verify' => [ 'type' => 'string', 'newOption' => [ 'webmasterTools', 'baidu' ] ],
58
+ 'aiosp_google_analytics_id' => [ 'type' => 'string', 'newOption' => [ 'deprecated', 'webmasterTools', 'googleAnalytics', 'id' ] ],
59
+ 'aiosp_ga_advanced_options' => [ 'type' => 'boolean', 'newOption' => [ 'deprecated', 'webmasterTools', 'googleAnalytics', 'advanced' ] ],
60
+ 'aiosp_ga_domain' => [ 'type' => 'string', 'newOption' => [ 'deprecated', 'webmasterTools', 'googleAnalytics', 'trackingDomain' ] ],
61
+ 'aiosp_ga_multi_domain' => [ 'type' => 'boolean', 'newOption' => [ 'deprecated', 'webmasterTools', 'googleAnalytics', 'multipleDomains' ] ],
62
+ 'aiosp_ga_addl_domains' => [ 'type' => 'string', 'newOption' => [ 'deprecated', 'webmasterTools', 'googleAnalytics', 'additionalDomains' ] ],
63
+ 'aiosp_ga_anonymize_ip' => [ 'type' => 'boolean', 'newOption' => [ 'deprecated', 'webmasterTools', 'googleAnalytics', 'anonymizeIp' ] ],
64
+ 'aiosp_ga_display_advertising' => [ 'type' => 'boolean', 'newOption' => [ 'deprecated', 'webmasterTools', 'googleAnalytics', 'displayAdvertiserTracking' ] ],
65
+ 'aiosp_ga_exclude_users' => [ 'type' => 'array', 'newOption' => [ 'deprecated', 'webmasterTools', 'googleAnalytics', 'excludeUsers' ] ],
66
+ 'aiosp_ga_track_outbound_links' => [ 'type' => 'boolean', 'newOption' => [ 'deprecated', 'webmasterTools', 'googleAnalytics', 'trackOutboundLinks' ] ],
67
+ 'aiosp_ga_link_attribution' => [ 'type' => 'boolean', 'newOption' => [ 'deprecated', 'webmasterTools', 'googleAnalytics', 'enhancedLinkAttribution' ] ],
68
+ 'aiosp_ga_enhanced_ecommerce' => [ 'type' => 'boolean', 'newOption' => [ 'deprecated', 'webmasterTools', 'googleAnalytics', 'enhancedEcommerce' ] ],
69
+ 'aiosp_schema_site_represents' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'global', 'schema', 'siteRepresents' ] ],
70
+ 'aiosp_schema_organization_name' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'global', 'schema', 'organizationName' ] ],
71
+ 'aiosp_schema_person_manual_name' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'global', 'schema', 'personName' ] ],
72
+ 'aiosp_schema_organization_logo' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'global', 'schema', 'organizationLogo' ] ],
73
+ 'aiosp_schema_person_manual_image' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'global', 'schema', 'personLogo' ] ],
74
+ 'aiosp_schema_search_results_page' => [ 'type' => 'boolean', 'newOption' => [ 'searchAppearance', 'advanced', 'sitelinks' ] ],
75
+ 'aiosp_togglekeywords' => [ 'type' => 'boolean', 'newOption' => [ 'searchAppearance', 'advanced', 'useKeywords' ] ],
76
+ 'aiosp_use_categories' => [ 'type' => 'boolean', 'newOption' => [ 'searchAppearance', 'advanced', 'useCategoriesForMetaKeywords' ] ],
77
+ 'aiosp_use_tags_as_keywords' => [ 'type' => 'boolean', 'newOption' => [ 'searchAppearance', 'advanced', 'useTagsForMetaKeywords' ] ],
78
+ 'aiosp_dynamic_postspage_keywords' => [ 'type' => 'boolean', 'newOption' => [ 'searchAppearance', 'advanced', 'dynamicallyGenerateKeywords' ] ],
79
+ ];
80
+
81
+ aioseo()->migration->helpers->mapOldToNew( $settings, aioseo()->migration->oldOptions );
82
+ }
83
+
84
+ /**
85
+ * Migrates the separator character.
86
+ *
87
+ * @since 4.0.0
88
+ *
89
+ * @return void
90
+ */
91
+ private function migrateSeparatorCharacter() {
92
+ aioseo()->options->searchAppearance->global->separator = '|';
93
+ }
94
+
95
+ /**
96
+ * Set the default posts schema type to Article.
97
+ *
98
+ * @since 4.0.0
99
+ *
100
+ * @return void
101
+ */
102
+ private function setDefaultArticleType() {
103
+ if ( aioseo()->options->searchAppearance->dynamic->postTypes->has( 'post' ) ) {
104
+ aioseo()->options->searchAppearance->dynamic->postTypes->post->articleType = 'Article';
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Migrates the homepage meta.
110
+ *
111
+ * @since 4.0.0
112
+ *
113
+ * @return void
114
+ */
115
+ private function migrateHomePageMeta() {
116
+ $this->migrateHomePageTitle();
117
+ $this->migrateHomePageDescription();
118
+
119
+ // If the homepage is a static one, we should migrate the meta now.
120
+ $showOnFront = get_option( 'show_on_front' );
121
+ $pageOnFront = (int) get_option( 'page_on_front' );
122
+ if ( 'page' !== $showOnFront || ! $pageOnFront ) {
123
+ return;
124
+ }
125
+
126
+ $post = 'page' === $showOnFront && $pageOnFront ? get_post( $pageOnFront ) : '';
127
+ $aioseoPost = Models\Post::getPost( $post->ID );
128
+
129
+ $postMeta = aioseo()->db
130
+ ->start( 'postmeta' . ' as pm' )
131
+ ->select( 'pm.meta_key, pm.meta_value' )
132
+ ->where( 'pm.post_id', $post->ID )
133
+ ->whereRaw( "`pm`.`meta_key` LIKE '_aioseop_%'" )
134
+ ->run()
135
+ ->result();
136
+
137
+ $mappedMeta = [
138
+ '_aioseop_nofollow' => 'robots_nofollow',
139
+ '_aioseop_sitemap_priority' => 'priority',
140
+ '_aioseop_sitemap_frequency' => 'frequency',
141
+ '_aioseop_keywords' => 'keywords',
142
+ '_aioseop_opengraph_settings' => '',
143
+ ];
144
+
145
+ $meta = [
146
+ 'post_id' => $post->ID,
147
+ ];
148
+
149
+ foreach ( $postMeta as $record ) {
150
+ $name = $record->meta_key;
151
+ $value = $record->meta_value;
152
+
153
+ if ( ! in_array( $name, array_keys( $mappedMeta ), true ) ) {
154
+ continue;
155
+ }
156
+
157
+ switch ( $name ) {
158
+ case '_aioseop_nofollow':
159
+ $meta[ $mappedMeta[ $name ] ] = ! empty( $value );
160
+ if ( ! empty( $value ) ) {
161
+ $meta['robots_default'] = false;
162
+ }
163
+ break;
164
+ case '_aioseop_keywords':
165
+ $meta[ $mappedMeta[ $name ] ] = aioseo()->migration->helpers->oldKeywordsToNewKeywords( $value );
166
+ break;
167
+ case '_aioseop_opengraph_settings':
168
+ $class = new Meta();
169
+ $meta += $class->convertOpenGraphMeta( $value );
170
+
171
+ // We'll deal with the OG title/description in the Social Meta migration class.
172
+ if ( isset( $meta['og_title'] ) ) {
173
+ unset( $meta['og_title'] );
174
+ }
175
+ if ( isset( $meta['og_description'] ) ) {
176
+ unset( $meta['og_description'] );
177
+ }
178
+ break;
179
+ default:
180
+ $meta[ $mappedMeta[ $name ] ] = aioseo()->helpers->sanitizeOption( $value );
181
+ break;
182
+ }
183
+ }
184
+
185
+ $aioseoPost->set( $meta );
186
+ $aioseoPost->save();
187
+ }
188
+
189
+ /**
190
+ * Migrates the homepage title.
191
+ *
192
+ * @since 4.0.0
193
+ *
194
+ * @return void
195
+ */
196
+ private function migrateHomePageTitle() {
197
+ $showOnFront = get_option( 'show_on_front' );
198
+ $pageOnFront = (int) get_option( 'page_on_front' );
199
+
200
+ $homePageTitle = ! empty( $this->oldOptions['aiosp_home_title'] ) ? $this->oldOptions['aiosp_home_title'] : '';
201
+ $format = $this->oldOptions['aiosp_home_page_title_format'];
202
+
203
+ if ( 'posts' === $showOnFront ) {
204
+ $homePageTitle = $homePageTitle ? $homePageTitle : get_bloginfo( 'name' );
205
+ $title = aioseo()->migration->helpers->macrosToSmartTags( preg_replace( '#%page_title%#', $homePageTitle, $format ) );
206
+ aioseo()->options->searchAppearance->global->siteTitle = aioseo()->helpers->sanitizeOption( $title );
207
+ return;
208
+ }
209
+
210
+ // Set the setting globally regardless of what happens below.
211
+ if ( ! empty( $homePageTitle ) ) {
212
+ $title = aioseo()->migration->helpers->macrosToSmartTags( preg_replace( '#%page_title%#', $homePageTitle, $format ) );
213
+ aioseo()->options->searchAppearance->global->siteTitle = aioseo()->helpers->sanitizeOption( $title );
214
+ }
215
+
216
+ $post = 'page' === $showOnFront && $pageOnFront ? get_post( $pageOnFront ) : '';
217
+ $metaTitle = get_post_meta( $post->ID, '_aioseop_title', true );
218
+
219
+ $homePageTitle = '';
220
+ if ( empty( $this->oldOptions['aiosp_use_static_home_info'] ) ) {
221
+ $homePageTitle = ! empty( $this->oldOptions['aiosp_home_title'] ) ? $this->oldOptions['aiosp_home_title'] : '#site_title';
222
+ $homePageTitle = ! empty( $metaTitle ) ? $metaTitle : $homePageTitle;
223
+ $homePageTitle = aioseo()->migration->helpers->macrosToSmartTags( preg_replace( '#%page_title%#', $homePageTitle, $format ) );
224
+ } else {
225
+ if ( ! empty( $metaTitle ) ) {
226
+ $homePageTitle = aioseo()->migration->helpers->macrosToSmartTags( preg_replace( '#%page_title%#', $metaTitle, $format ) );
227
+ }
228
+ }
229
+
230
+ $aioseoPost = Models\Post::getPost( $post->ID );
231
+ $aioseoPost->set( [
232
+ 'post_id' => $post->ID,
233
+ 'title' => aioseo()->helpers->sanitizeOption( $homePageTitle )
234
+ ] );
235
+ $aioseoPost->save();
236
+
237
+ $this->maybeShowHomePageTitleNotice( $post );
238
+ }
239
+
240
+ /**
241
+ * Check if we should display a notice warning users that their homepage title may have changed.
242
+ *
243
+ * @since 4.0.0
244
+ *
245
+ * @param WP_Post $post The post object.
246
+ * @return void
247
+ */
248
+ private function maybeShowHomePageTitleNotice( $post ) {
249
+ $metaTitle = get_post_meta( $post->ID, '_aioseop_title', true );
250
+ $homePageTitle = ! empty( $this->oldOptions['aiosp_home_title'] ) ? $this->oldOptions['aiosp_home_title'] : '';
251
+
252
+ if (
253
+ empty( $this->oldOptions['aiosp_use_static_home_info'] ) &&
254
+ $metaTitle &&
255
+ ( trim( $homePageTitle ) !== trim( $metaTitle ) )
256
+ ) {
257
+ $this->showHomePageSettingsNotice();
258
+ }
259
+ }
260
+
261
+ /**
262
+ * Migrates the homepage description.
263
+ *
264
+ * @since 4.0.0
265
+ *
266
+ * @return void
267
+ */
268
+ private function migrateHomePageDescription() {
269
+ $showOnFront = get_option( 'show_on_front' );
270
+ $pageOnFront = (int) get_option( 'page_on_front' );
271
+
272
+ $homePageDescription = ! empty( $this->oldOptions['aiosp_home_description'] ) ? $this->oldOptions['aiosp_home_description'] : '';
273
+ $format = $this->oldOptions['aiosp_description_format'];
274
+
275
+ if ( 'posts' === $showOnFront ) {
276
+ // If the description had the page_title macro, we want to replace it with the actual page title itself.
277
+ $homePageDescription = $homePageDescription ? $homePageDescription : get_bloginfo( 'description' );
278
+ $homePageTitle = ! empty( $this->oldOptions['aiosp_home_title'] ) ? $this->oldOptions['aiosp_home_title'] : get_bloginfo( 'name' );
279
+ $format = preg_replace( '#%page_title%#', $homePageTitle, $format );
280
+ $description = aioseo()->migration->helpers->macrosToSmartTags( preg_replace( '#%description%#', $homePageDescription, $format ) );
281
+ aioseo()->options->searchAppearance->global->metaDescription = aioseo()->helpers->sanitizeOption( $description );
282
+ return;
283
+ }
284
+
285
+ // Set the setting globally regardless of what happens below.
286
+ if ( ! empty( $homePageDescription ) ) {
287
+ $homePageTitle = ! empty( $this->oldOptions['aiosp_home_title'] ) ? $this->oldOptions['aiosp_home_title'] : get_bloginfo( 'name' );
288
+ $format = preg_replace( '#%page_title%#', $homePageTitle, $format );
289
+ $description = aioseo()->migration->helpers->macrosToSmartTags( preg_replace( '#%description%#', $homePageDescription, $format ) );
290
+ aioseo()->options->searchAppearance->global->metaDescription = aioseo()->helpers->sanitizeOption( $description );
291
+ }
292
+
293
+ $post = 'page' === $showOnFront && $pageOnFront ? get_post( $pageOnFront ) : '';
294
+ $metaDescription = get_post_meta( $post->ID, '_aioseop_description', true );
295
+
296
+ $homePageDescription = '';
297
+ if ( empty( $this->oldOptions['aiosp_use_static_home_info'] ) ) {
298
+ $homePageDescription = ! empty( $this->oldOptions['aiosp_home_description'] ) ? $this->oldOptions['aiosp_home_description'] : '';
299
+ $homePageDescription = ! empty( $metaDescription ) ? $metaDescription : $homePageDescription;
300
+ } else {
301
+ if ( ! empty( $metaDescription ) ) {
302
+ $homePageDescription = aioseo()->migration->helpers->macrosToSmartTags( preg_replace( '#%description%#', $metaDescription, $format ) );
303
+ }
304
+ }
305
+
306
+ $homePageDescription = aioseo()->migration->helpers->macrosToSmartTags( preg_replace( '#%description%#', $homePageDescription, $format ) );
307
+
308
+ $aioseoPost = Models\Post::getPost( $post->ID );
309
+ $aioseoPost->set( [
310
+ 'post_id' => $post->ID,
311
+ 'description' => aioseo()->helpers->sanitizeOption( $homePageDescription )
312
+ ] );
313
+ $aioseoPost->save();
314
+
315
+ $this->maybeShowHomePageDescriptionNotice( $post );
316
+ }
317
+
318
+ /**
319
+ * Check if we should display a notice warning users that their homepage title may have changed.
320
+ *
321
+ * @since 4.0.0
322
+ *
323
+ * @param WP_Post $post The post object.
324
+ * @return void
325
+ */
326
+ private function maybeShowHomePageDescriptionNotice( $post ) {
327
+ $metaDescription = get_post_meta( $post->ID, '_aioseop_description', true );
328
+ $homePageDescription = ! empty( $this->oldOptions['aiosp_home_description'] ) ? $this->oldOptions['aiosp_home_description'] : '';
329
+
330
+ if (
331
+ empty( $this->oldOptions['aiosp_use_static_home_info'] ) &&
332
+ $metaDescription &&
333
+ ( trim( $homePageDescription ) !== trim( $metaDescription ) )
334
+ ) {
335
+ $this->showHomePageSettingsNotice();
336
+ }
337
+ }
338
+
339
+ /**
340
+ * Shows the homepage settings notice.
341
+ *
342
+ * @since 4.0.0
343
+ *
344
+ * @return void
345
+ */
346
+ private function showHomePageSettingsNotice() {
347
+ $notification = Models\Notification::getNotificationByName( 'v3-migration-homepage-settings' );
348
+ if ( $notification->notification_name ) {
349
+ return;
350
+ }
351
+
352
+ Models\Notification::addNotification( [
353
+ 'slug' => uniqid(),
354
+ 'notification_name' => 'v3-migration-homepage-settings',
355
+ 'title' => __( 'Review Your Homepage Title & Description', 'all-in-one-seo-pack' ),
356
+ 'content' => sprintf(
357
+ __( 'Due to a bug in the previous version of %1$s, your homepage title and description may have changed. Please take a minute to review your homepage settings to verify that they are correct.', 'all-in-one-seo-pack' ), // phpcs:ignore Generic.Files.LineLength.MaxExceeded
358
+ AIOSEO_PLUGIN_NAME
359
+ ),
360
+ 'type' => 'warning',
361
+ 'level' => [ 'all' ],
362
+ 'button1_label' => __( 'Review Now', 'all-in-one-seo-pack' ),
363
+ 'button1_action' => 'http://route#aioseo-search-appearance&aioseo-scroll=home-page-settings&aioseo-highlight=home-page-settings:global-settings',
364
+ 'start' => gmdate( 'Y-m-d H:i:s' )
365
+ ] );
366
+ }
367
+
368
+ /**
369
+ * Migrates the title formats.
370
+ *
371
+ * @since 4.0.0
372
+ *
373
+ * @return void
374
+ */
375
+ private function migrateTitleFormats() {
376
+ if ( ! empty( $this->oldOptions['aiosp_archive_title_format'] ) ) {
377
+ $archives = array_keys( aioseo()->options->searchAppearance->dynamic->archives->all() );
378
+ $format = aioseo()->helpers->sanitizeOption( aioseo()->migration->helpers->macrosToSmartTags( $this->oldOptions['aiosp_archive_title_format'] ) );
379
+ foreach ( $archives as $archive ) {
380
+ aioseo()->options->searchAppearance->dynamic->archives->$archive->title = $format;
381
+ }
382
+ }
383
+
384
+ $settings = [
385
+ 'aiosp_post_title_format' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'dynamic', 'postTypes', 'post', 'title' ] ],
386
+ 'aiosp_page_title_format' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'dynamic', 'postTypes', 'page', 'title' ] ],
387
+ 'aiosp_attachment_title_format' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'dynamic', 'postTypes', 'attachment', 'title' ] ],
388
+ 'aiosp_category_title_format' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'dynamic', 'taxonomies', 'category', 'title' ] ],
389
+ 'aiosp_tag_title_format' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'dynamic', 'taxonomies', 'post_tag', 'title' ] ],
390
+ 'aiosp_date_title_format' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'archives', 'date', 'title' ] ],
391
+ 'aiosp_author_title_format' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'archives', 'author', 'title' ] ],
392
+ 'aiosp_search_title_format' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'archives', 'search', 'title' ] ],
393
+ 'aiosp_paged_format' => [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'advanced', 'pagedFormat' ] ]
394
+ ];
395
+
396
+ foreach ( $this->oldOptions as $name => $value ) {
397
+ if (
398
+ ! in_array( $name, array_keys( $settings ), true ) &&
399
+ preg_match( '#aiosp_(.*)_title_format#', $name, $slug )
400
+ ) {
401
+ if ( empty( $slug ) && empty( $slug[1] ) ) {
402
+ continue;
403
+ }
404
+
405
+ $objectSlug = preg_replace( '#_tax#', '', $slug[1] );
406
+ if ( in_array( $objectSlug, aioseo()->helpers->getPublicPostTypes( true ), true ) ) {
407
+ $settings[ $name ] = [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'dynamic', 'postTypes', $objectSlug, 'title' ] ];
408
+ continue;
409
+ }
410
+ if ( in_array( $objectSlug, aioseo()->helpers->getPublicTaxonomies( true ), true ) ) {
411
+ $settings[ $name ] = [ 'type' => 'string', 'newOption' => [ 'searchAppearance', 'dynamic', 'taxonomies', $objectSlug, 'title' ] ];
412
+ }
413
+ }
414
+ }
415
+
416
+ aioseo()->migration->helpers->mapOldToNew( $settings, $this->oldOptions, true );
417
+
418
+ // Check if any of the title formats were empty and register a notification if so.
419
+ $found = false;
420
+ foreach ( $settings as $k => $v ) {
421
+ if ( 'aiosp_home_page_title_format' === $k ) {
422
+ continue;
423
+ }
424
+
425
+ if ( isset( $this->oldOptions[ $k ] ) && empty( $this->oldOptions[ $k ] ) ) {
426
+ $found = true;
427
+ break;
428
+ }
429
+ }
430
+
431
+ if ( ! $found ) {
432
+ Models\Notification::deleteNotificationByName( 'v3-migration-title-formats-blank' );
433
+ return;
434
+ }
435
+
436
+ $notification = Models\Notification::getNotificationByName( 'v3-migration-title-formats-blank' );
437
+ if ( $notification->notification_name ) {
438
+ return;
439
+ }
440
+
441
+ $p1 = sprintf(
442
+ // Translators: 1 - The plugin name ("All in One SEO"), 2 - Same as previous.
443
+ __( '%1$s migrated all your title formats, some of which were blank. As a result, we were not able to rewrite the title for these content types,
444
+ even if you manually entered a custom title.', 'all-in-one-seo-pack' ),
445
+ AIOSEO_PLUGIN_SHORT_NAME,
446
+ AIOSEO_PLUGIN_SHORT_NAME
447
+ );
448
+
449
+ $p2 = sprintf(
450
+ // Translators: 1 - The plugin name ("All in One SEO"), 2 - Same as previous.
451
+ __( 'If you were purposely using blank formats in the previous version of %1$s and want WordPress to handle your titles,
452
+ you can safely ignore this message. If this was not intended, you can let %2$s automatically fix this for you by clicking the button below. For more information,
453
+ please contact our support team.', 'all-in-one-seo-pack' ),
454
+ AIOSEO_PLUGIN_SHORT_NAME,
455
+ AIOSEO_PLUGIN_SHORT_NAME
456
+ );
457
+
458
+ Models\Notification::addNotification( [
459
+ 'slug' => uniqid(),
460
+ 'notification_name' => 'v3-migration-title-formats-blank',
461
+ 'title' => __( 'Blank Title Formats Detected', 'all-in-one-seo-pack' ),
462
+ 'content' => '<p>' . $p1 . '</p><p>' . $p2 . '</p>',
463
+ 'type' => 'error',
464
+ 'level' => [ 'all' ],
465
+ 'button1_label' => __( 'Fix Now', 'all-in-one-seo-pack' ),
466
+ 'button1_action' => 'http://action#migration/fix-blank-formats',
467
+ 'start' => gmdate( 'Y-m-d H:i:s' )
468
+ ] );
469
+ }
470
+
471
+ /**
472
+ * Migrates the description format.
473
+ *
474
+ * @since 4.0.0
475
+ *
476
+ * @return void
477
+ */
478
+ private function migrateDescriptionFormat() {
479
+ if (
480
+ ! empty( $this->oldOptions['aiosp_generate_descriptions'] ) &&
481
+ empty( $this->oldOptions['aiosp_skip_excerpt'] )
482
+ ) {
483
+ foreach ( aioseo()->helpers->getPublicPostTypes() as $postType ) {
484
+ if ( empty( $postType['hasExcerpt'] ) ) {
485
+ continue;
486
+ }
487
+
488
+ if ( aioseo()->options->searchAppearance->dynamic->postTypes->has( $postType['name'] ) ) {
489
+ aioseo()->options->searchAppearance->dynamic->postTypes->{$postType['name']}->metaDescription = '#post_excerpt';
490
+ }
491
+ }
492
+ }
493
+
494
+ if (
495
+ empty( $this->oldOptions['aiosp_description_format'] ) ||
496
+ '%description%' === trim( $this->oldOptions['aiosp_description_format'] )
497
+ ) {
498
+ return;
499
+ }
500
+
501
+ $deprecatedOptions = aioseo()->internalOptions->internal->deprecatedOptions;
502
+ array_push( $deprecatedOptions, 'descriptionFormat' );
503
+ aioseo()->internalOptions->internal->deprecatedOptions = $deprecatedOptions;
504
+
505
+ $format = aioseo()->migration->helpers->macrosToSmartTags( $this->oldOptions['aiosp_description_format'] );
506
+ aioseo()->options->deprecated->searchAppearance->global->descriptionFormat = aioseo()->helpers->sanitizeOption( $format );
507
+ }
508
+
509
+ /**
510
+ * Migrates the noindex settings.
511
+ *
512
+ * @since 4.0.0
513
+ *
514
+ * @return void
515
+ */
516
+ private function migrateNoindexSettings() {
517
+ if ( ! isset( $this->oldOptions['aiosp_cpostnoindex'] ) && ! isset( $this->oldOptions['aiosp_tax_noindex'] ) ) {
518
+ return;
519
+ }
520
+
521
+ $noindexedPostTypes = is_array( $this->oldOptions['aiosp_cpostnoindex'] ) ? $this->oldOptions['aiosp_cpostnoindex'] : explode( ', ', $this->oldOptions['aiosp_cpostnoindex'] );
522
+ foreach ( array_intersect( aioseo()->helpers->getPublicPostTypes( true ), $noindexedPostTypes ) as $postType ) {
523
+ if ( aioseo()->options->noConflict()->searchAppearance->dynamic->postTypes->has( $postType ) ) {
524
+ aioseo()->options->searchAppearance->dynamic->postTypes->$postType->show = false;
525
+ aioseo()->options->searchAppearance->dynamic->postTypes->$postType->advanced->robotsMeta->default = false;
526
+ aioseo()->options->searchAppearance->dynamic->postTypes->$postType->advanced->robotsMeta->noindex = true;
527
+ }
528
+ }
529
+
530
+ $noindexedTaxonomies = isset( $this->oldOptions['aiosp_tax_noindex'] ) ? (array) $this->oldOptions['aiosp_tax_noindex'] : [];
531
+ if ( ! empty( $this->oldOptions['aiosp_category_noindex'] ) ) {
532
+ $noindexedTaxonomies[] = 'category';
533
+ }
534
+
535
+ if ( ! empty( $this->oldOptions['aiosp_tags_noindex'] ) ) {
536
+ $noindexedTaxonomies[] = 'post_tag';
537
+ }
538
+
539
+ if ( ! empty( $noindexedTaxonomies ) ) {
540
+ foreach ( array_intersect( aioseo()->helpers->getPublicTaxonomies( true ), $noindexedTaxonomies ) as $taxonomy ) {
541
+ if ( aioseo()->options->noConflict()->searchAppearance->dynamic->taxonomies->has( $taxonomy ) ) {
542
+ aioseo()->options->searchAppearance->dynamic->taxonomies->$taxonomy->show = false;
543
+ aioseo()->options->searchAppearance->dynamic->taxonomies->$taxonomy->advanced->robotsMeta->default = false;
544
+ aioseo()->options->searchAppearance->dynamic->taxonomies->$taxonomy->advanced->robotsMeta->noindex = true;
545
+ }
546
+ }
547
+ }
548
+
549
+ if ( ! empty( $this->oldOptions['aiosp_archive_date_noindex'] ) ) {
550
+ aioseo()->options->searchAppearance->archives->date->show = false;
551
+ aioseo()->options->searchAppearance->archives->date->advanced->robotsMeta->default = false;
552
+ aioseo()->options->searchAppearance->archives->date->advanced->robotsMeta->noindex = true;
553
+ }
554
+
555
+ if ( ! empty( $this->oldOptions['aiosp_archive_author_noindex'] ) ) {
556
+ aioseo()->options->searchAppearance->archives->author->show = false;
557
+ aioseo()->options->searchAppearance->archives->author->advanced->robotsMeta->default = false;
558
+ aioseo()->options->searchAppearance->archives->author->advanced->robotsMeta->noindex = true;
559
+ }
560
+
561
+ if ( ! empty( $this->oldOptions['aiosp_search_noindex'] ) ) {
562
+ aioseo()->options->searchAppearance->archives->search->show = false;
563
+ aioseo()->options->searchAppearance->archives->search->advanced->robotsMeta->default = false;
564
+ aioseo()->options->searchAppearance->archives->search->advanced->robotsMeta->noindex = true;
565
+ } else {
566
+ // We need to do this as V4 will noindex the search page otherwise.
567
+ aioseo()->options->searchAppearance->archives->search->show = true;
568
+ aioseo()->options->searchAppearance->archives->search->advanced->robotsMeta->default = true;
569
+ aioseo()->options->searchAppearance->archives->search->advanced->robotsMeta->noindex = false;
570
+ }
571
+
572
+ if ( ! empty( $this->oldOptions['aiosp_paginated_noindex'] ) ) {
573
+ aioseo()->options->searchAppearance->advanced->globalRobotsMeta->default = false;
574
+ aioseo()->options->searchAppearance->advanced->globalRobotsMeta->noindexPaginated = true;
575
+ }
576
+ }
577
+
578
+ /**
579
+ * Migrates the nofollow settings.
580
+ *
581
+ * @since 4.0.0
582
+ *
583
+ * @return void
584
+ */
585
+ private function migrateNofollowSettings() {
586
+ if ( ! empty( $this->oldOptions['aiosp_cpostnofollow'] ) ) {
587
+ foreach ( array_intersect( aioseo()->helpers->getPublicPostTypes( true ), $this->oldOptions['aiosp_cpostnofollow'] ) as $postType ) {
588
+ if ( aioseo()->options->noConflict()->searchAppearance->dynamic->postTypes->has( $postType ) ) {
589
+ aioseo()->options->searchAppearance->dynamic->postTypes->$postType->advanced->robotsMeta->default = false;
590
+ aioseo()->options->searchAppearance->dynamic->postTypes->$postType->advanced->robotsMeta->nofollow = true;
591
+ }
592
+ }
593
+ }
594
+
595
+ if ( ! empty( $this->oldOptions['aiosp_paginated_nofollow'] ) ) {
596
+ aioseo()->options->searchAppearance->advanced->globalRobotsMeta->nofollowPaginated = true;
597
+ }
598
+ }
599
+
600
+ /**
601
+ * Migrates the post SEO columns.
602
+ *
603
+ * @since 4.0.0
604
+ *
605
+ * @return void
606
+ */
607
+ private function migratePostSeoColumns() {
608
+ if ( ! isset( $this->oldOptions['aiosp_posttypecolumns'] ) ) {
609
+ return;
610
+ }
611
+
612
+ $publicPostTypes = aioseo()->helpers->getPublicPostTypes( true );
613
+ $postTypes = array_intersect( (array) $this->oldOptions['aiosp_posttypecolumns'], $publicPostTypes );
614
+
615
+ aioseo()->options->advanced->postTypes->included = array_values( $postTypes );
616
+ if ( count( $publicPostTypes ) !== count( $postTypes ) ) {
617
+ aioseo()->options->advanced->postTypes->all = false;
618
+ }
619
+ }
620
+
621
+ /**
622
+ * Migrates the schema social URLs.
623
+ *
624
+ * @since 4.0.0
625
+ *
626
+ * @return void
627
+ */
628
+ private function migrateSocialUrls() {
629
+ if ( ! empty( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_facebook_publisher'] ) ) {
630
+ aioseo()->options->social->profiles->urls->facebookPageUrl = esc_url( wp_strip_all_tags( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_facebook_publisher'] ) );
631
+ aioseo()->options->social->profiles->sameUsername->enable = false;
632
+ }
633
+
634
+ if ( empty( $this->oldOptions['aiosp_schema_social_profile_links'] ) ) {
635
+ return;
636
+ }
637
+
638
+ $socialUrls = preg_replace( '/\s/', '\r\n', $this->oldOptions['aiosp_schema_social_profile_links'] );
639
+ $socialUrls = array_filter( explode( '\r\n', $socialUrls ) );
640
+
641
+ if ( ! count( $socialUrls ) ) {
642
+ return;
643
+ }
644
+
645
+ $supportedNetworks = [
646
+ 'facebook.com' => 'facebookPageUrl',
647
+ 'twitter.com' => 'twitterUrl',
648
+ 'instagram.com' => 'instagramUrl',
649
+ 'pinterest.com' => 'pinterestUrl',
650
+ 'youtube.com' => 'youtubeUrl',
651
+ 'linkedin.com' => 'linkedinUrl',
652
+ 'tumblr.com' => 'tumblrUrl',
653
+ 'yelp.com' => 'yelpPageUrl',
654
+ 'soundcloud.com' => 'soundCloudUrl',
655
+ 'wikipedia.org' => 'wikipediaUrl',
656
+ 'myspace.com' => 'myspaceUrl',
657
+ ];
658
+
659
+ $found = false;
660
+ foreach ( $supportedNetworks as $url => $settingName ) {
661
+ $url = aioseo()->helpers->escapeRegex( $url );
662
+ foreach ( $socialUrls as $socialUrl ) {
663
+ if ( preg_match( "/.*$url.*/", $socialUrl ) ) {
664
+ aioseo()->options->social->profiles->urls->$settingName = esc_url( wp_strip_all_tags( $socialUrl ) );
665
+ $found = true;
666
+ }
667
+ }
668
+ }
669
+
670
+ if ( $found ) {
671
+ aioseo()->options->social->profiles->sameUsername->enable = false;
672
+ }
673
+ }
674
+
675
+ /**
676
+ * Migrates the Schema Markup settings in the General Settings menu.
677
+ *
678
+ * @since 4.0.0
679
+ *
680
+ * @return void
681
+ */
682
+ private function migrateSchemaMarkupSettings() {
683
+ $this->migrateSchemaPhoneNumber();
684
+
685
+ if (
686
+ isset( $this->oldOptions['aiosp_schema_markup'] ) &&
687
+ empty( $this->oldOptions['aiosp_schema_markup'] )
688
+ ) {
689
+ $deprecatedOptions = aioseo()->internalOptions->internal->deprecatedOptions;
690
+ array_push( $deprecatedOptions, 'enableSchemaMarkup' );
691
+ aioseo()->internalOptions->internal->deprecatedOptions = $deprecatedOptions;
692
+ aioseo()->options->deprecated->searchAppearance->global->schema->enableSchemaMarkup = false;
693
+ }
694
+
695
+ if ( ! empty( $this->oldOptions['aiosp_schema_person_user'] ) ) {
696
+ if ( -1 === (int) $this->oldOptions['aiosp_schema_person_user'] ) {
697
+ aioseo()->options->searchAppearance->global->schema->person = 'manual';
698
+ } else {
699
+ aioseo()->options->searchAppearance->global->schema->person = intval( $this->oldOptions['aiosp_schema_person_user'] );
700
+ }
701
+ }
702
+
703
+ if ( ! empty( $this->oldOptions['aiosp_schema_contact_type'] ) ) {
704
+ aioseo()->options->searchAppearance->global->schema->contactType = 'manual';
705
+ aioseo()->options->searchAppearance->global->schema->contactTypeManual = aioseo()->helpers->sanitizeOption( $this->oldOptions['aiosp_schema_contact_type'] );
706
+ }
707
+ }
708
+
709
+ /**
710
+ * Migrates the schema phone number.
711
+ *
712
+ * @since 4.0.0
713
+ *
714
+ * @return void
715
+ */
716
+ private function migrateSchemaPhoneNumber() {
717
+ if ( empty( $this->oldOptions['aiosp_schema_phone_number'] ) ) {
718
+ return;
719
+ }
720
+
721
+ $phoneNumber = aioseo()->helpers->sanitizeOption( $this->oldOptions['aiosp_schema_phone_number'] );
722
+ if ( ! preg_match( '#\+\d+#', $phoneNumber ) ) {
723
+ $notification = Models\Notification::getNotificationByName( 'v3-migration-schema-number' );
724
+ if ( $notification->notification_name ) {
725
+ return;
726
+ }
727
+
728
+ Models\Notification::addNotification( [
729
+ 'slug' => uniqid(),
730
+ 'notification_name' => 'v3-migration-schema-number',
731
+ 'title' => __( 'Invalid Phone Number for Knowledge Graph', 'all-in-one-seo-pack' ),
732
+ 'content' => sprintf(
733
+ // Translators: 1 - The phone number.
734
+ __( 'The phone number that you previously entered for your Knowledge Graph schema markup is invalid. As it needs to be internationally formatted, please enter it (%1$s) again with the country code, e.g. +1 (555) 555-1234.', 'all-in-one-seo-pack' ), // phpcs:ignore Generic.Files.LineLength.MaxExceeded
735
+ "<strong>$phoneNumber</strong>"
736
+ ),
737
+ 'type' => 'warning',
738
+ 'level' => [ 'all' ],
739
+ 'button1_label' => __( 'Fix Now', 'all-in-one-seo-pack' ),
740
+ 'button1_action' => 'http://route#aioseo-search-appearance&aioseo-scroll=schema-graph-phone&aioseo-highlight=schema-graph-phone:global-settings',
741
+ 'button2_label' => __( 'Remind Me Later', 'all-in-one-seo-pack' ),
742
+ 'button2_action' => 'http://action#notification/v3-migration-schema-number-reminder',
743
+ 'start' => gmdate( 'Y-m-d H:i:s' )
744
+ ] );
745
+ return;
746
+ }
747
+ aioseo()->options->searchAppearance->global->schema->phone = $phoneNumber;
748
+ }
749
+
750
+ /**
751
+ * Migrates the homepage keywords.
752
+ *
753
+ * @since 4.0.0
754
+ *
755
+ * @return void
756
+ */
757
+ private function migrateHomePageKeywords() {
758
+ if ( ! empty( $this->oldOptions['aiosp_home_keywords'] ) ) {
759
+ aioseo()->options->searchAppearance->global->keywords = aioseo()->migration->helpers->oldKeywordsToNewKeywords( $this->oldOptions['aiosp_home_keywords'] );
760
+ }
761
+ }
762
+
763
+ /**
764
+ * Migrates the deprecated V3 advanced General Settings options.
765
+ *
766
+ * @since 4.0.0
767
+ *
768
+ * @return void
769
+ */
770
+ private function migrateDeprecatedAdvancedOptions() {
771
+ $deprecatedOptions = aioseo()->internalOptions->internal->deprecatedOptions;
772
+
773
+ if ( empty( $this->oldOptions['aiosp_generate_descriptions'] ) ) {
774
+ array_push( $deprecatedOptions, 'autogenerateDescriptions' );
775
+ aioseo()->options->deprecated->searchAppearance->advanced->autogenerateDescriptions = false;
776
+ } else {
777
+ if ( empty( $this->oldOptions['aiosp_run_shortcodes'] ) ) {
778
+ array_push( $deprecatedOptions, 'runShortcodesInDescription' );
779
+ aioseo()->options->deprecated->searchAppearance->advanced->runShortcodesInDescription = false;
780
+ }
781
+
782
+ if ( ! empty( $this->oldOptions['aiosp_skip_excerpt'] ) ) {
783
+ array_push( $deprecatedOptions, 'useContentForAutogeneratedDescriptions' );
784
+ aioseo()->options->deprecated->searchAppearance->advanced->useContentForAutogeneratedDescriptions = true;
785
+ }
786
+ }
787
+
788
+ aioseo()->internalOptions->internal->deprecatedOptions = $deprecatedOptions;
789
+ }
790
+
791
+ /**
792
+ * Migrates the RSS content settings.
793
+ *
794
+ * @since 4.0.0
795
+ *
796
+ * @return void
797
+ */
798
+ private function migrateRssContentSettings() {
799
+ if ( isset( $this->oldOptions['aiosp_rss_content_before'] ) ) {
800
+ aioseo()->options->rssContent->before = esc_html( aioseo()->migration->helpers->macrosToSmartTags( $this->oldOptions['aiosp_rss_content_before'] ) );
801
+ }
802
+
803
+ if ( isset( $this->oldOptions['aiosp_rss_content_after'] ) ) {
804
+ aioseo()->options->rssContent->after = esc_html( aioseo()->migration->helpers->macrosToSmartTags( $this->oldOptions['aiosp_rss_content_after'] ) );
805
+ }
806
+ }
807
+
808
+ /**
809
+ * Migrates the Redirect Attachment to Parent setting.
810
+ *
811
+ * @since 4.0.0
812
+ *
813
+ * @return void
814
+ */
815
+ private function migrateRedirectToParent() {
816
+ if ( isset( $this->oldOptions['aiosp_redirect_attachement_parent'] ) ) {
817
+ if ( ! empty( $this->oldOptions['aiosp_redirect_attachement_parent'] ) ) {
818
+ aioseo()->options->searchAppearance->dynamic->postTypes->attachment->redirectAttachmentUrls = 'attachment_parent';
819
+ } else {
820
+ aioseo()->options->searchAppearance->dynamic->postTypes->attachment->redirectAttachmentUrls = 'disabled';
821
+ }
822
+ }
823
+ }
824
+
825
+ /**
826
+ * Migrates the excluded posts.
827
+ *
828
+ * @since 4.0.0
829
+ *
830
+ * @return void
831
+ */
832
+ private function migrateDisabledPosts() {
833
+ if ( empty( $this->oldOptions['aiosp_ex_pages'] ) ) {
834
+ return;
835
+ }
836
+
837
+ $deprecatedOptions = aioseo()->internalOptions->internal->deprecatedOptions;
838
+ if ( ! in_array( 'excludePosts', $deprecatedOptions, true ) ) {
839
+ array_push( $deprecatedOptions, 'excludePosts' );
840
+ aioseo()->internalOptions->internal->deprecatedOptions = $deprecatedOptions;
841
+ }
842
+
843
+ $excludedPosts = aioseo()->options->deprecated->searchAppearance->advanced->excludePosts;
844
+ $pages = explode( ', ', $this->oldOptions['aiosp_ex_pages'] );
845
+ if ( count( $pages ) ) {
846
+ foreach ( $pages as $page ) {
847
+ $id = intval( $page );
848
+ if ( ! $id ) {
849
+ $post = get_page_by_path( $page, OBJECT, aioseo()->helpers->getPublicPostTypes( true ) );
850
+ if ( $post && is_object( $post ) ) {
851
+ $id = $post->ID;
852
+ }
853
+ }
854
+
855
+ if ( $id ) {
856
+ $post = get_post( $id );
857
+ if ( ! is_object( $post ) ) {
858
+ continue;
859
+ }
860
+
861
+ $excludedPost = new \stdClass();
862
+ $excludedPost->value = $id;
863
+ $excludedPost->type = $post->post_type;
864
+ $excludedPost->label = $post->post_title;
865
+ $excludedPost->link = get_permalink( $id );
866
+
867
+ array_push( $excludedPosts, wp_json_encode( $excludedPost ) );
868
+ }
869
+ }
870
+ }
871
+ aioseo()->options->deprecated->searchAppearance->advanced->excludePosts = $excludedPosts;
872
+ }
873
+
874
+ /**
875
+ * Enables deprecated Google Analytics if there is an existing GA id.
876
+ *
877
+ * @since 4.0.6
878
+ *
879
+ * @return void
880
+ */
881
+ private function migrateGoogleAnalytics() {
882
+ if ( empty( $this->oldOptions['aiosp_google_analytics_id'] ) ) {
883
+ return;
884
+ }
885
+
886
+ $deprecatedOptions = aioseo()->internalOptions->internal->deprecatedOptions;
887
+ if ( ! in_array( 'googleAnalytics', $deprecatedOptions, true ) ) {
888
+ array_push( $deprecatedOptions, 'googleAnalytics' );
889
+ aioseo()->internalOptions->internal->deprecatedOptions = $deprecatedOptions;
890
+ }
891
+ }
892
+ }
app/Common/Migration/Helpers.php ADDED
@@ -0,0 +1,250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Migration;
3
+
4
+ use AIOSEO\Plugin\Common\Models;
5
+
6
+ /**
7
+ * Contains a number of helper functions for the V3 migration.
8
+ *
9
+ * @since 4.0.0
10
+ */
11
+ class Helpers {
12
+
13
+ /**
14
+ * Maps a list of old settings from V3 to their counterparts in V4.
15
+ *
16
+ * @since 4.0.0
17
+ *
18
+ * @param array $mappings The old settings, mapped to their new settings.
19
+ * @param array $group The old settings group.
20
+ * @param bool $convertMacros Whether to convert the old V3 macros to V4 smart tags.
21
+ * @return void
22
+ */
23
+ public function mapOldToNew( $mappings, $group, $convertMacros = false ) {
24
+ if (
25
+ ! is_array( $mappings ) ||
26
+ ! is_array( $group ) ||
27
+ ! count( $mappings ) ||
28
+ ! count( $group )
29
+ ) {
30
+ return;
31
+ }
32
+
33
+ foreach ( $mappings as $name => $values ) {
34
+ if ( ! isset( $group[ $name ] ) ) {
35
+ continue;
36
+ }
37
+
38
+ $error = false;
39
+ $options = aioseo()->options->noConflict();
40
+ $lastOption = '';
41
+ for ( $i = 0; $i < count( $values['newOption'] ); $i++ ) {
42
+ $lastOption = $values['newOption'][ $i ];
43
+ if ( ! $options->has( $lastOption, false ) ) {
44
+ $error = true;
45
+ break;
46
+ };
47
+ if ( count( $values['newOption'] ) - 1 !== $i ) {
48
+ $options = $options->$lastOption;
49
+ }
50
+ }
51
+
52
+ if ( $error ) {
53
+ continue;
54
+ }
55
+
56
+ switch ( $values['type'] ) {
57
+ case 'boolean':
58
+ if ( ! empty( $group[ $name ] ) ) {
59
+ $options->$lastOption = true;
60
+ break;
61
+ }
62
+ $options->$lastOption = false;
63
+ break;
64
+ case 'integer':
65
+ case 'float':
66
+ $value = aioseo()->helpers->sanitizeOption( $group[ $name ] );
67
+ if ( $value ) {
68
+ $options->$lastOption = $value;
69
+ }
70
+ break;
71
+ default:
72
+ $value = $group[ $name ];
73
+ if ( $convertMacros ) {
74
+ $value = $this->macrosToSmartTags( $value );
75
+ }
76
+ $options->$lastOption = aioseo()->helpers->sanitizeOption( $value );
77
+ break;
78
+ }
79
+ }
80
+
81
+ aioseo()->options->refresh();
82
+ }
83
+
84
+ /**
85
+ * Replaces the macros from V3 with our new Smart Tags from V4.
86
+ *
87
+ * @since 4.0.0
88
+ *
89
+ * @param string $string The string.
90
+ * @return string $string The converted string.
91
+ */
92
+ public function macrosToSmartTags( $string ) {
93
+ $macros = [
94
+ '%site_title%' => '#site_title',
95
+ '%blog_title%' => '#site_title',
96
+ '%site_description%' => '#tagline',
97
+ '%blog_description%' => '#tagline',
98
+ '%wp_title%' => '#post_title',
99
+ '%post_title%' => '#post_title',
100
+ '%page_title%' => '#post_title',
101
+ '%post_date%' => '#post_date',
102
+ '%post_month%' => '#post_month',
103
+ '%post_year%' => '#post_year',
104
+ '%date%' => '#archive_date',
105
+ '%day%' => '#post_day',
106
+ '%month%' => '#post_month',
107
+ '%monthnum%' => '#monthnum',
108
+ '%year%' => '#post_year',
109
+ '%current_date%' => '#current_date',
110
+ '%current_day%' => '#current_day',
111
+ '%current_month%' => '#current_month',
112
+ '%current_month_i18n%' => '#current_month',
113
+ '%current_year%' => '#current_year',
114
+ '%category_title%' => '#taxonomy_title',
115
+ '%tag%' => '#taxonomy_title',
116
+ '%tag_title%' => '#taxonomy_title',
117
+ '%archive_title%' => '#archive_title',
118
+ '%taxonomy_title%' => '#taxonomy_title',
119
+ '%taxonomy_description%' => '#taxonomy_description',
120
+ '%tag_description%' => '#taxonomy_description',
121
+ '%category_description%' => '#taxonomy_description',
122
+ '%author%' => '#author_name',
123
+ '%search%' => '#search_term',
124
+ '%page%' => '#page_number',
125
+ '%site_link%' => '#site_link',
126
+ '%site_link_raw%' => '#site_link_alt',
127
+ '%post_link%' => '#post_link',
128
+ '%post_link_raw%' => '#post_link_alt',
129
+ '%author_name%' => '#author_name',
130
+ '%author_link%' => '#author_link',
131
+ '%image_title%' => '#image_title',
132
+ '%image_seo_title%' => '#image_seo_title',
133
+ '%image_seo_description%' => '#image_seo_description',
134
+ '%post_seo_title%' => '#post_seo_title',
135
+ '%post_seo_description%' => '#post_seo_description',
136
+ '%alt_tag%' => '#alt_tag',
137
+ '%description%' => '#description',
138
+ // These need to run last so we don't replace other known tags.
139
+ '%.*_title%' => '#post_title',
140
+ '%[^%]*_author_login%' => '#author_first_name #author_last_name',
141
+ '%[^%]*_author_nicename%' => '#author_first_name #author_last_name',
142
+ '%[^%]*_author_firstname%' => '#author_first_name',
143
+ '%[^%]*_author_lastname%' => '#author_last_name',
144
+ ];
145
+
146
+ if ( preg_match_all( '#%cf_([^%]*)%#', $string, $matches ) && ! empty( $matches[1] ) ) {
147
+ foreach ( $matches[1] as $name ) {
148
+ if ( preg_match( '#\s#', $name ) ) {
149
+ $notification = Models\Notification::getNotificationByName( 'v3-migration-custom-field' );
150
+ if ( ! $notification->notification_name ) {
151
+ Models\Notification::addNotification( [
152
+ 'slug' => uniqid(),
153
+ 'notification_name' => 'v3-migration-custom-field',
154
+ 'title' => __( 'Custom field names with spaces detected', 'all-in-one-seo-pack' ),
155
+ 'content' => sprintf(
156
+ // Translators: 1 - The plugin short name ("AIOSEO"), 2 - Same as previous.
157
+ __( '%1$s has detected that you have one or more custom fields with spaces in their name.
158
+ In order for %2$s to correctly parse these custom fields, their names cannot contain any spaces.', 'all-in-one-seo-pack' ),
159
+ AIOSEO_PLUGIN_SHORT_NAME,
160
+ AIOSEO_PLUGIN_SHORT_NAME
161
+ ),
162
+ 'type' => 'warning',
163
+ 'level' => [ 'all' ],
164
+ 'button1_label' => __( 'Remind Me Later', 'all-in-one-seo-pack' ),
165
+ 'button1_action' => 'http://action#notification/v3-migration-custom-field-reminder',
166
+ 'start' => gmdate( 'Y-m-d H:i:s' )
167
+ ] );
168
+ }
169
+ } else {
170
+ $string = preg_replace( "#%cf_$name%#", "#custom_field-$name", $string );
171
+ }
172
+ }
173
+ }
174
+
175
+ if ( preg_match_all( '#%tax_([^%]*)%#', $string, $matches ) && ! empty( $matches[1] ) ) {
176
+ foreach ( $matches[1] as $name ) {
177
+ if ( ! preg_match( '#\s#', $name ) ) {
178
+ $string = preg_replace( "#%tax_$name%#", "#tax_name-$name", $string );
179
+ }
180
+ }
181
+ }
182
+
183
+ foreach ( $macros as $macro => $tag ) {
184
+ $string = preg_replace( "#$macro(?![a-zA-Z0-9_])#im", $tag, $string );
185
+ }
186
+
187
+ $string = preg_replace( '/%([a-f0-9]{2}[^%]*)%/i', '#$1#', $string );
188
+ return $string;
189
+ }
190
+
191
+ /**
192
+ * Converts the old comma-separated keywords format to the new JSON format.
193
+ *
194
+ * @since 4.0.0
195
+ *
196
+ * @param string $keywords A comma-separated list of keywords.
197
+ * @return string $keywords The keywords formatted in JSON.
198
+ */
199
+ public function oldKeywordsToNewKeywords( $keywords ) {
200
+ if ( ! $keywords ) {
201
+ return '';
202
+ }
203
+
204
+ $oldKeywords = array_filter( explode( ', ', $keywords ) );
205
+ if ( ! is_array( $oldKeywords ) ) {
206
+ return '';
207
+ }
208
+
209
+ $keywords = [];
210
+ foreach ( $oldKeywords as $oldKeyword ) {
211
+ $oldKeyword = aioseo()->helpers->sanitizeOption( $oldKeyword );
212
+
213
+ $keyword = new \stdClass();
214
+ $keyword->label = $oldKeyword;
215
+ $keyword->value = $oldKeyword;
216
+
217
+ $keywords[] = $keyword;
218
+ }
219
+
220
+ return wp_json_encode( $keywords );
221
+ }
222
+
223
+ /**
224
+ * Resets the plugin so that the migration can run again.
225
+ *
226
+ * @since 4.0.0
227
+ *
228
+ * @return void
229
+ */
230
+ public static function redoMigration() {
231
+ aioseo()->db->delete( 'options' )
232
+ ->whereRaw( "`option_name` LIKE 'aioseo_options_internal%'" )
233
+ ->run();
234
+
235
+ delete_transient( 'aioseo_v3_migration_in_progress_posts' );
236
+ delete_transient( 'aioseo_v3_migration_in_progress_terms' );
237
+
238
+ try {
239
+ if ( as_next_scheduled_action( 'aioseo_migrate_post_meta' ) ) {
240
+ as_unschedule_action( 'aioseo_migrate_post_meta', [], 'aioseo' );
241
+ }
242
+
243
+ if ( as_next_scheduled_action( 'aioseo_migrate_term_meta' ) ) {
244
+ as_unschedule_action( 'aioseo_migrate_term_meta', [], 'aioseo' );
245
+ }
246
+ } catch ( \Exception $e ) {
247
+ // Do nothing.
248
+ }
249
+ }
250
+ }
app/Common/Migration/Meta.php ADDED
@@ -0,0 +1,396 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Migration;
3
+
4
+ // phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
5
+
6
+ use AIOSEO\Plugin\Common\Models;
7
+
8
+ /**
9
+ * Migrates the post meta from V3.
10
+ *
11
+ * @since 4.0.0
12
+ */
13
+ class Meta {
14
+ /**
15
+ * Holds the old options array.
16
+ *
17
+ * @since 4.0.3
18
+ *
19
+ * @var array|null
20
+ */
21
+ protected static $oldOptions = null;
22
+
23
+ /**
24
+ * Migrates the plugin meta data.
25
+ *
26
+ * @since 4.0.0
27
+ *
28
+ * @return void
29
+ */
30
+ public function migrateMeta() {
31
+ try {
32
+ if ( as_next_scheduled_action( 'aioseo_migrate_post_meta' ) ) {
33
+ return;
34
+ }
35
+
36
+ as_schedule_single_action( time(), 'aioseo_migrate_post_meta', [], 'aioseo', [], 'aioseo' );
37
+ } catch ( \Exception $e ) {
38
+ // Do nothing.
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Migrates the post meta data from V3.
44
+ *
45
+ * @since 4.0.0
46
+ *
47
+ * @return void
48
+ */
49
+ public function migratePostMeta() {
50
+ $postsPerAction = 100;
51
+ $publicPostTypes = implode( "', '", aioseo()->helpers->getPublicPostTypes( true ) );
52
+ $timeStarted = gmdate( 'Y-m-d H:i:s', get_transient( 'aioseo_v3_migration_in_progress_posts' ) );
53
+
54
+ $postsToMigrate = aioseo()->db
55
+ ->start( 'posts' . ' as p' )
56
+ ->select( 'p.ID' )
57
+ ->leftJoin( 'aioseo_posts as ap', '`p`.`ID` = `ap`.`post_id`' )
58
+ ->whereRaw( "( ap.post_id IS NULL OR ap.updated < '$timeStarted' )" )
59
+ ->whereRaw( "( p.post_type IN ( '$publicPostTypes' ) )" )
60
+ ->orderBy( 'p.ID DESC' )
61
+ ->limit( $postsPerAction )
62
+ ->run()
63
+ ->result();
64
+
65
+ if ( ! $postsToMigrate || ! count( $postsToMigrate ) ) {
66
+ delete_transient( 'aioseo_v3_migration_in_progress_posts' );
67
+ return;
68
+ }
69
+
70
+ foreach ( $postsToMigrate as $post ) {
71
+ $newPostMeta = $this->getMigratedPostMeta( $post->ID );
72
+
73
+ $aioseoPost = Models\Post::getPost( $post->ID );
74
+ $aioseoPost->set( $newPostMeta );
75
+ $aioseoPost->save();
76
+
77
+ $this->migrateAdditionalPostMeta( $post->ID );
78
+ }
79
+
80
+ if ( count( $postsToMigrate ) === $postsPerAction ) {
81
+ try {
82
+ as_schedule_single_action( time() + 5, 'aioseo_migrate_post_meta', [], 'aioseo' );
83
+ } catch ( \Exception $e ) {
84
+ // Do nothing.
85
+ }
86
+ } else {
87
+ delete_transient( 'aioseo_v3_migration_in_progress_posts' );
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Returns the migrated post meta for a given post.
93
+ *
94
+ * @since 4.0.3
95
+ *
96
+ * @param int $postId The post ID.
97
+ * @return array $meta The post meta.
98
+ */
99
+ public function getMigratedPostMeta( $postId ) {
100
+ if ( is_category() || is_tag() || is_tax() ) {
101
+ return [];
102
+ }
103
+
104
+ if ( null === self::$oldOptions ) {
105
+ self::$oldOptions = get_option( 'aioseop_options' );
106
+ }
107
+
108
+ if ( empty( self::$oldOptions ) ) {
109
+ return [];
110
+ }
111
+
112
+ $post = get_post( $postId );
113
+ $postMeta = aioseo()->db
114
+ ->start( 'postmeta' . ' as pm' )
115
+ ->select( 'pm.meta_key, pm.meta_value' )
116
+ ->where( 'pm.post_id', $post->ID )
117
+ ->whereRaw( "`pm`.`meta_key` LIKE '_aioseop_%'" )
118
+ ->run()
119
+ ->result();
120
+
121
+ $mappedMeta = [
122
+ '_aioseop_title' => 'title',
123
+ '_aioseop_description' => 'description',
124
+ '_aioseop_custom_link' => 'canonical_url',
125
+ '_aioseop_sitemap_exclude' => '',
126
+ '_aioseop_disable' => '',
127
+ '_aioseop_noindex' => 'robots_noindex',
128
+ '_aioseop_nofollow' => 'robots_nofollow',
129
+ '_aioseop_sitemap_priority' => 'priority',
130
+ '_aioseop_sitemap_frequency' => 'frequency',
131
+ '_aioseop_keywords' => 'keywords',
132
+ '_aioseop_opengraph_settings' => ''
133
+ ];
134
+
135
+ $meta = [
136
+ 'post_id' => $post->ID,
137
+ ];
138
+
139
+ if ( ! $postMeta || ! count( $postMeta ) ) {
140
+ return $meta;
141
+ }
142
+
143
+ foreach ( $postMeta as $record ) {
144
+ $name = $record->meta_key;
145
+ $value = $record->meta_value;
146
+
147
+ if ( ! in_array( $name, array_keys( $mappedMeta ), true ) ) {
148
+ continue;
149
+ }
150
+
151
+ switch ( $name ) {
152
+ case '_aioseop_description':
153
+ $meta[ $mappedMeta[ $name ] ] = aioseo()->helpers->sanitizeOption( aioseo()->migration->helpers->macrosToSmartTags( $value ) );
154
+ break;
155
+ case '_aioseop_title':
156
+ if ( ! empty( $value ) ) {
157
+ $meta[ $mappedMeta[ $name ] ] = $this->getTitleValue( $post, $value );
158
+ }
159
+ break;
160
+ case '_aioseop_sitemap_exclude':
161
+ if ( empty( $value ) ) {
162
+ break;
163
+ }
164
+ $this->migrateExcludedPost( $postId );
165
+ break;
166
+ case '_aioseop_disable':
167
+ if ( empty( $value ) ) {
168
+ break;
169
+ }
170
+ $this->migrateSitemapExcludedPost( $postId );
171
+ break;
172
+ case '_aioseop_noindex':
173
+ case '_aioseop_nofollow':
174
+ if ( 'on' === (string) $value ) {
175
+ $meta['robots_default'] = false;
176
+ $meta[ $mappedMeta[ $name ] ] = true;
177
+ } elseif ( 'off' === (string) $value ) {
178
+ $meta['robots_default'] = false;
179
+ }
180
+ break;
181
+ case '_aioseop_keywords':
182
+ $meta[ $mappedMeta[ $name ] ] = aioseo()->migration->helpers->oldKeywordsToNewKeywords( $value );
183
+ break;
184
+ case '_aioseop_opengraph_settings':
185
+ $meta += $this->convertOpenGraphMeta( $value );
186
+ break;
187
+ case '_aioseop_sitemap_priority':
188
+ case '_aioseop_sitemap_frequency':
189
+ if ( empty( $value ) ) {
190
+ $meta[ $mappedMeta[ $name ] ] = 'default';
191
+ break;
192
+ }
193
+ $meta[ $mappedMeta[ $name ] ] = $value;
194
+ break;
195
+ default:
196
+ $meta[ $mappedMeta[ $name ] ] = esc_html( wp_strip_all_tags( strval( $value ) ) );
197
+ break;
198
+ }
199
+ }
200
+ return $meta;
201
+ }
202
+
203
+ /**
204
+ * Migrates a given disabled post from V3.
205
+ *
206
+ * @since 4.0.3
207
+ *
208
+ * @param int $postId The post ID.
209
+ * @return void
210
+ */
211
+ private function migrateExcludedPost( $postId ) {
212
+ $post = get_post( $postId );
213
+ if ( ! is_object( $post ) ) {
214
+ return;
215
+ }
216
+
217
+ aioseo()->options->sitemap->general->advancedSettings->enable = true;
218
+ $excludedPosts = aioseo()->options->sitemap->general->advancedSettings->excludePosts;
219
+
220
+ foreach ( $excludedPosts as $excludedPost ) {
221
+ $excludedPost = json_decode( $excludedPost );
222
+ if ( $excludedPost->value === $postId ) {
223
+ return;
224
+ }
225
+ }
226
+
227
+ $excludedPost = [
228
+ 'value' => $post->ID,
229
+ 'type' => $post->post_type,
230
+ 'label' => $post->post_title,
231
+ 'link' => get_permalink( $post )
232
+ ];
233
+
234
+ $excludedPosts[] = wp_json_encode( $excludedPost );
235
+ aioseo()->options->sitemap->general->advancedSettings->excludePosts = $excludedPosts;
236
+
237
+ $deprecatedOptions = aioseo()->internalOptions->internal->deprecatedOptions;
238
+ if ( ! in_array( 'excludePosts', $deprecatedOptions, true ) ) {
239
+ array_push( $deprecatedOptions, 'excludePosts' );
240
+ aioseo()->internalOptions->internal->deprecatedOptions = $deprecatedOptions;
241
+ }
242
+ }
243
+
244
+ /**
245
+ * Migrates a given sitemap excluded post from V3.
246
+ *
247
+ * @since 4.0.3
248
+ *
249
+ * @param int $postId The post ID.
250
+ * @return void
251
+ */
252
+ private function migrateSitemapExcludedPost( $postId ) {
253
+ $post = get_post( $postId );
254
+ if ( ! is_object( $post ) ) {
255
+ return;
256
+ }
257
+
258
+ $excludedPosts = aioseo()->options->deprecated->searchAppearance->advanced->excludePosts;
259
+ foreach ( $excludedPosts as $excludedPost ) {
260
+ $excludedPost = json_decode( $excludedPost );
261
+ if ( $excludedPost->value === $postId ) {
262
+ return;
263
+ }
264
+ }
265
+
266
+ $excludedPost = [
267
+ 'value' => $post->ID,
268
+ 'type' => $post->post_type,
269
+ 'label' => $post->post_title,
270
+ 'link' => get_permalink( $post )
271
+ ];
272
+
273
+ $excludedPosts[] = wp_json_encode( $excludedPost );
274
+ aioseo()->options->deprecated->searchAppearance->advanced->excludePosts = $excludedPosts;
275
+ }
276
+
277
+ /**
278
+ * Migrates additional post meta data.
279
+ *
280
+ * @since 4.0.2
281
+ *
282
+ * @param int $postId The post ID.
283
+ * @return void
284
+ */
285
+ protected function migrateAdditionalPostMeta( $postId ) {
286
+ return $postId;
287
+ }
288
+
289
+ /**
290
+ * Maps the old Open Graph meta to the social meta columns in V4.
291
+ *
292
+ * @since 4.0.0
293
+ *
294
+ * @param array $ogMeta The old V3 Open Graph meta.
295
+ * @return array $meta The mapped meta.
296
+ */
297
+ public function convertOpenGraphMeta( $ogMeta ) {
298
+ $ogMeta = maybe_unserialize( $ogMeta );
299
+
300
+ if ( ! is_array( $ogMeta ) ) {
301
+ return [];
302
+ }
303
+
304
+ $mappedSocialMeta = [
305
+ 'aioseop_opengraph_settings_title' => 'og_title',
306
+ 'aioseop_opengraph_settings_desc' => 'og_description',
307
+ 'aioseop_opengraph_settings_image' => 'og_image_custom_url',
308
+ 'aioseop_opengraph_settings_imagewidth' => 'og_image_width',
309
+ 'aioseop_opengraph_settings_imageheight' => 'og_image_height',
310
+ 'aioseop_opengraph_settings_video' => 'og_video',
311
+ 'aioseop_opengraph_settings_videowidth' => 'og_video_width',
312
+ 'aioseop_opengraph_settings_videoheight' => 'og_video_height',
313
+ 'aioseop_opengraph_settings_category' => 'og_object_type',
314
+ 'aioseop_opengraph_settings_section' => 'og_article_section',
315
+ 'aioseop_opengraph_settings_tag' => 'og_article_tags',
316
+ 'aioseop_opengraph_settings_setcard' => 'twitter_card',
317
+ 'aioseop_opengraph_settings_customimg_twitter' => 'twitter_image_custom_url',
318
+ ];
319
+
320
+ $meta = [];
321
+ foreach ( $ogMeta as $name => $value ) {
322
+ if ( ! in_array( $name, array_keys( $mappedSocialMeta ), true ) ) {
323
+ continue;
324
+ }
325
+
326
+ switch ( $name ) {
327
+ case 'aioseop_opengraph_settings_desc':
328
+ case 'aioseop_opengraph_settings_title':
329
+ $meta[ $mappedSocialMeta[ $name ] ] = aioseo()->helpers->sanitizeOption( aioseo()->migration->helpers->macrosToSmartTags( $value ) );
330
+ break;
331
+ case 'aioseop_opengraph_settings_image':
332
+ $value = strval( $value );
333
+ if ( empty( $value ) ) {
334
+ break;
335
+ }
336
+
337
+ $meta['og_image_type'] = 'custom_image';
338
+ $meta[ $mappedSocialMeta[ $name ] ] = strval( $value );
339
+ break;
340
+ case 'aioseop_opengraph_settings_video':
341
+ $meta[ $mappedSocialMeta[ $name ] ] = esc_url( $value );
342
+ break;
343
+ case 'aioseop_opengraph_settings_customimg_twitter':
344
+ $value = strval( $value );
345
+ if ( empty( $value ) ) {
346
+ break;
347
+ }
348
+ $meta['twitter_image_type'] = 'custom_image';
349
+ $meta['twitter_use_og'] = false;
350
+ $meta[ $mappedSocialMeta[ $name ] ] = strval( $value );
351
+ break;
352
+ case 'aioseop_opengraph_settings_imagewidth':
353
+ case 'aioseop_opengraph_settings_imageheight':
354
+ case 'aioseop_opengraph_settings_videowidth':
355
+ case 'aioseop_opengraph_settings_videoheight':
356
+ $value = intval( $value );
357
+ if ( ! $value || $value <= 0 ) {
358
+ break;
359
+ }
360
+ $meta[ $mappedSocialMeta[ $name ] ] = $value;
361
+ break;
362
+ case 'aioseop_opengraph_settings_tag':
363
+ $meta[ $mappedSocialMeta[ $name ] ] = aioseo()->migration->helpers->oldKeywordsToNewKeywords( $value );
364
+ break;
365
+ default:
366
+ $meta[ $mappedSocialMeta[ $name ] ] = esc_html( strval( $value ) );
367
+ break;
368
+ }
369
+ }
370
+
371
+ return $meta;
372
+ }
373
+
374
+ /**
375
+ * Returns the title as it was in V3.
376
+ *
377
+ * @since 4.0.0
378
+ *
379
+ * @param int $post The post object.
380
+ * @param string $seoTitle The old SEO title.
381
+ * @return string The title.
382
+ */
383
+ protected function getTitleValue( $post, $seoTitle = '' ) {
384
+ if ( ! is_object( $post ) ) {
385
+ return '';
386
+ }
387
+
388
+ $titleFormat = '#post_title #separator_sa #site_title';
389
+ if ( aioseo()->options->searchAppearance->dynamic->postTypes->has( $post->post_type ) ) {
390
+ $titleFormat = aioseo()->options->searchAppearance->dynamic->postTypes->{$post->post_type}->title;
391
+ }
392
+
393
+ $seoTitle = preg_replace( '/(#post_title)/', $seoTitle, $titleFormat );
394
+ return aioseo()->helpers->sanitizeOption( aioseo()->migration->helpers->macrosToSmartTags( $seoTitle ) );
395
+ }
396
+ }
app/Common/Migration/Migration.php ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Migration;
3
+
4
+ // phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
5
+
6
+ /**
7
+ * Handles the migration from V3 to V4.
8
+ */
9
+ class Migration {
10
+
11
+ /**
12
+ * The old V3 options.
13
+ *
14
+ * @since 4.0.0
15
+ *
16
+ * @var array
17
+ */
18
+ public $oldOptions = [];
19
+
20
+ /**
21
+ * Class constructor.
22
+ *
23
+ * @since 4.0.0
24
+ */
25
+ public function __construct() {
26
+ $this->meta = new Meta();
27
+ $this->helpers = new Helpers();
28
+
29
+ add_action( 'aioseo_migrate_post_meta', [ $this->meta, 'migratePostMeta' ] );
30
+
31
+ if ( wp_doing_ajax() || wp_doing_cron() ) {
32
+ return;
33
+ }
34
+
35
+ add_action( 'init', [ $this, 'init' ], 2000 );
36
+ }
37
+
38
+ /**
39
+ * Initializes the class.
40
+ *
41
+ * @since 4.0.0
42
+ *
43
+ * @return void
44
+ */
45
+ public function init() {
46
+ // Since the version numbers may vary, we only want to compare the first 3 numbers.
47
+ $lastActiveVersion = aioseo()->internalOptions->internal->lastActiveVersion;
48
+ $lastActiveVersion = $lastActiveVersion ? explode( '-', $lastActiveVersion ) : null;
49
+
50
+ if ( version_compare( $lastActiveVersion[0], '4.0.0', '<' ) ) {
51
+ aioseo()->internalOptions->internal->migratedVersion = $lastActiveVersion[0];
52
+ add_action( 'wp_loaded', [ $this, 'doMigration' ] );
53
+ }
54
+
55
+ // Run our migration again for V4 users between v4.0.0 and v4.0.4.
56
+ if (
57
+ version_compare( $lastActiveVersion[0], '4.0.0', '>=' ) &&
58
+ version_compare( $lastActiveVersion[0], '4.0.4', '<' ) &&
59
+ get_option( 'aioseop_options' )
60
+ ) {
61
+ add_action( 'wp_loaded', [ $this, 'redoMetaMigration' ] );
62
+ }
63
+
64
+ // Stop migration for new v4 users where it was incorrectly triggered.
65
+ if ( version_compare( $lastActiveVersion[0], '4.0.4', '=' ) && ! get_option( 'aioseop_options' ) ) {
66
+ delete_transient( 'aioseo_v3_migration_in_progress_posts' );
67
+ delete_transient( 'aioseo_v3_migration_in_progress_terms' );
68
+
69
+ try {
70
+ if ( as_next_scheduled_action( 'aioseo_migrate_post_meta' ) ) {
71
+ as_unschedule_action( 'aioseo_migrate_post_meta', [], 'aioseo' );
72
+ }
73
+ if ( as_next_scheduled_action( 'aioseo_migrate_term_meta' ) ) {
74
+ as_unschedule_action( 'aioseo_migrate_term_meta', [], 'aioseo' );
75
+ }
76
+ } catch ( \Exception $e ) {
77
+ // Do nothing.
78
+ }
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Starts the migration.
84
+ *
85
+ * @since 4.0.0
86
+ *
87
+ * @param array $oldOptions The old options. We pass it in directly via the Importer/Exporter.
88
+ * @return void
89
+ */
90
+ public function doMigration( $oldOptions = [] ) {
91
+ // If our tables do not exist, create them now.
92
+ if ( ! aioseo()->db->tableExists( 'aioseo_posts' ) ) {
93
+ aioseo()->updates->addInitialCustomTablesForV4();
94
+ }
95
+
96
+ $this->oldOptions = ( new OldOptions( $oldOptions ) )->oldOptions;
97
+
98
+ if (
99
+ ! $this->oldOptions ||
100
+ ! is_array( $this->oldOptions ) ||
101
+ ! count( $this->oldOptions )
102
+ ) {
103
+ return;
104
+ }
105
+
106
+ set_transient( 'aioseo_v3_migration_in_progress_posts', time(), WEEK_IN_SECONDS );
107
+
108
+ $this->migrateSettings();
109
+ $this->meta->migrateMeta();
110
+ }
111
+
112
+ /**
113
+ * Reruns the post meta migration.
114
+ *
115
+ * This is meant for users on v4.0.0, v4.0.1 or v4.0.2 where the migration might have failed.
116
+ *
117
+ * @since 4.0.3
118
+ *
119
+ * @return void
120
+ */
121
+ public function redoMetaMigration() {
122
+ set_transient( 'aioseo_v3_migration_in_progress_posts', time(), WEEK_IN_SECONDS );
123
+ $this->meta->migrateMeta();
124
+ }
125
+
126
+ /**
127
+ * Migrates the plugin settings.
128
+ *
129
+ * @since 4.0.0
130
+ *
131
+ * @return void
132
+ */
133
+ protected function migrateSettings() {
134
+ new GeneralSettings();
135
+
136
+ if ( ! isset( $this->oldOptions['modules']['aiosp_feature_manager_options'] ) ) {
137
+ new Sitemap();
138
+ return;
139
+ }
140
+
141
+ $this->migrateFeatureManager();
142
+
143
+ if ( isset( $this->oldOptions['modules']['aiosp_feature_manager_options']['aiosp_feature_manager_enable_opengraph'] ) ) {
144
+ new SocialMeta();
145
+ }
146
+
147
+ if ( isset( $this->oldOptions['modules']['aiosp_feature_manager_options']['aiosp_feature_manager_enable_sitemap'] ) ) {
148
+ new Sitemap();
149
+ }
150
+
151
+ if ( isset( $this->oldOptions['modules']['aiosp_feature_manager_options']['aiosp_feature_manager_enable_robots'] ) ) {
152
+ new RobotsTxt();
153
+ }
154
+
155
+ if ( ! empty( $this->oldOptions['modules']['aiosp_feature_manager_options']['aiosp_feature_manager_enable_bad_robots'] ) ) {
156
+ new BadRobots();
157
+ }
158
+
159
+ if ( aioseo()->helpers->isWpmlActive() ) {
160
+ new Wpml();
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Migrates the Feature Manager settings.
166
+ *
167
+ * @since 4.0.0
168
+ *
169
+ * @return void
170
+ */
171
+ protected function migrateFeatureManager() {
172
+ if ( empty( $this->oldOptions['modules']['aiosp_feature_manager_options'] ) ) {
173
+ return;
174
+ }
175
+
176
+ if ( empty( $this->oldOptions['modules']['aiosp_feature_manager_options']['aiosp_feature_manager_enable_opengraph'] ) ) {
177
+ aioseo()->options->social->facebook->general->enable = false;
178
+ aioseo()->options->social->twitter->general->enable = false;
179
+ }
180
+
181
+ if ( empty( $this->oldOptions['modules']['aiosp_feature_manager_options']['aiosp_feature_manager_enable_sitemap'] ) ) {
182
+ aioseo()->options->sitemap->general->enable = false;
183
+ aioseo()->options->sitemap->rss->enable = false;
184
+ }
185
+
186
+ if ( ! empty( $this->oldOptions['modules']['aiosp_feature_manager_options']['aiosp_feature_manager_enable_robots'] ) ) {
187
+ aioseo()->options->tools->robots->enable = true;
188
+ }
189
+ }
190
+ }
app/Common/Migration/OldOptions.php ADDED
@@ -0,0 +1,265 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Migration;
3
+
4
+ /**
5
+ * Updates and holds the old options from V3.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class OldOptions {
10
+
11
+ /**
12
+ * The old options from V3.
13
+ *
14
+ * @since 4.0.0
15
+ *
16
+ * @var array
17
+ */
18
+ public $oldOptions = [];
19
+
20
+ /**
21
+ * Class constructor.
22
+ *
23
+ * @since 4.0.0
24
+ *
25
+ * @param array $oldOptions The old options. We pass it in directly via the Importer/Exporter.
26
+ */
27
+ public function __construct( $oldOptions = [] ) {
28
+ $this->oldOptions = ! empty( $oldOptions ) ? $oldOptions : get_option( 'aioseop_options' );
29
+
30
+ if (
31
+ ! $this->oldOptions ||
32
+ ! is_array( $this->oldOptions ) ||
33
+ ! count( $this->oldOptions )
34
+ ) {
35
+ return;
36
+ }
37
+
38
+ $this->runPreV4Migrations();
39
+ $this->fixSettingValues();
40
+ }
41
+
42
+ /**
43
+ * Runs all pre-V4 migrations to update the old options to the latest state.
44
+ *
45
+ * @since 4.0.0
46
+ *
47
+ * @return void
48
+ */
49
+ public function runPreV4Migrations() {
50
+ $lastActiveVersion = aioseo()->internalOptions->internal->lastActiveVersion;
51
+ if ( version_compare( $lastActiveVersion, aioseo()->version, '<' ) ) {
52
+
53
+ $this->doVersionUpdates( $lastActiveVersion );
54
+ aioseo()->internalOptions->internal->lastActiveVersion = aioseo()->version;
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Runs all pre-V4 version-based migrations.
60
+ *
61
+ * @since 4.0.0
62
+ *
63
+ * @param string $oldVersion The old version number to compare against.
64
+ * @return void
65
+ */
66
+ protected function doVersionUpdates( $oldVersion ) {
67
+ if ( version_compare( $oldVersion, '3.0', '<' ) ) {
68
+ $this->removeBadBots();
69
+ $this->sitemapExclTerms201905();
70
+ }
71
+
72
+ if ( version_compare( $oldVersion, '3.1', '<' ) ) {
73
+ $this->resetFlushRewriteRules201906();
74
+ }
75
+
76
+ if (
77
+ version_compare( $oldVersion, '3.2', '<' ) ||
78
+ version_compare( $oldVersion, '3.2.6', '<' )
79
+ ) {
80
+ $this->updateSchemaMarkup201907();
81
+ }
82
+
83
+ if ( version_compare( $oldVersion, '4.0.0', '<' ) ) {
84
+ $this->updateArchiveNoIndexSettings20200413();
85
+ $this->updateArchiveTitleFormatSettings20200413();
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Removes various entries from the bad bots list.
91
+ *
92
+ * @since 4.0.0
93
+ *
94
+ * @return void
95
+ */
96
+ protected function removeBadBots() {
97
+ if (
98
+ empty( $this->oldOptions['modules']['aiosp_bad_robots_options'] ) ||
99
+ empty( $this->oldOptions['modules']['aiosp_bad_robots_options']['aiosp_bad_robots_blocklist'] )
100
+ ) {
101
+ return;
102
+ }
103
+
104
+ $this->oldOptions['modules']['aiosp_bad_robots_options']['aiosp_bad_robots_blocklist'] = str_replace(
105
+ [
106
+ "DOC\r\n",
107
+ "DOC\n",
108
+ "yandex\r\n",
109
+ "yandex\n",
110
+ "SeznamBot\r\n",
111
+ "SeznamBot\n",
112
+ "SemrushBot\r\n",
113
+ "SemrushBot\n",
114
+ "Exabot\r\n",
115
+ "Exabot\n",
116
+ ],
117
+ '',
118
+ $this->oldOptions['modules']['aiosp_bad_robots_options']['aiosp_bad_robots_blocklist']
119
+ );
120
+ }
121
+
122
+ /**
123
+ * Converts "excl_categories" to "excl_terms".
124
+ *
125
+ * @since 4.0.0
126
+ *
127
+ * @return void
128
+ */
129
+ protected function sitemapExclTerms201905() {
130
+ if (
131
+ empty( $this->oldOptions['modules'] ) ||
132
+ empty( $this->oldOptions['modules']['aiosp_sitemap_options'] )
133
+ ) {
134
+ return;
135
+ }
136
+
137
+ $options = $this->oldOptions['modules']['aiosp_sitemap_options'];
138
+ if ( ! empty( $options['aiosp_sitemap_excl_categories'] ) ) {
139
+ $options['aiosp_sitemap_excl_terms']['category']['taxonomy'] = 'category';
140
+ $options['aiosp_sitemap_excl_terms']['category']['terms'] = $options['aiosp_sitemap_excl_categories'];
141
+ unset( $options['aiosp_sitemap_excl_categories'] );
142
+
143
+ $this->oldOptions['modules']['aiosp_sitemap_options'] = $options;
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Flushes rewrite rules for XML Sitemap URL changes.
149
+ *
150
+ * @since 4.0.0
151
+ *
152
+ * @return void
153
+ */
154
+ protected function resetFlushRewriteRules201906() {
155
+ add_action( 'shutdown', 'flush_rewrite_rules' );
156
+ }
157
+
158
+ /**
159
+ * Adds a number of schema markup settings.
160
+ *
161
+ * @since 4.0.0
162
+ *
163
+ * @return void
164
+ */
165
+ protected function updateSchemaMarkup201907() {
166
+ $updateValues = [
167
+ 'aiosp_schema_markup' => '1',
168
+ 'aiosp_schema_search_results_page' => '1',
169
+ 'aiosp_schema_social_profile_links' => '',
170
+ 'aiosp_schema_site_represents' => 'organization',
171
+ 'aiosp_schema_organization_name' => '',
172
+ 'aiosp_schema_organization_logo' => '',
173
+ 'aiosp_schema_person_user' => '1',
174
+ 'aiosp_schema_phone_number' => '',
175
+ 'aiosp_schema_contact_type' => 'none',
176
+ ];
177
+
178
+ if ( isset( $this->oldOptions['aiosp_schema_markup'] ) ) {
179
+ if ( empty( $this->oldOptions['aiosp_schema_markup'] ) || 'off' === $this->oldOptions['aiosp_schema_markup'] ) {
180
+ $updateValues['aiosp_schema_markup'] = '0';
181
+ }
182
+ }
183
+ if ( isset( $this->oldOptions['aiosp_google_sitelinks_search'] ) ) {
184
+ if ( empty( $this->oldOptions['aiosp_google_sitelinks_search'] ) || 'off' === $this->oldOptions['aiosp_google_sitelinks_search'] ) {
185
+ $updateValues['aiosp_schema_search_results_page'] = '0';
186
+ }
187
+ }
188
+ if ( isset( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_profile_links'] ) ) {
189
+ $updateValues['aiosp_schema_social_profile_links'] = $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_profile_links'];
190
+ }
191
+ if ( isset( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_person_or_org'] ) ) {
192
+ if ( 'person' === $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_person_or_org'] ) {
193
+ $updateValues['aiosp_schema_site_represents'] = 'person';
194
+ }
195
+ }
196
+ if ( isset( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_social_name'] ) ) {
197
+ $updateValues['aiosp_schema_organization_name'] = $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_social_name'];
198
+ }
199
+
200
+ foreach ( $updateValues as $k => $v ) {
201
+ $this->oldOptions[ $k ] = $v;
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Migrate setting for noindex archives.
207
+ *
208
+ * @since 4.0.0
209
+ *
210
+ * @return void
211
+ */
212
+ protected function updateArchiveNoIndexSettings20200413() {
213
+ if ( isset( $this->oldOptions['aiosp_archive_noindex'] ) ) {
214
+ $this->oldOptions['aiosp_archive_date_noindex'] = $this->oldOptions['aiosp_archive_noindex'];
215
+ $this->oldOptions['aiosp_archive_author_noindex'] = $this->oldOptions['aiosp_archive_noindex'];
216
+ unset( $this->oldOptions['aiosp_archive_noindex'] );
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Migrate settings for archive title formats.
222
+ *
223
+ * @since 4.0.0
224
+ *
225
+ * @return void
226
+ */
227
+ protected function updateArchiveTitleFormatSettings20200413() {
228
+ if (
229
+ isset( $this->oldOptions['aiosp_archive_title_format'] ) &&
230
+ empty( $this->oldOptions['aiosp_date_title_format'] )
231
+ ) {
232
+ $this->oldOptions['aiosp_date_title_format'] = $this->oldOptions['aiosp_archive_title_format'];
233
+ unset( $this->oldOptions['aiosp_archive_title_format'] );
234
+ }
235
+
236
+ if (
237
+ isset( $this->oldOptions['aiosp_archive_title_format'] ) &&
238
+ '%date% | %site_title%' === $this->oldOptions['aiosp_archive_title_format']
239
+ ) {
240
+ unset( $this->oldOptions['aiosp_archive_title_format'] );
241
+ }
242
+ }
243
+
244
+ /**
245
+ * Corrects the value of a number of settings in V3 that are illogical.
246
+ *
247
+ * @since 4.0.0
248
+ *
249
+ * @return void
250
+ */
251
+ protected function fixSettingValues() {
252
+ $settingsToFix = [
253
+ 'aiosp_togglekeywords'
254
+ ];
255
+ foreach ( $settingsToFix as $settingToFix ) {
256
+ if ( isset( $this->oldOptions[ $settingToFix ] ) ) {
257
+ if ( '1' === (string) $this->oldOptions[ $settingToFix ] ) {
258
+ $this->oldOptions[ $settingToFix ] = '';
259
+ continue;
260
+ }
261
+ $this->oldOptions[ $settingToFix ] = 'on';
262
+ }
263
+ }
264
+ }
265
+ }
app/Common/Migration/RobotsTxt.php ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Migration;
3
+
4
+ // phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
5
+
6
+ /**
7
+ * Migrates the Robots.txt settings from V3.
8
+ *
9
+ * @since 4.0.0
10
+ */
11
+ class RobotsTxt {
12
+
13
+ /**
14
+ * Class constructor.
15
+ *
16
+ * @since 4.0.0
17
+ */
18
+ public function __construct() {
19
+ $oldOptions = aioseo()->migration->oldOptions;
20
+
21
+ $rules = aioseo()->options->tools->robots->rules;
22
+
23
+ if (
24
+ ! empty( $oldOptions['modules']['aiosp_robots_options'] ) &&
25
+ ! empty( $oldOptions['modules']['aiosp_robots_options']['aiosp_robots_rules'] )
26
+ ) {
27
+ $rules += $this->convertRules( $oldOptions['modules']['aiosp_robots_options']['aiosp_robots_rules'] );
28
+ }
29
+
30
+ aioseo()->options->tools->robots->rules = $rules;
31
+ }
32
+
33
+ /**
34
+ * Converts the old Robots.txt rules to the new format.
35
+ *
36
+ * @since 4.0.0
37
+ *
38
+ * @param array $oldRules The old rules.
39
+ * @return array $newRules The converted rules.
40
+ */
41
+ private function convertRules( $oldRules ) {
42
+ $newRules = [];
43
+ foreach ( $oldRules as $oldRule ) {
44
+ $newRule = new \stdClass();
45
+ $newRule->userAgent = aioseo()->helpers->sanitizeOption( $oldRule['agent'] );
46
+ $newRule->rule = aioseo()->helpers->sanitizeOption( lcfirst( $oldRule['type'] ) );
47
+ $newRule->directoryPath = aioseo()->helpers->sanitizeOption( $oldRule['path'] );
48
+
49
+ array_push( $newRules, wp_json_encode( $newRule ) );
50
+ }
51
+ return $newRules;
52
+ }
53
+ }
app/Common/Migration/Sitemap.php ADDED
@@ -0,0 +1,397 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Migration;
3
+
4
+ // phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
5
+
6
+ /**
7
+ * Migrates the XML Sitemap settings from V3.
8
+ *
9
+ * @since 4.0.0
10
+ */
11
+ class Sitemap {
12
+
13
+ /**
14
+ * The old V3 options.
15
+ *
16
+ * @since 4.0.0
17
+ *
18
+ * @var array
19
+ */
20
+ protected $oldOptions = [];
21
+
22
+ /**
23
+ * Class constructor.
24
+ *
25
+ * @since 4.0.0
26
+ */
27
+ public function __construct() {
28
+ $this->oldOptions = aioseo()->migration->oldOptions;
29
+
30
+ if ( empty( $this->oldOptions['modules']['aiosp_sitemap_options'] ) ) {
31
+ return;
32
+ }
33
+
34
+ $this->checkIfStatic();
35
+ $this->migrateLinksPerIndex();
36
+ $this->migrateIncludedObjects();
37
+ $this->migratePrioFreq();
38
+ $this->migrateAdditionalPages();
39
+ $this->migrateExcludedPages();
40
+ $this->regenerateSitemap();
41
+
42
+ $settings = [
43
+ 'aiosp_sitemap_indexes' => [ 'type' => 'boolean', 'newOption' => [ 'sitemap', 'general', 'indexes' ] ],
44
+ 'aiosp_sitemap_archive' => [ 'type' => 'boolean', 'newOption' => [ 'sitemap', 'general', 'date' ] ],
45
+ 'aiosp_sitemap_author' => [ 'type' => 'boolean', 'newOption' => [ 'sitemap', 'general', 'author' ] ],
46
+ 'aiosp_sitemap_images' => [ 'type' => 'boolean', 'newOption' => [ 'sitemap', 'general', 'advancedSettings', 'excludeImages' ] ],
47
+ 'aiosp_sitemap_rss_sitemap' => [ 'type' => 'boolean', 'newOption' => [ 'sitemap', 'rss', 'enable' ] ],
48
+ 'aiosp_sitemap_filename' => [ 'type' => 'string', 'newOption' => [ 'sitemap', 'general', 'filename' ] ],
49
+ 'aiosp_sitemap_publication_name' => [ 'type' => 'boolean', 'newOption' => [ 'sitemap', 'news', 'publicationName' ] ],
50
+ 'aiosp_sitemap_rewrite' => [ 'type' => 'boolean', 'newOption' => [ 'deprecated', 'sitemap', 'general', 'advancedSettings', 'dynamic' ] ]
51
+ ];
52
+
53
+ aioseo()->migration->helpers->mapOldToNew( $settings, $this->oldOptions['modules']['aiosp_sitemap_options'] );
54
+
55
+ if (
56
+ aioseo()->options->sitemap->general->advancedSettings->excludePosts ||
57
+ aioseo()->options->sitemap->general->advancedSettings->excludeTerms ||
58
+ aioseo()->options->sitemap->general->advancedSettings->excludeImages ||
59
+ ( in_array( 'staticSitemap', aioseo()->internalOptions->internal->deprecatedOptions, true ) && ! aioseo()->options->deprecated->sitemap->general->advancedSettings->dynamic )
60
+ ) {
61
+ aioseo()->options->sitemap->general->advancedSettings->enable = true;
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Check if the sitemap is statically generated.
67
+ *
68
+ * @since 4.0.0
69
+ *
70
+ * @return void
71
+ */
72
+ private function checkIfStatic() {
73
+ if (
74
+ isset( $this->oldOptions['modules']['aiosp_sitemap_options']['aiosp_sitemap_rewrite'] ) &&
75
+ empty( $this->oldOptions['modules']['aiosp_sitemap_options']['aiosp_sitemap_rewrite'] )
76
+ ) {
77
+ $deprecatedOptions = aioseo()->internalOptions->internal->deprecatedOptions;
78
+ array_push( $deprecatedOptions, 'staticSitemap' );
79
+ aioseo()->internalOptions->internal->deprecatedOptions = $deprecatedOptions;
80
+
81
+ aioseo()->options->deprecated->sitemap->general->advancedSettings->dynamic = false;
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Migrates the amount of links per sitemap index.
87
+ *
88
+ * @since 4.0.0
89
+ *
90
+ * @return void
91
+ */
92
+ private function migrateLinksPerIndex() {
93
+ if ( ! empty( $this->oldOptions['modules']['aiosp_sitemap_options']['aiosp_sitemap_max_posts'] ) ) {
94
+ $value = intval( $this->oldOptions['modules']['aiosp_sitemap_options']['aiosp_sitemap_max_posts'] );
95
+ if ( ! $value ) {
96
+ return;
97
+ }
98
+ $value = $value > 50000 ? 50000 : $value;
99
+ aioseo()->options->sitemap->general->linksPerIndex = $value;
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Migrates the excluded object settings.
105
+ *
106
+ * @since 4.0.0
107
+ *
108
+ * @return void
109
+ */
110
+ protected function migrateExcludedPages() {
111
+ if (
112
+ empty( $this->oldOptions['modules']['aiosp_sitemap_options']['aiosp_sitemap_excl_terms'] ) &&
113
+ empty( $this->oldOptions['modules']['aiosp_sitemap_options']['aiosp_sitemap_excl_pages'] )
114
+ ) {
115
+ return;
116
+ }
117
+
118
+ $excludedPosts = aioseo()->options->sitemap->general->advancedSettings->excludePosts;
119
+ if ( ! empty( $this->oldOptions['modules']['aiosp_sitemap_options']['aiosp_sitemap_excl_pages'] ) ) {
120
+ $pages = explode( ', ', $this->oldOptions['modules']['aiosp_sitemap_options']['aiosp_sitemap_excl_pages'] );
121
+ if ( count( $pages ) ) {
122
+ foreach ( $pages as $page ) {
123
+ $id = intval( $page );
124
+ if ( ! $id ) {
125
+ $post = get_page_by_path( $page, OBJECT, aioseo()->helpers->getPublicPostTypes( true ) );
126
+ if ( $post && is_object( $post ) ) {
127
+ $id = $post->ID;
128
+ }
129
+ }
130
+
131
+ if ( $id ) {
132
+ $post = get_post( $id );
133
+ if ( ! is_object( $post ) ) {
134
+ continue;
135
+ }
136
+
137
+ $excludedPost = new \stdClass();
138
+ $excludedPost->value = $id;
139
+ $excludedPost->type = $post->post_type;
140
+ $excludedPost->label = $post->post_title;
141
+ $excludedPost->link = get_permalink( $id );
142
+
143
+ array_push( $excludedPosts, wp_json_encode( $excludedPost ) );
144
+ }
145
+ }
146
+ }
147
+ }
148
+ aioseo()->options->sitemap->general->advancedSettings->excludePosts = $excludedPosts;
149
+
150
+ $excludedTerms = aioseo()->options->sitemap->general->advancedSettings->excludeTerms;
151
+ if ( ! empty( $this->oldOptions['modules']['aiosp_sitemap_options']['aiosp_sitemap_excl_terms'] ) ) {
152
+ foreach ( $this->oldOptions['modules']['aiosp_sitemap_options']['aiosp_sitemap_excl_terms'] as $taxonomy ) {
153
+ foreach ( $taxonomy['terms'] as $k => $id ) {
154
+ $term = get_term( $id );
155
+ if ( ! is_object( $term ) ) {
156
+ continue;
157
+ }
158
+
159
+ $excludedTerm = new \stdClass();
160
+ $excludedTerm->value = $id;
161
+ $excludedTerm->type = $term->taxonomy;
162
+ $excludedTerm->label = $term->name;
163
+ $excludedTerm->link = get_term_link( $term );
164
+
165
+ array_push( $excludedTerms, wp_json_encode( $excludedTerm ) );
166
+ }
167
+ }
168
+ }
169
+ aioseo()->options->sitemap->general->advancedSettings->excludeTerms = $excludedTerms;
170
+ }
171
+
172
+ /**
173
+ * Migrates the objects that are included in the sitemap.
174
+ *
175
+ * @since 4.0.0
176
+ *
177
+ * @return void
178
+ */
179
+ protected function migrateIncludedObjects() {
180
+ if (
181
+ ! isset( $this->oldOptions['modules']['aiosp_sitemap_options']['aiosp_sitemap_posttypes'] ) &&
182
+ ! isset( $this->oldOptions['modules']['aiosp_sitemap_options']['aiosp_sitemap_taxonomies'] )
183
+ ) {
184
+ return;
185
+ }
186
+
187
+ $publicPostTypes = aioseo()->helpers->getPublicPostTypes( true );
188
+ $publicTaxonomies = aioseo()->helpers->getPublicTaxonomies( true );
189
+
190
+ if ( in_array( 'all', $this->oldOptions['modules']['aiosp_sitemap_options']['aiosp_sitemap_posttypes'], true ) ) {
191
+ aioseo()->options->sitemap->general->postTypes->all = true;
192
+ aioseo()->options->sitemap->general->postTypes->included = array_values( $publicPostTypes );
193
+ } else {
194
+ $allPostTypes = true;
195
+ foreach ( $publicPostTypes as $postType ) {
196
+ if ( ! in_array( $postType, $this->oldOptions['modules']['aiosp_sitemap_options']['aiosp_sitemap_posttypes'], true ) ) {
197
+ $allPostTypes = false;
198
+ }
199
+ }
200
+
201
+ aioseo()->options->sitemap->general->postTypes->all = $allPostTypes;
202
+ aioseo()->options->sitemap->general->postTypes->included = array_values(
203
+ array_intersect( $publicPostTypes, $this->oldOptions['modules']['aiosp_sitemap_options']['aiosp_sitemap_posttypes'] )
204
+ );
205
+ }
206
+
207
+ if ( in_array( 'all', $this->oldOptions['modules']['aiosp_sitemap_options']['aiosp_sitemap_taxonomies'], true ) ) {
208
+ aioseo()->options->sitemap->general->taxonomies->all = true;
209
+ aioseo()->options->sitemap->general->taxonomies->included = array_values( $publicTaxonomies );
210
+ } else {
211
+ $allTaxonomies = true;
212
+ foreach ( $publicTaxonomies as $taxonomy ) {
213
+ if ( ! in_array( $taxonomy, $this->oldOptions['modules']['aiosp_sitemap_options']['aiosp_sitemap_taxonomies'], true ) ) {
214
+ $allTaxonomies = false;
215
+ }
216
+ }
217
+
218
+ aioseo()->options->sitemap->general->taxonomies->all = $allTaxonomies;
219
+ aioseo()->options->sitemap->general->taxonomies->included = array_values(
220
+ array_intersect( $publicTaxonomies, $this->oldOptions['modules']['aiosp_sitemap_options']['aiosp_sitemap_taxonomies'] )
221
+ );
222
+ }
223
+ }
224
+
225
+ /**
226
+ * Migrates the additional pages that are included in the sitemap.
227
+ *
228
+ * @since 4.0.0
229
+ *
230
+ * @return void
231
+ */
232
+ private function migrateAdditionalPages() {
233
+ if ( empty( $this->oldOptions['modules']['aiosp_sitemap_options']['aiosp_sitemap_addl_pages'] ) ) {
234
+ return;
235
+ }
236
+
237
+ $pages = [];
238
+ foreach ( $this->oldOptions['modules']['aiosp_sitemap_options']['aiosp_sitemap_addl_pages'] as $url => $values ) {
239
+ $page = new \stdClass();
240
+ $page->url = esc_url( wp_strip_all_tags( $url ) );
241
+ $page->priority = [ 'label' => $values['prio'], 'value' => $values['prio'] ];
242
+ $page->frequency = [ 'label' => $values['freq'], 'value' => $values['freq'] ];
243
+ $page->lastModified = gmdate( 'm/d/Y', strtotime( $values['mod'] ) );
244
+
245
+ $pages[] = wp_json_encode( $page );
246
+ }
247
+
248
+ aioseo()->options->sitemap->general->additionalPages->enable = true;
249
+ aioseo()->options->sitemap->general->additionalPages->pages = $pages;
250
+ }
251
+
252
+ /**
253
+ * Migrates the priority/frequency settings.
254
+ *
255
+ * @since 4.0.0
256
+ *
257
+ * @return void
258
+ */
259
+ private function migratePrioFreq() {
260
+ $settings = [
261
+ 'aiosp_sitemap_prio_homepage' => [ 'type' => 'float', 'newOption' => [ 'sitemap', 'general', 'advancedSettings', 'priority', 'homePage', 'priority' ] ],
262
+ 'aiosp_sitemap_freq_homepage' => [ 'type' => 'string', 'newOption' => [ 'sitemap', 'general', 'advancedSettings', 'priority', 'homePage', 'frequency' ] ],
263
+ 'aiosp_sitemap_prio_post' => [ 'type' => 'float', 'newOption' => [ 'sitemap', 'general', 'advancedSettings', 'priority', 'postTypes', 'priority' ] ],
264
+ 'aiosp_sitemap_freq_post' => [ 'type' => 'string', 'newOption' => [ 'sitemap', 'general', 'advancedSettings', 'priority', 'postTypes', 'frequency' ] ],
265
+ 'aiosp_sitemap_prio_post_post' => [ 'type' => 'float', 'newOption' => [ 'sitemap', 'dynamic', 'priority', 'postTypes', 'post', 'priority' ] ],
266
+ 'aiosp_sitemap_freq_post_post' => [ 'type' => 'string', 'newOption' => [ 'sitemap', 'dynamic', 'priority', 'postTypes', 'post', 'frequency' ] ],
267
+ 'aiosp_sitemap_prio_taxonomies' => [ 'type' => 'float', 'newOption' => [ 'sitemap', 'general', 'advancedSettings', 'priority', 'taxonomies', 'priority' ] ],
268
+ 'aiosp_sitemap_freq_taxonomies' => [ 'type' => 'string', 'newOption' => [ 'sitemap', 'general', 'advancedSettings', 'priority', 'taxonomies', 'frequency' ] ],
269
+ 'aiosp_sitemap_prio_archive' => [ 'type' => 'float', 'newOption' => [ 'sitemap', 'general', 'advancedSettings', 'priority', 'archive', 'priority' ] ],
270
+ 'aiosp_sitemap_freq_archive' => [ 'type' => 'string', 'newOption' => [ 'sitemap', 'general', 'advancedSettings', 'priority', 'archive', 'frequency' ] ],
271
+ 'aiosp_sitemap_prio_author' => [ 'type' => 'float', 'newOption' => [ 'sitemap', 'general', 'advancedSettings', 'priority', 'author', 'priority' ] ],
272
+ 'aiosp_sitemap_freq_author' => [ 'type' => 'string', 'newOption' => [ 'sitemap', 'general', 'advancedSettings', 'priority', 'author', 'frequency' ] ],
273
+ ];
274
+
275
+ foreach ( $this->oldOptions['modules']['aiosp_sitemap_options'] as $name => $value ) {
276
+ // Ignore fixed settings.
277
+ if ( in_array( $name, array_keys( $settings ), true ) ) {
278
+ continue;
279
+ }
280
+
281
+ $type = false;
282
+ $slug = '';
283
+ if ( preg_match( '#aiosp_sitemap_prio_(.*)#', $name, $slug ) ) {
284
+ $type = 'priority';
285
+ } elseif ( preg_match( '#aiosp_sitemap_freq_(.*)#', $name, $slug ) ) {
286
+ $type = 'frequency';
287
+ }
288
+
289
+ if ( empty( $slug ) || empty( $slug[1] ) ) {
290
+ continue;
291
+ }
292
+
293
+ $objectSlug = preg_replace( '#post_(?!tag)|taxonomies_#', '', $slug[1] );
294
+
295
+ if ( in_array( $objectSlug, aioseo()->helpers->getPublicPostTypes( true ), true ) ) {
296
+ $settings[ $name ] = [
297
+ 'type' => 'priority' === $type ? 'float' : 'string',
298
+ 'newOption' => [ 'sitemap', 'dynamic', 'priority', 'postTypes', $objectSlug, $type ]
299
+ ];
300
+ continue;
301
+ }
302
+
303
+ if ( in_array( $objectSlug, aioseo()->helpers->getPublicTaxonomies( true ), true ) ) {
304
+ $settings[ $name ] = [
305
+ 'type' => 'priority' === $type ? 'float' : 'string',
306
+ 'newOption' => [ 'sitemap', 'dynamic', 'priority', 'taxonomies', $objectSlug, $type ]
307
+ ];
308
+ }
309
+ }
310
+
311
+ foreach ( $settings as $name => $values ) {
312
+ // If setting is set to default, do nothing.
313
+ if (
314
+ empty( $this->oldOptions['modules']['aiosp_sitemap_options'][ $name ] ) ||
315
+ 'no' === $this->oldOptions['modules']['aiosp_sitemap_options'][ $name ]
316
+ ) {
317
+ unset( $settings[ $name ] );
318
+ continue;
319
+ }
320
+
321
+ // If value is "Select Individual", set grouped to false.
322
+ $value = $this->oldOptions['modules']['aiosp_sitemap_options'][ $name ];
323
+ if ( 'sel' === $value ) {
324
+ if ( preg_match( '#post$#', $name ) ) {
325
+ aioseo()->options->sitemap->general->advancedSettings->priority->postTypes->grouped = false;
326
+ } else {
327
+ aioseo()->options->sitemap->general->advancedSettings->priority->taxonomies->grouped = false;
328
+ }
329
+ continue;
330
+ }
331
+
332
+ $object = new \stdClass();
333
+ $object->label = $value;
334
+ $object->value = $value;
335
+
336
+ $error = false;
337
+ $options = aioseo()->options->noConflict();
338
+ $lastOption = '';
339
+ for ( $i = 0; $i < count( $values['newOption'] ); $i++ ) {
340
+ $lastOption = $values['newOption'][ $i ];
341
+ if ( ! $options->has( $lastOption, false ) ) {
342
+ $error = true;
343
+ break;
344
+ };
345
+ if ( count( $values['newOption'] ) - 1 !== $i ) {
346
+ $options = $options->$lastOption;
347
+ }
348
+ }
349
+
350
+ if ( $error ) {
351
+ continue;
352
+ }
353
+
354
+ $options->$lastOption = wp_json_encode( $object );
355
+ aioseo()->options->refresh();
356
+ }
357
+
358
+ if ( count( $settings ) ) {
359
+ aioseo()->options->sitemap->general->advancedSettings->enable = true;
360
+ }
361
+ }
362
+
363
+ /**
364
+ * Regenerates the sitemap if it is static.
365
+ *
366
+ * We need to do this since the stylesheet URLs have changed.
367
+ *
368
+ * @since 4.0.0
369
+ *
370
+ * @return void
371
+ */
372
+ private function regenerateSitemap() {
373
+ if (
374
+ isset( $this->oldOptions['modules']['aiosp_sitemap_options']['aiosp_sitemap_rewrite'] ) &&
375
+ empty( $this->oldOptions['modules']['aiosp_sitemap_options']['aiosp_sitemap_rewrite'] )
376
+ ) {
377
+ $files = aioseo()->sitemap->file->files();
378
+ $detectedFiles = [];
379
+ foreach ( $files as $filename ) {
380
+ // We don't want to delete the video sitemap here at all.
381
+ $isVideoSitemap = preg_match( '#.*video.*#', $filename ) ? true : false;
382
+ if ( ! $isVideoSitemap ) {
383
+ $detectedFiles[] = $filename;
384
+ }
385
+ }
386
+
387
+ $wpfs = aioseo()->helpers->wpfs();
388
+ if ( count( $detectedFiles ) && is_object( $wpfs ) ) {
389
+ foreach ( $detectedFiles as $file ) {
390
+ $wpfs->delete( $file, false, 'f' );
391
+ }
392
+ }
393
+
394
+ aioseo()->sitemap->file->generate( true );
395
+ }
396
+ }
397
+ }
app/Common/Migration/SocialMeta.php ADDED
@@ -0,0 +1,485 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Migration;
3
+
4
+ use AIOSEO\Plugin\Common\Models;
5
+
6
+ // phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.AssociativeArrayFound
7
+
8
+ /**
9
+ * Migrates the Social Meta settings from V3.
10
+ *
11
+ * @since 4.0.0
12
+ */
13
+ class SocialMeta {
14
+
15
+ /**
16
+ * The old V3 options.
17
+ *
18
+ * @since 4.0.0
19
+ *
20
+ * @var array
21
+ */
22
+ protected $oldOptions = [];
23
+
24
+ /**
25
+ * Class constructor.
26
+ *
27
+ * @since 4.0.0
28
+ */
29
+ public function __construct() {
30
+ $this->oldOptions = aioseo()->migration->oldOptions;
31
+
32
+ if ( empty( $this->oldOptions['modules']['aiosp_opengraph_options'] ) ) {
33
+ return;
34
+ }
35
+
36
+ $this->migrateHomePageOgTitle();
37
+ $this->migrateHomePageOgDescription();
38
+ $this->migrateTwitterUsername();
39
+ $this->migrateTwitterCardType();
40
+ $this->migrateSocialPostImageSettings();
41
+ $this->migrateDefaultObjectTypes();
42
+ $this->migrateAdvancedSettings();
43
+ $this->migrateProfileSocialUrls();
44
+
45
+ if ( ! empty( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_sitename'] ) ) {
46
+ aioseo()->options->social->facebook->general->siteName = aioseo()->helpers->sanitizeOption( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_sitename'] );
47
+ }
48
+
49
+ $settings = [
50
+ 'aiosp_opengraph_facebook_author' => [ 'type' => 'boolean', 'newOption' => [ 'social', 'facebook', 'general', 'showAuthor' ] ],
51
+ 'aiosp_opengraph_twitter_creator' => [ 'type' => 'boolean', 'newOption' => [ 'social', 'twitter', 'general', 'showAuthor' ] ],
52
+ ];
53
+
54
+ aioseo()->migration->helpers->mapOldToNew( $settings, $this->oldOptions['modules']['aiosp_opengraph_options'] );
55
+
56
+ $this->maybeShowOgNotices();
57
+ }
58
+
59
+ /**
60
+ * Check if we need to add a notice about the OG deprecated settings.
61
+ *
62
+ * @since 4.0.0
63
+ *
64
+ * @return void
65
+ */
66
+ private function maybeShowOgNotices() {
67
+ $include = [];
68
+
69
+ // Check if any of thw following are set to true.
70
+ if ( ! empty( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_generate_descriptions'] ) ) {
71
+ $include[] = __( 'Use Content for Autogenerated Descriptions', 'all-in-one-seo-pack' );
72
+ }
73
+
74
+ if ( ! empty( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_description_shortcodes'] ) ) {
75
+ $include[] = __( 'Run Shortcodes in Description', 'all-in-one-seo-pack' );
76
+ }
77
+
78
+ if ( ! empty( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_title_shortcodes'] ) ) {
79
+ $include[] = __( 'Run Shortcodes in Title', 'all-in-one-seo-pack' );
80
+ }
81
+
82
+ if ( empty( $include ) ) {
83
+ return;
84
+ }
85
+
86
+ $content = __( 'Due to some changes in how our Open Graph integration works, your Facebook Titles and Descriptions may have changed. You were using the following options that have been removed:', 'all-in-one-seo-pack' ) . '<ul>'; // phpcs:ignore Generic.Files.LineLength.MaxExceeded
87
+
88
+ foreach ( $include as $setting ) {
89
+ $content .= '<li><strong>' . $setting . '</strong></li>';
90
+ }
91
+
92
+ $content .= '</ul>';
93
+
94
+ $notification = Models\Notification::getNotificationByName( 'v3-migration-deprecated-opengraph' );
95
+ if ( $notification->notification_name ) {
96
+ return;
97
+ }
98
+
99
+ Models\Notification::addNotification( [
100
+ 'slug' => uniqid(),
101
+ 'notification_name' => 'v3-migration-deprecated-opengraph',
102
+ 'title' => __( 'Review Your Facebook Open Graph Titles and Descriptions', 'all-in-one-seo-pack' ),
103
+ 'content' => $content,
104
+ 'type' => 'warning',
105
+ 'level' => [ 'all' ],
106
+ 'button1_label' => __( 'Learn More', 'all-in-one-seo-pack' ),
107
+ 'button1_action' => AIOSEO_MARKETING_URL . '/docs/deprecated-opengraph-settings',
108
+ 'start' => gmdate( 'Y-m-d H:i:s' )
109
+ ] );
110
+ }
111
+
112
+ /**
113
+ * Migrates the Open Graph homepage title.
114
+ *
115
+ * @since 4.0.0
116
+ *
117
+ * @return void
118
+ */
119
+ private function migrateHomePageOgTitle() {
120
+ $showOnFront = get_option( 'show_on_front' );
121
+ $pageOnFront = (int) get_option( 'page_on_front' );
122
+ $useHomePageMeta = ! empty( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_setmeta'] );
123
+ $format = $this->oldOptions['aiosp_home_page_title_format'];
124
+
125
+ // Latest Posts.
126
+ if ( 'posts' === $showOnFront ) {
127
+ $ogTitle = preg_replace( '#%page_title%#', '#site_title', $format );
128
+ if ( ! $useHomePageMeta ) {
129
+ if ( ! empty( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_hometitle'] ) ) {
130
+ $ogTitle = $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_hometitle'];
131
+ }
132
+ aioseo()->options->social->facebook->homePage->title = aioseo()->helpers->sanitizeOption( aioseo()->migration->helpers->macrosToSmartTags( $ogTitle ) );
133
+ aioseo()->options->social->twitter->homePage->title = aioseo()->helpers->sanitizeOption( aioseo()->migration->helpers->macrosToSmartTags( $ogTitle ) );
134
+ return;
135
+ }
136
+ $title = aioseo()->options->searchAppearance->global->siteTitle;
137
+ $ogTitle = $title ? $title : $ogTitle;
138
+ aioseo()->options->social->facebook->homePage->title = aioseo()->helpers->sanitizeOption( $ogTitle );
139
+ aioseo()->options->social->twitter->homePage->title = aioseo()->helpers->sanitizeOption( $ogTitle );
140
+ return;
141
+ }
142
+
143
+ // Static Home Page.
144
+ $post = 'page' === $showOnFront && $pageOnFront ? aioseo()->helpers->getPost( $pageOnFront ) : '';
145
+ $aioseoPost = Models\Post::getPost( $post->ID );
146
+ $seoTitle = get_post_meta( $post->ID, '_aioseop_title', true );
147
+ $ogMeta = get_post_meta( $post->ID, '_aioseop_opengraph_settings', true );
148
+
149
+ if ( ! $ogMeta ) {
150
+ return;
151
+ }
152
+
153
+ $ogMeta = maybe_unserialize( $ogMeta );
154
+
155
+ $ogTitle = '';
156
+ if ( ! $useHomePageMeta ) {
157
+ if ( empty( $this->oldOptions['aiosp_use_static_home_info'] ) ) {
158
+ $ogTitle = ! empty( $this->oldOptions['aiosp_home_title'] ) ? $this->oldOptions['aiosp_home_title'] : $ogTitle;
159
+ if ( ! empty( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_hometitle'] ) ) {
160
+ $ogTitle = $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_hometitle'];
161
+ }
162
+ if ( ! empty( $ogMeta['aioseop_opengraph_settings_title'] ) ) {
163
+ $ogTitle = $ogMeta['aioseop_opengraph_settings_title'];
164
+ } elseif ( ! empty( $seoTitle ) ) {
165
+ if ( empty( $ogTitle ) ) {
166
+ $ogTitle = $seoTitle;
167
+ } elseif ( empty( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_hometitle'] ) ) {
168
+ $ogTitle = $seoTitle;
169
+ }
170
+ }
171
+ }
172
+ } else {
173
+ if ( empty( $this->oldOptions['aiosp_use_static_home_info'] ) ) {
174
+ $ogTitle = $aioseoPost->title;
175
+ if ( ! empty( $ogMeta['aioseop_opengraph_settings_title'] ) ) {
176
+ $ogTitle = $ogMeta['aioseop_opengraph_settings_title'];
177
+ }
178
+ $ogTitle = ! empty( $this->oldOptions['aiosp_home_title'] ) ? $this->oldOptions['aiosp_home_title'] : $ogTitle;
179
+ if ( ! empty( $seoTitle ) ) {
180
+ $ogTitle = $seoTitle;
181
+ }
182
+ } else {
183
+ $ogTitle = ! empty( $seoTitle ) ? $seoTitle : $ogTitle;
184
+ }
185
+ }
186
+
187
+ $ogTitle = aioseo()->helpers->sanitizeOption( aioseo()->migration->helpers->macrosToSmartTags( $ogTitle ) );
188
+ $aioseoPost->set( [
189
+ 'post_id' => $post->ID,
190
+ 'og_title' => $ogTitle,
191
+ 'twitter_title' => $ogTitle
192
+ ] );
193
+ $aioseoPost->save();
194
+ }
195
+
196
+ /**
197
+ * Migrates the Open Graph homepage description.
198
+ *
199
+ * @since 4.0.0
200
+ *
201
+ * @return void
202
+ */
203
+ private function migrateHomePageOgDescription() {
204
+ $showOnFront = get_option( 'show_on_front' );
205
+ $pageOnFront = (int) get_option( 'page_on_front' );
206
+ $useHomePageMeta = ! empty( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_setmeta'] );
207
+ $format = $this->oldOptions['aiosp_description_format'];
208
+
209
+ if ( 'posts' === $showOnFront ) {
210
+ $ogDescription = preg_replace( '#%description%#', '#tagline', $format );
211
+ if ( ! $useHomePageMeta ) {
212
+ if ( ! empty( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_description'] ) ) {
213
+ $ogDescription = $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_description'];
214
+ }
215
+ aioseo()->options->social->facebook->homePage->description = aioseo()->helpers->sanitizeOption( aioseo()->migration->helpers->macrosToSmartTags( $ogDescription ) );
216
+ aioseo()->options->social->twitter->homePage->description = aioseo()->helpers->sanitizeOption( aioseo()->migration->helpers->macrosToSmartTags( $ogDescription ) );
217
+ return;
218
+ }
219
+ $description = aioseo()->options->searchAppearance->global->metaDescription;
220
+ $ogDescription = $description ? $description : $ogDescription;
221
+ aioseo()->options->social->facebook->homePage->description = aioseo()->helpers->sanitizeOption( $ogDescription );
222
+ aioseo()->options->social->twitter->homePage->description = aioseo()->helpers->sanitizeOption( $ogDescription );
223
+ return;
224
+ }
225
+
226
+ $post = 'page' === $showOnFront && $pageOnFront ? aioseo()->helpers->getPost( $pageOnFront ) : '';
227
+ $aioseoPost = Models\Post::getPost( $post->ID );
228
+ $seoDescription = get_post_meta( $post->ID, '_aioseop_description', true );
229
+ $ogMeta = get_post_meta( $post->ID, '_aioseop_opengraph_settings', true );
230
+
231
+ if ( ! $ogMeta ) {
232
+ return;
233
+ }
234
+
235
+ $ogMeta = maybe_unserialize( $ogMeta );
236
+
237
+ $ogDescription = '';
238
+ if ( ! $useHomePageMeta ) {
239
+ if ( empty( $this->oldOptions['aiosp_use_static_home_info'] ) ) {
240
+ $ogDescription = ! empty( $this->oldOptions['aiosp_home_description'] ) ? $this->oldOptions['aiosp_home_description'] : $ogDescription;
241
+ if ( ! empty( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_description'] ) ) {
242
+ $ogDescription = $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_description'];
243
+ }
244
+ if ( ! empty( $ogMeta['aioseop_opengraph_settings_desc'] ) ) {
245
+ $ogDescription = $ogMeta['aioseop_opengraph_settings_desc'];
246
+ } elseif ( ! empty( $seoDescription ) ) {
247
+ if ( empty( $ogDescription ) ) {
248
+ $ogDescription = $seoDescription;
249
+ } elseif ( empty( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_description'] ) ) {
250
+ $ogDescription = $seoDescription;
251
+ }
252
+ }
253
+ }
254
+ } else {
255
+ if ( empty( $this->oldOptions['aiosp_use_static_home_info'] ) ) {
256
+ $ogDescription = $aioseoPost->description;
257
+ if ( ! empty( $ogMeta['aioseop_opengraph_settings_desc'] ) ) {
258
+ $ogDescription = $ogMeta['aioseop_opengraph_settings_desc'];
259
+ }
260
+ $ogDescription = ! empty( $this->oldOptions['aiosp_home_description'] ) ? $this->oldOptions['aiosp_home_description'] : $ogDescription;
261
+ if ( ! empty( $seoDescription ) ) {
262
+ $ogDescription = $seoDescription;
263
+ }
264
+ } else {
265
+ $ogDescription = ! empty( $seoDescription ) ? $seoDescription : $ogDescription;
266
+ }
267
+ }
268
+
269
+ $ogDescription = aioseo()->helpers->sanitizeOption( aioseo()->migration->helpers->macrosToSmartTags( $ogDescription ) );
270
+ $aioseoPost->set( [
271
+ 'post_id' => $post->ID,
272
+ 'og_description' => $ogDescription,
273
+ 'twitter_description' => $ogDescription
274
+ ] );
275
+ $aioseoPost->save();
276
+ }
277
+
278
+ /**
279
+ * Migrates the Open Graph default post images.
280
+ *
281
+ * @since 4.0.0
282
+ *
283
+ * @return void
284
+ */
285
+ private function migrateSocialPostImageSettings() {
286
+ if ( ! empty( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_homeimage'] ) ) {
287
+ $value = esc_url( wp_strip_all_tags( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_homeimage'] ) );
288
+ aioseo()->options->social->facebook->homePage->image = $value;
289
+ aioseo()->options->social->twitter->homePage->image = $value;
290
+ }
291
+
292
+ if ( ! empty( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_defimg'] ) ) {
293
+ $value = aioseo()->helpers->sanitizeOption( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_defimg'] );
294
+ aioseo()->options->social->facebook->general->defaultImageSourcePosts = $value;
295
+ aioseo()->options->social->twitter->general->defaultImageSourcePosts = $value;
296
+ }
297
+
298
+ if (
299
+ ! empty( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_dimg'] ) &&
300
+ ! preg_match( '/default-user-image.png$/', $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_dimg'] )
301
+ ) {
302
+ $value = esc_url( wp_strip_all_tags( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_dimg'] ) );
303
+ aioseo()->options->social->facebook->general->defaultImagePosts = $value;
304
+ aioseo()->options->social->twitter->general->defaultImagePosts = $value;
305
+ } else {
306
+ aioseo()->options->social->facebook->general->defaultImagePosts = '';
307
+ aioseo()->options->social->twitter->general->defaultImagePosts = '';
308
+ }
309
+
310
+ if (
311
+ ! empty( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_dimgwidth'] ) ||
312
+ ! empty( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_dimgheight'] )
313
+ ) {
314
+ aioseo()->options->social->facebook->general->defaultImageWidthPosts =
315
+ aioseo()->helpers->sanitizeOption( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_dimgwidth'] );
316
+ aioseo()->options->social->facebook->general->defaultImageHeightPosts =
317
+ aioseo()->helpers->sanitizeOption( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_dimgheight'] );
318
+ }
319
+
320
+ if ( ! empty( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_meta_key'] ) ) {
321
+ $value = aioseo()->helpers->sanitizeOption( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_meta_key'] );
322
+ aioseo()->options->social->facebook->general->customFieldImagePosts = $value;
323
+ aioseo()->options->social->twitter->general->customFieldImagePosts = $value;
324
+ }
325
+ }
326
+
327
+ /**
328
+ * Migrates the Twitter username.
329
+ *
330
+ * @since 4.0.0
331
+ *
332
+ * @return void
333
+ */
334
+ private function migrateTwitterUsername() {
335
+ if (
336
+ ! empty( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_twitter_site'] ) &&
337
+ ! aioseo()->options->social->profiles->urls->twitterUrl
338
+ ) {
339
+ $username = ltrim( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_twitter_site'], '@' );
340
+ aioseo()->options->social->profiles->urls->twitterUrl =
341
+ esc_url( 'https://twitter.com/' . aioseo()->social->twitter->prepareUsername( aioseo()->helpers->sanitizeOption( $username ), false ) );
342
+ }
343
+ }
344
+
345
+ /**
346
+ * Migrates the Twitter card type.
347
+ *
348
+ * @since 4.0.0
349
+ *
350
+ * @return void
351
+ */
352
+ private function migrateTwitterCardType() {
353
+ if ( ! empty( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_defcard'] ) ) {
354
+ aioseo()->options->social->twitter->general->defaultCardType =
355
+ aioseo()->helpers->sanitizeOption( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_defcard'] );
356
+ aioseo()->options->social->twitter->homePage->cardType =
357
+ aioseo()->helpers->sanitizeOption( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_defcard'] );
358
+ }
359
+ }
360
+
361
+ /**
362
+ * Migrates the default object types.
363
+ *
364
+ * @since 4.0.0
365
+ *
366
+ * @return void
367
+ */
368
+ private function migrateDefaultObjectTypes() {
369
+ foreach ( aioseo()->helpers->getPublicPostTypes( true ) as $postType ) {
370
+ $settingName = "aiosp_opengraph_{$postType}_fb_object_type";
371
+ if ( ! in_array( $settingName, array_keys( $this->oldOptions['modules']['aiosp_opengraph_options'] ), true ) ) {
372
+ continue;
373
+ }
374
+
375
+ $options = aioseo()->options->noConflict();
376
+ if ( $options->social->facebook->general->dynamic->postTypes->has( $postType ) ) {
377
+ aioseo()->options->social->facebook->general->dynamic->postTypes->$postType->objectType =
378
+ aioseo()->helpers->sanitizeOption( $this->oldOptions['modules']['aiosp_opengraph_options'][ $settingName ] );
379
+ }
380
+
381
+ if ( 'post' === $postType ) {
382
+ aioseo()->options->social->facebook->homePage->objectType =
383
+ aioseo()->helpers->sanitizeOption( $this->oldOptions['modules']['aiosp_opengraph_options'][ $settingName ] );
384
+ }
385
+ }
386
+ }
387
+
388
+ /**
389
+ * Migrates a number of advanced settings.
390
+ *
391
+ * @since 4.0.0
392
+ *
393
+ * @return void
394
+ */
395
+ private function migrateAdvancedSettings() {
396
+ $advancedEnabled = false;
397
+
398
+ if ( ! empty( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_key'] ) ) {
399
+ $advancedEnabled = true;
400
+ aioseo()->options->social->facebook->advanced->adminId = aioseo()->helpers->sanitizeOption( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_key'] );
401
+ }
402
+
403
+ if ( ! empty( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_appid'] ) ) {
404
+ $advancedEnabled = true;
405
+ aioseo()->options->social->facebook->advanced->appId = aioseo()->helpers->sanitizeOption( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_appid'] );
406
+ }
407
+
408
+ if ( ! empty( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_gen_tags'] ) ) {
409
+ $advancedEnabled = true;
410
+ aioseo()->options->social->facebook->advanced->generateArticleTags = true;
411
+ } else {
412
+ aioseo()->options->social->facebook->advanced->generateArticleTags = false;
413
+ }
414
+
415
+ if ( ! empty( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_gen_keywords'] ) ) {
416
+ $advancedEnabled = true;
417
+ aioseo()->options->social->facebook->advanced->useKeywordsInTags = true;
418
+ } else {
419
+ aioseo()->options->social->facebook->advanced->useKeywordsInTags = false;
420
+ }
421
+
422
+ if ( ! empty( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_gen_categories'] ) ) {
423
+ $advancedEnabled = true;
424
+ aioseo()->options->social->facebook->advanced->useCategoriesInTags = true;
425
+ } else {
426
+ aioseo()->options->social->facebook->advanced->useCategoriesInTags = false;
427
+ }
428
+
429
+ if ( ! empty( $this->oldOptions['modules']['aiosp_opengraph_options']['aiosp_opengraph_gen_post_tags'] ) ) {
430
+ $advancedEnabled = true;
431
+ aioseo()->options->social->facebook->advanced->usePostTagsInTags = true;
432
+ } else {
433
+ aioseo()->options->social->facebook->advanced->usePostTagsInTags = false;
434
+ }
435
+
436
+ aioseo()->options->social->facebook->advanced->enable = $advancedEnabled;
437
+ }
438
+
439
+ /**
440
+ * Migrates the social URLs for the author users.
441
+ *
442
+ * @since 4.0.0
443
+ *
444
+ * @return void
445
+ */
446
+ private function migrateProfileSocialUrls() {
447
+ $records = aioseo()->db
448
+ ->start( 'usermeta' )
449
+ ->select( '*' )
450
+ ->where( 'meta_key', 'facebook' )
451
+ ->run()
452
+ ->result();
453
+
454
+ if ( count( $records ) ) {
455
+ foreach ( $records as $record ) {
456
+ if ( ! empty( $record->user_id ) && ! empty( $record->meta_value ) ) {
457
+ update_user_meta(
458
+ (int) $record->user_id,
459
+ 'aioseo_facebook',
460
+ esc_url( $record->meta_value )
461
+ );
462
+ }
463
+ }
464
+ }
465
+
466
+ $records = aioseo()->db
467
+ ->start( 'usermeta' )
468
+ ->select( '*' )
469
+ ->where( 'meta_key', 'twitter' )
470
+ ->run()
471
+ ->result();
472
+
473
+ if ( count( $records ) ) {
474
+ foreach ( $records as $record ) {
475
+ if ( ! empty( $record->user_id ) && ! empty( $record->meta_value ) ) {
476
+ update_user_meta(
477
+ (int) $record->user_id,
478
+ 'aioseo_twitter',
479
+ sanitize_text_field( $record->meta_value )
480
+ );
481
+ }
482
+ }
483
+ }
484
+ }
485
+ }
app/Common/Migration/Wpml.php ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Migration;
3
+
4
+ /**
5
+ * Migrates the WPML settings from V3.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class Wpml {
10
+ /**
11
+ * Class constructor.
12
+ *
13
+ * @since 4.0.0
14
+ */
15
+ public function __construct() {
16
+ // If the tables don't exist (could happen), return early.
17
+ if ( ! aioseo()->db->tableExists( 'icl_strings' ) && ! aioseo()->db->tableExists( 'icl_string_translations' ) ) {
18
+ return;
19
+ }
20
+
21
+ $strings = [
22
+ '[aioseop_options]aiosp_home_title' => '[aioseo_options_localized]searchAppearance_global_siteTitle',
23
+ '[aioseop_options]aiosp_home_description' => '[aioseo_options_localized]searchAppearance_global_metaDescription',
24
+ '[aioseop_options]aiosp_home_keywords' => '[aioseo_options_localized]searchAppearance_global_keywords'
25
+ ];
26
+
27
+ try {
28
+ $v3Results = aioseo()->db->start( 'icl_strings' )
29
+ ->where( 'context', 'admin_texts_aioseop_options' )
30
+ ->whereIn( 'name', array_keys( $strings ) )
31
+ ->run()
32
+ ->result();
33
+
34
+ $v4Results = aioseo()->db->start( 'icl_strings' )
35
+ ->where( 'context', 'admin_texts_aioseo_options_localized' )
36
+ ->whereIn( 'name', array_values( $strings ) )
37
+ ->run()
38
+ ->result();
39
+
40
+ if ( ! empty( $v3Results ) ) {
41
+ foreach ( $v3Results as $result ) {
42
+ $translations = aioseo()->db->start( 'icl_string_translations' )
43
+ ->where( 'string_id', $result->id )
44
+ ->run()
45
+ ->result();
46
+
47
+ if ( empty( $translations ) ) {
48
+ continue;
49
+ }
50
+
51
+ $v4ResultId = null;
52
+ if ( ! empty( $v4Results ) ) {
53
+ foreach ( $v4Results as $r ) {
54
+ if ( $r->name === $strings[ $result->name ] ) {
55
+ $v4ResultId = $r->id;
56
+ break;
57
+ }
58
+ }
59
+ }
60
+
61
+ if ( ! $v4ResultId ) {
62
+ $v4ResultId = aioseo()->db
63
+ ->insert( 'icl_strings' )
64
+ ->set( [
65
+ 'language' => $result->language,
66
+ 'context' => 'admin_texts_aioseo_options_localized',
67
+ 'name' => $strings[ $result->name ],
68
+ 'value' => $result->value,
69
+ 'string_package_id' => $result->string_package_id,
70
+ 'location' => $result->location,
71
+ 'wrap_tag' => $result->wrap_tag,
72
+ 'type' => $result->type,
73
+ 'title' => $result->title,
74
+ 'status' => $result->status,
75
+ 'gettext_context' => $result->gettext_context,
76
+ 'domain_name_context_md5' => md5( 'admin_texts_aioseo_options_localized' . $strings[ $result->name ] ),
77
+ 'translation_priority' => $result->translation_priority,
78
+ 'word_count' => $result->word_count
79
+ ] )
80
+ ->run()
81
+ ->insertId();
82
+ }
83
+
84
+ foreach ( $translations as $translation ) {
85
+ // Check if the translation exists first or we'll get a DB error.
86
+ $v4Translation = aioseo()->db->start( 'icl_string_translations' )
87
+ ->where( 'string_id', $v4ResultId )
88
+ ->where( 'language', $translation->language )
89
+ ->run()
90
+ ->result();
91
+
92
+ if ( ! empty( $v4Translation ) ) {
93
+ aioseo()->db->update( 'icl_string_translations' )
94
+ ->where( 'string_id', $v4ResultId )
95
+ ->where( 'language', $translation->language )
96
+ ->set( [
97
+ 'value' => $translation->value
98
+ ] )
99
+ ->run();
100
+ continue;
101
+ }
102
+
103
+ aioseo()->db
104
+ ->insert( 'icl_string_translations' )
105
+ ->set( [
106
+ 'string_id' => $v4ResultId,
107
+ 'language' => $translation->language,
108
+ 'status' => $translation->status,
109
+ 'value' => $translation->value,
110
+ 'mo_string' => $translation->mo_string,
111
+ 'translator_id' => $translation->translator_id,
112
+ 'translation_service' => $translation->translation_service,
113
+ 'batch_id' => $translation->batch_id,
114
+ 'translation_date' => $translation->translation_date
115
+ ] )
116
+ ->run();
117
+ }
118
+ }
119
+ }
120
+ } catch ( \Exception $e ) {
121
+ // If there are any errors, let's just abort. We dont' want to do anything more.
122
+ }
123
+ }
124
+ }
app/Common/Models/Model.php ADDED
@@ -0,0 +1,442 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Models;
3
+
4
+ /**
5
+ * Base Model class.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class Model implements \JsonSerializable {
10
+ /**
11
+ * Fields that should be null when saving to the database.
12
+ *
13
+ * @since 4.0.0
14
+ *
15
+ * @var array
16
+ */
17
+ protected $nullFields = [];
18
+
19
+ /**
20
+ * Fields that should be encoded/decoded on save/get.
21
+ *
22
+ * @since 4.0.0
23
+ *
24
+ * @var array
25
+ */
26
+ protected $jsonFields = [];
27
+
28
+ /**
29
+ * Fields that should be boolean values.
30
+ *
31
+ * @since 4.0.0
32
+ *
33
+ * @var array
34
+ */
35
+ protected $booleanFields = [];
36
+
37
+ /**
38
+ * Fields that should be hidden when serialized.
39
+ *
40
+ * @since 4.0.0
41
+ *
42
+ * @var array
43
+ */
44
+ protected $hidden = [];
45
+
46
+ /**
47
+ * The table used in the SQL query.
48
+ *
49
+ * @since 4.0.0
50
+ *
51
+ * @var string
52
+ */
53
+ protected $table = '';
54
+
55
+ /**
56
+ * The primary key retrieved from the table.
57
+ *
58
+ * @since 4.0.0
59
+ *
60
+ * @var string
61
+ */
62
+ protected $pk = 'id';
63
+
64
+ /**
65
+ * An array of columns from the DB that we can use.
66
+ *
67
+ * @since 4.0.0
68
+ *
69
+ * @var array
70
+ */
71
+ private static $columns;
72
+
73
+ /**
74
+ * Class constructor.
75
+ *
76
+ * @since 4.0.0
77
+ *
78
+ * @param mixed $var This can be the primary key of the resource, or it could be an array of data to manufacture a resource without a database query.
79
+ */
80
+ public function __construct( $var = null ) {
81
+ $skip = [ 'id', 'created', 'updated' ];
82
+ $fields = [];
83
+ foreach ( $this->getColumns() as $column => $value ) {
84
+ if ( ! in_array( $column, $skip, true ) ) {
85
+ $fields[ $column ] = $value;
86
+ }
87
+ }
88
+
89
+ $this->applyKeys( $fields );
90
+
91
+ // Process straight through if we were given a valid array.
92
+ if ( is_array( $var ) || is_object( $var ) ) {
93
+ // Apply keys to object.
94
+ $this->applyKeys( $var );
95
+
96
+ if ( $this->exists() ) {
97
+ return true;
98
+ }
99
+
100
+ return false;
101
+ }
102
+
103
+ return $this->loadData( $var );
104
+ }
105
+
106
+ /**
107
+ * Load the data from the database!
108
+ *
109
+ * @since 4.0.0
110
+ *
111
+ * @param mixed $var Generally the primary key to load up the model from the DB.
112
+ *
113
+ * @return BaseModelResource|bool Returns the current object.
114
+ */
115
+ protected function loadData( $var = null ) {
116
+ // Return false if var is invalid or not supplied.
117
+ if ( null === $var ) {
118
+ return false;
119
+ }
120
+
121
+ $query = aioseo()->db
122
+ ->start( $this->table )
123
+ ->where( $this->pk, $var )
124
+ ->limit( 1 )
125
+ ->output( 'ARRAY_A' );
126
+
127
+ $result = $query->run();
128
+ if ( ! $result || $result->nullSet() ) {
129
+ return $this;
130
+ }
131
+
132
+ // Apply keys to object.
133
+ $this->applyKeys( $result->result()[0] );
134
+
135
+ return $this;
136
+ }
137
+
138
+ /**
139
+ * Take the keys from the result array and add them to the Model.
140
+ *
141
+ * @since 4.0.0
142
+ *
143
+ * @param array $array The array of keys and values to add to the Model.
144
+ * @return void
145
+ */
146
+ protected function applyKeys( $array ) {
147
+ if ( ! is_object( $array ) && ! is_array( $array ) ) {
148
+ throw new \Exception( '$array must either be an object or an array.' );
149
+ }
150
+
151
+ foreach ( (array) $array as $key => $value ) {
152
+ trim( $key );
153
+ $this->$key = in_array( $key, $this->jsonFields, true )
154
+ ? json_decode( $value )
155
+ : (
156
+ in_array( $key, $this->booleanFields, true )
157
+ ? (bool) $value
158
+ : $value
159
+ );
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Let's filter out any properties we cannot save to the database.
165
+ *
166
+ * @since 4.0.0
167
+ *
168
+ * @param string $key The table column.
169
+ * @param string $table The table we are looking in.
170
+ * @return array The array of valid columns for the database query.
171
+ */
172
+ protected function filter( $key ) {
173
+ $table = aioseo()->db->prefix . $this->table;
174
+ $results = aioseo()->db->execute( 'SHOW COLUMNS FROM `' . $table . '`', true );
175
+ $fields = [];
176
+ $skip = [ 'created', 'updated' ];
177
+ $columns = $results->result();
178
+
179
+ foreach ( $columns as $col ) {
180
+ if ( ! in_array( $col->Field, $skip, true ) && array_key_exists( $col->Field, $key ) ) {
181
+ $fields[ $col->Field ] = $key[ $col->Field ];
182
+ }
183
+ }
184
+
185
+ return $fields;
186
+ }
187
+
188
+ /**
189
+ * Transforms the data to be null if it exists in the nullFields variables.
190
+ *
191
+ * @since 4.0.0
192
+ *
193
+ * @param array $data The data array to transform.
194
+ * @return array The transformed data.
195
+ */
196
+ protected function transform( $data, $set = false ) {
197
+ foreach ( $this->nullFields as $field ) {
198
+ if ( isset( $data[ $field ] ) && empty( $data[ $field ] ) ) {
199
+ $data[ $field ] = null;
200
+ }
201
+ }
202
+
203
+ foreach ( $this->booleanFields as $field ) {
204
+ if ( isset( $data[ $field ] ) && '' === $data[ $field ] ) {
205
+ unset( $data[ $field ] );
206
+ } elseif ( isset( $data[ $field ] ) ) {
207
+ $data[ $field ] = (bool) $data[ $field ];
208
+ }
209
+ }
210
+
211
+ if ( $set ) {
212
+ return $data;
213
+ }
214
+
215
+ foreach ( $this->jsonFields as $field ) {
216
+ if ( isset( $data[ $field ] ) && ! empty( $data[ $field ] ) ) {
217
+ $data[ $field ] = wp_json_encode( $data[ $field ] );
218
+ }
219
+ }
220
+
221
+ return $data;
222
+ }
223
+
224
+ /**
225
+ * Sets a piece of data to the requested resource.
226
+ *
227
+ * @since 4.0.0
228
+ */
229
+ public function set() {
230
+ $args = func_get_args();
231
+ $count = func_num_args();
232
+
233
+ if ( ! is_array( $args[0] ) && $count < 2 ) {
234
+ throw new \Exception( 'The set method must contain at least 2 arguments: key and value. Or an array of data. Only one argument was passed and it was not an array.' );
235
+ }
236
+
237
+ $key = $args[0];
238
+ $value = ! empty( $args[1] ) ? $args[1] : null;
239
+
240
+ // Make sure we have a key.
241
+ if ( false === $key ) {
242
+ return false;
243
+ }
244
+
245
+ // If it's not an array, make it one.
246
+ if ( ! is_array( $key ) ) {
247
+ $key = [ $key => $value ];
248
+ }
249
+
250
+ // Preprocess data.
251
+ $key = $this->transform( $key, true );
252
+
253
+ // Save the items in this object.
254
+ foreach ( $key as $k => $v ) {
255
+ if ( ! empty( $k ) ) {
256
+ $this->$k = $v;
257
+ }
258
+ }
259
+ }
260
+
261
+ /**
262
+ * Delete the Model Resource itself.
263
+ *
264
+ * @since 4.0.0
265
+ *
266
+ * @return null
267
+ */
268
+ public function delete() {
269
+ aioseo()->db
270
+ ->delete( $this->table )
271
+ ->where( $this->pk, $this->id )
272
+ ->run();
273
+
274
+ return null;
275
+ }
276
+
277
+ /**
278
+ * Saves data to the requested resource.
279
+ *
280
+ * @since 4.0.0
281
+ */
282
+ public function save() {
283
+ $fields = $this->transform( $this->filter( (array) get_object_vars( $this ) ) );
284
+
285
+ $id = null;
286
+ if ( count( $fields ) > 0 ) {
287
+ $pk = $this->pk;
288
+
289
+ if ( isset( $this->$pk ) && '' !== $this->$pk ) {
290
+ // PK specified.
291
+ $pkv = $this->$pk;
292
+ $query = aioseo()->db
293
+ ->start( $this->table )
294
+ ->where( [ $pk => $pkv ] )
295
+ ->run();
296
+
297
+ if ( ! $query->nullSet() ) {
298
+ // Row exists in database.
299
+ $fields['updated'] = gmdate( 'Y-m-d H:i:s' );
300
+ aioseo()->db
301
+ ->update( $this->table )
302
+ ->set( $fields )
303
+ ->where( [ $pk => $pkv ] )
304
+ ->run();
305
+ $id = $this->$pk;
306
+ } else {
307
+ // Row does not exist.
308
+ $fields[ $pk ] = $pkv;
309
+ $fields['created'] = gmdate( 'Y-m-d H:i:s' );
310
+ $fields['updated'] = gmdate( 'Y-m-d H:i:s' );
311
+
312
+ $id = aioseo()->db
313
+ ->insert( $this->table )
314
+ ->set( $fields )
315
+ ->run()
316
+ ->insertId();
317
+
318
+ if ( $id ) {
319
+ $this->$pk = $id;
320
+ }
321
+ }
322
+ } else {
323
+ $fields['created'] = gmdate( 'Y-m-d H:i:s' );
324
+ $fields['updated'] = gmdate( 'Y-m-d H:i:s' );
325
+
326
+ $id = aioseo()->db
327
+ ->insert( $this->table )
328
+ ->set( $fields )
329
+ ->run()
330
+ ->insertId();
331
+
332
+ if ( $id ) {
333
+ $this->$pk = $id;
334
+ }
335
+ }
336
+ }
337
+
338
+ // Refresh the resource.
339
+ $this->reset( $id );
340
+ }
341
+
342
+ /**
343
+ * Check if the model exists in the database.
344
+ *
345
+ * @since 4.0.0
346
+ *
347
+ * @return bool If the model exists, true otherwise false.
348
+ */
349
+ public function exists() {
350
+ return ( ! empty( $this->{$this->pk} ) ) ? true : false;
351
+ }
352
+
353
+ /**
354
+ * Resets a resource by forcing internal updates to be applied.
355
+ *
356
+ * @since 4.0.0
357
+ *
358
+ * @param string $id The resource ID.
359
+ * @return void
360
+ */
361
+ public function reset( $id = null ) {
362
+ $id = ! empty( $id ) ? $id : $this->{$this->pk};
363
+ $this->__construct( $id );
364
+ }
365
+
366
+ /**
367
+ * Helper function to remove data we don't want serialized.
368
+ *
369
+ * @since 4.0.0
370
+ *
371
+ * @return array An array of data that we are okay with serializing.
372
+ */
373
+ public function jsonSerialize() {
374
+ $array = [];
375
+
376
+ foreach ( $this->getColumns() as $column => $value ) {
377
+ if ( in_array( $column, $this->hidden, true ) ) {
378
+ continue;
379
+ }
380
+
381
+ $array[ $column ] = ! empty( $this->$column ) ? $this->$column : null;
382
+ }
383
+
384
+ return $array;
385
+ }
386
+
387
+ /**
388
+ * Retrieves the columns for the model.
389
+ *
390
+ * @since 4.0.0
391
+ *
392
+ * @return array An array of columns.
393
+ */
394
+ public function getColumns() {
395
+ if ( empty( self::$columns[ get_called_class() ] ) ) {
396
+ self::$columns[ get_called_class() ] = [];
397
+
398
+ // Let's set the columns that are available by default.
399
+ $table = aioseo()->db->prefix . $this->table;
400
+ $results = aioseo()->db->execute( 'SHOW COLUMNS FROM `' . $table . '`', true );
401
+
402
+ foreach ( $results->result() as $col ) {
403
+ self::$columns[ get_called_class() ][ $col->Field ] = $col->Default;
404
+ }
405
+ }
406
+
407
+ return self::$columns[ get_called_class() ];
408
+ }
409
+
410
+ /**
411
+ * Returns a JSON object with default tabs options.
412
+ *
413
+ * @since 4.0.0
414
+ *
415
+ * @return string JSON object.
416
+ */
417
+ public static function getDefaultTabsOptions() {
418
+ return '{"tab":"general","tab_social":"facebook","tab_sidebar":"general","tab_modal":"general","tab_modal_social":"facebook"}';
419
+ }
420
+
421
+ /**
422
+ * Returns a JSON object with default schema options.
423
+ *
424
+ * @since 4.0.0
425
+ *
426
+ * @return string JSON object.
427
+ */
428
+ public static function getDefaultSchemaOptions() {
429
+ return '{"webPage":{"webPageType":"WebPage"},"article":{"articleType":"BlogPosting"},"book":{},"course":{},"event":{},"jobPosting":{},"music":{},"person":{},"product":{},"recipe":{},"restaurant":{},"service":{},"software":{},"video":{}}'; // phpcs:ignore Generic.Files.LineLength.MaxExceeded
430
+ }
431
+
432
+ /**
433
+ * Returns a JSON object with default local seo options.
434
+ *
435
+ * @since 4.0.0
436
+ *
437
+ * @return string JSON object.
438
+ */
439
+ public static function getDefaultLocalSeoOptions() {
440
+ return '{"businessInfo":{"name":"","urls":{"website":"","aboutPage":"","contactPage":""},"address":{"line1":"","line2":"","zip":"","city":"","state":"","country":""},"contact":{"email":"","phone":"","fax":""},"ids":{"vatID":"","taxID":"","chamberID":""},"payment":{"priceIndication":"","currenciesAccepted":"","methodsAccepted":""},"areaServed":""},"openingHours":{"show":false,"closedLabel":"","open24h":false,"open24hLabel":"","open247":false,"use24hFormat":false,"twoSets":false,"timezone":"","hours":{}}}'; // phpcs:ignore Generic.Files.LineLength.MaxExceeded
441
+ }
442
+ }
app/Common/Models/Notification.php ADDED
@@ -0,0 +1,224 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Models;
3
+
4
+ /**
5
+ * The Notification DB Model.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class Notification extends Model {
10
+ /**
11
+ * The name of the table in the database, without the prefix.
12
+ *
13
+ * @since 4.0.0
14
+ *
15
+ * @var string
16
+ */
17
+ protected $table = 'aioseo_notifications';
18
+
19
+ /**
20
+ * An array of fields to set to null if already empty when saving to the database.
21
+ *
22
+ * @since 4.0.0
23
+ *
24
+ * @var array
25
+ */
26
+ protected $nullFields = [
27
+ 'start',
28
+ 'end',
29
+ 'notification_id',
30
+ 'notification_name',
31
+ 'button1_label',
32
+ 'button1_action',
33
+ 'button2_label',
34
+ 'button2_action'
35
+ ];
36
+
37
+ /**
38
+ * Fields that should be json encoded on save and decoded on get.
39
+ *
40
+ * @since 4.0.0
41
+ *
42
+ * @var array
43
+ */
44
+ protected $jsonFields = [ 'level' ];
45
+
46
+ /**
47
+ * Fields that should be boolean values.
48
+ *
49
+ * @since 4.0.0
50
+ *
51
+ * @var array
52
+ */
53
+ protected $booleanFields = [ 'dismissed' ];
54
+
55
+ /**
56
+ * Fields that should be hidden when serialized.
57
+ *
58
+ * @var array
59
+ */
60
+ protected $hidden = [ 'id' ];
61
+
62
+ /**
63
+ * An array of fields attached to this resource.
64
+ *
65
+ * @since 4.0.0
66
+ *
67
+ * @var array
68
+ */
69
+ protected $columns = [
70
+ 'id',
71
+ 'slug',
72
+ 'title',
73
+ 'content',
74
+ 'type',
75
+ 'level',
76
+ 'notification_id',
77
+ 'notification_name',
78
+ 'start',
79
+ 'end',
80
+ 'button1_label',
81
+ 'button1_action',
82
+ 'button2_label',
83
+ 'button2_action',
84
+ 'dismissed',
85
+ 'created',
86
+ 'updated'
87
+ ];
88
+
89
+ /**
90
+ * Get an array of active notifications.
91
+ *
92
+ * @since 4.0.0
93
+ *
94
+ * @return array An array of active notifications.
95
+ */
96
+ public static function getAllActiveNotifications() {
97
+ return array_values( json_decode( wp_json_encode( self::getActiveNotifications() ), true ) );
98
+ }
99
+
100
+ /**
101
+ * Retrieve active notifications.
102
+ *
103
+ * @since 4.0.0
104
+ *
105
+ * @return array An array of active notifications or empty.
106
+ */
107
+ public static function getActiveNotifications() {
108
+ return self::filterNotifications(
109
+ aioseo()->db
110
+ ->start( 'aioseo_notifications' )
111
+ ->where( 'dismissed', 0 )
112
+ ->whereRaw( "(start <= '" . gmdate( 'Y-m-d H:i:s' ) . "' OR start IS NULL)" )
113
+ ->whereRaw( "(end >= '" . gmdate( 'Y-m-d H:i:s' ) . "' OR end IS NULL)" )
114
+ ->orderBy( 'start DESC' )
115
+ ->run()
116
+ ->models( 'AIOSEO\\Plugin\\Common\\Models\\Notification' )
117
+ );
118
+ }
119
+
120
+ /**
121
+ * Get an array of dismissed notifications.
122
+ *
123
+ * @since 4.0.0
124
+ *
125
+ * @return array An array of dismissed notifications.
126
+ */
127
+ public static function getAllDismissedNotifications() {
128
+ return array_values( json_decode( wp_json_encode( self::getDismissedNotifications() ), true ) );
129
+ }
130
+
131
+ /**
132
+ * Retrieve dismissed notifications.
133
+ *
134
+ * @since 4.0.0
135
+ *
136
+ * @return array An array of dismissed notifications or empty.
137
+ */
138
+ public static function getDismissedNotifications() {
139
+ return self::filterNotifications(
140
+ aioseo()->db
141
+ ->start( 'aioseo_notifications' )
142
+ ->where( 'dismissed', 1 )
143
+ ->orderBy( 'updated DESC' )
144
+ ->run()
145
+ ->models( 'AIOSEO\\Plugin\\Common\\Models\\Notification' )
146
+ );
147
+ }
148
+
149
+ /**
150
+ * Returns a notification by its name.
151
+ *
152
+ * @since 4.0.0
153
+ *
154
+ * @param string $name The notification name.
155
+ * @return Notification The notification.
156
+ */
157
+ public static function getNotificationByName( $name ) {
158
+ return aioseo()->db
159
+ ->start( 'aioseo_notifications' )
160
+ ->where( 'notification_name', $name )
161
+ ->run()
162
+ ->model( 'AIOSEO\\Plugin\\Common\\Models\\Notification' );
163
+ }
164
+
165
+ /**
166
+ * Stores a new notification in the DB.
167
+ *
168
+ * @since 4.0.0
169
+ *
170
+ * @param array $fields The fields.
171
+ * @return Notification $notification The notification.
172
+ */
173
+ public static function addNotification( $fields ) {
174
+ // Set the dismissed status to false.
175
+ $fields['dismissed'] = 0;
176
+
177
+ $notification = new self;
178
+ $notification->set( $fields );
179
+ $notification->save();
180
+ return $notification;
181
+ }
182
+
183
+ /**
184
+ * Deletes a notification by its name.
185
+ *
186
+ * @since 4.0.0
187
+ *
188
+ * @param string $name The notification name.
189
+ * @return void
190
+ */
191
+ public static function deleteNotificationByName( $name ) {
192
+ aioseo()->db
193
+ ->delete( 'aioseo_notifications' )
194
+ ->where( 'notification_name', $name )
195
+ ->run();
196
+ }
197
+
198
+ /**
199
+ * Filters the notifications based on the targeted plan levels.
200
+ *
201
+ * @since 4.0.0
202
+ *
203
+ * @param array $notifications The notifications
204
+ * @return array $remainingNotifications The remaining notifications.
205
+ */
206
+ public static function filterNotifications( $notifications ) {
207
+ $remainingNotifications = [];
208
+ foreach ( $notifications as $notification ) {
209
+ $levels = $notification->level;
210
+ if ( ! is_array( $levels ) ) {
211
+ $levels = empty( $notification->level ) ? [ 'all' ] : [ $notification->level ];
212
+ }
213
+
214
+ foreach ( $levels as $level ) {
215
+ if ( ! aioseo()->notices->validateType( $level ) ) {
216
+ continue 2;
217
+ }
218
+ }
219
+
220
+ $remainingNotifications[] = $notification;
221
+ }
222
+ return $remainingNotifications;
223
+ }
224
+ }
app/Common/Models/Post.php ADDED
@@ -0,0 +1,319 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Models;
3
+
4
+ /**
5
+ * The Post DB Model.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class Post extends Model {
10
+ /**
11
+ * The name of the table in the database, without the prefix.
12
+ *
13
+ * @since 4.0.0
14
+ *
15
+ * @var string
16
+ */
17
+ protected $table = 'aioseo_posts';
18
+
19
+ /**
20
+ * Fields that should be json encoded on save and decoded on get.
21
+ *
22
+ * @since 4.0.0
23
+ *
24
+ * @var array
25
+ */
26
+ protected $jsonFields = [ 'images', 'videos' ];
27
+
28
+ /**
29
+ * Fields that should be hidden when serialized.
30
+ *
31
+ * @since 4.0.0
32
+ *
33
+ * @var array
34
+ */
35
+ protected $hidden = [ 'id' ];
36
+
37
+ /**
38
+ * Class constructor.
39
+ *
40
+ * @since 4.0.0
41
+ *
42
+ * @param mixed $var This can be the primary key of the resource, or it could be an array of data to manufacture a resource without a database query.
43
+ */
44
+ public function __construct( $var = null ) {
45
+ parent::__construct( $var );
46
+
47
+ // Duplicate Post integration.
48
+ add_action( 'dp_duplicate_post', [ $this, 'duplicatePostIntegration' ], 10, 3 );
49
+ }
50
+
51
+ /**
52
+ * Duplicates the model when duplicate post is triggered.
53
+ *
54
+ * @since 4.0.0
55
+ *
56
+ * @param integer $newPostId The new Post ID.
57
+ * @param WP_Post $originalPost The original post object.
58
+ * @param string $status The status of the post.
59
+ * @return void
60
+ */
61
+ public function duplicatePostIntegration( $newPostId, $originalPost, $status ) {
62
+ $originalAioseoPost = self::getPost( $originalPost->ID );
63
+ if ( ! $originalAioseoPost->exists() ) {
64
+ return;
65
+ }
66
+
67
+ $newPost = self::getPost( $newPostId );
68
+ if ( $newPost->exists() ) {
69
+ return;
70
+ }
71
+
72
+ $columns = $originalAioseoPost->getColumns();
73
+ foreach ( $columns as $column => $value ) {
74
+ // Skip the ID columns.
75
+ if ( 'id' === $column || 'post_id' === $column ) {
76
+ continue;
77
+ }
78
+
79
+ if ( 'post_id' === $column ) {
80
+ $newPost->$column = $newPostId;
81
+ continue;
82
+ }
83
+
84
+ $newPost->$column = $originalAioseoPost->$column;
85
+ }
86
+
87
+ $newPost->save();
88
+ }
89
+
90
+ /**
91
+ * Returns a Post with a given ID.
92
+ *
93
+ * @since 4.0.0
94
+ *
95
+ * @param int $postId The Post ID.
96
+ * @return Post The Post object.
97
+ */
98
+ public static function getPost( $postId ) {
99
+ return aioseo()->db
100
+ ->start( 'aioseo_posts' )
101
+ ->where( 'post_id', $postId )
102
+ ->run()
103
+ ->model( 'AIOSEO\\Plugin\\Common\\Models\\Post' );
104
+ }
105
+
106
+ /**
107
+ * Saves Post AIOSEO settings.
108
+ *
109
+ * @since 4.0.3
110
+ *
111
+ * @param int $postId The Post ID.
112
+ * @param array $data The post data to save.
113
+ * @return bool|WP_REST_Response True if post saved or Response Error.
114
+ */
115
+ public static function savePost( $postId, $data ) {
116
+ $thePost = aioseo()->db
117
+ ->start( 'aioseo_posts' )
118
+ ->where( 'post_id', $postId )
119
+ ->run()
120
+ ->model( 'AIOSEO\\Plugin\\Common\\Models\\Post' );
121
+
122
+ $post = aioseo()->helpers->getPost( $postId );
123
+
124
+ // Reset title/descriptions if they are the same as the defaults.
125
+ if ( $thePost->exists() ) {
126
+ $metaTitle = aioseo()->meta->title->getPostTypeTitle( $post->post_type );
127
+ $metaDescription = aioseo()->meta->description->getPostTypeDescription( $post->post_type );
128
+ if ( empty( $thePost->title ) && ! empty( $data['title'] ) && trim( $data['title'] ) === trim( $metaTitle ) ) {
129
+ $data['title'] = null;
130
+ }
131
+
132
+ if ( empty( $thePost->description ) && ! empty( $data['description'] ) && trim( $data['description'] ) === trim( $metaDescription ) ) {
133
+ $data['description'] = null;
134
+ }
135
+ }
136
+
137
+ $thePost->post_id = $postId;
138
+ $thePost->priority = ! empty( $data['priority'] ) ? sanitize_text_field( $data['priority'] ) : null;
139
+ $thePost->frequency = ! empty( $data['frequency'] ) ? sanitize_text_field( $data['frequency'] ) : null;
140
+ $thePost->title = ! empty( $data['title'] ) ? sanitize_text_field( $data['title'] ) : null;
141
+ $thePost->description = ! empty( $data['description'] ) ? sanitize_text_field( $data['description'] ) : null;
142
+ $thePost->keywords = ! empty( $data['keywords'] ) ? sanitize_text_field( $data['keywords'] ) : null;
143
+ $thePost->keyphrases = ! empty( $data['keyphrases'] ) ? wp_json_encode( $data['keyphrases'] ) : null;
144
+ $thePost->page_analysis = ! empty( $data['page_analysis'] ) ? wp_json_encode( $data['page_analysis'] ) : null;
145
+ $thePost->seo_score = ! empty( $data['seo_score'] ) ? sanitize_text_field( $data['seo_score'] ) : 0;
146
+ $thePost->canonical_url = ! empty( $data['canonicalUrl'] ) ? esc_url_raw( $data['canonicalUrl'] ) : null;
147
+ $thePost->pillar_content = isset( $data['pillar_content'] ) ? rest_sanitize_boolean( $data['pillar_content'] ) : 0;
148
+ $thePost->robots_default = isset( $data['default'] ) ? rest_sanitize_boolean( $data['default'] ) : 1; // robots_enabled
149
+ $thePost->robots_noindex = isset( $data['noindex'] ) ? rest_sanitize_boolean( $data['noindex'] ) : 0;
150
+ $thePost->robots_nofollow = isset( $data['nofollow'] ) ? rest_sanitize_boolean( $data['nofollow'] ) : 0;
151
+ $thePost->robots_noarchive = isset( $data['noarchive'] ) ? rest_sanitize_boolean( $data['noarchive'] ) : 0;
152
+ $thePost->robots_notranslate = isset( $data['notranslate'] ) ? rest_sanitize_boolean( $data['notranslate'] ) : 0;
153
+ $thePost->robots_noimageindex = isset( $data['noimageindex'] ) ? rest_sanitize_boolean( $data['noimageindex'] ) : 0;
154
+ $thePost->robots_nosnippet = isset( $data['nosnippet'] ) ? rest_sanitize_boolean( $data['nosnippet'] ) : 0;
155
+ $thePost->robots_noodp = isset( $data['noodp'] ) ? rest_sanitize_boolean( $data['noodp'] ) : 0;
156
+ $thePost->robots_max_snippet = ! empty( $data['maxSnippet'] ) ? sanitize_text_field( $data['maxSnippet'] ) : 0;
157
+ $thePost->robots_max_videopreview = ! empty( $data['maxVideoPreview'] ) ? (int) sanitize_text_field( $data['maxVideoPreview'] ) : 0;
158
+ $thePost->robots_max_imagepreview = ! empty( $data['maxImagePreview'] ) ? sanitize_text_field( $data['maxImagePreview'] ) : 'none';
159
+ $thePost->og_object_type = ! empty( $data['og_object_type'] ) ? sanitize_text_field( $data['og_object_type'] ) : 'default';
160
+ $thePost->og_title = ! empty( $data['og_title'] ) ? sanitize_text_field( $data['og_title'] ) : null;
161
+ $thePost->og_description = ! empty( $data['og_description'] ) ? sanitize_text_field( $data['og_description'] ) : null;
162
+ $thePost->og_image_custom_url = ! empty( $data['og_image_custom_url'] ) ? esc_url_raw( $data['og_image_custom_url'] ) : null;
163
+ $thePost->og_image_custom_fields = ! empty( $data['og_image_custom_fields'] ) ? sanitize_text_field( $data['og_image_custom_fields'] ) : null;
164
+ $thePost->og_image_type = ! empty( $data['og_image_type'] ) ? sanitize_text_field( $data['og_image_type'] ) : 'default';
165
+ $thePost->og_video = ! empty( $data['og_video'] ) ? sanitize_text_field( $data['og_video'] ) : '';
166
+ $thePost->og_article_section = ! empty( $data['og_article_section'] ) ? sanitize_text_field( $data['og_article_section'] ) : null;
167
+ $thePost->og_article_tags = ! empty( $data['og_article_tags'] ) ? sanitize_text_field( $data['og_article_tags'] ) : null;
168
+ $thePost->twitter_use_og = isset( $data['twitter_use_og'] ) ? rest_sanitize_boolean( $data['twitter_use_og'] ) : 0;
169
+ $thePost->twitter_card = ! empty( $data['twitter_card'] ) ? sanitize_text_field( $data['twitter_card'] ) : 'default';
170
+ $thePost->twitter_image_custom_url = ! empty( $data['twitter_image_custom_url'] ) ? esc_url_raw( $data['twitter_image_custom_url'] ) : null;
171
+ $thePost->twitter_image_custom_fields = ! empty( $data['twitter_image_custom_fields'] ) ? sanitize_text_field( $data['twitter_image_custom_fields'] ) : null;
172
+ $thePost->twitter_image_type = ! empty( $data['twitter_image_type'] ) ? sanitize_text_field( $data['twitter_image_type'] ) : 'default';
173
+ $thePost->twitter_title = ! empty( $data['twitter_title'] ) ? sanitize_text_field( $data['twitter_title'] ) : null;
174
+ $thePost->twitter_description = ! empty( $data['twitter_description'] ) ? sanitize_text_field( $data['twitter_description'] ) : null;
175
+ $thePost->schema_type = ! empty( $data['schema_type'] ) ? sanitize_text_field( $data['schema_type'] ) : 'none';
176
+ $thePost->schema_type_options = ! empty( $data['schema_type_options'] ) ? wp_json_encode( $data['schema_type_options'] ) : parent::getDefaultSchemaOptions();
177
+ $thePost->tabs = ! empty( $data['tabs'] ) ? wp_json_encode( $data['tabs'] ) : parent::getDefaultTabsOptions();
178
+ $thePost->local_seo = ! empty( $data['local_seo'] ) ? wp_json_encode( $data['local_seo'] ) : null;
179
+ $thePost->updated = gmdate( 'Y-m-d H:i:s' );
180
+
181
+ if ( ! $thePost->exists() ) {
182
+ $thePost->created = gmdate( 'Y-m-d H:i:s' );
183
+ }
184
+ $thePost->save();
185
+ $thePost->reset();
186
+
187
+ // Update the post meta as well for localization.
188
+ $keywords = ! empty( $data['keywords'] ) ? json_decode( $data['keywords'] ) : [];
189
+ foreach ( $keywords as $k => $keyword ) {
190
+ $keywords[ $k ] = $keyword->value;
191
+ }
192
+ $keywords = implode( ',', $keywords );
193
+
194
+ $ogArticleTags = ! empty( $data['og_article_tags'] ) ? json_decode( $data['og_article_tags'] ) : [];
195
+ foreach ( $ogArticleTags as $k => $tag ) {
196
+ $ogArticleTags[ $k ] = $tag->value;
197
+ }
198
+ $ogArticleTags = implode( ',', $ogArticleTags );
199
+
200
+ if ( ! empty( $data ) ) {
201
+ update_post_meta( $postId, '_aioseo_title', $data['title'] );
202
+ update_post_meta( $postId, '_aioseo_description', $data['description'] );
203
+ update_post_meta( $postId, '_aioseo_keywords', $keywords );
204
+ update_post_meta( $postId, '_aioseo_og_title', $data['og_title'] );
205
+ update_post_meta( $postId, '_aioseo_og_description', $data['og_description'] );
206
+ update_post_meta( $postId, '_aioseo_og_article_section', $data['og_article_section'] );
207
+ update_post_meta( $postId, '_aioseo_og_article_tags', $ogArticleTags );
208
+ update_post_meta( $postId, '_aioseo_twitter_title', $data['twitter_title'] );
209
+ update_post_meta( $postId, '_aioseo_twitter_description', $data['twitter_description'] );
210
+ }
211
+
212
+ $lastError = aioseo()->db->lastError();
213
+ if ( ! empty( $lastError ) ) {
214
+ return $lastError;
215
+ }
216
+
217
+ return null;
218
+ }
219
+
220
+ /**
221
+ * Get default values for TruSEO page analysis
222
+ *
223
+ * @since 4.0.0
224
+ *
225
+ * @return object The default TruSEO page analysis values.
226
+ */
227
+ public static function getPageAnalysisDefaults() {
228
+ $analysisDefaults = [
229
+ 'analysis' => [
230
+ 'basic' => [
231
+ 'metadescriptionLength' => [
232
+ 'error' => 1,
233
+ 'maxScore' => 9,
234
+ 'score' => 6,
235
+ 'title' => __( 'Meta description length', 'all-in-one-seo-pack' ),
236
+ 'description' => __( 'The meta description is too short.', 'all-in-one-seo-pack' )
237
+ ],
238
+ 'isInternalLink' => [
239
+ 'error' => 1,
240
+ 'maxScore' => 9,
241
+ 'score' => 3,
242
+ 'title' => __( 'Internal links', 'all-in-one-seo-pack' ),
243
+ 'description' => __( 'We couldn\'t find any internal links in your content. Add internal links in your content.', 'all-in-one-seo-pack' )
244
+ ],
245
+ 'isExternalLink' => [
246
+ 'error' => 1,
247
+ 'maxScore' => 9,
248
+ 'score' => 3,
249
+ 'title' => __( 'External links', 'all-in-one-seo-pack' ),
250
+ 'description' => __( 'No outbound links were found. Link out to external resources.', 'all-in-one-seo-pack' )
251
+ ],
252
+ 'errors' => 3
253
+ ],
254
+ 'title' => [
255
+ 'titleLength' => [
256
+ 'error' => 1,
257
+ 'maxScore' => 9,
258
+ 'score' => 1,
259
+ 'title' => __( 'SEO Title length', 'all-in-one-seo-pack' ),
260
+ 'description' => __( 'No title has been specified. Make sure to write one!', 'all-in-one-seo-pack' )
261
+ ],
262
+ // 'titleHasNumber' => [
263
+ // 'error' => 1,
264
+ // 'maxScore' => 9,
265
+ // 'score' => 5,
266
+ // 'title' => __( 'Title has number', 'all-in-one-seo-pack' ),
267
+ // 'description' => __( 'Your SEO title doesn\'t contain a number. Add a number to your title to improve CTR.', 'all-in-one-seo-pack' )
268
+ // ],
269
+ // 'titleHasPowerWords' => [
270
+ // 'error' => 1,
271
+ // 'maxScore' => 9,
272
+ // 'score' => 5,
273
+ // 'title' => __( 'Title has Power Words', 'all-in-one-seo-pack' ),
274
+ // 'description' => __(
275
+ // 'Your SEO title doesn\'t contain a power word. Add at least one. Power Words are tried-and-true words that copywriters use to attract more clicks.',
276
+ // 'all-in-one-seo-pack'
277
+ // ) // phpcs:ignore Generic.Files.LineLength.MaxExceeded
278
+ // ],
279
+ // 'titleSentiment' => [
280
+ // 'error' => 1,
281
+ // 'maxScore' => 9,
282
+ // 'score' => 5,
283
+ // 'title' => __( 'Title Sentiment Words', 'all-in-one-seo-pack' ),
284
+ // 'description' => __(
285
+ // 'Your SEO title doesn\'t contain a sentiment word. Headlines with a strong emotional sentiment (positive or negative) tend to receive more clicks.',
286
+ // 'all-in-one-seo-pack'
287
+ // ) // phpcs:ignore Generic.Files.LineLength.MaxExceeded
288
+ // ],
289
+ 'errors' => 1
290
+ ],
291
+ 'readability' => [
292
+ 'contentHasAssets' => [
293
+ 'error' => 1,
294
+ 'maxScore' => 5,
295
+ 'score' => 0,
296
+ 'title' => __( 'Images/Videos in content', 'all-in-one-seo-pack' ),
297
+ 'description' => __( 'You are not using rich media like images or videos.', 'all-in-one-seo-pack' )
298
+ ],
299
+ 'passiveVoice' => [
300
+ 'error' => 1,
301
+ 'maxScore' => 9,
302
+ 'score' => 3,
303
+ 'title' => __( 'Passive Voice', 'all-in-one-seo-pack' ),
304
+ 'description' => __( 'Use active voice.', 'all-in-one-seo-pack' )
305
+ ],
306
+ 'subheadingsDistribution' => [
307
+ 'error' => 1,
308
+ 'maxScore' => 9,
309
+ 'score' => 2,
310
+ 'title' => __( 'Subheading distribution', 'all-in-one-seo-pack' ),
311
+ 'description' => __( 'You are not using any subheadings, although your text is rather long. Try and add some subheadings.', 'all-in-one-seo-pack' )
312
+ ],
313
+ 'errors' => 3
314
+ ]
315
+ ]
316
+ ];
317
+ return json_decode( wp_json_encode( $analysisDefaults ) );
318
+ }
319
+ }
app/Common/Rss.php ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common;
3
+
4
+ /**
5
+ * Adds content before or after posts in the RSS feed.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class Rss {
10
+ /**
11
+ * Class constructor.
12
+ *
13
+ * @since 4.0.0
14
+ */
15
+ public function __construct() {
16
+ add_filter( 'the_content_feed', [ $this, 'addRssContent' ] );
17
+ add_filter( 'the_excerpt_rss', [ $this, 'addRssContentExcerpt' ] );
18
+ }
19
+
20
+ /**
21
+ * Adds content before or after the RSS excerpt.
22
+ *
23
+ * @since 4.0.0
24
+ *
25
+ * @param string $content The
26
+ * @return void
27
+ */
28
+ public function addRssContentExcerpt( $content ) {
29
+ return $this->addRssContent( $content, 'excerpt' );
30
+ }
31
+
32
+ /**
33
+ * Adds content before or after the RSS post.
34
+ *
35
+ * @since 4.0.0
36
+ *
37
+ * @param string $content The
38
+ * @return void
39
+ */
40
+ public function addRssContent( $content, $type = 'complete' ) {
41
+ $content = trim( $content );
42
+ if ( empty( $content ) ) {
43
+ return '';
44
+ }
45
+
46
+ if ( is_feed() ) {
47
+ $before = aioseo()->tags->replaceTags( aioseo()->options->rssContent->before, get_the_ID() );
48
+ $after = aioseo()->tags->replaceTags( aioseo()->options->rssContent->after, get_the_ID() );
49
+
50
+ if ( $before || $after ) {
51
+ if ( 'excerpt' === $type ) {
52
+ $content = wpautop( $content );
53
+ }
54
+ $content = aioseo()->helpers->decodeHtmlEntities( $before ) . $content . aioseo()->helpers->decodeHtmlEntities( $after );
55
+ }
56
+ }
57
+
58
+ return $content;
59
+ }
60
+ }
app/Common/Schema/Breadcrumb.php ADDED
@@ -0,0 +1,312 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Schema;
3
+
4
+ /**
5
+ * Determines the breadcrumb trail.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class Breadcrumb {
10
+
11
+ /**
12
+ * Returns the breadcrumb trail for the homepage.
13
+ *
14
+ * @since 4.0.0
15
+ *
16
+ * @return array The breadcrumb trail.
17
+ */
18
+ public function home() {
19
+ // Since we just need the root breadcrumb (homepage), we can call this immediately without passing any breadcrumbs.
20
+ return $this->setPositions();
21
+ }
22
+
23
+ /**
24
+ * Returns the breadcrumb trail for the requested post.
25
+ *
26
+ * @since 4.0.0
27
+ *
28
+ * @param WP_Post $post The post object.
29
+ * @return array The breadcrumb trail.
30
+ */
31
+ public function post( $post ) {
32
+ if ( is_post_type_hierarchical( $post->post_type ) ) {
33
+ return $this->setPositions( $this->postHierarchical( $post ) );
34
+ }
35
+ return $this->setPositions( $this->postNonHierarchical( $post ) );
36
+ }
37
+
38
+ /**
39
+ * Returns the breadcrumb trail for a hierarchical post.
40
+ *
41
+ * @since 4.0.0
42
+ *
43
+ * @param WP_Post $post The post object.
44
+ * @return array $breadcrumbs The breadcrumb trail.
45
+ */
46
+ private function postHierarchical( $post ) {
47
+ $breadcrumbs = [];
48
+ do {
49
+ array_unshift(
50
+ $breadcrumbs,
51
+ [
52
+ 'name' => aioseo()->meta->title->getTitle( $post ),
53
+ 'description' => aioseo()->meta->description->getDescription( $post ),
54
+ 'url' => get_permalink( $post ),
55
+ 'type' => aioseo()->helpers->isWooCommerceShopPage() || is_home() ? 'CollectionPage' : $this->getPostGraph()
56
+ ]
57
+ );
58
+
59
+ if ( $post->post_parent ) {
60
+ $post = get_post( $post->post_parent );
61
+ } else {
62
+ $post = false;
63
+ }
64
+ } while ( $post );
65
+ return $breadcrumbs;
66
+ }
67
+
68
+ /**
69
+ * Returns the breadcrumb trail for a non-hierarchical post.
70
+ *
71
+ * In this case we need to compare the permalink structure with the permalink of the requested post and loop through all objects we're able to find.
72
+ *
73
+ * @since 4.0.0
74
+ *
75
+ * @param WP_Post $post The post object.
76
+ * @return array $breadcrumbs The breadcrumb trail.
77
+ */
78
+ private function postNonHierarchical( $post ) {
79
+ global $wp_query;
80
+ $homeUrl = aioseo()->helpers->escapeRegex( home_url() );
81
+ $permalink = get_permalink();
82
+ $slug = preg_replace( "/$homeUrl/", '', $permalink );
83
+ $tags = array_filter( explode( '/', get_option( 'permalink_structure' ) ) ); // Permalink structure exploded into separate tag strings.
84
+ $objects = array_filter( explode( '/', $slug ) ); // Permalink slug exploded into separate object slugs.
85
+
86
+ if ( count( $tags ) !== count( $objects ) ) {
87
+ return [
88
+ 'name' => aioseo()->meta->title->getTitle( $post ),
89
+ 'description' => aioseo()->meta->description->getDescription( $post ),
90
+ 'url' => $permalink,
91
+ 'type' => $this->getPostGraph()
92
+ ];
93
+ }
94
+
95
+ $pairs = array_reverse( array_combine( $tags, $objects ) );
96
+
97
+ $breadcrumbs = [];
98
+ foreach ( $pairs as $tag => $object ) {
99
+ // Determine the slug for the object.
100
+ preg_match( "#.*${object}[/]#", $permalink, $url );
101
+ if ( empty( $url[0] ) ) {
102
+ continue;
103
+ }
104
+
105
+ $breadcrumb = [];
106
+ switch ( $tag ) {
107
+ case '%category%':
108
+ $term = get_category_by_slug( $object );
109
+ if ( ! $term ) {
110
+ break;
111
+ }
112
+
113
+ $oldQueriedObject = $wp_query->queried_object;
114
+ $wp_query->queried_object = $term;
115
+ $wp_query->is_category = true;
116
+
117
+ $breadcrumb = [
118
+ 'name' => aioseo()->meta->title->getTitle(),
119
+ 'description' => aioseo()->meta->description->getDescription(),
120
+ 'url' => $url[0],
121
+ 'type' => 'CollectionPage'
122
+ ];
123
+
124
+ $wp_query->queried_object = $oldQueriedObject;
125
+ $wp_query->is_category = false;
126
+ break;
127
+ case '%author%':
128
+ $breadcrumb = [
129
+ 'name' => aioseo()->meta->title->prepareTitle( aioseo()->options->searchAppearance->archives->author->title ),
130
+ 'description' => aioseo()->meta->description->prepareDescription( aioseo()->options->searchAppearance->archives->author->metaDescription ),
131
+ 'url' => $url[0],
132
+ 'type' => 'ProfilePage'
133
+ ];
134
+ break;
135
+ case '%postid%':
136
+ case '%postname%':
137
+ $breadcrumb = [
138
+ 'name' => aioseo()->meta->title->getTitle( $post ),
139
+ 'description' => aioseo()->meta->description->getDescription( $post ),
140
+ 'url' => $url[0],
141
+ 'type' => $this->getPostGraph()
142
+ ];
143
+ break;
144
+ case '%year%':
145
+ case '%monthnum%':
146
+ case '%day%':
147
+ $breadcrumb = [
148
+ 'name' => aioseo()->meta->title->prepareTitle( aioseo()->options->searchAppearance->archives->date->title ),
149
+ 'description' => aioseo()->meta->description->prepareDescription( aioseo()->options->searchAppearance->archives->date->metaDescription ),
150
+ 'url' => $url[0],
151
+ 'type' => 'CollectionPage'
152
+ ];
153
+ break;
154
+ default:
155
+ break;
156
+ }
157
+
158
+ if ( $breadcrumb ) {
159
+ array_unshift( $breadcrumbs, $breadcrumb );
160
+ }
161
+ }
162
+ return $breadcrumbs;
163
+ }
164
+
165
+ /**
166
+ * Returns the breadcrumb trail for the requested term.
167
+ *
168
+ * @since 4.0.0
169
+ *
170
+ * @param WP_Term $term The term object.
171
+ * @return array The breadcrumb trail.
172
+ */
173
+ public function term( $term ) {
174
+ $breadcrumbs = [];
175
+ do {
176
+ array_unshift(
177
+ $breadcrumbs,
178
+ [
179
+ 'name' => aioseo()->meta->title->getTitle(),
180
+ 'description' => aioseo()->meta->description->getDescription(),
181
+ 'url' => get_term_link( $term, $term->taxonomy ),
182
+ 'type' => 'CollectionPage'
183
+ ]
184
+ );
185
+
186
+ if ( $term->parent ) {
187
+ $term = get_term( $term->parent );
188
+ } else {
189
+ $term = false;
190
+ }
191
+ } while ( $term );
192
+ return $this->setPositions( $breadcrumbs );
193
+ }
194
+
195
+ /**
196
+ * Returns the breadcrumb trail for the requested date archive.
197
+ *
198
+ * @since 4.0.0
199
+ *
200
+ * @return array $breadcrumbs The breadcrumb trail.
201
+ */
202
+ public function date() {
203
+ global $wp_query;
204
+
205
+ $oldYear = $wp_query->is_year;
206
+ $oldMonth = $wp_query->is_month;
207
+ $oldDay = $wp_query->is_day;
208
+ $wp_query->is_year = true;
209
+ $wp_query->is_month = false;
210
+ $wp_query->is_day = false;
211
+
212
+ $breadcrumbs = [
213
+ [
214
+ 'name' => aioseo()->meta->title->getTitle(),
215
+ 'description' => aioseo()->meta->description->getDescription(),
216
+ 'url' => trailingslashit( get_year_link( $wp_query->query_vars['year'] ) ),
217
+ 'type' => 'CollectionPage'
218
+ ]
219
+ ];
220
+
221
+ $wp_query->is_year = $oldYear;
222
+
223
+ // Fall through if data archive is more specific than the year.
224
+ if ( is_year() ) {
225
+ return $this->setPositions( $breadcrumbs );
226
+ }
227
+
228
+ $wp_query->is_month = true;
229
+
230
+ $breadcrumbs[] = [
231
+ 'name' => aioseo()->meta->title->getTitle(),
232
+ 'description' => aioseo()->meta->description->getDescription(),
233
+ 'url' => trailingslashit( get_month_link(
234
+ $wp_query->query_vars['year'],
235
+ $wp_query->query_vars['monthnum']
236
+ ) ),
237
+ 'type' => 'CollectionPage'
238
+ ];
239
+
240
+ $wp_query->is_month = $oldMonth;
241
+
242
+ // Fall through if data archive is more specific than the year & month.
243
+ if ( is_month() ) {
244
+ return $this->setPositions( $breadcrumbs );
245
+ }
246
+
247
+ $wp_query->is_day = $oldDay;
248
+
249
+ $breadcrumbs[] = [
250
+ 'name' => aioseo()->meta->title->getTitle(),
251
+ 'description' => aioseo()->meta->description->getDescription(),
252
+ 'url' => trailingslashit( get_day_link(
253
+ $wp_query->query_vars['year'],
254
+ $wp_query->query_vars['monthnum'],
255
+ $wp_query->query_vars['day']
256
+ ) ),
257
+ 'type' => 'CollectionPage'
258
+ ];
259
+ return $this->setPositions( $breadcrumbs );
260
+ }
261
+
262
+ /**
263
+ * Sets the position for each breadcrumb after adding the root breadcrumb first.
264
+ *
265
+ * If no breadcrumbs are passed, then we assume we're on the homepage and just need the root breadcrumb.
266
+ *
267
+ * @since 4.0.0
268
+ *
269
+ * @param array $breadcrumbs The breadcrumb trail.
270
+ * @return array $breadcrumbs The modified breadcrumb trail.
271
+ */
272
+ public function setPositions( $breadcrumbs = [] ) {
273
+ // If the array isn't two-dimensional, then we need to wrap it in another array before continuing.
274
+ if (
275
+ count( $breadcrumbs ) &&
276
+ count( $breadcrumbs ) === count( $breadcrumbs, COUNT_RECURSIVE )
277
+ ) {
278
+ $breadcrumbs = [ $breadcrumbs ];
279
+ }
280
+
281
+ // The homepage needs to be root item of all trails.
282
+ $homepage = [
283
+ 'name' => aioseo()->meta->title->getHomePageTitle(),
284
+ 'description' => aioseo()->meta->description->getHomePageDescription(),
285
+ 'url' => trailingslashit( home_url() ),
286
+ 'type' => 'posts' === get_option( 'show_on_front' ) ? 'CollectionPage' : 'WebPage'
287
+ ];
288
+ array_unshift( $breadcrumbs, $homepage );
289
+
290
+ $breadcrumbs = array_filter( $breadcrumbs );
291
+ foreach ( $breadcrumbs as $index => &$breadcrumb ) {
292
+ $breadcrumb['position'] = $index + 1;
293
+ }
294
+ return $breadcrumbs;
295
+ }
296
+
297
+ /**
298
+ * Returns the most relevant graph for the post.
299
+ *
300
+ * @since 4.0.0
301
+ *
302
+ * @return string $graph The graph name.
303
+ */
304
+ private function getPostGraph() {
305
+ $graph = aioseo()->schema->getPostGraphs();
306
+ if ( is_array( $graph ) ) {
307
+ $graph = array_values( array_diff( $graph, [ 'WebPage' ] ) );
308
+ $graph = 1 === count( $graph ) ? $graph[0] : 'WebPage';
309
+ }
310
+ return $graph;
311
+ }
312
+ }
app/Common/Schema/Context.php ADDED
@@ -0,0 +1,221 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace AIOSEO\Plugin\Common\Schema;
3
+
4
+ /**
5
+ * Determines the context.
6
+ *
7
+ * @since 4.0.0
8
+ */
9
+ class Context {
10
+
11
+ /**
12
+ * Class constructor.
13
+ *
14
+ * @since 4.0.0
15
+ */
16
+ public function __construct() {
17
+ $this->breadcrumb = new Breadcrumb();
18
+ }
19
+
20
+ /**
21
+ * Returns the context data for the homepage.
22
+ *
23
+ * @since 4.0.0
24
+ *
25
+ * @return array $context The context data.
26
+ */
27
+ public function home() {
28
+ $context = [
29
+ 'url' => aioseo()->helpers->getUrl(),
30
+ 'breadcrumb' => $this->breadcrumb->home(),
31
+ 'name' => aioseo()->meta->title->getTitle(),
32
+ 'description' => aioseo()->meta->description->getDescription()
33
+ ];
34
+
35
+ // Homepage set to show latest posts.
36
+ if ( 'posts' === get_option( 'show_on_front' ) && is_home() ) {
37
+ return $context;
38
+ }
39
+
40
+ // Homepage set to static page.
41
+ $post = aioseo()->helpers->getPost();
42
+ if ( ! $post ) {
43
+ return [];
44
+ }
45
+
46
+ $context['object'] = $post;
47
+ return $context;
48
+ }
49
+
50
+ /**
51
+ * Returns the context data for the requested post.
52
+ *
53
+ * @since 4.0.0
54
+ *
55
+ * @return array The context data.
56
+ */
57
+ public function post() {
58
+ $post = aioseo()->helpers->getPost();
59
+ if ( ! $post ) {
60
+ return [];
61
+ }
62
+
63
+ return [
64
+ 'name' => aioseo()->meta->title->getTitle(