The SEO Framework - Version 2.6.6

Version Description

  • Semantic Structures =

Release date:

  • June 14th 2016

Summarized

  • Page builders are great for if you want to style your website, and when you want to do it fast.
  • So from this update the Divi Builder, Visual Composer, Beaver Builder, and the Page Builder by SiteOrigin are now fully supported.
  • Various bugs have also been fixed, to improve your experience. One particular bugfix is related to Polylang; this bugfix should improve performance and reduce other bugs from happening as well.
  • The Automated and Manual description output has been improved in several ways as well.
  • For developers, a class has been removed. All functions within have been moved to more suitable classes. This change will reduce server resource usage and increase overall performance.

SEO Tip of the Update - Know your Keywords:

  • To know how your website is found, sign up for Google Search Console. Over a couple of days (or weeks), elegant data has been accumulated about your website.
  • When you go to the Search Analytics within the Search Console, you'll see a list of queries people have used to find your website.
  • The queries can be used as keywords, they are excellent starting points for new post titles and subjects. Go ahead, use them!

For developers - About the class removal:

  • Because the plugin makes use of a "Facade pattern", all functions within the plugin are available at all times.
  • In trade for increased resource usage this does make the plugin very compatible, faster, and easier to work with.
  • This plugin serves one single responsibility: Outputting SEO data, and determining the reason why. Therefore, although it looks like a "God object", it's not.
  • Please keep in mind that all functions from any class are available through the "Facade object". Because of this, the class removal shouldn't cause issues for developers.
  • You can call this "Facade object" through a single cached function (rather than a global variable), this cached function is the_seo_framework().
  • If you wish to extend this plugin, feel free to ask me for any details or suggestions at the Support forums.

Detailed Log:

There's something to be said about all these details.

Download this release

Release Info

Developer Cybr
Plugin Icon 128x128 The SEO Framework
Version 2.6.6
Comparing to
See all releases

Version 2.6.6

Files changed (47) hide show
  1. autodescription.php +159 -0
  2. inc/classes/admininit.class.php +468 -0
  3. inc/classes/adminpages.class.php +889 -0
  4. inc/classes/compat.class.php +100 -0
  5. inc/classes/core.class.php +568 -0
  6. inc/classes/debug.class.php +890 -0
  7. inc/classes/detect.class.php +1129 -0
  8. inc/classes/doingitright.class.php +1656 -0
  9. inc/classes/feed.class.php +142 -0
  10. inc/classes/generate-description.class.php +812 -0
  11. inc/classes/generate-image.class.php +363 -0
  12. inc/classes/generate-ldjson.class.php +933 -0
  13. inc/classes/generate-title.class.php +1394 -0
  14. inc/classes/generate-url.class.php +1343 -0
  15. inc/classes/generate.class.php +313 -0
  16. inc/classes/init.class.php +404 -0
  17. inc/classes/inpost.class.php +664 -0
  18. inc/classes/metaboxes.class.php +2424 -0
  19. inc/classes/postdata.class.php +421 -0
  20. inc/classes/query.class.php +1107 -0
  21. inc/classes/render.class.php +1122 -0
  22. inc/classes/sanitize.class.php +978 -0
  23. inc/classes/search.class.php +124 -0
  24. inc/classes/sitemaps.class.php +980 -0
  25. inc/classes/siteoptions.class.php +1090 -0
  26. inc/classes/termdata.class.php +417 -0
  27. inc/classes/transients.class.php +660 -0
  28. inc/deprecated/deprecated.class.php +729 -0
  29. inc/deprecated/deprecated.php +29 -0
  30. inc/functions/benchmark.php +369 -0
  31. inc/functions/compat.php +359 -0
  32. inc/functions/optionsapi.php +229 -0
  33. index.php +2 -0
  34. language/autodescription.pot +1718 -0
  35. lib/css/autodescription-rtl.css +589 -0
  36. lib/css/autodescription-rtl.min.css +1 -0
  37. lib/css/autodescription.css +593 -0
  38. lib/css/autodescription.min.css +1 -0
  39. lib/js/autodescription.js +1276 -0
  40. lib/js/autodescription.min.js +24 -0
  41. license.txt +552 -0
  42. load.class.php +256 -0
  43. patch/2.6.6.patch +3633 -0
  44. patch/index.php +10 -0
  45. readme.txt +753 -0
  46. seotips/index.php +2 -0
  47. seotips/seotips.txt +59 -0
autodescription.php ADDED
@@ -0,0 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Plugin Name: The SEO Framework
4
+ * Plugin URI: https://wordpress.org/plugins/autodescription/
5
+ * Description: An automated, advanced, accessible, unbranded and extremely fast SEO solution for any WordPress website.
6
+ * Version: 2.6.6
7
+ * Author: Sybre Waaijer
8
+ * Author URI: https://cyberwire.nl/
9
+ * License: GPLv3
10
+ * Text Domain: autodescription
11
+ * Domain Path: /language
12
+ */
13
+
14
+ /**
15
+ * The SEO Framework plugin
16
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
17
+ *
18
+ * This program is free software: you can redistribute it and/or modify
19
+ * it under the terms of the GNU General Public License version 3 as published
20
+ * by the Free Software Foundation.
21
+ *
22
+ * This program is distributed in the hope that it will be useful,
23
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
24
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25
+ * GNU General Public License for more details.
26
+ *
27
+ * You should have received a copy of the GNU General Public License
28
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
29
+ */
30
+
31
+ //* Debug. Not to be used on production websites as it dumps and/or disables all kinds of stuff everywhere.
32
+ //add_action( 'plugins_loaded', function() { if ( is_super_admin() ) {
33
+ //if ( is_admin() ) {
34
+ // define( 'THE_SEO_FRAMEWORK_DEBUG', true );
35
+ // define( 'THE_SEO_FRAMEWORK_DEBUG_HIDDEN', true );
36
+ // define( 'THE_SEO_FRAMEWORK_DISABLE_TRANSIENTS', true );
37
+ //}
38
+ //}},0);
39
+
40
+ /**
41
+ * CDN Cache buster. 3 to 4 point.
42
+ * Not many caching plugins use CDN in dashboard. What a shame. Firefox does cache.
43
+ * @since 1.0.0
44
+ */
45
+ define( 'THE_SEO_FRAMEWORK_VERSION', '2.6.6' );
46
+
47
+ /**
48
+ * Plugin options filter.
49
+ * @since 2.2.2
50
+ */
51
+ define( 'THE_SEO_FRAMEWORK_SITE_OPTIONS', (string) apply_filters( 'the_seo_framework_site_options', 'autodescription-site-settings' ) );
52
+
53
+ /**
54
+ * Plugin options filter.
55
+ * @since 2.2.2
56
+ */
57
+ define( 'THE_SEO_FRAMEWORK_NETWORK_OPTIONS', (string) apply_filters( 'the_seo_framework_network_settings', 'autodescription-network-settings' ) );
58
+
59
+ /**
60
+ * The plugin map url.
61
+ * Used for calling browser files.
62
+ * @since 2.2.2
63
+ */
64
+ define( 'THE_SEO_FRAMEWORK_DIR_URL', plugin_dir_url( __FILE__ ) );
65
+
66
+ /**
67
+ * The plugin map absolute path.
68
+ * Used for calling php files.
69
+ * @since 2.2.2
70
+ */
71
+ define( 'THE_SEO_FRAMEWORK_DIR_PATH', plugin_dir_path( __FILE__ ) );
72
+
73
+ /**
74
+ * The plugin file relative to the plugins dir.
75
+ * @since 2.2.8
76
+ */
77
+ define( 'THE_SEO_FRAMEWORK_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );
78
+
79
+ /**
80
+ * The plugin file, absolute unix path.
81
+ * @since 2.2.9
82
+ */
83
+ define( 'THE_SEO_FRAMEWORK_PLUGIN_BASE_FILE', __FILE__ );
84
+
85
+ /**
86
+ * The plugin class map absolute path.
87
+ * @since 2.2.9
88
+ */
89
+ define( 'THE_SEO_FRAMEWORK_DIR_PATH_CLASS', THE_SEO_FRAMEWORK_DIR_PATH . '/inc/classes/' );
90
+
91
+ /**
92
+ * The plugin function map absolute path.
93
+ * @since 2.2.9
94
+ */
95
+ define( 'THE_SEO_FRAMEWORK_DIR_PATH_FUNCT', THE_SEO_FRAMEWORK_DIR_PATH . '/inc/functions/' );
96
+
97
+ add_action( 'plugins_loaded', 'the_seo_framework_locale_init', 10 );
98
+ /**
99
+ * Plugin locale 'autodescription'
100
+ * File located in plugin folder autodescription/language/
101
+ * @since 1.0.0
102
+ */
103
+ function the_seo_framework_locale_init() {
104
+ load_plugin_textdomain( 'autodescription', false, basename( dirname( __FILE__ ) ) . '/language/' );
105
+ }
106
+
107
+ /**
108
+ * Load plugin files.
109
+ * @since 1.0.0
110
+ * @uses THE_SEO_FRAMEWORK_DIR_PATH
111
+ */
112
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH . '/load.class.php' );
113
+
114
+ //* Load deprecated functions.
115
+ //require_once( THE_SEO_FRAMEWORK_DIR_PATH . 'inc/deprecated/deprecated.php' );
116
+
117
+ /**
118
+ * FLush permalinks on activation/deactivation.
119
+ * Calls functions statically.
120
+ * @since 2.2.9
121
+ */
122
+ register_activation_hook( THE_SEO_FRAMEWORK_PLUGIN_BASE_FILE, 'the_seo_framework_flush_rewrite_rules_activation' );
123
+ register_deactivation_hook( THE_SEO_FRAMEWORK_PLUGIN_BASE_FILE, 'the_seo_framework_flush_rewrite_rules_deactivation' );
124
+
125
+ /**
126
+ * Add and Flush rewrite rules on plugin activation.
127
+ *
128
+ * @global object $wp_rewrite
129
+ *
130
+ * @since 2.6.6
131
+ * @access private
132
+ */
133
+ function the_seo_framework_flush_rewrite_rules_activation() {
134
+ global $wp_rewrite;
135
+
136
+ $the_seo_framework = the_seo_framework();
137
+ $the_seo_framework->rewrite_rule_sitemap( true );
138
+
139
+ $wp_rewrite->init();
140
+ $wp_rewrite->flush_rules( true );
141
+ }
142
+
143
+ /**
144
+ * Flush rewrite rules on plugin deactivation.
145
+ *
146
+ * @global object $wp_rewrite
147
+ *
148
+ * @since 2.6.6
149
+ * @access private
150
+ */
151
+ function the_seo_framework_flush_rewrite_rules_deactivation() {
152
+ global $wp_rewrite;
153
+
154
+ $wp_rewrite->init();
155
+
156
+ unset( $wp_rewrite->extra_rules_top['sitemap\.xml$'] );
157
+
158
+ $wp_rewrite->flush_rules( true );
159
+ }
inc/classes/admininit.class.php ADDED
@@ -0,0 +1,468 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The SEO Framework plugin
4
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License version 3 as published
8
+ * by the Free Software Foundation.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ /**
20
+ * Class AutoDescription_Admin_Init
21
+ *
22
+ * Initializes the plugin for the wp-admin screens.
23
+ * Enqueues CSS and Javascript.
24
+ *
25
+ * @since 2.1.6
26
+ */
27
+ class AutoDescription_Admin_Init extends AutoDescription_Init {
28
+
29
+ /**
30
+ * Page Hook.
31
+ *
32
+ * @since 2.5.2.2
33
+ *
34
+ * @var String Holds Admin Page hook.
35
+ */
36
+ protected $page_hook;
37
+
38
+ /**
39
+ * JavaScript name identifier to be used with enqueuing.
40
+ *
41
+ * @since 2.5.2.2
42
+ *
43
+ * @var array JavaScript name identifier.
44
+ */
45
+ public $js_name;
46
+
47
+ /**
48
+ * CSS script name identifier to be used with enqueuing.
49
+ *
50
+ * @since 2.6.0
51
+ *
52
+ * @var array CSS name identifier.
53
+ */
54
+ public $css_name;
55
+
56
+ /**
57
+ * Constructor, load parent constructor
58
+ *
59
+ * Initalizes wp-admin functions
60
+ */
61
+ public function __construct() {
62
+ parent::__construct();
63
+
64
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ), 0, 1 );
65
+
66
+ $this->js_name = 'autodescription';
67
+ $this->css_name = 'autodescription';
68
+
69
+ //* Admin AJAX for counter options.
70
+ add_action( 'wp_ajax_the_seo_framework_update_counter', array( $this, 'the_counter_visualized' ) );
71
+
72
+ }
73
+
74
+ /**
75
+ * Enqueues scripts in the admin area on the supported screens.
76
+ *
77
+ * @since 2.3.3
78
+ *
79
+ * @param $hook the current page
80
+ */
81
+ public function enqueue_admin_scripts( $hook ) {
82
+
83
+ $enqueue_hooks = array(
84
+ 'edit.php',
85
+ 'post.php',
86
+ 'post-new.php',
87
+ 'edit-tags.php',
88
+ 'term.php',
89
+ );
90
+
91
+ /**
92
+ * Check hook first.
93
+ * @since 2.3.9
94
+ */
95
+ if ( isset( $hook ) && $hook && in_array( $hook, $enqueue_hooks ) ) {
96
+ /**
97
+ * @uses $this->post_type_supports_custom_seo()
98
+ * @since 2.3.9
99
+ */
100
+ if ( $this->post_type_supports_custom_seo() )
101
+ $this->init_admin_scripts();
102
+
103
+ }
104
+
105
+ }
106
+
107
+ /**
108
+ * Register and output Admin scripts.
109
+ *
110
+ * @since 2.6.0
111
+ */
112
+ public function init_admin_scripts() {
113
+
114
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_css' ), 1 );
115
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_javascript' ), 1 );
116
+
117
+ }
118
+
119
+ /**
120
+ * AutoDescription JavaScript helper file
121
+ *
122
+ * @since 2.0.2
123
+ *
124
+ * @usedby add_inpost_seo_box
125
+ * @usedby enqueue_javascript
126
+ *
127
+ * @param string|array|object $hook the current page
128
+ */
129
+ public function enqueue_admin_javascript( $hook ) {
130
+
131
+ /**
132
+ * Put hook and js name in class vars.
133
+ * @since 2.5.2.2
134
+ */
135
+ $this->page_hook = $this->page_hook ? $this->page_hook : $hook;
136
+
137
+ //* Register the script.
138
+ $this->register_admin_javascript();
139
+
140
+ wp_enqueue_script( $this->js_name );
141
+
142
+ /**
143
+ * Localize JavaScript.
144
+ * @since 2.5.2.2
145
+ */
146
+ add_action( 'admin_footer', array( $this, 'localize_admin_javascript' ) );
147
+
148
+ }
149
+
150
+ /**
151
+ * Registers Admin CSS.
152
+ *
153
+ * @since 2.6.0
154
+ * @staticvar bool $registered : Prevents Re-registering of the style.
155
+ *
156
+ * @access private
157
+ */
158
+ public function register_admin_javascript() {
159
+
160
+ static $registered = null;
161
+
162
+ if ( isset( $registered ) )
163
+ return;
164
+
165
+ $suffix = $this->script_debug ? '' : '.min';
166
+
167
+ wp_register_script( $this->js_name, THE_SEO_FRAMEWORK_DIR_URL . "lib/js/autodescription{$suffix}.js", array( 'jquery' ), THE_SEO_FRAMEWORK_VERSION, true );
168
+
169
+ $registered = true;
170
+
171
+ }
172
+
173
+ /**
174
+ * Localizes admin javascript.
175
+ *
176
+ * @since 2.5.2.2
177
+ */
178
+ public function localize_admin_javascript() {
179
+
180
+ static $localized = null;
181
+
182
+ if ( isset( $localized ) )
183
+ return;
184
+
185
+ $strings = $this->get_javascript_l10n();
186
+
187
+ wp_localize_script( $this->js_name, 'autodescriptionL10n', $strings );
188
+
189
+ $localized = true;
190
+
191
+ }
192
+
193
+ /**
194
+ * Generate Javascript Localization.
195
+ *
196
+ * @since 2.6.0
197
+ * @staticvar array $strings : The l10n strings.
198
+ *
199
+ * @return array $strings The l10n strings.
200
+ */
201
+ protected function get_javascript_l10n() {
202
+
203
+ static $strings = null;
204
+
205
+ if ( isset( $strings ) )
206
+ return $strings;
207
+
208
+ $blog_name = $this->get_blogname();
209
+ $description = $this->get_blogdescription();
210
+ $title = '';
211
+ $additions = '';
212
+
213
+ $tagline = (bool) $this->get_option( 'homepage_tagline' );
214
+ $home_tagline = $this->get_option( 'homepage_title_tagline' );
215
+ $title_location = $this->get_option( 'title_location' );
216
+ $title_add_additions = $this->add_title_additions();
217
+ $counter_type = $this->get_option( 'counter_type' );
218
+
219
+ //* Enunciate the lenghts of Titles and Descriptions.
220
+ $good = __( 'Good', 'autodescription' );
221
+ $okay = __( 'Okay', 'autodescription' );
222
+ $bad = __( 'Bad', 'autodescription' );
223
+ $unknown = __( 'Unknown', 'autodescription' );
224
+
225
+ $separator = $this->get_separator( 'title', true );
226
+
227
+ $rtl = (bool) is_rtl();
228
+ $ishome = false;
229
+
230
+ /**
231
+ * We're gaining UX in exchange for resource usage.
232
+ *
233
+ * Any way to cache this?
234
+ *
235
+ * @since 2.2.4
236
+ */
237
+ if ( isset( $this->page_hook ) && $this->page_hook ) {
238
+ // We're somewhere within default WordPress pages.
239
+ $post_id = $this->get_the_real_ID();
240
+
241
+ if ( $this->is_static_frontpage( $post_id ) ) {
242
+ $title = $blog_name;
243
+ $title_location = $this->get_option( 'home_title_location' );
244
+ $ishome = true;
245
+
246
+ if ( $tagline ) {
247
+ $additions = $home_tagline ? $home_tagline : $description;
248
+ } else {
249
+ $additions = '';
250
+ }
251
+ } else if ( $post_id ) {
252
+ //* We're on post.php
253
+ $generated_doctitle_args = array(
254
+ 'term_id' => $post_id,
255
+ 'notagline' => true,
256
+ 'get_custom_field' => false,
257
+ );
258
+
259
+ $title = $this->title( '', '', '', $generated_doctitle_args );
260
+
261
+ if ( $title_add_additions ) {
262
+ $additions = $blog_name;
263
+ $tagline = true;
264
+ } else {
265
+ $additions = '';
266
+ $tagline = false;
267
+ }
268
+ } else if ( $this->is_archive() ) {
269
+ //* Category or Tag.
270
+ global $current_screen;
271
+
272
+ if ( isset( $current_screen->taxonomy ) ) {
273
+
274
+ $term_id = $this->get_admin_term_id();
275
+
276
+ if ( $term_id ) {
277
+ $generated_doctitle_args = array(
278
+ 'term_id' => $term_id,
279
+ 'taxonomy' => $current_screen->taxonomy,
280
+ 'notagline' => true,
281
+ 'get_custom_field' => false
282
+ );
283
+
284
+ $title = $this->title( '', '', '', $generated_doctitle_args );
285
+ $additions = $title_add_additions ? $blog_name : '';
286
+ }
287
+ }
288
+
289
+ } else {
290
+ //* We're in a special place.
291
+ // Can't fetch title.
292
+ $title = '';
293
+ $additions = $title_add_additions ? $blog_name : '';
294
+ }
295
+
296
+ } else {
297
+ // We're on our SEO settings pages.
298
+ if ( $this->has_page_on_front() ) {
299
+ // Home is a page.
300
+ $inpost_title = $this->get_custom_field( '_genesis_title', get_option( 'page_on_front' ) );
301
+ } else {
302
+ // Home is a blog.
303
+ $inpost_title = '';
304
+ }
305
+ $title = $inpost_title ? $inpost_title : $blog_name;
306
+ $additions = $home_tagline ? $home_tagline : $description;
307
+ }
308
+
309
+ return $strings = array(
310
+ 'saveAlert' => __( 'The changes you made will be lost if you navigate away from this page.', 'autodescription' ),
311
+ 'confirmReset' => __( 'Are you sure you want to reset all SEO settings to their defaults?', 'autodescription' ),
312
+ 'siteTitle' => $title,
313
+ 'titleAdditions' => $additions,
314
+ 'blogDescription' => $description,
315
+ 'titleTagline' => $tagline,
316
+ 'titleSeparator' => $separator,
317
+ 'titleLocation' => $title_location,
318
+ 'isRTL' => $rtl,
319
+ 'isHome' => $ishome,
320
+ 'counterType' => $counter_type,
321
+ 'good' => $good,
322
+ 'okay' => $okay,
323
+ 'bad' => $bad,
324
+ 'unknown' => $unknown,
325
+ );
326
+ }
327
+
328
+ /**
329
+ * CSS for the AutoDescription Bar
330
+ *
331
+ * @since 2.1.9
332
+ *
333
+ * @param $hook the current page
334
+ *
335
+ * @todo get_network_option
336
+ * @priority low 3.0.0
337
+ */
338
+ public function enqueue_admin_css( $hook ) {
339
+
340
+ /**
341
+ * Put hook and js name in class vars.
342
+ * @since 2.5.2.2
343
+ */
344
+ $this->page_hook = $this->page_hook ? $this->page_hook : $hook;
345
+
346
+ //* Register the script.
347
+ $this->register_admin_css();
348
+
349
+ wp_enqueue_style( $this->css_name );
350
+
351
+ }
352
+
353
+ /**
354
+ * Registers Admin CSS.
355
+ *
356
+ * @since 2.6.0
357
+ * @staticvar bool $registered : Prevents Re-registering of the style.
358
+ *
359
+ * @access private
360
+ */
361
+ protected function register_admin_css() {
362
+
363
+ static $registered = null;
364
+
365
+ if ( isset( $registered ) )
366
+ return;
367
+
368
+ $rtl = '';
369
+
370
+ if ( is_rtl() )
371
+ $rtl = '-rtl';
372
+
373
+ $suffix = $this->script_debug ? '' : '.min';
374
+
375
+ wp_register_style( $this->css_name, THE_SEO_FRAMEWORK_DIR_URL . "lib/css/autodescription{$rtl}{$suffix}.css", array(), THE_SEO_FRAMEWORK_VERSION, 'all' );
376
+
377
+ $registered = true;
378
+
379
+ }
380
+
381
+ /**
382
+ * Checks the screen hook.
383
+ *
384
+ * @since 2.2.2
385
+ * @global string $page_hook the current page hook.
386
+ *
387
+ * @return bool true if screen match.
388
+ */
389
+ public function is_menu_page( $pagehook = '' ) {
390
+ global $page_hook;
391
+
392
+ if ( isset( $page_hook ) && $page_hook === $pagehook )
393
+ return true;
394
+
395
+ //* May be too early for $page_hook
396
+ if ( isset( $_REQUEST['page'] ) && $_REQUEST['page'] === $pagehook )
397
+ return true;
398
+
399
+ return false;
400
+ }
401
+
402
+ /**
403
+ * Redirect the user to an admin page, and add query args to the URL string
404
+ * for alerts, etc.
405
+ *
406
+ * @since 2.2.2
407
+ *
408
+ * @param string $page Menu slug.
409
+ * @param array $query_args Optional. Associative array of query string arguments
410
+ * (key => value). Default is an empty array.
411
+ *
412
+ * @credits StudioPress for some code.
413
+ *
414
+ * @return null Return early if first argument is false.
415
+ */
416
+ public function admin_redirect( $page, array $query_args = array() ) {
417
+
418
+ if ( empty( $page ) )
419
+ return;
420
+
421
+ $url = html_entity_decode( menu_page_url( $page, 0 ) );
422
+
423
+ foreach ( $query_args as $key => $value ) {
424
+ if ( empty( $key ) || empty( $value ) )
425
+ unset( $query_args[$key] );
426
+ }
427
+
428
+ $url = add_query_arg( $query_args, $url );
429
+
430
+ wp_redirect( esc_url_raw( $url ) );
431
+ exit;
432
+ }
433
+
434
+
435
+ /**
436
+ * Handles counter option update on AJAX request.
437
+ *
438
+ * @since 2.6.0
439
+ * @access private
440
+ */
441
+ public function the_counter_visualized() {
442
+
443
+ if ( $this->is_admin() && defined( 'DOING_AJAX' ) && DOING_AJAX ) {
444
+ //* If current user isn't allowed to edit posts, don't do anything.
445
+ if ( ! current_user_can( 'publish_posts' ) )
446
+ exit;
447
+
448
+ $options = $this->get_all_options();
449
+
450
+ /**
451
+ * Count up, reset to 0 if needed. We have 4 options: 0, 1, 2, 3
452
+ * We're not accepting any $_POST values. Keeping it clean.
453
+ * Yet we should for consistency. @TODO
454
+ * @priority high 2.6.2
455
+ */
456
+ $options['counter_type'] = $options['counter_type'] + 1;
457
+ if ( $options['counter_type'] > 3 )
458
+ $options['counter_type'] = 0;
459
+
460
+ update_option( $this->settings_field, $options );
461
+
462
+ //* Kill PHP.
463
+ exit;
464
+ }
465
+
466
+ }
467
+
468
+ }
inc/classes/adminpages.class.php ADDED
@@ -0,0 +1,889 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The SEO Framework plugin
4
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License version 3 as published
8
+ * by the Free Software Foundation.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ /**
20
+ * Class AutoDescription_Siteoptions
21
+ *
22
+ * Renders admin pages content for AutoDescription.
23
+ *
24
+ * @since 2.2.2
25
+ */
26
+ class AutoDescription_Adminpages extends AutoDescription_Inpost {
27
+
28
+ /**
29
+ * Page Defaults.
30
+ *
31
+ * @since 2.2.2
32
+ *
33
+ * @var array Holds Page output defaults.
34
+ */
35
+ public $page_defaults = array();
36
+
37
+ /**
38
+ * Name of the page hook when the menu is registered.
39
+ *
40
+ * @since 2.2.2
41
+ *
42
+ * @var string Page hook
43
+ */
44
+ public $pagehook;
45
+
46
+ /**
47
+ * Name of the network page hook when the menu is registered.
48
+ *
49
+ * @since 2.2.2
50
+ *
51
+ * @var string Page hook
52
+ */
53
+ public $network_pagehook;
54
+
55
+ /**
56
+ * Load the options.
57
+ *
58
+ * @since 2.6.0
59
+ *
60
+ * @var bool Load options.
61
+ */
62
+ public $load_options;
63
+
64
+ /**
65
+ * Constructor, load parent constructor and set up variables.
66
+ */
67
+ public function __construct() {
68
+ parent::__construct();
69
+
70
+ /**
71
+ * Applies filters the_seo_framework_load_options : Boolean Allows the options page to be removed
72
+ * @since 2.2.2
73
+ */
74
+ $this->load_options = (bool) apply_filters( 'the_seo_framework_load_options', true );
75
+
76
+ if ( $this->load_options ) {
77
+ add_action( 'admin_init', array( $this, 'enqueue_page_defaults' ), 1 );
78
+
79
+ // Add menu links and register $this->pagehook
80
+ add_action( 'admin_menu', array( $this, 'add_menu_link' ) );
81
+
82
+ /**
83
+ * Add specific Multisite options
84
+ * @TODO
85
+ * @priority low 3.0.0
86
+ */
87
+ // if ( is_multisite() ) add_action( 'network_admin_menu', array( $this, 'add_network_menu_link' ) );
88
+
89
+ //* Load the page content
90
+ add_action( 'admin_init', array( $this, 'settings_init' ) );
91
+
92
+ // Set up notices
93
+ add_action( 'admin_notices', array( $this, 'notices' ) );
94
+
95
+ // Load nessecary assets
96
+ add_action( 'admin_init', array( $this, 'load_assets' ) );
97
+ }
98
+
99
+ }
100
+
101
+ /**
102
+ * Enqueue page defaults early.
103
+ *
104
+ * Applies filter 'the_seo_framework_admin_page_defaults' : Array
105
+ * This filter adds i18n support for buttons and notices.
106
+ *
107
+ * @since 2.3.1
108
+ */
109
+ public function enqueue_page_defaults() {
110
+
111
+ $this->page_defaults = (array) apply_filters(
112
+ 'the_seo_framework_admin_page_defaults',
113
+ array(
114
+ 'save_button_text' => __( 'Save Settings', 'autodescription' ),
115
+ 'reset_button_text' => __( 'Reset Settings', 'autodescription' ),
116
+ 'saved_notice_text' => __( 'Settings are saved.', 'autodescription' ),
117
+ 'reset_notice_text' => __( 'Settings are reset.', 'autodescription' ),
118
+ 'error_notice_text' => __( 'Error saving settings.', 'autodescription' ),
119
+ 'plugin_update_text' => __( 'New SEO Settings have been updated.', 'autodescription' ),
120
+ )
121
+ );
122
+
123
+ }
124
+
125
+ /**
126
+ * Adds menu links under "settings" in the wp-admin dashboard
127
+ *
128
+ * @since 2.2.2
129
+ *
130
+ * @return void
131
+ */
132
+ public function add_menu_link() {
133
+
134
+ $menu = array(
135
+ 'pagetitle' => __( 'SEO Settings', 'autodescription' ),
136
+ 'menutitle' => __( 'SEO', 'autodescription' ),
137
+ 'capability' => $this->settings_capability(),
138
+ 'menu_slug' => 'autodescription-settings',
139
+ 'callback' => array( $this, 'admin' ),
140
+ 'icon' => 'dashicons-search',
141
+ 'position' => '90.9001',
142
+ );
143
+
144
+ $this->pagehook = add_menu_page(
145
+ $menu['pagetitle'],
146
+ $menu['menutitle'],
147
+ $menu['capability'],
148
+ $menu['menu_slug'],
149
+ $menu['callback'],
150
+ $menu['icon'],
151
+ $menu['position']
152
+ );
153
+
154
+ //* Enqueue styles
155
+ add_action( 'admin_print_styles-' . $this->pagehook, array( $this, 'enqueue_admin_css' ), 11 );
156
+
157
+ //* Enqueue scripts
158
+ add_action( 'admin_print_scripts-' . $this->pagehook, array( $this, 'enqueue_admin_javascript' ), 11 );
159
+
160
+ }
161
+
162
+ /**
163
+ * Adds menu links under "settings" in the wp-admin dashboard
164
+ *
165
+ * Applies `autodescription_settings_capability` filters.
166
+ * This filter changes the minimum role for viewing and editing the plugin's settings.
167
+ *
168
+ * @since 2.2.2
169
+ * @return void
170
+ *
171
+ * @TODO Everything.
172
+ * @priority low 3.0.0
173
+ */
174
+ public function add_network_menu_link() {
175
+
176
+ $menu = array(
177
+ 'pagetitle' => __( 'Network SEO Settings', 'autodescription' ),
178
+ 'menutitle' => __( 'Network SEO', 'autodescription' ),
179
+
180
+ 'capability' => 'manage_network',
181
+
182
+ 'menu_slug' => 'autodescription-network-settings',
183
+ 'callback' => array( $this, 'network_admin' ),
184
+ 'icon' => 'dashicons-search',
185
+ 'position' => '99.9001',
186
+ );
187
+
188
+ $this->network_pagehook = add_menu_page(
189
+ $menu['pagetitle'],
190
+ $menu['menutitle'],
191
+ $menu['capability'],
192
+ $menu['menu_slug'],
193
+ $menu['callback'],
194
+ $menu['icon'],
195
+ $menu['position']
196
+ );
197
+
198
+ // Enqueue styles
199
+ add_action( 'admin_print_styles-' . $this->network_pagehook, array( $this, 'enqueue_admin_css' ), 11 );
200
+
201
+ // Enqueue scripts
202
+ add_action( 'admin_print_scripts-' . $this->network_pagehook, array( $this, 'enqueue_admin_javascript' ), 11 );
203
+
204
+ }
205
+
206
+ /**
207
+ * Initialize the settings page.
208
+ *
209
+ * @since 2.2.2
210
+ */
211
+ public function settings_init() {
212
+
213
+ add_action( $this->pagehook . '_settings_page_boxes', array( $this, 'do_metaboxes' ) );
214
+ add_action( 'load-' . $this->pagehook, array( $this, 'metaboxes' ) );
215
+
216
+ }
217
+
218
+ /**
219
+ * Echo out the do_metaboxes() and wrapping markup.
220
+ *
221
+ * @since 2.2.2
222
+ *
223
+ * @global array $wp_meta_boxes Holds all metaboxes data.
224
+ */
225
+ public function do_metaboxes() {
226
+ global $wp_meta_boxes;
227
+
228
+ ?>
229
+ <div class="metabox-holder columns-2">
230
+ <div class="postbox-container-1">
231
+ <?php
232
+ do_action( 'the_seo_framework_before_siteadmin_metaboxes', $this->pagehook );
233
+
234
+ do_meta_boxes( $this->pagehook, 'main', null );
235
+
236
+ if ( isset( $wp_meta_boxes[$this->pagehook]['main_extra'] ) )
237
+ do_meta_boxes( $this->pagehook, 'main_extra', null );
238
+
239
+ do_action( 'the_seo_framework_after_siteadmin_metaboxes', $this->pagehook );
240
+ ?>
241
+ </div>
242
+ <div class="postbox-container-2">
243
+ <?php
244
+ do_action( 'the_seo_framework_before_siteadmin_metaboxes_side', $this->pagehook );
245
+
246
+ /**
247
+ * @TODO fill this in
248
+ * @priority low 2.9.0
249
+ */
250
+
251
+ do_action( 'the_seo_framework_after_siteadmin_metaboxes_side', $this->pagehook );
252
+ ?>
253
+ </div>
254
+ </div>
255
+ <?php
256
+ }
257
+
258
+ /**
259
+ * Register meta boxes on the Site SEO Settings page.
260
+ *
261
+ * @since 2.2.2
262
+ *
263
+ * @see $this->title_metabox() Callback for Title Settings box.
264
+ * @see $this->description_metabox() Callback for Description Settings box.
265
+ * @see $this->robots_metabox() Callback for Robots Settings box.
266
+ * @see $this->homepage_metabox() Callback for Home Page Settings box.
267
+ * @see $this->social_metabox() Callback for Social Settings box.
268
+ * @see $this->knowledge_metabox() Callback for Knowledge Graph Settings box.
269
+ * @see $this->schema_metabox() Callback for Schema Settings box.
270
+ * @see $this->webmaster_metabox() Callback for Webmaster Settings box.
271
+ * @see $this->sitemaps_metabox() Callback for Sitemap Settings box.
272
+ * @see $this->feed_metabox() Callback for Feed Settings box.
273
+ */
274
+ public function metaboxes() {
275
+
276
+ /**
277
+ * Various metabox filters.
278
+ * Set any to false if you wish the meta box to be removed.
279
+ *
280
+ * @since 2.2.4
281
+ */
282
+ $title = (bool) apply_filters( 'the_seo_framework_title_metabox', true );
283
+ $description = (bool) apply_filters( 'the_seo_framework_description_metabox', true );
284
+ $robots = (bool) apply_filters( 'the_seo_framework_robots_metabox', true );
285
+ $home = (bool) apply_filters( 'the_seo_framework_home_metabox', true );
286
+ $social = (bool) apply_filters( 'the_seo_framework_social_metabox', true );
287
+ $knowledge = (bool) apply_filters( 'the_seo_framework_knowledge_metabox', true );
288
+ $schema = (bool) apply_filters( 'the_seo_framework_schema_metabox', true );
289
+ $webmaster = (bool) apply_filters( 'the_seo_framework_webmaster_metabox', true );
290
+ $sitemap = (bool) apply_filters( 'the_seo_framework_sitemap_metabox', true );
291
+ $feed = (bool) apply_filters( 'the_seo_framework_feed_metabox', true );
292
+
293
+ //* Title Meta Box
294
+ if ( $title )
295
+ add_meta_box(
296
+ 'autodescription-title-settings',
297
+ __( 'Title Settings', 'autodescription' ),
298
+ array( $this, 'title_metabox' ),
299
+ $this->pagehook,
300
+ 'main'
301
+ );
302
+
303
+ //* Description Meta Box
304
+ if ( $description )
305
+ add_meta_box(
306
+ 'autodescription-description-settings',
307
+ __( 'Description Meta Settings', 'autodescription' ),
308
+ array( $this, 'description_metabox' ),
309
+ $this->pagehook,
310
+ 'main'
311
+ );
312
+
313
+ //* Home Page Meta Box
314
+ if ( $home )
315
+ add_meta_box(
316
+ 'autodescription-homepage-settings',
317
+ __( 'Home Page Settings', 'autodescription' ),
318
+ array( $this, 'homepage_metabox' ),
319
+ $this->pagehook,
320
+ 'main'
321
+ );
322
+
323
+ //* Social Meta Box
324
+ if ( $social )
325
+ add_meta_box(
326
+ 'autodescription-social-settings',
327
+ __( 'Social Meta Settings', 'autodescription' ),
328
+ array( $this, 'social_metabox' ),
329
+ $this->pagehook,
330
+ 'main'
331
+ );
332
+
333
+ //* Knowledge Graph Meta Box
334
+ if ( $knowledge )
335
+ add_meta_box(
336
+ 'autodescription-knowledgegraph-settings',
337
+ __( 'Knowledge Graph Settings', 'autodescription' ),
338
+ array( $this, 'knowledge_metabox' ),
339
+ $this->pagehook,
340
+ 'main'
341
+ );
342
+
343
+ //* Title Meta Box
344
+ if ( $schema )
345
+ add_meta_box(
346
+ 'autodescription-schema-settings',
347
+ __( 'Schema Settings', 'autodescription' ),
348
+ array( $this, 'schema_metabox' ),
349
+ $this->pagehook,
350
+ 'main'
351
+ );
352
+
353
+ //* Robots Meta Box
354
+ if ( $robots )
355
+ add_meta_box(
356
+ 'autodescription-robots-settings',
357
+ __( 'Robots Meta Settings', 'autodescription' ),
358
+ array( $this, 'robots_metabox' ),
359
+ $this->pagehook,
360
+ 'main'
361
+ );
362
+
363
+ //* Webmaster Meta Box
364
+ if ( $webmaster )
365
+ add_meta_box(
366
+ 'autodescription-webmaster-settings',
367
+ __( 'Webmaster Meta Settings', 'autodescription' ),
368
+ array( $this, 'webmaster_metabox' ),
369
+ $this->pagehook,
370
+ 'main'
371
+ );
372
+
373
+ //* Sitemaps Meta Box
374
+ if ( $sitemap )
375
+ add_meta_box(
376
+ 'autodescription-sitemap-settings',
377
+ __( 'Sitemap Settings', 'autodescription' ),
378
+ array( $this, 'sitemaps_metabox' ),
379
+ $this->pagehook,
380
+ 'main'
381
+ );
382
+
383
+ //* Feed Meta Box
384
+ if ( $feed )
385
+ add_meta_box(
386
+ 'autodescription-feed-settings',
387
+ __( 'Feed Settings', 'autodescription' ),
388
+ array( $this, 'feed_metabox' ),
389
+ $this->pagehook,
390
+ 'main'
391
+ );
392
+
393
+ }
394
+
395
+ /**
396
+ * Use this as the settings admin callback to create an admin page with sortable metaboxes.
397
+ * Create a 'settings_boxes' method to add metaboxes.
398
+ *
399
+ * @since 2.2.2
400
+ */
401
+ public function admin() {
402
+
403
+ ?>
404
+ <div class="wrap autodescription-metaboxes">
405
+ <form method="post" action="options.php">
406
+
407
+ <?php wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false ); ?>
408
+ <?php wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false ); ?>
409
+ <?php settings_fields( $this->settings_field ); ?>
410
+
411
+ <div class="top-wrap">
412
+ <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
413
+ <p class="top-buttons">
414
+ <?php
415
+ submit_button( $this->page_defaults['save_button_text'], 'primary', 'submit', false, array( 'id' => '' ) );
416
+ submit_button( $this->page_defaults['reset_button_text'], 'secondary autodescription-js-confirm-reset', $this->get_field_name( 'reset' ), false, array( 'id' => '' ) );
417
+ ?>
418
+ </p>
419
+ </div>
420
+
421
+ <?php do_action( "{$this->pagehook}_settings_page_boxes", $this->pagehook ); ?>
422
+
423
+ <div class="bottom-buttons">
424
+ <?php
425
+ submit_button( $this->page_defaults['save_button_text'], 'primary', 'submit', false, array( 'id' => '' ) );
426
+ submit_button( $this->page_defaults['reset_button_text'], 'secondary autodescription-js-confirm-reset', $this->get_field_name( 'reset' ), false, array( 'id' => '' ) );
427
+ ?>
428
+ </div>
429
+ </form>
430
+ </div>
431
+ <?php // Add postbox listeners ?>
432
+ <script type="text/javascript">
433
+ //<![CDATA[
434
+ jQuery(document).ready( function ($) {
435
+ // close postboxes that should be closed
436
+ $('.if-js-closed').removeClass('if-js-closed').addClass('closed');
437
+ // postboxes setup
438
+ postboxes.add_postbox_toggles('<?php echo $this->pagehook; ?>');
439
+ });
440
+ //]]>
441
+ </script>
442
+ <?php
443
+
444
+ }
445
+
446
+ /**
447
+ * Use this as the settings admin callback to create an admin page with sortable metaboxes.
448
+ * Create a 'settings_boxes' method to add metaboxes.
449
+ *
450
+ * @since 2.2.2
451
+ */
452
+ public function network_admin() {
453
+
454
+ ?>
455
+ <div class="wrap autodescription-metaboxes">
456
+ <form method="post" action="options.php">
457
+
458
+ <?php wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false ); ?>
459
+ <?php wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false ); ?>
460
+ <?php settings_fields( $this->network_settings_field ); ?>
461
+
462
+ <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
463
+ <p class="top-buttons">
464
+ <?php
465
+ submit_button( $this->page_defaults['save_button_text'], 'primary', 'submit', false, array( 'id' => '' ) );
466
+ submit_button( $this->page_defaults['reset_button_text'], 'secondary autodescription-js-confirm-reset', $this->get_field_name( 'reset' ), false, array( 'id' => '' ) );
467
+ ?>
468
+ </p>
469
+
470
+ <?php do_action( "{$this->network_pagehook}_settings_page_boxes", $this->network_pagehook ); ?>
471
+
472
+ <div class="bottom-buttons">
473
+ <?php
474
+ submit_button( $this->page_defaults['save_button_text'], 'primary', 'submit', false, array( 'id' => '' ) );
475
+ submit_button( $this->page_defaults['reset_button_text'], 'secondary autodescription-js-confirm-reset', $this->get_field_name( 'reset' ), false, array( 'id' => '' ) );
476
+ ?>
477
+ </div>
478
+ </form>
479
+ </div>
480
+ <?php // Add postbox listeners ?>
481
+ <script type="text/javascript">
482
+ //<![CDATA[
483
+ jQuery(document).ready( function ($) {
484
+ // close postboxes that should be closed
485
+ $('.if-js-closed').removeClass('if-js-closed').addClass('closed');
486
+ // postboxes setup
487
+ postboxes.add_postbox_toggles('<?php echo $this->network_pagehook; ?>');
488
+ });
489
+ //]]>
490
+ </script>
491
+ <?php
492
+
493
+ }
494
+
495
+ /**
496
+ * Display notices on the save or reset of settings.
497
+ *
498
+ * @since 2.2.2
499
+ *
500
+ * @return void
501
+ */
502
+ public function notices() {
503
+
504
+ if ( false === $this->is_seo_settings_page() )
505
+ return;
506
+
507
+ if ( isset( $_REQUEST['settings-updated'] ) && 'true' === $_REQUEST['settings-updated'] )
508
+ echo $this->generate_dismissible_notice( $this->page_defaults['saved_notice_text'], 'updated' );
509
+ elseif ( isset( $_REQUEST['reset'] ) && 'true' === $_REQUEST['reset'] )
510
+ echo $this->generate_dismissible_notice( $this->page_defaults['reset_notice_text'], 'warning' );
511
+ elseif ( isset( $_REQUEST['error'] ) && 'true' === $_REQUEST['error'] )
512
+ echo $this->generate_dismissible_notice( $this->page_defaults['error_notice_text'], 'error' );
513
+ elseif ( isset( $_REQUEST['seo-updated'] ) && 'true' === $_REQUEST['seo-updated'] )
514
+ echo $this->generate_dismissible_notice( $this->page_defaults['plugin_update_text'], 'updated' );
515
+
516
+ }
517
+
518
+ /**
519
+ * Helper function that constructs name attributes for use in form fields.
520
+ *
521
+ * Other page implementation classes may wish to construct and use a
522
+ * get_field_id() method, if the naming format needs to be different.
523
+ *
524
+ * @since 2.2.2
525
+ *
526
+ * @param string $name Field name base
527
+ * @return string Full field name
528
+ */
529
+ public function get_field_name( $name ) {
530
+ return sprintf( '%s[%s]', $this->settings_field, $name );
531
+ }
532
+
533
+ /**
534
+ * Echo constructed name attributes in form fields.
535
+ *
536
+ * @since 2.2.2
537
+ *
538
+ * @uses $this->get_field_name() Construct name attributes for use in form fields.
539
+ *
540
+ * @param string $name Field name base
541
+ */
542
+ public function field_name( $name ) {
543
+ echo $this->get_field_name( $name );
544
+ }
545
+
546
+ /**
547
+ * Helper function that constructs id attributes for use in form fields.
548
+ *
549
+ * @since 2.2.2
550
+ *
551
+ * @param string $id Field id base
552
+ * @return string Full field id
553
+ */
554
+ public function get_field_id( $id ) {
555
+ return sprintf( '%s[%s]', $this->settings_field, $id );
556
+ }
557
+
558
+ /**
559
+ * Echo constructed id attributes in form fields.
560
+ *
561
+ * @since 2.2.2
562
+ *
563
+ * @uses $this->get_field_id() Constructs id attributes for use in form fields.
564
+ *
565
+ * @param string $id Field id base
566
+ * @param boolean $echo echo or return
567
+ * @return string Full field id
568
+ */
569
+ public function field_id( $id, $echo = true ) {
570
+
571
+ if ( $echo ) {
572
+ echo $this->get_field_id( $id );
573
+ } else {
574
+ return $this->get_field_id( $id );
575
+ }
576
+ }
577
+
578
+ /**
579
+ * Helper function that returns a setting value from this form's settings
580
+ * field for use in form fields.
581
+ *
582
+ * Fetches blog option.
583
+ *
584
+ * @since 2.2.2
585
+ *
586
+ * @param string $key Field key
587
+ * @return string Field value
588
+ */
589
+ public function get_field_value( $key ) {
590
+ return $this->get_option( $key, $this->settings_field );
591
+ }
592
+
593
+ /**
594
+ * Helper function that returns a setting value from this form's settings
595
+ * field for use in form fields.
596
+ *
597
+ * Fetches network option.
598
+ *
599
+ * @since 2.2.2
600
+ *
601
+ * @param string $key Field key
602
+ * @return string Field value
603
+ */
604
+ public function get_field_value_network( $key ) {
605
+ return $this->get_site_option( $key, $this->settings_field );
606
+ }
607
+
608
+ /**
609
+ * Echo a setting value from this form's settings field for use in form fields.
610
+ *
611
+ * @uses $this->get_field_value() Constructs value attributes for use in form fields.
612
+ *
613
+ * @since 2.2.2
614
+ *
615
+ * @param string $key Field key
616
+ */
617
+ public function field_value( $key ) {
618
+ echo $this->get_field_value( $key );
619
+ }
620
+
621
+ /**
622
+ * Echo or return a chechbox fields wrapper.
623
+ *
624
+ * @since 2.6.0
625
+ *
626
+ * @param string $input The input to wrap.
627
+ * @param bool $echo Whether to echo or return.
628
+ *
629
+ * @return Wrapped $input.
630
+ */
631
+ public function wrap_fields( $input = '', $echo = false ) {
632
+
633
+ if ( is_array( $input ) )
634
+ $input = implode( "\r\n", $input );
635
+
636
+ if ( $echo )
637
+ echo '<div class="theseoframework-fields">' . "\r\n" . $input . "\r\n" . '</div>';
638
+ else
639
+ return '<div class="theseoframework-fields">' . "\r\n" . $input . "\r\n" . '</div>';
640
+ }
641
+
642
+ /**
643
+ * Return a chechbox wrapper.
644
+ *
645
+ * @since 2.6.0
646
+ *
647
+ * @param string $field_id The option ID. Must be within the Autodescription settings.
648
+ * @param string $label The checkbox description label
649
+ * @param string $description Addition description to place beneath the checkbox.
650
+ *
651
+ * @return HTML checkbox output.
652
+ */
653
+ public function make_checkbox( $field_id = '', $label = '', $description = '' ) {
654
+
655
+ $description = $description ? '<p class="description theseoframework-option-spacer">' . $description . '</p>' : '';
656
+
657
+ $output = '<span class="toblock">'
658
+ . '<label for="' . $this->get_field_id( $field_id ) . '">'
659
+ . '<input '
660
+ . 'type="checkbox" '
661
+ . 'name="' . $this->get_field_name( $field_id ) . '" '
662
+ . 'id="' . $this->get_field_id( $field_id ) . '" '
663
+ . $this->get_is_conditional_checked( $field_id ) . ' '
664
+ . 'value="1" '
665
+ . checked( $this->get_field_value( $field_id ), true, false ) .
666
+ ' />'
667
+ . $label
668
+ . '</label>'
669
+ . '</span>'
670
+ . $description
671
+ ;
672
+
673
+ return $output;
674
+ }
675
+
676
+ /**
677
+ * Return a wrapped question mark.
678
+ *
679
+ * @since 2.6.0
680
+ *
681
+ * @param string $description The descriptive on-hover title.
682
+ * @param string $link The non-escaped link.
683
+ * @param bool $echo Whether to echo or return.
684
+ *
685
+ * @return HTML checkbox output.
686
+ */
687
+ public function make_info( $description = '', $link = '', $echo = true ) {
688
+
689
+ if ( $link )
690
+ $output = '<a href="' . esc_url( $link ) . '" target="_blank" title="' . esc_attr( $description ) . '">[?]</a>';
691
+ else
692
+ $output = '<span title="' . esc_attr( $description ) . '">[?]</span>';
693
+
694
+ if ( $echo )
695
+ echo $output;
696
+ else
697
+ return $output;
698
+ }
699
+
700
+ /**
701
+ * Load script and stylesheet assets via metabox_scripts() methods.
702
+ *
703
+ * @since 2.2.2
704
+ */
705
+ public function load_assets() {
706
+ //* Hook scripts method
707
+ add_action( "load-{$this->pagehook}", array( $this, 'metabox_scripts' ) );
708
+ }
709
+
710
+ /**
711
+ * Include the necessary sortable metabox scripts.
712
+ *
713
+ * @since 2.2.2
714
+ */
715
+ public function metabox_scripts() {
716
+ wp_enqueue_script( 'common' );
717
+ wp_enqueue_script( 'wp-lists' );
718
+ wp_enqueue_script( 'postbox' );
719
+ }
720
+
721
+ /**
722
+ * Returns the HTML class wrap for default Checkbox options.
723
+ *
724
+ * This function does nothing special. But is merely a simple wrapper.
725
+ * Just like code_wrap.
726
+ *
727
+ * @param string $key required The option name which returns boolean.
728
+ * @param string $setting optional The settings field
729
+ * @param bool $wrap optional output class="" or just the class name.
730
+ * @param bool $echo optional echo or return the output.
731
+ *
732
+ * @since 2.2.5
733
+ */
734
+ public function is_default_checked( $key, $setting = '', $wrap = true, $echo = true ) {
735
+
736
+ $class = '';
737
+
738
+ $default = $this->get_default_settings( $key, $setting );
739
+
740
+ if ( 1 === $default )
741
+ $class = 'seoframework-default-selected';
742
+
743
+ if ( $echo ) {
744
+ if ( $wrap )
745
+ printf( 'class="%s"', $class );
746
+ else
747
+ echo $class;
748
+ } else {
749
+ if ( $wrap )
750
+ return sprintf( 'class="%s"', $class );
751
+
752
+ return $class;
753
+ }
754
+ }
755
+
756
+ /**
757
+ * Returns the HTML class wrap for warning Checkbox options.
758
+ *
759
+ * This function does nothing special. But is merely a simple wrapper.
760
+ * Just like code_wrap.
761
+ *
762
+ * @param string $key required The option name which returns boolean.
763
+ * @param string $setting optional The settings field
764
+ * @param bool $wrap optional output class="" or just the class name.
765
+ * @param bool $echo optional echo or return the output.
766
+ *
767
+ * @since 2.3.4
768
+ *
769
+ * @return string Empty on echo or The class with an optional wrapper.
770
+ */
771
+ public function is_warning_checked( $key, $setting = '', $wrap = true, $echo = true ) {
772
+
773
+ $class = '';
774
+
775
+ $warned = $this->get_warned_settings( $key, $setting );
776
+
777
+ if ( 1 === $warned )
778
+ $class = 'seoframework-warning-selected';
779
+
780
+ if ( $echo ) {
781
+ if ( $wrap ) {
782
+ printf( 'class="%s"', $class );
783
+ } else {
784
+ echo $class;
785
+ }
786
+ } else {
787
+ if ( $wrap )
788
+ return sprintf( 'class="%s"', $class );
789
+
790
+ return $class;
791
+ }
792
+ }
793
+
794
+ /**
795
+ * Helper function that constructs id attributes for use in form fields.
796
+ *
797
+ * @since 2.6.0
798
+ *
799
+ * @param string $key The option name which returns boolean.
800
+ */
801
+ public function get_is_conditional_checked( $key ) {
802
+ return $this->is_conditional_checked( $key, $this->settings_field, true, false );
803
+ }
804
+
805
+ /**
806
+ * Returns the HTML class wrap for warning/default Checkbox options.
807
+ *
808
+ * This function does nothing special. But is merely a simple wrapper.
809
+ * Just like code_wrap.
810
+ *
811
+ * @param string $key required The option name which returns boolean.
812
+ * @param string $setting optional The settings field
813
+ * @param bool $wrap optional output class="" or just the class name.
814
+ * @param bool $echo optional echo or return the output.
815
+ *
816
+ * @since 2.3.4
817
+ *
818
+ * @return string Empty on echo or The class with an optional wrapper.
819
+ */
820
+ public function is_conditional_checked( $key, $setting = '', $wrap = true, $echo = true ) {
821
+
822
+ $class = '';
823
+
824
+ $default = $this->is_default_checked( $key, $setting, false, false );
825
+ $warned = $this->is_warning_checked( $key, $setting, false, false );
826
+
827
+ if ( '' !== $default && '' !== $warned ) {
828
+ $class = $default . ' ' . $warned;
829
+ } else if ( '' !== $default ) {
830
+ $class = $default;
831
+ } else if ( '' !== $warned ) {
832
+ $class = $warned;
833
+ }
834
+
835
+ if ( $echo ) {
836
+ if ( $wrap ) {
837
+ printf( 'class="%s"', $class );
838
+ } else {
839
+ echo $class;
840
+ }
841
+ } else {
842
+ if ( $wrap ) {
843
+ return sprintf( 'class="%s"', $class );
844
+ } else {
845
+ return $class;
846
+ }
847
+ }
848
+ }
849
+
850
+ /**
851
+ * Returns the HTML class wrap for default radio options.
852
+ *
853
+ * @param string $key required The option name which returns boolean.
854
+ * @param string $value required The option value which returns boolean.
855
+ * @param string $setting optional The settings field
856
+ * @param bool $wrap optional output class="" or just the class name.
857
+ * @param bool $echo optional echo or return the output.
858
+ *
859
+ * @since 2.2.5
860
+ *
861
+ * @TODO use this
862
+ * @priority low 2.8.0+
863
+ *
864
+ * @return string|null the default selected class.
865
+ */
866
+ public function is_default_radio( $key, $value, $setting = '', $wrap = true, $echo = true ) {
867
+
868
+ $class = '';
869
+
870
+ $default = $this->get_default_settings( $key, $setting );
871
+
872
+ if ( $value === $default )
873
+ $class = 'seoframework-default-selected';
874
+
875
+ if ( $echo ) {
876
+ if ( $wrap ) {
877
+ echo sprintf( 'class="%s"', $class );
878
+ } else {
879
+ echo $class;
880
+ }
881
+ } else {
882
+ if ( $wrap )
883
+ return sprintf( 'class="%s"', $class );
884
+
885
+ return $class;
886
+ }
887
+ }
888
+
889
+ }
inc/classes/compat.class.php ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The SEO Framework plugin
4
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License version 3 as published
8
+ * by the Free Software Foundation.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ /**
20
+ * Class AutoDescription_Compat
21
+ *
22
+ * Adds theme/plugin compatibility.
23
+ *
24
+ * @since 2.6.0
25
+ */
26
+ class AutoDescription_Compat extends AutoDescription_Debug {
27
+
28
+ /**
29
+ * Constructor, load parent constructor
30
+ */
31
+ public function __construct() {
32
+ parent::__construct();
33
+
34
+ //* Genesis compat.
35
+ add_action( 'init', array( $this, 'genesis_compat' ) );
36
+ add_filter( 'genesis_detect_seo_plugins', array( $this, 'no_more_genesis_seo' ), 10 );
37
+
38
+ //* Headway compat.
39
+ add_filter( 'headway_seo_disabled', '__return_true' );
40
+
41
+ //* Jetpack compat.
42
+ add_action( 'init', array( $this, 'jetpack_compat' ) );
43
+
44
+ }
45
+
46
+ /**
47
+ * Adds Genesis SEO compatibility.
48
+ *
49
+ * @since 2.6.0
50
+ */
51
+ public function genesis_compat() {
52
+
53
+ //* Nothing to do on admin.
54
+ if ( $this->is_admin() )
55
+ return;
56
+
57
+ //* Reverse the removal of head attributes, this shouldn't affect SEO.
58
+ remove_filter( 'genesis_attr_head', 'genesis_attributes_empty_class' );
59
+ add_filter( 'genesis_attr_head', 'genesis_attributes_head' );
60
+
61
+ }
62
+
63
+ /**
64
+ * Removes the Genesis SEO meta boxes on the SEO Settings page
65
+ *
66
+ * @since 2.2.4
67
+ *
68
+ * @param array $plugins, overwritten as this filter will fire the
69
+ * detection, regardless of other SEO plugins.
70
+ * @return array Plugins to detect.
71
+ */
72
+ public function no_more_genesis_seo( $plugins ) {
73
+
74
+ $plugins = array(
75
+ 'classes' => array(
76
+ 'The_SEO_Framework_Load',
77
+ ),
78
+ 'functions' => array(),
79
+ 'constants' => array(),
80
+ );
81
+
82
+ return $plugins;
83
+ }
84
+
85
+ /**
86
+ * Adds compatibility with various JetPack modules.
87
+ *
88
+ * @since 2.6.0
89
+ */
90
+ public function jetpack_compat() {
91
+
92
+ if ( $this->use_og_tags() ) {
93
+ //* Disable Jetpack Publicize's Open Graph.
94
+ add_filter( 'jetpack_enable_open_graph', '__return_false', 99 );
95
+ }
96
+
97
+ }
98
+
99
+
100
+ }
inc/classes/core.class.php ADDED
@@ -0,0 +1,568 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The SEO Framework plugin
4
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License version 3 as published
8
+ * by the Free Software Foundation.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ /**
20
+ * Class AutoDescription_Core
21
+ *
22
+ * Initializes the plugin & Holds plugin core functions.
23
+ *
24
+ * @since 2.6.0
25
+ */
26
+ class AutoDescription_Core {
27
+
28
+ /**
29
+ * Constructor. Loads actions and filters.
30
+ * Latest Class. Doesn't have parent.
31
+ */
32
+ public function __construct() {
33
+
34
+ add_action( 'current_screen', array( $this, 'post_type_support' ), 0 );
35
+
36
+ /**
37
+ * Add plugin links to the plugin activation page.
38
+ * @since 2.2.8
39
+ */
40
+ add_filter( 'plugin_action_links_' . THE_SEO_FRAMEWORK_PLUGIN_BASENAME, array( $this, 'plugin_action_links' ), 10, 2 );
41
+
42
+ }
43
+
44
+ /**
45
+ * Proportionate dimensions based on Width and Height.
46
+ * AKA Aspect Ratio.
47
+ *
48
+ * @param int $i The dimension to resize.
49
+ * @param int $r1 The deminsion that determines the ratio.
50
+ * @param int $r2 The dimension to proportionate to.
51
+ *
52
+ * @since 2.6.0
53
+ *
54
+ * @return int The proportional dimension, rounded.
55
+ */
56
+ public function proportionate_dimensions( $i, $r1, $r2 ) {
57
+
58
+ //* Get aspect ratio.
59
+ $ar = $r1 / $r2;
60
+
61
+ $i = $i / $ar;
62
+ return round( $i );
63
+ }
64
+
65
+ /**
66
+ * Adds post type support
67
+ *
68
+ * Applies filters the_seo_framework_supported_post_types : The supported post types.
69
+ * @since 2.3.1
70
+ *
71
+ * @since 2.1.6
72
+ */
73
+ public function post_type_support() {
74
+
75
+ /**
76
+ * Added product post type.
77
+ *
78
+ * @since 2.3.1
79
+ */
80
+ $defaults = array(
81
+ 'post', 'page',
82
+ 'product',
83
+ 'forum', 'topic',
84
+ 'jetpack-testimonial', 'jetpack-portfolio',
85
+ );
86
+
87
+ $post_types = (array) apply_filters( 'the_seo_framework_supported_post_types', $defaults );
88
+
89
+ $types = wp_parse_args( $defaults, $post_types );
90
+
91
+ foreach ( $types as $type )
92
+ add_post_type_support( $type, array( 'autodescription-meta' ) );
93
+
94
+ }
95
+
96
+ /**
97
+ * Adds link from plugins page to SEO Settings page.
98
+ *
99
+ * @param array $links The current links.
100
+ *
101
+ * @since 2.2.8
102
+ */
103
+ public function plugin_action_links( $links = array() ) {
104
+
105
+ $framework_links = array();
106
+
107
+ if ( $this->load_options )
108
+ $framework_links['settings'] = '<a href="' . esc_url( admin_url( 'admin.php?page=' . $this->page_id ) ) . '">' . __( 'SEO Settings', 'autodescription' ) . '</a>';
109
+
110
+ $framework_links['home'] = '<a href="'. esc_url( 'https://theseoframework.com/' ) . '" target="_blank">' . _x( 'Plugin Home', 'As in: The Plugin Home Page', 'autodescription' ) . '</a>';
111
+
112
+ return array_merge( $framework_links, $links );
113
+ }
114
+
115
+ /**
116
+ * Returns the front page ID, if home is a page.
117
+ *
118
+ * @since 2.6.0
119
+ *
120
+ * @return int the ID.
121
+ */
122
+ public function get_the_front_page_ID() {
123
+
124
+ static $front_id = null;
125
+
126
+ if ( isset( $front_id ) )
127
+ return $front_id;
128
+
129
+ return $front_id = $this->has_page_on_front() ? (int) get_option( 'page_on_front' ) : 0;
130
+ }
131
+
132
+ /**
133
+ * Generate dismissible notice.
134
+ *
135
+ * @param $message The notice message.
136
+ * @param $type The notice type : 'updated', 'error', 'warning'
137
+ *
138
+ * @since 2.6.0
139
+ */
140
+ public function generate_dismissible_notice( $message = '', $type = 'updated' ) {
141
+
142
+ if ( empty( $message ) )
143
+ return '';
144
+
145
+ if ( 'warning' === $type )
146
+ $type = 'notice-warning';
147
+
148
+ $notice = '<div class="notice ' . $type . ' seo-notice"><p>';
149
+ $notice .= '<a class="hide-if-no-js autodescription-dismiss" title="' . __( 'Dismiss', 'AutoDescription' ) . '"></a>';
150
+ $notice .= '<strong>' . $message . '</strong>';
151
+ $notice .= '</p></div>';
152
+
153
+ return $notice;
154
+ }
155
+
156
+ /**
157
+ * Mark up content with code tags.
158
+ *
159
+ * Escapes all HTML, so `<` gets changed to `&lt;` and displays correctly.
160
+ *
161
+ * @since 2.0.0
162
+ *
163
+ * @param string $content Content to be wrapped in code tags.
164
+ *
165
+ * @return string Content wrapped in code tags.
166
+ */
167
+ public function code_wrap( $content ) {
168
+ return '<code>' . esc_html( $content ) . '</code>';
169
+ }
170
+
171
+ /**
172
+ * Mark up content with code tags.
173
+ *
174
+ * Escapes no HTML.
175
+ *
176
+ * @since 2.2.2
177
+ *
178
+ * @param string $content Content to be wrapped in code tags.
179
+ *
180
+ * @return string Content wrapped in code tags.
181
+ */
182
+ public function code_wrap_noesc( $content ) {
183
+ return '<code>' . $content . '</code>';
184
+ }
185
+
186
+ /**
187
+ * Return custom field post meta data.
188
+ *
189
+ * Return only the first value of custom field. Return false if field is
190
+ * blank or not set.
191
+ *
192
+ * @since 2.0.0
193
+ *
194
+ * @param string $field Custom field key.
195
+ * @param int $post_id The post ID
196
+ *
197
+ * @return string|boolean Return value or false on failure.
198
+ *
199
+ * @thanks StudioPress (http://www.studiopress.com/) for some code.
200
+ *
201
+ * @staticvar array $field_cache
202
+ * @since 2.2.5
203
+ */
204
+ public function get_custom_field( $field, $post_id = null ) {
205
+
206
+ //* No field has been provided.
207
+ if ( empty( $field ) )
208
+ return false;
209
+
210
+ //* Setup cache.
211
+ static $field_cache = array();
212
+
213
+ //* Check field cache.
214
+ if ( isset( $field_cache[$field][$post_id] ) )
215
+ //* Field has been cached.
216
+ return $field_cache[$field][$post_id];
217
+
218
+ if ( null === $post_id || empty( $post_id ) )
219
+ $post_id = $this->get_the_real_ID();
220
+
221
+ if ( null === $post_id || empty( $post_id ) )
222
+ return '';
223
+
224
+ $custom_field = get_post_meta( $post_id, $field, true );
225
+
226
+ // If custom field is empty, return null.
227
+ if ( empty( $custom_field ) )
228
+ $field_cache[$field][$post_id] = '';
229
+
230
+ //* Render custom field, slashes stripped, sanitized if string
231
+ $field_cache[$field][$post_id] = is_array( $custom_field ) ? stripslashes_deep( $custom_field ) : stripslashes( wp_kses_decode_entities( $custom_field ) );
232
+
233
+ return $field_cache[$field][$post_id];
234
+ }
235
+
236
+ /**
237
+ * Google docs language determinator.
238
+ *
239
+ * @since 2.2.2
240
+ *
241
+ * @staticvar string $language
242
+ *
243
+ * @return string language code
244
+ */
245
+ protected function google_language() {
246
+
247
+ /**
248
+ * Cache value
249
+ * @since 2.2.4
250
+ */
251
+ static $language = null;
252
+
253
+ if ( isset( $language ) )
254
+ return $language;
255
+
256
+ //* Language shorttag to be used in Google help pages,
257
+ $language = _x( 'en', 'e.g. en for English, nl for Dutch, fi for Finish, de for German', 'autodescription' );
258
+
259
+ return $language;
260
+ }
261
+
262
+ /**
263
+ * Whether to allow external redirect through the 301 redirect option.
264
+ *
265
+ * Applies filters the_seo_framework_allow_external_redirect : bool
266
+ * @staticvar bool $allowed
267
+ *
268
+ * @since 2.6.0
269
+ *
270
+ * @return bool Whether external redirect is allowed.
271
+ */
272
+ public function allow_external_redirect() {
273
+
274
+ static $allowed = null;
275
+
276
+ if ( isset( $allowed ) )
277
+ return $allowed;
278
+
279
+ return $allowed = (bool) apply_filters( 'the_seo_framework_allow_external_redirect', true );
280
+ }
281
+
282
+ /**
283
+ * Object cache set wrapper.
284
+ *
285
+ * @since 2.4.3
286
+ *
287
+ * @param string $key The Object cache key.
288
+ * @param mixed $data The Object cache data.
289
+ * @param int $expire The Object cache expire time.
290
+ * @param string $group The Object cache group.
291
+ * @return bool true on set, false when disabled.
292
+ */
293
+ public function object_cache_set( $key, $data, $expire = 0, $group = 'the_seo_framework' ) {
294
+
295
+ if ( $this->use_object_cache )
296
+ return wp_cache_set( $key, $data, $group, $expire );
297
+
298
+ return false;
299
+ }
300
+
301
+ /**
302
+ * Object cache get wrapper.
303
+ *
304
+ * @param string $key The Object cache key.
305
+ * @param string $group The Object cache group.
306
+ * @param bool $force Whether to force an update of the local cache.
307
+ * @param bool $found Whether the key was found in the cache. Disambiguates a return of false, a storable value.
308
+ *
309
+ * @since 2.4.3
310
+ *
311
+ * @return mixed wp_cache_get if object caching is allowed. False otherwise.
312
+ */
313
+ public function object_cache_get( $key, $group = 'the_seo_framework', $force = false, &$found = null ) {
314
+
315
+ if ( $this->use_object_cache )
316
+ return wp_cache_get( $key, $group, $force, $found );
317
+
318
+ return false;
319
+ }
320
+
321
+ /**
322
+ * Faster way of doing an in_array search compared to default PHP behavior.
323
+ * @NOTE only to show improvement with large arrays. Might slow down with small arrays.
324
+ * @NOTE can't do type checks. Always assume the comparing value is a string.
325
+ *
326
+ * @since 2.5.2
327
+ *
328
+ * @param string|array $needle The needle(s) to search for
329
+ * @param array $array The single dimensional array to search in.
330
+ * @return bool true if value is in array.
331
+ */
332
+ public function in_array( $needle, $array ) {
333
+
334
+ $array = array_flip( $array );
335
+
336
+ if ( is_string( $needle ) ) {
337
+ if ( isset( $array[$needle] ) )
338
+ return true;
339
+ } else if ( is_array( $needle ) ) {
340
+ foreach ( $needle as $str ) {
341
+ if ( isset( $array[$str] ) )
342
+ return true;
343
+ }
344
+ }
345
+
346
+ return false;
347
+ }
348
+
349
+ /**
350
+ * Checks if the string input is exactly '1'.
351
+ *
352
+ * @since 2.6.0
353
+ *
354
+ * @param string $value The value to check.
355
+ * @return bool true if value is '1'
356
+ */
357
+ public function is_checked( $value ) {
358
+
359
+ if ( '1' === $value )
360
+ return true;
361
+
362
+ return false;
363
+ }
364
+
365
+ /**
366
+ * Checks if the option is used and checked.
367
+ *
368
+ * @since 2.6.0
369
+ *
370
+ * @param string $option The option name.
371
+ * @return bool Option is checked.
372
+ */
373
+ public function is_option_checked( $option ) {
374
+
375
+ $option = $this->get_option( $option );
376
+
377
+ if ( $this->is_checked( $option ) )
378
+ return true;
379
+
380
+ return false;
381
+ }
382
+
383
+ /**
384
+ * Checks if blog is public through WordPress core settings.
385
+ *
386
+ * @since 2.6.0
387
+ * @staticvar bool $cache
388
+ *
389
+ * @return bool True is blog is public.
390
+ */
391
+ public function is_blog_public() {
392
+
393
+ static $cache = null;
394
+
395
+ if ( isset( $cache ) )
396
+ return $cache;
397
+
398
+ if ( '1' === get_option( 'blog_public' ) )
399
+ return $cache = true;
400
+
401
+ return $cache = false;
402
+ }
403
+
404
+ /**
405
+ * Whether the current blog is spam or deleted.
406
+ * Multisite Only.
407
+ *
408
+ * @since 2.6.0
409
+ * @global object $current_blog. NULL on single site.
410
+ *
411
+ * @return bool Current blog is spam.
412
+ */
413
+ public function current_blog_is_spam_or_deleted() {
414
+ global $current_blog;
415
+
416
+ if ( isset( $current_blog ) && ( '1' === $current_blog->spam || '1' === $current_blog->deleted ) )
417
+ return true;
418
+
419
+ return false;
420
+ }
421
+
422
+ /**
423
+ * Whether to lowercase the noun or keep it UCfirst.
424
+ * Depending if language is German.
425
+ *
426
+ * @since 2.6.0
427
+ * @staticvar array $lowercase Contains nouns.
428
+ *
429
+ * @return string The maybe lowercase noun.
430
+ */
431
+ public function maybe_lowercase_noun( $noun ) {
432
+
433
+ static $lowercase = array();
434
+
435
+ if ( isset( $lowercase[$noun] ) )
436
+ return $lowercase[$noun];
437
+
438
+ return $lowercase[$noun] = $this->check_wp_locale( 'de' ) ? $noun : strtolower( $noun );
439
+ }
440
+
441
+ /**
442
+ * Returns the minimum role required to adjust settings.
443
+ *
444
+ * Applies filter 'the_seo_framework_settings_capability' : string
445
+ * This filter changes the minimum role for viewing and editing the plugin's settings.
446
+ *
447
+ * @since 2.6.0
448
+ * @access private
449
+ *
450
+ * @return string The minimum required capability for SEO Settings.
451
+ */
452
+ public function settings_capability() {
453
+ return (string) apply_filters( 'the_seo_framework_settings_capability', 'manage_options' );
454
+ }
455
+
456
+ /**
457
+ * Returns the SEO Settings page URL.
458
+ *
459
+ * @since 2.6.0
460
+ *
461
+ * @return string The escaped SEO Settings page URL.
462
+ */
463
+ public function seo_settings_page_url() {
464
+
465
+ if ( $this->load_options ) {
466
+ //* Options are allowed to be loaded.
467
+
468
+ $url = html_entity_decode( menu_page_url( $this->page_id, 0 ) );
469
+
470
+ return esc_url( $url );
471
+ }
472
+
473
+ return '';
474
+ }
475
+
476
+ /**
477
+ * Returns the PHP timezone compatible string.
478
+ * UTC offsets are unreliable.
479
+ *
480
+ * @since 2.6.0
481
+ *
482
+ * @param bool $guess : If true, the timezone will be guessed from the
483
+ * WordPress core gmt_offset option.
484
+ *
485
+ * @return string|empty PHP Timezone String.
486
+ */
487
+ public function get_timezone_string( $guess = false ) {
488
+
489
+ $tzstring = get_option( 'timezone_string' );
490
+
491
+ if ( false !== strpos( $tzstring, 'Etc/GMT' ) )
492
+ $tzstring = '';
493
+
494
+ if ( $guess && empty( $tzstring ) ) {
495
+ $offset = get_option( 'gmt_offset' );
496
+ $tzstring = $this->get_tzstring_from_offset( $offset );
497
+ }
498
+
499
+ return $tzstring;
500
+ }
501
+
502
+ /**
503
+ * Fetches the Timezone String from given offset.
504
+ *
505
+ * @since 2.6.0
506
+ *
507
+ * @param int $offset The GMT offzet.
508
+ *
509
+ * @return string PHP Timezone String.
510
+ */
511
+ protected function get_tzstring_from_offset( $offset = 0 ) {
512
+
513
+ $seconds = round( $offset * HOUR_IN_SECONDS );
514
+
515
+ //* Try Daylight savings.
516
+ $tzstring = timezone_name_from_abbr( '', $seconds, 1 );
517
+ /**
518
+ * PHP bug workaround.
519
+ * @link https://bugs.php.net/bug.php?id=44780
520
+ */
521
+ if ( false === $tzstring )
522
+ $tzstring = timezone_name_from_abbr( '', $seconds, 0 );
523
+
524
+ return $tzstring;
525
+ }
526
+
527
+ /**
528
+ * Sets and resets the timezone.
529
+ *
530
+ * @since 2.6.0
531
+ *
532
+ * @param string $tzstring Optional. The PHP Timezone string. Best to leave empty to always get a correct one.
533
+ * @link http://php.net/manual/en/timezones.php
534
+ * @param bool $reset Whether to reset to default. Ignoring first parameter.
535
+ *
536
+ * @return bool True on success. False on failure.
537
+ */
538
+ public function set_timezone( $tzstring = '', $reset = false ) {
539
+
540
+ static $old_tz = null;
541
+
542
+ if ( is_null( $old_tz ) ) {
543
+ $old_tz = date_default_timezone_get();
544
+ if ( empty( $old_tz ) )
545
+ $old_tz = 'UTC';
546
+ }
547
+
548
+ if ( $reset )
549
+ return date_default_timezone_set( $old_tz );
550
+
551
+ if ( empty( $tzstring ) )
552
+ $tzstring = $this->get_timezone_string( true );
553
+
554
+ return date_default_timezone_set( $tzstring );
555
+ }
556
+
557
+ /**
558
+ * Resets the timezone to default or UTC.
559
+ *
560
+ * @since 2.6.0
561
+ *
562
+ * @return bool True on success. False on failure.
563
+ */
564
+ public function reset_timezone() {
565
+ return $this->set_timezone( '', true );
566
+ }
567
+
568
+ }
inc/classes/debug.class.php ADDED
@@ -0,0 +1,890 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The SEO Framework plugin
4
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License version 3 as published
8
+ * by the Free Software Foundation.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ /**
20
+ * Class AutoDescription_Debug
21
+ *
22
+ * Holds plugin debug functions.
23
+ *
24
+ * @since 2.6.0
25
+ */
26
+ class AutoDescription_Debug extends AutoDescription_Core {
27
+
28
+ /**
29
+ * Enqueue the debug output.
30
+ *
31
+ * @since 2.6.0
32
+ *
33
+ * @var string The debug output.
34
+ */
35
+ protected $debug_output = '';
36
+
37
+ /**
38
+ * Whether to accumulate data.
39
+ *
40
+ * @since 2.6.5
41
+ *
42
+ * @var bool Whether to add to AutoDescription_Debug::debug_output.
43
+ */
44
+ protected $add_debug_output = true;
45
+
46
+ /**
47
+ * Constructor, load parent constructor and add actions.
48
+ */
49
+ public function __construct() {
50
+ parent::__construct();
51
+
52
+ if ( $this->the_seo_framework_debug ) {
53
+ add_action( 'admin_footer', array( $this, 'debug_screens' ) );
54
+ add_action( 'admin_footer', array( $this, 'debug_output' ) );
55
+ add_action( 'wp_footer', array( $this, 'debug_output' ) );
56
+ }
57
+
58
+ }
59
+
60
+ /**
61
+ * Mark a function as deprecated and inform when it has been used.
62
+ *
63
+ * Taken from WordPress core, but added extra parameters and linguistic alterations.
64
+ *
65
+ * The current behavior is to trigger a user error if WP_DEBUG is true.
66
+ *
67
+ * @since 2.6.0
68
+ * @access private
69
+ *
70
+ * @param string $function The function that was called.
71
+ * @param string $version The version of WordPress that deprecated the function.
72
+ * @param string $replacement Optional. The function that should have been called. Default null.
73
+ */
74
+ public function _deprecated_function( $function, $version, $replacement = null ) {
75
+ /**
76
+ * Fires when a deprecated function is called.
77
+ *
78
+ * @since WP Core 2.5.0
79
+ *
80
+ * @param string $function The function that was called.
81
+ * @param string $replacement The function that should have been called.
82
+ * @param string $version The version of WordPress that deprecated the function.
83
+ */
84
+ do_action( 'deprecated_function_run', $function, $replacement, $version );
85
+
86
+ /**
87
+ * Filter whether to trigger an error for deprecated functions.
88
+ *
89
+ * @since WP Core 2.5.0
90
+ *
91
+ * @param bool $trigger Whether to trigger the error for deprecated functions. Default true.
92
+ */
93
+ if ( WP_DEBUG && apply_filters( 'deprecated_function_trigger_error', true ) ) {
94
+
95
+ set_error_handler( array( $this, 'error_handler_deprecated' ) );
96
+
97
+ if ( function_exists( '__' ) ) {
98
+ if ( isset( $replacement ) )
99
+ trigger_error( sprintf( __( '%1$s is <strong>deprecated</strong> since version %2$s of The SEO Framework! Use %3$s instead.', 'autodescription' ), $function, $version, $replacement ) );
100
+ else
101
+ trigger_error( sprintf( __( '%1$s is <strong>deprecated</strong> since version %2$s of The SEO Framework with no alternative available.' ), $function, $version ) );
102
+ } else {
103
+ if ( isset( $replacement ) )
104
+ trigger_error( sprintf( '%1$s is <strong>deprecated</strong> since version %2$s of The SEO Framework! Use %3$s instead.', $function, $version, $replacement ) );
105
+ else
106
+ trigger_error( sprintf( '%1$s is <strong>deprecated</strong> since version %2$s of The SEO Framework with no alternative available.', $function, $version ) );
107
+ }
108
+
109
+ restore_error_handler();
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Mark a function as deprecated and inform when it has been used.
115
+ *
116
+ * Taken from WordPress core, but added extra parameters and linguistic alterations.
117
+ *
118
+ * The current behavior is to trigger a user error if WP_DEBUG is true.
119
+ *
120
+ * @since 2.6.0
121
+ * @access private
122
+ *
123
+ * @param string $function The function that was called.
124
+ * @param string $message A message explaining what has been done incorrectly.
125
+ * @param string $version The version of WordPress where the message was added.
126
+ */
127
+ public function _doing_it_wrong( $function, $message, $version ) {
128
+ /**
129
+ * Fires when the given function is being used incorrectly.
130
+ *
131
+ * @since WP Core 3.1.0
132
+ *
133
+ * @param string $function The function that was called.
134
+ * @param string $message A message explaining what has been done incorrectly.
135
+ * @param string $version The version of WordPress where the message was added.
136
+ */
137
+ do_action( 'doing_it_wrong_run', $function, $message, $version );
138
+
139
+ /**
140
+ * Filter whether to trigger an error for _doing_it_wrong() calls.
141
+ *
142
+ * @since 3.1.0
143
+ *
144
+ * @param bool $trigger Whether to trigger the error for _doing_it_wrong() calls. Default true.
145
+ */
146
+ if ( WP_DEBUG && apply_filters( 'doing_it_wrong_trigger_error', true ) ) {
147
+
148
+ set_error_handler( array( $this, 'error_handler_doing_it_wrong' ) );
149
+
150
+ if ( function_exists( '__' ) ) {
151
+ $version = is_null( $version ) ? '' : sprintf( __( '(This message was added in version %s of The SEO Framework.)' ), $version );
152
+ /* translators: %s: Codex URL */
153
+ $message .= ' ' . sprintf( __( 'Please see <a href="%s">Debugging in WordPress</a> for more information.', 'autodescription' ),
154
+ __( 'https://codex.wordpress.org/Debugging_in_WordPress', 'autodescription' )
155
+ );
156
+ /* translators: 1: Function name, 2: Message, 3: Plugin Version notification */
157
+ trigger_error( sprintf( __( '%1$s was called <strong>incorrectly</strong>. %2$s %3$s', 'autodescription' ), $function, $message, $version ) );
158
+ } else {
159
+ $version = is_null( $version ) ? '' : sprintf( '(This message was added in version %s of The SEO Framework.)', $version );
160
+ $message .= ' ' . sprintf( 'Please see <a href="%s">Debugging in WordPress</a> for more information.',
161
+ 'https://codex.wordpress.org/Debugging_in_WordPress'
162
+ );
163
+
164
+ /* translators: 1: Function name, 2: Message, 3: Plugin Version notification */
165
+ trigger_error( sprintf( '%1$s was called <strong>incorrectly</strong>. %2$s %3$s', $function, $message, $version ) );
166
+ }
167
+
168
+ restore_error_handler();
169
+ }
170
+ }
171
+
172
+ /**
173
+ * The SEO Framework error handler.
174
+ *
175
+ * Only handles notices.
176
+ * @see E_USER_NOTICE
177
+ *
178
+ * @since 2.6.0
179
+ *
180
+ * @param int Error handling code.
181
+ * @param string The error message.
182
+ */
183
+ protected function error_handler_deprecated( $code, $message ) {
184
+
185
+ //* Only do so if E_USER_NOTICE is pased.
186
+ if ( $code >= 1024 && isset( $message ) ) {
187
+
188
+ $backtrace = debug_backtrace();
189
+ /**
190
+ * 0 = This function. 1 = Debug function. 2 = Error trigger. 3 = Deprecated call.
191
+ */
192
+ $error = $backtrace[3];
193
+
194
+ $this->error_handler( $error, $message );
195
+ }
196
+
197
+ }
198
+
199
+ /**
200
+ * The SEO Framework error handler.
201
+ *
202
+ * Only handles notices.
203
+ * @see E_USER_NOTICE
204
+ *
205
+ * @since 2.6.0
206
+ *
207
+ * @param int Error handling code.
208
+ * @param string The error message.
209
+ */
210
+ protected function error_handler_doing_it_wrong( $code, $message ) {
211
+
212
+ //* Only do so if E_USER_NOTICE is pased.
213
+ if ( $code >= 1024 && isset( $message ) ) {
214
+
215
+ $backtrace = debug_backtrace();
216
+ /**
217
+ * 0 = This function. 1 = Debug function. 2 = Error trigger.
218
+ */
219
+ $error = $backtrace[2];
220
+
221
+ $this->error_handler( $error, $message );
222
+ }
223
+
224
+ }
225
+
226
+ /**
227
+ * Echo's error.
228
+ *
229
+ * @access private
230
+ * Please don't use this error handler.
231
+ *
232
+ * @since 2.6.0
233
+ *
234
+ * @param array $error The Error location and file.
235
+ * @param string $message The error message.
236
+ */
237
+ protected function error_handler( $error, $message ) {
238
+
239
+ $file = isset( $error['file'] ) ? $error['file'] : '';
240
+ $line = isset( $error['line'] ) ? $error['line'] : '';
241
+
242
+ if ( isset( $message ) ) {
243
+ echo "\r\n" . '<strong>Notice:</strong> ' . $message;
244
+ echo $file ? ' In ' . $file : '';
245
+ echo $line ? ' on line ' . $line : '';
246
+ echo ".<br>\r\n";
247
+ }
248
+ }
249
+
250
+ /**
251
+ * Echo found screens in the admin footer when debugging is enabled.
252
+ *
253
+ * @uses bool $this->the_seo_framework_debug
254
+ * @global array $current_screen
255
+ *
256
+ * @access private
257
+ * @since 2.5.2
258
+ */
259
+ public function debug_screens() {
260
+ global $current_screen;
261
+
262
+ $this->debug_init( __CLASS__, __FUNCTION__, false, '', get_defined_vars() );
263
+
264
+ }
265
+
266
+ /**
267
+ * Echos debug output.
268
+ *
269
+ * @access private
270
+ * @since 2.6.0
271
+ */
272
+ public function debug_output() {
273
+
274
+ if ( $this->debug_output ) {
275
+ if ( $this->the_seo_framework_debug_hidden ) {
276
+ echo "\r\n<!--\r\n:: THE SEO FRAMEWORK DEBUG :: \r\n" . $this->debug_output . "\r\n:: / THE SEO FRAMEWORK DEBUG ::\r\n-->\r\n";
277
+ } else {
278
+
279
+ $id = $this->get_the_real_ID();
280
+ $mdash = ' &mdash; ';
281
+ $term = $this->is_archive() ? $this->fetch_the_term( $id ) : '';
282
+ $taxonomy = isset( $term->taxonomy ) ? $term->taxonomy : '';
283
+
284
+ //* This will return 'Page' on all non-archive types (except the home page)
285
+ $type = ! $this->is_archive() && $this->is_front_page( $id ) ? 'Front Page' : $this->get_the_term_name( $term );
286
+ $cache_key = $this->generate_cache_key( $this->get_the_real_ID(), $taxonomy );
287
+
288
+ if ( $this->is_admin() ) {
289
+ ?>
290
+ <div style="color:#444;font-family:Georgio,sans-serif;font-size:14px;clear:both;float:left;position:relative;width:calc( 100% - 200px );min-height:700px;padding:0;margin:20px 20px 40px 180px;overflow:hidden;border:1px solid #ccc;border-radius:3px;font:'Open Sans',sans-serif">
291
+ <h3 style="font-size:14px;padding:0 12px;margin:0;line-height:39px;border-bottom:2px solid #aaa;position:absolute;z-index:1;width:100%;right:0;left:0;top:0;background:#fff;border-radius:3px 3px 0 0;height:39px;">
292
+ SEO Debug Information
293
+ <?php
294
+ if ( $this->is_post_edit() || $this->is_term_edit() ) :
295
+ echo ' :: ';
296
+ echo 'Type: ' . $type;
297
+ echo $mdash . 'ID: ' . $id;
298
+ echo $mdash . 'Cache key: ' . $cache_key;
299
+ endif;
300
+ ?>
301
+ </h3>
302
+ <div style="position:absolute;bottom:0;right:0;left:0;top:41px;margin:0;padding:0;background:#fff;border-radius:3px;overflow-x:hidden;">
303
+ <?php echo $this->debug_header_output(); ?>
304
+ <?php echo $this->debug_query_output(); ?>
305
+ <?php echo $this->debug_output; ?>
306
+ </div>
307
+ </div>
308
+ <?php
309
+ } else {
310
+ ?>
311
+ <style type="text/css">.wp-ui-notification{color:#fff;background-color:#d54e21}.code.highlight{font-family:Consolas,Monaco,monospace;font-size:14px;}</style>
312
+ <div style="color:#444;font-family:Georgio,sans-serif;font-size:14px;clear:both;float:left;position:relative;width:calc( 100% - 80px );min-height:700px;padding:0;margin:40px;overflow:hidden;border:1px solid #ccc;border-radius:3px;font:'Open Sans',sans-serif">
313
+ <h3 style="font-size:14px;padding:0 12px;margin:0;line-height:39px;border-bottom:2px solid #aaa;position:absolute;z-index:1;width:100%;right:0;left:0;top:0;background:#fff;border-radius:3px 3px 0 0;height:39px;">
314
+ SEO Debug Information
315
+ <?php
316
+ echo ' :: ';
317
+ echo 'Type: ' . $type;
318
+ echo $mdash . 'ID: ' . $id;
319
+ echo $mdash . 'Cache key: ' . $cache_key;
320
+ ?>
321
+ </h3>
322
+ <div style="position:absolute;bottom:0;right:0;left:0;top:41px;margin:0;padding:0;background:#fff;border-radius:3px;overflow-x:hidden;">
323
+ <?php echo $this->debug_header_output(); ?>
324
+ <?php echo $this->debug_query_output(); ?>
325
+ <?php echo $this->debug_output; ?>
326
+ </div>
327
+ </div>
328
+ <?php
329
+ }
330
+ }
331
+ }
332
+
333
+ }
334
+
335
+ /**
336
+ * Parses input values and wraps them in human-readable elements.
337
+ *
338
+ * @access private
339
+ * @since 2.6.0
340
+ *
341
+ * @param mixed $values Values to be parsed.
342
+ * @return string $output The parsed value.
343
+ */
344
+ public function get_debug_information( $values = null ) {
345
+
346
+ $output = '';
347
+
348
+ if ( $this->the_seo_framework_debug ) {
349
+
350
+ $output .= "\r\n";
351
+ $output .= $this->the_seo_framework_debug_hidden ? '' : '<span class="code highlight">';
352
+
353
+ if ( is_null( $values ) ) {
354
+ $output .= $this->debug_value_wrapper( "Debug message: Value isn't set." ) . "\r\n";
355
+ $output .= $this->the_seo_framework_debug_hidden ? '' : '</span>';
356
+
357
+ return $output;
358
+ }
359
+
360
+ if ( is_object( $values ) ) {
361
+ //* Turn objects into arrays.
362
+ $values = (array) $values;
363
+
364
+ foreach ( $values as $key => $value ) {
365
+ if ( is_object( $value ) ) {
366
+ foreach ( (array) $value as $key => $v ) {
367
+ $values = $v;
368
+ break;
369
+ }
370
+ }
371
+ break;
372
+ }
373
+ }
374
+
375
+ /**
376
+ * @TODO Use var_export()?
377
+ */
378
+ if ( is_array( $values ) ) {
379
+ $output .= $this->the_seo_framework_debug_hidden ? '' : '<div style="margin:0;padding-left:12px">';
380
+ foreach ( $values as $key => $value ) {
381
+ $output .= "\t\t";
382
+ if ( '' === $value ) {
383
+ $output .= $this->debug_key_wrapper( $key ) . ' => ';
384
+ $output .= $this->debug_value_wrapper( "''" );
385
+ $output .= "\r\n";
386
+ } else if ( is_string( $value ) || is_int( $value ) ) {
387
+ $output .= $this->debug_key_wrapper( $key ) . ' => ';
388
+ $output .= $this->debug_value_wrapper( $value );
389
+ $output .= "\r\n";
390
+ } else if ( is_bool( $value ) ) {
391
+ $output .= $this->debug_key_wrapper( $key ) . ' => ';
392
+ $output .= $this->debug_value_wrapper( $value ? 'true' : 'false' );
393
+ $output .= "\r\n";
394
+ } else if ( is_array( $value ) ) {
395
+ $output .= $this->debug_key_wrapper( $key ) . ' => ';
396
+ $output .= "Array[\r\n";
397
+ $output .= $this->the_seo_framework_debug_hidden ? '' : '<p style="margin:0;padding-left:12px">';
398
+ foreach ( $value as $k => $v ) {
399
+ $output .= "\t\t\t";
400
+ if ( '' === $v ) {
401
+ $output .= $this->debug_key_wrapper( $k ) . ' => ';
402
+ $output .= $this->debug_value_wrapper( "''" );
403
+ } else if ( is_string( $v ) || is_int( $v ) ) {
404
+ $output .= $this->debug_key_wrapper( $k ) . ' => ';
405
+ $output .= $this->debug_value_wrapper( $v );
406
+ } else if ( is_bool( $v ) ) {
407
+ $output .= $this->debug_key_wrapper( $k ) . ' => ';
408
+ $output .= $this->debug_value_wrapper( $v ? 'true' : 'false' );
409
+ } else if ( is_array( $v ) ) {
410
+ $output .= $this->debug_key_wrapper( $k ) . ' => ';
411
+ $output .= $this->debug_value_wrapper( 'Debug message: Three+ dimensional array' );
412
+ } else {
413
+ $output .= $this->debug_key_wrapper( $k ) . ' => ';
414
+ $output .= $this->debug_value_wrapper( $v );
415
+ }
416
+ $output .= ',';
417
+ $output .= "\r\n";
418
+ $output .= $this->the_seo_framework_debug_hidden ? '' : '<br>';
419
+ }
420
+ $output .= $this->the_seo_framework_debug_hidden ? '' : '</p>';
421
+ $output .= "]";
422
+ } else {
423
+ $output .= $this->debug_key_wrapper( $key ) . ' => ';
424
+ $output .= $this->debug_value_wrapper( $value );
425
+ $output .= "\r\n";
426
+ }
427
+ $output .= $this->the_seo_framework_debug_hidden ? '' : '<br>';
428
+ }
429
+ $output .= $this->the_seo_framework_debug_hidden ? '' : '</div>';
430
+ } else if ( '' === $values ) {
431
+ $output .= "\t\t";
432
+ $output .= $this->debug_value_wrapper( "''" );
433
+ } else if ( is_string( $values ) || is_int( $values ) ) {
434
+ $output .= "\t\t";
435
+ $output .= $this->debug_value_wrapper( $values );
436
+ } else if ( is_bool( $values ) ) {
437
+ $output .= "\t\t";
438
+ $output .= $this->debug_value_wrapper( $values ? 'true' : 'false' );
439
+ } else {
440
+ $output .= "\t\t";
441
+ $output .= $this->debug_value_wrapper( $values );
442
+ }
443
+
444
+ $output .= $this->the_seo_framework_debug_hidden ? '' : '</span>';
445
+ $output .= "\r\n";
446
+ }
447
+
448
+ return $output;
449
+ }
450
+
451
+ /**
452
+ * Wrap debug key in a colored span.
453
+ *
454
+ * @param string $key The debug key.
455
+ * @param bool $ignore Ignore the hidden output.
456
+ *
457
+ * @since 2.3.9
458
+ * @access private
459
+ *
460
+ * @return string
461
+ */
462
+ public function debug_key_wrapper( $key, $ignore = false ) {
463
+
464
+ if ( $ignore || false === $this->the_seo_framework_debug_hidden )
465
+ return '<font color="chucknorris">' . esc_attr( (string) $key ) . '</font>';
466
+
467
+ return esc_attr( (string) $key );
468
+ }
469
+
470
+ /**
471
+ * Wrap debug value in a colored span.
472
+ *
473
+ * @param string $value The debug value.
474
+ * @param bool $ignore Ignore the hidden output.
475
+ *
476
+ * @since 2.3.9
477
+ * @access private
478
+ *
479
+ * @return string
480
+ */
481
+ public function debug_value_wrapper( $value, $ignore = false ) {
482
+
483
+ if ( ! is_scalar( $value ) )
484
+ return 'Debug message: not scalar';
485
+
486
+ if ( "''" === $value && $this->the_seo_framework_debug_hidden )
487
+ return html_entity_decode( $value );
488
+
489
+ if ( $ignore || false === $this->the_seo_framework_debug_hidden )
490
+ return '<span class="wp-ui-notification">' . esc_attr( (string) trim( $value ) ) . '</span>';
491
+
492
+ return esc_attr( (string) $value );
493
+ }
494
+
495
+ /**
496
+ * Debug init. Simplified way of debugging a function, only works in admin.
497
+ *
498
+ * @since 2.6.0
499
+ *
500
+ * @access private
501
+ *
502
+ * @param string $class The class name.
503
+ * @param string $method The function name.
504
+ * @param bool $store Whether to store the output in cache for next run to pick up on.
505
+ * @param double $debug_key Use $debug_key as variable, it's reserved.
506
+ * @param mixed function args.
507
+ * @return void early if debugging is disabled or when storing cache values.
508
+ */
509
+ protected function debug_init( $class, $method, $store, $debug_key ) {
510
+
511
+ if ( false === $this->the_seo_framework_debug || false === $this->add_debug_output )
512
+ return;
513
+
514
+ $output = '';
515
+
516
+ if ( func_num_args() >= 5 ) {
517
+
518
+ //* Cache the args for $store.
519
+ static $cached_args = array();
520
+ static $hold_args = array();
521
+
522
+ $args = array_slice( func_get_args(), 4 );
523
+ $key = $class . '_' . $method . '_' . $debug_key;
524
+
525
+ if ( $store ) {
526
+ $this->profile( false, false, 'time', $key ) . ' seconds';
527
+ $this->profile( false, false, 'memory', $key ) . ' bytes';
528
+
529
+ unset( $args[0]['debug_key'] );
530
+
531
+ $cached_args[$class][$method] = $args;
532
+ $hold_args[$class][$method] = $args;
533
+ return;
534
+ } else {
535
+
536
+ /**
537
+ * Generate human-readable debug keys and echo it when it's called.
538
+ * Matched value is found within the $output.
539
+ *
540
+ * @staticvar int $loop
541
+ */
542
+ static $loop = 0;
543
+ $loop++;
544
+ $debug_key = '[Debug key: ' . $loop . ' - ' . $method . ']';
545
+
546
+ if ( $this->is_admin() && 'admin_footer' !== current_action() ) {
547
+ echo "\r\n";
548
+ echo $this->the_seo_framework_debug_hidden ? $debug_key . ' action. ' : '<p>' . $debug_key . '</p>';
549
+ }
550
+
551
+ $output .= "\r\n";
552
+ $output .= $this->the_seo_framework_debug_hidden ? $debug_key . ' output. ' : '<h3>' . $debug_key . '</h3>';
553
+
554
+ if ( isset( $cached_args[$class][$method] ) ) {
555
+ $args[] = array(
556
+ 'profile' => array(
557
+ 'time' => $this->profile( false, true, 'time', $key ) . ' seconds',
558
+ 'memory' => $this->profile( false, true, 'memory', $key ) . ' bytes'
559
+ )
560
+ );
561
+
562
+ $args = array_merge( $cached_args[$class][$method], $args );
563
+
564
+ //* Reset args for next run.
565
+ $cached_args[$class][$method] = null;
566
+ }
567
+ }
568
+
569
+ if ( $args ) {
570
+
571
+ if ( $class ) {
572
+ $output .= $class . '::' . $method . '( ';
573
+ } else {
574
+ $output .= $method . '( ';
575
+ }
576
+
577
+ if ( isset( $hold_args[$class][$method][0] ) ) {
578
+ if ( is_array( $hold_args[$class][$method][0] ) ) {
579
+ foreach ( $hold_args[$class][$method][0] as $var => $a ) {
580
+ $output .= gettype( $a ) . ' $' . $var . ', ';
581
+ }
582
+ }
583
+ $output = rtrim( $output, ', ' );
584
+ $hold_args[$class][$method] = null;
585
+ }
586
+
587
+ $output .= ' )';
588
+ $output .= $this->the_seo_framework_debug_hidden ? "\r\n" : "<br>\r\n";
589
+
590
+ foreach ( $args as $num => $a ) {
591
+ if ( is_array( $a ) ) {
592
+ foreach ( $a as $k => $v ) {
593
+ $output .= $this->the_seo_framework_debug_hidden ? '' : '<div style="padding-left:12px">';
594
+ $output .= "\t" . (string) $k . ': ';
595
+ $output .= $this->the_seo_framework_debug_hidden ? "\r\n" : '<br><div style="padding-left:12px">' . "\r\n";
596
+ $output .= "\t " . gettype( $v ) . ': [';
597
+ $output .= $this->the_seo_framework_debug_hidden ? '' : '<div style="padding-left:12px">';
598
+ $output .= "\t\t" . $this->get_debug_information( $v );
599
+ $output .= $this->the_seo_framework_debug_hidden ? '' : '</div>';
600
+ $output .= "\t " . ']' . "\r\n";
601
+ $output .= $this->the_seo_framework_debug_hidden ? '' : '</div>';
602
+ $output .= $this->the_seo_framework_debug_hidden ? '' : '</div>';
603
+ }
604
+ } else {
605
+ $output .= $this->the_seo_framework_debug_hidden ? '' : '<div style="padding-left:12px">';
606
+ $output .= "\t" . (string) $num . ': ';
607
+ $output .= $this->the_seo_framework_debug_hidden ? "\r\n" : "<br>\r\n";
608
+ $output .= "\t " . gettype( $a ) . ': [';
609
+ $output .= $this->the_seo_framework_debug_hidden ? '' : '<div style="padding-left:12px">';
610
+ $output .= "\t\t" . $this->get_debug_information( $a );
611
+ $output .= $this->the_seo_framework_debug_hidden ? '' : "</div><br>\r\n";
612
+ $output .= "\t " . ']' . "\r\n";
613
+ $output .= $this->the_seo_framework_debug_hidden ? '' : '</div>';
614
+ }
615
+ }
616
+ }
617
+ }
618
+
619
+ if ( $output ) {
620
+
621
+ static $odd = false;
622
+ if ( $odd ) {
623
+ $bg = 'f1f1f1';
624
+ $odd = false;
625
+ } else {
626
+ $bg = 'dadada';
627
+ $odd = true;
628
+ }
629
+
630
+ //* Store debug output.
631
+ $this->debug_output .= $this->the_seo_framework_debug_hidden ? '' : '<div style="background:#' . $bg . ';margin-bottom:6px;padding:0px 14px 14px;clear:both;float:left;width:100%;display:inline-block;">';
632
+ $this->debug_output .= $output;
633
+ $this->debug_output .= $this->the_seo_framework_debug_hidden ? '' : '</div>';
634
+ }
635
+
636
+ }
637
+
638
+ /**
639
+ * Count the timings and memory usage.
640
+ * Memory usage fetching is unreliable, i.e. Opcode.
641
+ *
642
+ * @since 2.6.0
643
+ * @access private
644
+ *
645
+ * @param bool $echo Whether to echo the total plugin time.
646
+ * @param bool $from_last Whether to echo the differences from the last timing.
647
+ * @param string $what Whether to return the time or memory.
648
+ * @param string $key When used, it will detach the profiling separately.
649
+ *
650
+ * @staticvar bool $debug
651
+ *
652
+ * @return float The timer in seconds. Or memory in Bytes when $what is 'memory'.
653
+ */
654
+ public function profile( $echo = false, $from_last = false, $what = 'time', $key = '' ) {
655
+
656
+ static $timer_start = array();
657
+ static $memory_start = array();
658
+ static $plugin_time = array();
659
+ static $plugin_memory = array();
660
+
661
+ $timer_start[$key] = isset( $timer_start[$key] ) ? $timer_start[$key] : 0;
662
+ $memory_start[$key] = isset( $memory_start[$key] ) ? $memory_start[$key] : 0;
663
+ $plugin_time[$key] = isset( $plugin_time[$key] ) ? $plugin_time[$key] : 0;
664
+ $plugin_memory[$key] = isset( $plugin_memory[$key] ) ? $plugin_memory[$key] : 0;
665
+
666
+ //* Get now.
667
+ $time_now = microtime( true );
668
+ $memory_usage_now = memory_get_usage();
669
+
670
+ //* Calculate difference.
671
+ $difference_time = $time_now - $timer_start[$key];
672
+ $difference_memory = $memory_usage_now - $memory_start[$key];
673
+
674
+ //* Add difference to total.
675
+ $plugin_time[$key] = $plugin_time[$key] + $difference_time;
676
+ $plugin_memory[$key] = $plugin_memory[$key] + $difference_memory;
677
+
678
+ //* Reset timer and memory
679
+ $timer_start[$key] = $time_now;
680
+ $memory_start[$key] = $memory_usage_now;
681
+
682
+ if ( $from_last ) {
683
+ if ( false === $echo ) {
684
+ //* Return early if not allowed to echo.
685
+ if ( 'time' === $what )
686
+ return number_format( $difference_time, 5 );
687
+
688
+ return $difference_memory;
689
+ }
690
+
691
+ //* Convert to string and echo if not returned yet.
692
+ echo (string) "\r\n" . $difference_time . "s\r\n";
693
+ echo (string) ( $difference_memory / 1024 ) . "kiB\r\n";
694
+ } else {
695
+ if ( false === $echo ) {
696
+ //* Return early if not allowed to echo.
697
+ if ( 'time' === $what )
698
+ return number_format( $plugin_time[$key], 5 );
699
+
700
+ return $plugin_memory[$key];
701
+ }
702
+
703
+ //* Convert to string and echo if not returned yet.
704
+ echo (string) "\r\n" . $plugin_time[$key] . "s\r\n";
705
+ echo (string) ( $plugin_memory[$key] / 1024 ) . "kiB\r\n";
706
+ }
707
+
708
+ }
709
+
710
+ /**
711
+ * Times code until it's called again.
712
+ *
713
+ * @since 2.6.0
714
+ *
715
+ * @param bool $set Whether to reset the timer.
716
+ * @return float PHP Microtime for code execution.
717
+ */
718
+ protected function timer( $reset = false ) {
719
+
720
+ static $previous = null;
721
+
722
+ if ( isset( $previous ) && false === $reset ) {
723
+ $output = microtime( true ) - $previous;
724
+ $previous = null;
725
+ } else {
726
+ $output = $previous = microtime( true );
727
+ }
728
+
729
+ return $output;
730
+ }
731
+
732
+ /**
733
+ * Wraps header output in front-end code.
734
+ * This won't consider hiding the output.
735
+ *
736
+ * @since 2.6.5
737
+ *
738
+ * @return string Wrapped SEO meta tags output.
739
+ */
740
+ protected function debug_header_output() {
741
+
742
+ if ( $this->is_admin() && ! $this->is_term_edit() && ! $this->is_post_edit() && ! $this->is_seo_settings_page() )
743
+ return;
744
+
745
+ if ( $this->is_seo_settings_page() )
746
+ add_filter( 'the_seo_framework_current_object_id', array( $this, 'get_the_front_page_ID' ) );
747
+
748
+ //* Start timer.
749
+ $this->timer( true );
750
+
751
+ //* Don't register this output.
752
+ $this->add_debug_output = false;
753
+
754
+ $output = $this->the_description()
755
+ . $this->og_image()
756
+ . $this->og_locale()
757
+ . $this->og_type()
758
+ . $this->og_title()
759
+ . $this->og_description()
760
+ . $this->og_url()
761
+ . $this->og_sitename()
762
+ . $this->facebook_publisher()
763
+ . $this->facebook_author()
764
+ . $this->facebook_app_id()
765
+ . $this->article_published_time()
766
+ . $this->article_modified_time()
767
+ . $this->twitter_card()
768
+ . $this->twitter_site()
769
+ . $this->twitter_creator()
770
+ . $this->twitter_title()
771
+ . $this->twitter_description()
772
+ . $this->twitter_image()
773
+ . $this->shortlink()
774
+ . $this->canonical()
775
+ . $this->paged_urls()
776
+ . $this->ld_json()
777
+ . $this->google_site_output()
778
+ . $this->bing_site_output()
779
+ . $this->yandex_site_output()
780
+ . $this->pint_site_output()
781
+ ;
782
+
783
+ $timer = '<div style="display:inline-block;width:100%;padding:20px;border-bottom:1px solid #ccc;">Generated in: ' . number_format( $this->timer(), 5 ) . ' seconds</div>' ;
784
+
785
+ $title = $this->is_admin() ? 'Expected SEO Output' : 'Current SEO Output';
786
+ $title = '<div style="display:inline-block;width:100%;padding:20px;margin:0 auto;border-bottom:1px solid #ccc;"><h2 style="color:#ddd;font-size:22px;padding:0;margin:0">' . $title . '</h2></div>';
787
+
788
+ //* Escape it, replace EOL with breaks, and style everything between quotes (which are ending with space).
789
+ $output = str_replace( PHP_EOL, '<br>' . PHP_EOL, esc_html( $output ) );
790
+ $output = preg_replace( "/(&quot;.*?&quot;)(\s)/", '<font color="arnoldschwarzenegger">$1</font> ', $output );
791
+
792
+ $output = '<div style="display:inline-block;width:100%;padding:20px;font-family:Consolas,Monaco,monospace;font-size:14px;">' . $output . '</div>';
793
+ $output = '<div style="display:block;width:100%;background:#23282D;color:#ddd;border-bottom:1px solid #ccc">' . $title . $timer . $output . '</div>';
794
+
795
+ return $output;
796
+ }
797
+
798
+ /**
799
+ * Wraps query status booleans in human-readable code.
800
+ *
801
+ * @since 2.6.6
802
+ *
803
+ * @return string Wrapped Query State debug output.
804
+ */
805
+ protected function debug_query_output() {
806
+
807
+ //* Start timer.
808
+ $this->timer( true );
809
+
810
+ //* Don't register this output.
811
+ $this->add_debug_output = false;
812
+
813
+ //* Only get true/false values.
814
+ $is_404 = $this->is_404();
815
+ $is_admin = $this->is_admin();
816
+ $is_attachment = $this->is_attachment();
817
+ $is_archive = $this->is_archive();
818
+ $is_term_edit = $this->is_term_edit();
819
+ $is_post_edit = $this->is_post_edit();
820
+ $is_wp_lists_edit = $this->is_wp_lists_edit();
821
+ $is_wp_lists_edit = $this->is_wp_lists_edit();
822
+ $is_author = $this->is_author();
823
+ $is_blog_page = $this->is_blog_page();
824
+ $is_category = $this->is_category();
825
+ $is_date = $this->is_date();
826
+ $is_day = $this->is_day();
827
+ $is_feed = $this->is_feed();
828
+ $is_front_page = $this->is_front_page();
829
+ $is_home = $this->is_home();
830
+ $is_month = $this->is_month();
831
+ $is_page = $this->is_page();
832
+ $is_preview = $this->is_preview();
833
+ $is_search = $this->is_search();
834
+ $is_single = $this->is_single();
835
+ $is_singular = $this->is_singular();
836
+ $is_static_frontpage = $this->is_static_frontpage();
837
+ $is_tag = $this->is_tag();
838
+ $is_tax = $this->is_tax();
839
+ $is_ultimate_member_user_page = $this->is_ultimate_member_user_page();
840
+ $is_wc_shop = $this->is_wc_shop();
841
+ $is_wc_product = $this->is_wc_product();
842
+ $is_year = $this->is_year();
843
+ $is_seo_settings_page = $this->is_seo_settings_page();
844
+
845
+ //* Get all above vars, split them in two (true and false) and sort them by key names.
846
+ $vars = get_defined_vars();
847
+ $current = array_filter( $vars );
848
+ $not_current = array_diff_key( $vars, $current );
849
+ ksort( $current );
850
+ ksort( $not_current );
851
+
852
+ $timer = '<div style="display:inline-block;width:100%;padding:20px;border-bottom:1px solid #666;">Generated in: ' . number_format( $this->timer(), 5 ) . ' seconds</div>' ;
853
+
854
+ $output = '';
855
+ foreach ( $current as $name => $value ) {
856
+ $type = '(' . gettype( $value ) . ')';
857
+ if ( is_bool( $value ) )
858
+ $value = $value ? 'true' : 'false';
859
+ else
860
+ $value = esc_attr( var_export( $value, true ) );
861
+
862
+ $value = $this->the_seo_framework_debug_hidden ? $type . ' ' . $value : '<font color="harrisonford">' . $type . ' ' . $value . '</font>';
863
+ $out = $name . ' => ' . $value;
864
+ $output .= $this->the_seo_framework_debug_hidden ? $out . PHP_EOL : '<span style="background:#dadada">' . $out . '</span>' . PHP_EOL;
865
+ }
866
+
867
+ foreach ( $not_current as $name => $value ) {
868
+ $type = '(' . gettype( $value ) . ')';
869
+ if ( is_bool( $value ) )
870
+ $value = $value ? 'true' : 'false';
871
+ else
872
+ $value = esc_attr( var_export( $value, true ) );
873
+
874
+ $value = $this->the_seo_framework_debug_hidden ? $type . ' ' . $value : '<font color="harrisonford">' . $type . ' ' . $value . '</font>';
875
+ $out = $name . ' => ' . $value;
876
+ $output .= $out . PHP_EOL;
877
+ }
878
+
879
+ $title = $this->is_admin() ? 'Current WordPress Screen + Expected WordPress Query' : 'Current WordPress Query';
880
+ $title = '<div style="display:inline-block;width:100%;padding:20px;margin:0 auto;border-bottom:1px solid #666;"><h2 style="color:#222;font-size:22px;padding:0;margin:0">' . $title . '</h2></div>';
881
+
882
+ $output = $this->the_seo_framework_debug_hidden ? $output : str_replace( PHP_EOL, '<br>' . PHP_EOL, $output );
883
+
884
+ $output = '<div style="display:inline-block;width:100%;padding:20px;font-family:Consolas,Monaco,monospace;font-size:14px;">' . $output . '</div>';
885
+ $output = '<div style="display:block;width:100%;background:#fafafa;color:#333;border-bottom:1px solid #666">' . $title . $timer . $output . '</div>';
886
+
887
+ return $output;
888
+ }
889
+
890
+ }
inc/classes/detect.class.php ADDED
@@ -0,0 +1,1129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The SEO Framework plugin
4
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License version 3 as published
8
+ * by the Free Software Foundation.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ /**
20
+ * Class AutoDescription_Detect
21
+ *
22
+ * Detects other plugins and themes
23
+ *
24
+ * @since 2.1.6
25
+ */
26
+ class AutoDescription_Detect extends AutoDescription_Render {
27
+
28
+ /**
29
+ * Constructor, load parent constructor
30
+ */
31
+ public function __construct() {
32
+ parent::__construct();
33
+ }
34
+
35
+ /**
36
+ * Returns list of active plugins.
37
+ *
38
+ * @since 2.6.1
39
+ * @staticvar array $active_plugins
40
+ *
41
+ * @credits JetPack for most code.
42
+ *
43
+ * @return array List of active plugins.
44
+ */
45
+ public function active_plugins() {
46
+
47
+ static $active_plugins = null;
48
+
49
+ if ( isset( $active_plugins ) )
50
+ return $active_plugins;
51
+
52
+ $active_plugins = (array) get_option( 'active_plugins', array() );
53
+
54
+ if ( is_multisite() ) {
55
+ // Due to legacy code, active_sitewide_plugins stores them in the keys,
56
+ // whereas active_plugins stores them in the values.
57
+ $network_plugins = array_keys( get_site_option( 'active_sitewide_plugins', array() ) );
58
+ if ( $network_plugins ) {
59
+ $active_plugins = array_merge( $active_plugins, $network_plugins );
60
+ }
61
+ }
62
+
63
+ sort( $active_plugins );
64
+
65
+ return $active_plugins = array_unique( $active_plugins );
66
+ }
67
+
68
+ /**
69
+ * Filterable list of conflicting plugins.
70
+ *
71
+ * Applies filters 'the_seo_framework_conflicting_plugins' : array
72
+ * @since 2.6.0
73
+ *
74
+ * @credits JetPack for most code.
75
+ *
76
+ * @return array List of conflicting plugins.
77
+ */
78
+ public function conflicting_plugins() {
79
+
80
+ $conflicting_plugins = array(
81
+ 'seo_tools' => array(
82
+ 'Yoast SEO' => 'wordpress-seo/wp-seo.php',
83
+ 'Yoast SEO Premium' => 'wordpress-seo-premium/wp-seo-premium.php',
84
+ 'All in One SEO Pack' => 'all-in-one-seo-pack/all_in_one_seo_pack.php',
85
+ 'SEO Ultimate' => 'seo-ultimate/seo-ultimate.php',
86
+ 'Gregs High Performance SEO' => 'gregs-high-performance-seo/ghpseo.php',
87
+ ),
88
+ 'sitemaps' => array(
89
+ 'Google XML Sitemaps' => 'google-sitemap-generator/sitemap.php',
90
+ 'Better WordPress Google XML Sitemaps' => 'bwp-google-xml-sitemaps/bwp-simple-gxs.php',
91
+ 'Google XML Sitemaps for qTranslate' => 'google-xml-sitemaps-v3-for-qtranslate/sitemap.php',
92
+ 'XML Sitemap & Google News feeds' => 'xml-sitemap-feed/xml-sitemap.php',
93
+ 'Google Sitemap by BestWebSoft' => 'google-sitemap-plugin/google-sitemap-plugin.php',
94
+ 'WordPress SEO by Yoast' => 'wordpress-seo/wp-seo.php',
95
+ 'WordPress SEO Premium by Yoast' => 'wordpress-seo-premium/wp-seo-premium.php',
96
+ 'All in One SEO Pack' => 'all-in-one-seo-pack/all_in_one_seo_pack.php',
97
+ 'Sitemap' => 'sitemap/sitemap.php',
98
+ 'Simple Wp Sitemap' => 'simple-wp-sitemap/simple-wp-sitemap.php',
99
+ 'Simple Sitemap' => 'simple-sitemap/simple-sitemap.php',
100
+ 'XML Sitemaps' => 'xml-sitemaps/xml-sitemaps.php',
101
+ 'MSM Sitemaps' => 'msm-sitemap/msm-sitemap.php',
102
+ ),
103
+ 'open_graph' => array(
104
+ '2 Click Social Media Buttons' => '2-click-socialmedia-buttons/2-click-socialmedia-buttons.php',
105
+ 'Add Link to Facebook' => 'add-link-to-facebook/add-link-to-facebook.php',
106
+ 'Add Meta Tags' => 'add-meta-tags/add-meta-tags.php',
107
+ 'Easy Facebook Share Thumbnail' => 'easy-facebook-share-thumbnails/esft.php',
108
+ 'Facebook' => 'facebook/facebook.php',
109
+ 'Facebook AWD All in one' => 'facebook-awd/AWD_facebook.php',
110
+ 'Facebook Featured Image & OG Meta Tags' => 'facebook-featured-image-and-open-graph-meta-tags/fb-featured-image.php',
111
+ 'Facebook Meta Tags' => 'facebook-meta-tags/facebook-metatags.php',
112
+ 'Facebook Open Graph Meta Tags for WordPress' => 'wonderm00ns-simple-facebook-open-graph-tags/wonderm00n-open-graph.php',
113
+ 'Facebook Revised Open Graph Meta Tag' => 'facebook-revised-open-graph-meta-tag/index.php',
114
+ 'Facebook Thumb Fixer' => 'facebook-thumb-fixer/_facebook-thumb-fixer.php',
115
+ 'Fedmichs Facebook Open Graph Meta' => 'facebook-and-digg-thumbnail-generator/facebook-and-digg-thumbnail-generator.php',
116
+ 'Header and Footer' => 'header-footer/plugin.php',
117
+ 'Network Publisher' => 'network-publisher/networkpub.php',
118
+ 'NextGEN Facebook OG' => 'nextgen-facebook/nextgen-facebook.php',
119
+ 'NextScripts SNAP' => 'social-networks-auto-poster-facebook-twitter-g/NextScripts_SNAP.php',
120
+ 'Open Graph' => 'opengraph/opengraph.php',
121
+ 'Open Graph Protocol Framework' => 'open-graph-protocol-framework/open-graph-protocol-framework.php',
122
+ 'SEO Facebook Comments' => 'seo-facebook-comments/seofacebook.php',
123
+ 'Shareaholic' => 'sexybookmarks/sexy-bookmarks.php',
124
+ 'Shareaholic2' => 'shareaholic/sexy-bookmarks.php',
125
+ 'SharePress' => 'sharepress/sharepress.php',
126
+ 'Simple Facebook Connect' => 'simple-facebook-connect/sfc.php',
127
+ 'Social Discussions' => 'social-discussions/social-discussions.php',
128
+ 'Social Sharing Toolkit' => 'social-sharing-toolkit/social_sharing_toolkit.php',
129
+ 'Socialize' => 'socialize/socialize.php',
130
+ 'Tweet, Like, Google +1 and Share' => 'only-tweet-like-share-and-google-1/tweet-like-plusone.php',
131
+ 'Wordbooker' => 'wordbooker/wordbooker.php',
132
+ 'WordPress Social Sharing Optimization' => 'wpsso/wpsso.php',
133
+ 'WP Caregiver' => 'wp-caregiver/wp-caregiver.php',
134
+ 'WP Facebook Like Send & Open Graph Meta' => 'wp-facebook-like-send-open-graph-meta/wp-facebook-like-send-open-graph-meta.php',
135
+ 'WP Facebook Open Graph protocol' => 'wp-facebook-open-graph-protocol/wp-facebook-ogp.php',
136
+ 'WP-OGP' => 'wp-ogp/wp-ogp.php',
137
+ 'Zolton.org Social Plugin' => 'zoltonorg-social-plugin/zosp.php',
138
+ 'WP Facebook Like Button' => 'wp-fb-share-like-button/wp_fb_share-like_widget.php'
139
+ ),
140
+ 'twitter_card' => array(
141
+ 'Twitter' => 'twitter/twitter.php',
142
+ 'Eewee Twitter Card' => 'eewee-twitter-card/index.php',
143
+ 'IG:Twitter Cards' => 'ig-twitter-cards/ig-twitter-cards.php',
144
+ 'Twitter Cards' => 'twitter-cards/twitter-cards.php',
145
+ 'Twitter Cards Meta' => 'twitter-cards-meta/twitter-cards-meta.php',
146
+ 'WP Twitter Cards' => 'wp-twitter-cards/twitter_cards.php',
147
+ ),
148
+ );
149
+
150
+ return (array) apply_filters( 'the_seo_framework_conflicting_plugins', $conflicting_plugins );
151
+ }
152
+
153
+ /**
154
+ * Fetches type of conflicting plugins.
155
+ *
156
+ * @param string $type The Key from $this->conflicting_plugins()
157
+ *
158
+ * @since 2.6.0
159
+ *
160
+ * @return array
161
+ */
162
+ public function get_conflicting_plugins( $type = 'seo_tools' ) {
163
+
164
+ $conflicting_plugins = $this->conflicting_plugins();
165
+
166
+ if ( isset( $conflicting_plugins[ $type ] ) )
167
+ return (array) apply_filters( 'the_seo_framework_conflicting_plugins_type', $conflicting_plugins[ $type ], $type );
168
+
169
+ return array();
170
+ }
171
+
172
+ /**
173
+ * Detect active plugin by constant, class or function existence.
174
+ *
175
+ * @since 1.3.0
176
+ *
177
+ * @param array $plugins Array of array for constants, classes and / or functions to check for plugin existence.
178
+ *
179
+ * @return boolean True if plugin exists or false if plugin constant, class or function not detected.
180
+ */
181
+ public function detect_plugin( $plugins ) {
182
+
183
+ //* Check for classes
184
+ if ( isset( $plugins['classes'] ) ) {
185
+ foreach ( $plugins['classes'] as $name ) {
186
+ if ( class_exists( $name ) ) {
187
+ return true;
188
+ break;
189
+ }
190
+ }
191
+ }
192
+
193
+ //* Check for functions
194
+ if ( isset( $plugins['functions'] ) ) {
195
+ foreach ( $plugins['functions'] as $name ) {
196
+ if ( function_exists( $name ) ) {
197
+ return true;
198
+ break;
199
+ }
200
+ }
201
+ }
202
+
203
+ //* Check for constants
204
+ if ( isset( $plugins['constants'] ) ) {
205
+ foreach ( $plugins['constants'] as $name ) {
206
+ if ( defined( $name ) ) {
207
+ return true;
208
+ break;
209
+ }
210
+ }
211
+ }
212
+
213
+ //* No class, function or constant found to exist
214
+ return false;
215
+ }
216
+
217
+ /**
218
+ * Detect if you can use the given constants, functions and classes.
219
+ * All must be available to return true.
220
+ *
221
+ * @param array $plugins Array of array for constants, classes and / or functions to check for plugin existence.
222
+ * @param bool $use_cache Bypasses cache if false
223
+ *
224
+ * @staticvar array $cache
225
+ * @uses $this->detect_plugin_multi()
226
+ *
227
+ * @since 2.5.2
228
+ */
229
+ public function can_i_use( array $plugins = array(), $use_cache = true ) {
230
+
231
+ if ( ! $use_cache )
232
+ return $this->detect_plugin_multi( $plugins );
233
+
234
+ static $cache = array();
235
+
236
+ $mapped = array();
237
+
238
+ //* Prepare multidimensional array for cache.
239
+ foreach ( $plugins as $key => $func ) {
240
+ if ( ! is_array( $func ) )
241
+ return false;
242
+
243
+ //* Sort alphanumeric by value, put values back after sorting.
244
+ $func = array_flip( $func );
245
+ ksort( $func );
246
+ $func = array_flip( $func );
247
+
248
+ //* Glue with underscore and space for debugging purposes.
249
+ $mapped[$key] = $key . '_' . implode( ' ', $func );
250
+ }
251
+
252
+ ksort( $mapped );
253
+
254
+ //* Glue with dash instead of underscore for debugging purposes.
255
+ $plugins_cache = implode( '-', $mapped );
256
+
257
+ if ( isset( $cache[$plugins_cache] ) )
258
+ return $cache[$plugins_cache];
259
+
260
+ return $cache[$plugins_cache] = $this->detect_plugin_multi( $plugins );
261
+ }
262
+
263
+ /**
264
+ * Detect active plugin by constant, class or function existence.
265
+ * All parameters must match and return true.
266
+ *
267
+ * @since 2.5.2
268
+ *
269
+ * @param array $plugins Array of array for constants, classes and / or functions to check for plugin existence.
270
+ *
271
+ * @return boolean True if ALL functions classes and constants exists or false if plugin constant, class or function not detected.
272
+ */
273
+ public function detect_plugin_multi( array $plugins ) {
274
+
275
+ //* Check for classes
276
+ if ( isset( $plugins['classes'] ) ) {
277
+ foreach ( $plugins['classes'] as $name ) {
278
+ if ( ! class_exists( $name ) ) {
279
+ return false;
280
+ break;
281
+ }
282
+ }
283
+ }
284
+
285
+ //* Check for functions
286
+ if ( isset( $plugins['functions'] ) ) {
287
+ foreach ( $plugins['functions'] as $name ) {
288
+ if ( ! function_exists( $name ) ) {
289
+ return false;
290
+ break;
291
+ }
292
+ }
293
+ }
294
+
295
+ //* Check for constants
296
+ if ( isset( $plugins['constants'] ) ) {
297
+ foreach ( $plugins['constants'] as $name ) {
298
+ if ( ! defined( $name ) ) {
299
+ return false;
300
+ break;
301
+ }
302
+ }
303
+ }
304
+
305
+ //* All classes, functions and constant have been found to exist
306
+ return true;
307
+ }
308
+
309
+ /**
310
+ * Checks if the (parent) theme name is loaded.
311
+ *
312
+ * @NOTE will return true if ANY of the array values matches.
313
+ *
314
+ * @param string|array $themes the current theme name
315
+ * @param bool $depr If set to false don't use cache.
316
+ *
317
+ * @since 2.1.0
318
+ *
319
+ * @return bool is theme active.
320
+ */
321
+ public function is_theme( $themes = null, $depr = null ) {
322
+
323
+ if ( ! isset( $themes ) )
324
+ return false;
325
+
326
+ $wp_get_theme = wp_get_theme();
327
+
328
+ $theme_parent = strtolower( $wp_get_theme->get('Template') );
329
+ $theme_name = strtolower( $wp_get_theme->get('Name') );
330
+
331
+ if ( is_string( $themes ) ) {
332
+ $themes = strtolower( $themes );
333
+ if ( $themes === $theme_parent || $themes === $theme_name )
334
+ return true;
335
+ } else if ( is_array( $themes ) ) {
336
+ foreach ( $themes as $theme ) {
337
+ $theme = strtolower( $theme );
338
+ if ( $theme === $theme_parent || $theme === $theme_name ) {
339
+ return true;
340
+ break;
341
+ }
342
+ }
343
+ }
344
+
345
+ return false;
346
+ }
347
+
348
+ /**
349
+ * SEO plugin detection
350
+ *
351
+ * @since 1.3.0
352
+ *
353
+ * @staticvar bool $detected
354
+ * @since 2.2.5
355
+ *
356
+ * Applies filters 'the_seo_framework_seo_plugin_detected' : bool
357
+ * @since 2.6.1
358
+ *
359
+ * @return bool SEO plugin detected.
360
+ */
361
+ public function detect_seo_plugins() {
362
+
363
+ static $detected = null;
364
+
365
+ if ( isset( $detected ) )
366
+ return $detected;
367
+
368
+ //* Old style filter.
369
+ $detected = $this->detect_seo_plugins_old();
370
+ if ( isset( $detected ) )
371
+ return $detected;
372
+
373
+ $active_plugins = $this->active_plugins();
374
+
375
+ if ( ! empty( $active_plugins ) ) {
376
+ $conflicting_plugins = $this->get_conflicting_plugins( 'seo_tools' );
377
+
378
+ foreach ( $conflicting_plugins as $plugin ) {
379
+ if ( in_array( $plugin, $active_plugins ) ) {
380
+ $detected = apply_filters( 'the_seo_framework_seo_plugin_detected', true );
381
+ break;
382
+ }
383
+ }
384
+ }
385
+
386
+ return $detected = $detected ? true : false;
387
+ }
388
+
389
+ /**
390
+ * Open Graph plugin detection
391
+ *
392
+ * @since 1.3.0
393
+ *
394
+ * @staticvar bool $detected
395
+ * @since 2.2.5
396
+ *
397
+ * Applies filters 'the_seo_framework_og_plugin_detected' : bool
398
+ * @since 2.6.1
399
+ *
400
+ * @return bool OG plugin detected.
401
+ */
402
+ public function detect_og_plugin() {
403
+
404
+ static $detected = null;
405
+
406
+ if ( isset( $detected ) )
407
+ return $detected;
408
+
409
+ //* Detect SEO plugins beforehand.
410
+ $detected = $this->detect_seo_plugins();
411
+ if ( $detected )
412
+ return $detected;
413
+
414
+ //* Old style filter.
415
+ $detected = $this->has_og_plugin();
416
+ if ( isset( $detected ) )
417
+ return $detected;
418
+
419
+ $active_plugins = $this->active_plugins();
420
+
421
+ if ( ! empty( $active_plugins ) ) {
422
+ $conflicting_plugins = $this->get_conflicting_plugins( 'open_graph' );
423
+
424
+ foreach ( $conflicting_plugins as $plugin ) {
425
+ if ( in_array( $plugin, $active_plugins ) ) {
426
+ $detected = apply_filters( 'the_seo_framework_og_plugin_detected', true );
427
+ break;
428
+ }
429
+ }
430
+ }
431
+
432
+ return $detected = $detected ? true : false;
433
+ }
434
+
435
+ /**
436
+ * Open Graph plugin detection
437
+ *
438
+ * @since 2.6.0
439
+ *
440
+ * @staticvar bool $detected
441
+ * @since 2.6.0
442
+ *
443
+ * @return bool Twitter Card plugin detected.
444
+ */
445
+ public function detect_twitter_card_plugin() {
446
+
447
+ static $detected = null;
448
+
449
+ if ( isset( $detected ) )
450
+ return $detected;
451
+
452
+ //* Detect SEO plugins beforehand.
453
+ $detected = $this->detect_seo_plugins();
454
+ if ( $detected )
455
+ return $detected;
456
+
457
+ $active_plugins = $this->active_plugins();
458
+
459
+ if ( ! empty( $active_plugins ) ) {
460
+ $conflicting_plugins = $this->get_conflicting_plugins( 'twitter_card' );
461
+
462
+ foreach ( $conflicting_plugins as $plugin ) {
463
+ if ( in_array( $plugin, $active_plugins ) ) {
464
+ $detected = apply_filters( 'the_seo_framework_twittercard_plugin_detected', true );
465
+ break;
466
+ }
467
+ }
468
+ }
469
+
470
+ return $detected = $detected ? true : false;
471
+ }
472
+
473
+ /**
474
+ * Detects if plugins outputting ld+json exists
475
+ *
476
+ * @since 1.3.0
477
+ *
478
+ * Always return false.
479
+ * @since 2.6.1
480
+ *
481
+ * @return bool false
482
+ */
483
+ public function has_json_ld_plugin() {
484
+ /**
485
+ * Applies filters 'the_seo_framework_ldjson_plugin_detected' : boolean
486
+ * @since 2.6.5
487
+ */
488
+ return (bool) apply_filters( 'the_seo_framework_ldjson_plugin_detected', false );
489
+ }
490
+
491
+ /**
492
+ * Detecs sitemap plugins
493
+ *
494
+ * @since 2.1.0
495
+ * @staticvar bool $detected
496
+ *
497
+ * @return bool
498
+ */
499
+ public function detect_sitemap_plugin() {
500
+
501
+ static $detected = null;
502
+
503
+ if ( isset( $detected ) )
504
+ return $detected;
505
+
506
+ $active_plugins = $this->active_plugins();
507
+
508
+ if ( ! empty( $active_plugins ) ) {
509
+ $conflicting_plugins = $this->get_conflicting_plugins( 'sitemaps' );
510
+
511
+ foreach ( $conflicting_plugins as $plugin ) {
512
+ if ( in_array( $plugin, $active_plugins ) ) {
513
+ $detected = apply_filters( 'the_seo_framework_sitemap_plugin_detected', true );
514
+ break;
515
+ }
516
+ }
517
+ }
518
+
519
+ return $detected = $detected ? true : false;
520
+ }
521
+
522
+ /**
523
+ * Whether able to add a line within robots based by plugin detection, or sitemap output option.
524
+ *
525
+ * @since 2.6.0
526
+ *
527
+ * @return bool True when no conflicting plugins are detected or when The SEO Framework's Sitemaps are output.
528
+ */
529
+ public function can_do_sitemap_robots() {
530
+
531
+ $plugins = array(
532
+ 'functions' => array(
533
+ 'jetpack_sitemap_initialize' // Jetpack
534
+ ),
535
+ );
536
+
537
+ if ( $this->detect_plugin( $plugins ) )
538
+ return false;
539
+
540
+ if ( $this->is_option_checked( 'sitemaps_output' ) )
541
+ return true;
542
+
543
+ return false;
544
+ }
545
+
546
+ /**
547
+ * Detects presence of robots.txt in root folder.
548
+ *
549
+ * @staticvar $has_robots
550
+ *
551
+ * @since 2.5.2
552
+ */
553
+ public function has_robots_txt() {
554
+
555
+ static $has_robots = null;
556
+
557
+ if ( isset( $has_robots ) )
558
+ return $has_robots;
559
+
560
+ $path = get_home_path() . 'robots.txt';
561
+
562
+ return $has_robots = file_exists( $path );
563
+ }
564
+
565
+ /**
566
+ * Detects presence of sitemap.xml in root folder.
567
+ *
568
+ * @since 2.5.2
569
+ * @staticvar bool $has_map
570
+ *
571
+ * @return bool
572
+ */
573
+ public function has_sitemap_xml() {
574
+
575
+ static $has_map = null;
576
+
577
+ if ( isset( $has_map ) )
578
+ return $has_map;
579
+
580
+ $path = get_home_path() . 'sitemap.xml';
581
+
582
+ return $has_map = file_exists( $path );
583
+ }
584
+
585
+ /**
586
+ * Determines if WP is above or below a version
587
+ *
588
+ * @since 2.2.1
589
+ *
590
+ * @param string $version the three part version to compare to WordPress
591
+ * @param string $compare the comparing operator, default "$version >= Current WP Version"
592
+ *
593
+ * @staticvar array $cache
594
+ * @since 2.3.8
595
+ *
596
+ * @return bool wp version is "compare" to
597
+ */
598
+ public function wp_version( $version = '4.3.0', $compare = '>=' ) {
599
+
600
+ static $cache = array();
601
+
602
+ if ( empty( $compare ) )
603
+ $compare = '>=';
604
+
605
+ if ( isset( $cache[$version][$compare] ) )
606
+ return $cache[$version][$compare];
607
+
608
+ global $wp_version;
609
+
610
+ // Add a .0 if WP outputs something like 4.3 instead of 4.3.0
611
+ if ( 3 === strlen( $wp_version ) )
612
+ $wp_version = $wp_version . '.0';
613
+
614
+ if ( version_compare( $wp_version, $version, $compare ) )
615
+ return $cache[$version][$compare] = true;
616
+
617
+ return $cache[$version][$compare] = false;
618
+ }
619
+
620
+ /**
621
+ * Checks for current theme support.
622
+ *
623
+ * Also, if it's cached as true from an array, it will be cached as string as well.
624
+ * This is desired.
625
+ *
626
+ * @NOTE will return true if ANY of the array values matches.
627
+ *
628
+ * @since 2.2.5
629
+ *
630
+ * @param string|array required $feature The features to check for.
631
+ * @param bool $use_cache If set to false don't use cache.
632
+ *
633
+ * @staticvar array $cache
634
+ *
635
+ * @return bool theme support.
636
+ */
637
+ public function detect_theme_support( $features, $use_cache = true ) {
638
+
639
+ if ( ! $use_cache ) {
640
+ //* Don't use cache.
641
+
642
+ if ( is_string( $features ) && ( current_theme_supports( $features ) ) )
643
+ return true;
644
+
645
+ if ( is_array( $features ) ) {
646
+ foreach ( $features as $feature ) {
647
+ if ( current_theme_supports( $feature ) ) {
648
+ return true;
649
+ break;
650
+ }
651
+ }
652
+ }
653
+
654
+ return false;
655
+ }
656
+
657
+ //* Setup cache.
658
+ static $cache = array();
659
+
660
+ //* Check theme support cache
661
+ if ( is_string( $features ) && isset( $cache[$features] ) )
662
+ //* Feature support check has been cached
663
+ return $cache[$features];
664
+
665
+ //* Check theme support array cache
666
+ if ( is_array( $features ) ) {
667
+ foreach ( $features as $feature ) {
668
+ if ( isset( $cache[$feature] ) && in_array( $cache[$feature], $features ) && $cache[$feature] ) {
669
+ // Feature is found and true
670
+ return $cache[$feature];
671
+ break;
672
+ }
673
+ }
674
+ }
675
+
676
+ //* Setup cache values
677
+ if ( is_string( $features ) ) {
678
+ if ( current_theme_supports( $features ) ) {
679
+ return $cache[$features] = true;
680
+ } else {
681
+ return $cache[$features] = false;
682
+ }
683
+ } else if ( is_array( $features ) ) {
684
+ foreach ( $features as $feature ) {
685
+ if ( current_theme_supports( $feature ) ) {
686
+ return $cache[$feature] = true;
687
+ break;
688
+ } else {
689
+ $cache[$feature] = false;
690
+ }
691
+ }
692
+ return $cache[$feature];
693
+ }
694
+
695
+ // No true value found so far, let's return false.
696
+ if ( ! isset( $cache[$features] ) )
697
+ $cache[$features] = false;
698
+
699
+ return $cache[$features];
700
+ }
701
+
702
+ /**
703
+ * Checks a theme's support for title-tag.
704
+ *
705
+ * @since 2.6.0
706
+ * @staticvar bool $supports
707
+ *
708
+ * @global array $_wp_theme_features
709
+ *
710
+ * @return bool
711
+ */
712
+ public function current_theme_supports_title_tag() {
713
+
714
+ static $supports = null;
715
+
716
+ if ( isset( $supports ) )
717
+ return $supports;
718
+
719
+ global $_wp_theme_features;
720
+
721
+ if ( isset( $_wp_theme_features['title-tag'] ) && true === $_wp_theme_features['title-tag'] )
722
+ return $supports = true;
723
+
724
+ return $supports = false;
725
+ }
726
+
727
+ /**
728
+ * Add doing it wrong html code in the footer.
729
+ *
730
+ * @since 2.5.2.1
731
+ * @staticvar bool $no_spam
732
+ *
733
+ * @staticvar string $sep_output
734
+ * @staticvar string $display_output
735
+ * @staticvar string $seplocation_output
736
+ *
737
+ * @param null|string $title The given title
738
+ * @param null|string $sep The separator
739
+ * @param null|string $seplocation Whether the blogname is left or right.
740
+ * @param bool $output Whether to store cache values or echo the output in the footer.
741
+ *
742
+ * @return void
743
+ */
744
+ public function tell_title_doing_it_wrong( $title = null, $sep = null, $seplocation = null, $output = true ) {
745
+
746
+ if ( $output ) {
747
+ //* Prevent error log spam.
748
+ static $no_spam = null;
749
+
750
+ if ( isset( $no_spam ) )
751
+ return;
752
+
753
+ $no_spam = true;
754
+ }
755
+
756
+ static $title_output = null;
757
+ static $sep_output = null;
758
+ static $seplocation_output = null;
759
+
760
+ if ( ! isset( $title_output ) || ! isset( $sep_output ) || ! isset( $seplocation_output ) ) {
761
+ //* Initiate caches, set up variables.
762
+
763
+ if ( '' === $title )
764
+ $title = 'empty';
765
+
766
+ if ( '' === $sep )
767
+ $sep = 'empty';
768
+
769
+ if ( '' === $seplocation )
770
+ $seplocation = 'empty';
771
+
772
+ $title_output = ! isset( $title ) ? 'notset' : esc_attr( $title );
773
+ $sep_output = ! isset( $sep ) ? 'notset' : esc_attr( $sep );
774
+ $seplocation_output = ! isset( $seplocation ) ? 'notset' : esc_attr( $seplocation );
775
+ }
776
+
777
+ //* Echo the HTML comment.
778
+ if ( $output )
779
+ echo '<!-- Title diw: "' . $title_output . '" : "' . $sep_output . '" : "' . $seplocation_output . '" -->' . "\r\n";
780
+
781
+ return;
782
+ }
783
+
784
+ /**
785
+ * Detect WPMUdev Domain Mapping plugin.
786
+ *
787
+ * @since 2.3.0
788
+ * @staticvar bool $active
789
+ *
790
+ * @return bool
791
+ */
792
+ public function is_domainmapping_active() {
793
+
794
+ static $active = null;
795
+
796
+ if ( isset( $active ) )
797
+ return $active;
798
+
799
+ return $active = $this->detect_plugin( array( 'classes' => array( 'domain_map' ) ) );
800
+ }
801
+
802
+ /**
803
+ * Detect Donncha Domain Mapping plugin.
804
+ *
805
+ * @since 2.4.0
806
+ * @staticvar bool $active
807
+ *
808
+ * @return bool
809
+ */
810
+ public function is_donncha_domainmapping_active() {
811
+
812
+ static $active = null;
813
+
814
+ if ( isset( $active ) )
815
+ return $active;
816
+
817
+ return $active = $this->detect_plugin( array( 'functions' => array( 'redirect_to_mapped_domain' ) ) );
818
+ }
819
+
820
+ /**
821
+ * Detect WPML plugin.
822
+ *
823
+ * @since 2.6.0
824
+ * @staticvar bool $active
825
+ *
826
+ * @return bool
827
+ */
828
+ public function is_wpml_active() {
829
+
830
+ static $active = null;
831
+
832
+ if ( isset( $active ) )
833
+ return $active;
834
+
835
+ return $active = $this->detect_plugin( array( 'constants' => array( 'ICL_LANGUAGE_CODE' ) ) );
836
+ }
837
+
838
+ /**
839
+ * Detect qTranslate X plugin.
840
+ *
841
+ * @since 2.6.0
842
+ * @staticvar bool $active
843
+ *
844
+ * @return bool
845
+ */
846
+ public function is_qtranslate_active() {
847
+
848
+ static $active = null;
849
+
850
+ if ( isset( $active ) )
851
+ return $active;
852
+
853
+ return $active = $this->detect_plugin( array( 'constants' => array( 'QTX_VERSION' ) ) );
854
+ }
855
+
856
+ /**
857
+ * Detect if the current screen type is a page or taxonomy.
858
+ *
859
+ * @param string $type the Screen type
860
+ * @staticvar array $is_page
861
+ *
862
+ * @since 2.3.1
863
+ *
864
+ * @return bool true if post type is a page or post
865
+ */
866
+ public function is_post_type_page( $type ) {
867
+
868
+ static $is_page = array();
869
+
870
+ if ( isset( $is_page[$type] ) )
871
+ return $is_page[$type];
872
+
873
+ $post_page = (array) get_post_types( array( 'public' => true ) );
874
+
875
+ foreach ( $post_page as $screen ) {
876
+ if ( $type === $screen ) {
877
+ return $is_page[$type] = true;
878
+ break;
879
+ }
880
+ }
881
+
882
+ return $is_page[$type] = false;
883
+ }
884
+
885
+ /**
886
+ * Detect WordPress language.
887
+ * Considers en_UK, en_US, en, etc.
888
+ *
889
+ * @param string $locale Required, the locale.
890
+ * @param bool $use_cache Set to false to bypass the cache.
891
+ *
892
+ * @staticvar array $locale
893
+ * @staticvar string $get_locale
894
+ *
895
+ * @since 2.6.0
896
+ *
897
+ * @return bool Whether the locale is in the WordPress locale.
898
+ */
899
+ public function check_wp_locale( $locale = '', $use_cache = true ) {
900
+
901
+ if ( empty( $locale ) )
902
+ return false;
903
+
904
+ if ( true !== $use_cache )
905
+ return (bool) strpos( get_locale(), $locale );
906
+
907
+ static $cache = array();
908
+
909
+ if ( isset( $cache[$locale] ) )
910
+ return $cache[$locale];
911
+
912
+ static $get_locale = null;
913
+
914
+ if ( ! isset( $get_locale ) )
915
+ $get_locale = get_locale();
916
+
917
+ return $cache[$locale] = false !== strpos( $get_locale, $locale ) ? true : false;
918
+ }
919
+
920
+ /**
921
+ * Determines if the post type is compatible with The SEO Framework inpost metabox.
922
+ *
923
+ * @since 2.3.5
924
+ * @param string|null $post_type
925
+ *
926
+ * @return bool True if post type is supported.
927
+ */
928
+ public function post_type_supports_inpost( $post_type = null ) {
929
+
930
+ if ( isset( $post_type ) && $post_type ) {
931
+ $supports = (array) apply_filters( 'the_seo_framework_custom_post_type_support',
932
+ array(
933
+ 'title',
934
+ 'editor',
935
+ )
936
+ );
937
+
938
+ foreach ( $supports as $support ) {
939
+ if ( ! post_type_supports( $post_type, $support ) ) {
940
+ return false;
941
+ break;
942
+ }
943
+ continue;
944
+ }
945
+
946
+ return true;
947
+ }
948
+
949
+ return false;
950
+ }
951
+
952
+ /**
953
+ * Check if post type supports The SEO Framework.
954
+ * Doesn't work on admin_init.
955
+ *
956
+ * @since 2.3.9
957
+ *
958
+ * @param string $post_type The current post type.
959
+ *
960
+ * @staticvar string $post_type
961
+ * @staticvar bool $supported
962
+ * @staticvar array $post_page
963
+ *
964
+ * @return bool true of post type is supported.
965
+ */
966
+ public function post_type_supports_custom_seo( $post_type = '' ) {
967
+
968
+ $post_type = $this->get_supported_post_type( true, $post_type );
969
+
970
+ if ( empty( $post_type ) )
971
+ return false;
972
+
973
+ static $supported = array();
974
+
975
+ if ( isset( $supported[$post_type] ) )
976
+ return $supported[$post_type];
977
+
978
+ /**
979
+ * We now support all posts that allow a title, content editor and excerpt.
980
+ * To ease the flow, we have our basic list to check first.
981
+ *
982
+ * @since 2.3.5
983
+ */
984
+ if ( post_type_supports( $post_type, 'autodescription-meta' ) || $this->post_type_supports_inpost( $post_type ) )
985
+ return $supported[$post_type] = true;
986
+
987
+ return $supported[$post_type] = false;
988
+ }
989
+
990
+ /**
991
+ * Checks (current) Post Type for if this plugin may use it.
992
+ *
993
+ * @param bool $public Whether to only get Public Post types.
994
+ * @param string $post_type Optional. The post type to check.
995
+ *
996
+ * @since 2.6.0
997
+ * @staticvar string $cache
998
+ *
999
+ * @return bool|string The Allowed Post Type.
1000
+ */
1001
+ public function get_supported_post_type( $public = true, $post_type = '' ) {
1002
+
1003
+ if ( empty( $post_type ) ) {
1004
+ global $current_screen;
1005
+
1006
+ if ( isset( $current_screen->post_type ) ) {
1007
+ $post_type = $current_screen->post_type;
1008
+ } else {
1009
+ return false;
1010
+ }
1011
+ }
1012
+
1013
+ $post_type_evaluated = $post_type;
1014
+
1015
+ static $cache = array();
1016
+
1017
+ if ( isset( $cache[$public][$post_type] ) )
1018
+ return $cache[$public][$post_type];
1019
+
1020
+ $object = get_post_type_object( $post_type );
1021
+
1022
+ //* Check if rewrite is enabled. Bypass builtin post types.
1023
+ if ( isset( $object->_builtin ) && false === $object->_builtin )
1024
+ if ( isset( $object->rewrite ) && false === $object->rewrite )
1025
+ $post_type = false;
1026
+
1027
+ //* Check if post is public if public parameter is set.
1028
+ if ( $post_type && $public )
1029
+ if ( isset( $object->public ) && ! $object->public )
1030
+ $post_type = false;
1031
+
1032
+ /**
1033
+ * Applies filters 'the_seo_framework_supported_post_type' : string
1034
+ * @since 2.6.2
1035
+ *
1036
+ * @param string $post_type The supported post type. Is boolean false if not supported.
1037
+ * @param string $post_type_evaluated The evaluated post type.
1038
+ */
1039
+ $post_type = (string) apply_filters( 'the_seo_framework_supported_post_type', $post_type, $post_type_evaluated );
1040
+
1041
+ //* No supported post type has been found.
1042
+ if ( empty( $post_type ) )
1043
+ return $cache[$public][$post_type] = false;
1044
+
1045
+ return $cache[$public][$post_type] = $post_type;
1046
+ }
1047
+
1048
+ /**
1049
+ * Determines whether the theme is outputting the title correctly based on transient.
1050
+ *
1051
+ * @since 2.5.2
1052
+ * @staticvar bool $dir
1053
+ *
1054
+ * @return bool True theme is doing it right.
1055
+ */
1056
+ public function theme_title_doing_it_right() {
1057
+
1058
+ static $dir = null;
1059
+
1060
+ if ( isset( $dir ) )
1061
+ return $dir;
1062
+
1063
+ $transient = get_transient( $this->theme_doing_it_right_transient );
1064
+
1065
+ if ( '0' === $transient )
1066
+ return $dir = false;
1067
+
1068
+ /**
1069
+ * Transient has not been set yet (false)
1070
+ * or the theme is doing it right ('1').
1071
+ */
1072
+ return $dir = true;
1073
+ }
1074
+
1075
+ /**
1076
+ * Detect theme title fix extension plugin.
1077
+ *
1078
+ * @since 2.6.0
1079
+ * @staticvar bool $fixed
1080
+ *
1081
+ * @return bool True theme will do it right.
1082
+ */
1083
+ public function theme_title_fix_active() {
1084
+
1085
+ static $fixed = null;
1086
+
1087
+ if ( isset( $fixed ) )
1088
+ return $fixed;
1089
+
1090
+ if ( defined( 'THE_SEO_FRAMEWORK_TITLE_FIX' ) && THE_SEO_FRAMEWORK_TITLE_FIX )
1091
+ return $fixed = true;
1092
+
1093
+ return $fixed = false;
1094
+ }
1095
+
1096
+ /**
1097
+ * Checks whether we can use special manipulation filters.
1098
+ *
1099
+ * @since 2.6.0
1100
+ *
1101
+ * @return bool True if we can manipulate title.
1102
+ */
1103
+ public function can_manipulate_title() {
1104
+
1105
+ if ( $this->theme_title_doing_it_right() || $this->theme_title_fix_active() )
1106
+ return true;
1107
+
1108
+ return false;
1109
+ }
1110
+
1111
+ /**
1112
+ * Determines whether a page or blog is on front.
1113
+ *
1114
+ * @since 2.6.0
1115
+ * @staticvar bool $pof
1116
+ *
1117
+ * @return bool
1118
+ */
1119
+ public function has_page_on_front() {
1120
+
1121
+ static $pof = null;
1122
+
1123
+ if ( isset( $pof ) )
1124
+ return $pof;
1125
+
1126
+ return $pof = 'page' === get_option( 'show_on_front' ) ? true : false;
1127
+ }
1128
+
1129
+ }
inc/classes/doingitright.class.php ADDED
@@ -0,0 +1,1656 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The SEO Framework plugin
4
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License version 3 as published
8
+ * by the Free Software Foundation.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ /**
20
+ * Class AutoDescription_DoingItRight
21
+ *
22
+ * Adds data in a column to edit.php and edit-tags.php
23
+ * Shows you if you're doing the SEO right.
24
+ *
25
+ * @since 2.1.9
26
+ */
27
+ class AutoDescription_DoingItRight extends AutoDescription_Search {
28
+
29
+ /**
30
+ * Constructor, load parent constructor.
31
+ * Initalizes columns and load post states.
32
+ */
33
+ public function __construct() {
34
+ parent::__construct();
35
+
36
+ //* Initialize post states.
37
+ add_action( 'current_screen', array( $this, 'post_state' ) );
38
+
39
+ //* Ajax handlers for columns.
40
+ add_action( 'admin_init', array( $this, 'init_columns_ajax' ) );
41
+ //* Initialize columns.
42
+ add_action( 'current_screen', array( $this, 'init_columns' ) );
43
+
44
+ }
45
+
46
+ /**
47
+ * Add post state on edit.php to the page or post that has been altered
48
+ *
49
+ * Applies filters `the_seo_framework_allow_states` : boolean Whether to allow post states output.
50
+ *
51
+ * @uses $this->add_post_state
52
+ *
53
+ * @since 2.1.0
54
+ */
55
+ public function post_state() {
56
+
57
+ //* Only load on singular pages.
58
+ if ( $this->is_singular() ) {
59
+
60
+ $allow_states = (bool) apply_filters( 'the_seo_framework_allow_states', true );
61
+
62
+ if ( $allow_states )
63
+ add_filter( 'display_post_states', array( $this, 'add_post_state' ), 10, 2 );
64
+
65
+ }
66
+
67
+ }
68
+
69
+ /**
70
+ * Adds post states in post/page edit.php query
71
+ *
72
+ * @since 2.1.0
73
+ *
74
+ * @param array $states The current post states array
75
+ * @param object $post The Post Object.
76
+ */
77
+ public function add_post_state( $states = array(), $post ) {
78
+
79
+ $post_id = isset( $post->ID ) ? $post->ID : false;
80
+
81
+ if ( $post_id ) {
82
+ $searchexclude = (bool) $this->get_custom_field( 'exclude_local_search', $post_id );
83
+
84
+ if ( $searchexclude )
85
+ $states[] = __( 'No Search', 'autodescription' );
86
+ }
87
+
88
+ return $states;
89
+ }
90
+
91
+ /**
92
+ * AJAX wrapper for $this->init_columns
93
+ *
94
+ * @since 2.6.0
95
+ */
96
+ public function init_columns_ajax() {
97
+
98
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
99
+
100
+ /**
101
+ * Securely check the referrer, instead of leaving holes everywhere.
102
+ */
103
+ if ( current_user_can( 'publish_posts' ) && check_ajax_referer( 'add-tag', '_wpnonce_add-tag', false ) )
104
+ $this->init_columns( '', true );
105
+ }
106
+
107
+ }
108
+
109
+ /**
110
+ * Initializes columns
111
+ *
112
+ * Applies filter the_seo_framework_show_seo_column : Boolean Show the SEO column in edit.php
113
+ *
114
+ * @since 2.1.9
115
+ *
116
+ * @param object|empty $screen WP_Screen
117
+ * @param bool $doing_ajax Whether we're doing an AJAX response.
118
+ */
119
+ public function init_columns( $screen = '', $doing_ajax = false ) {
120
+
121
+ $show_seo_column = (bool) apply_filters( 'the_seo_framework_show_seo_column', true );
122
+
123
+ if ( $doing_ajax ) {
124
+ $post_type = isset( $_POST['post_type'] ) ? $_POST['post_type'] : '';
125
+ } else {
126
+ $post_type = isset( $screen->post_type ) ? $screen->post_type : '';
127
+ }
128
+
129
+ if ( $show_seo_column && $this->post_type_supports_custom_seo( $post_type ) ) {
130
+
131
+ if ( $doing_ajax ) {
132
+
133
+ $id = isset( $_POST['screen'] ) ? $_POST['screen'] : false;
134
+ $taxonomy = isset( $_POST['taxonomy'] ) ? $_POST['taxonomy'] : false;
135
+
136
+ if ( $taxonomy && $id ) {
137
+ add_filter( 'manage_' . $id . '_columns', array( $this, 'add_column' ), 1 );
138
+ add_action( 'manage_' . $taxonomy . '_custom_column', array( $this, 'seo_bar_ajax' ), 1, 3 );
139
+ }
140
+
141
+ } else {
142
+
143
+ $id = isset( $screen->id ) ? $screen->id : '';
144
+
145
+ if ( '' !== $id ) {
146
+
147
+ if ( $this->is_wp_lists_edit() ) {
148
+ add_filter( 'manage_' . $id . '_columns', array( $this, 'add_column' ), 10, 1 );
149
+
150
+ $taxonomy = isset( $screen->taxonomy ) ? $screen->taxonomy : '';
151
+
152
+ if ( $taxonomy )
153
+ add_action( 'manage_' . $taxonomy . '_custom_column', array( $this, 'seo_bar' ), 1, 3 );
154
+
155
+ /**
156
+ * Always load pages and posts.
157
+ * Many CPT plugins rely on these.
158
+ */
159
+ add_action( 'manage_posts_custom_column', array( $this, 'seo_bar' ), 1, 3 );
160
+ add_action( 'manage_pages_custom_column', array( $this, 'seo_bar' ), 1, 3 );
161
+ }
162
+
163
+ }
164
+ }
165
+
166
+ }
167
+
168
+ }
169
+
170
+ /**
171
+ * Adds SEO column on edit(-tags).php
172
+ *
173
+ * Also determines where the column should be placed. Prefered before comments, then data, then tags.
174
+ * If neither found, it will add the column to the end.
175
+ *
176
+ * @since 2.1.9
177
+ *
178
+ * @param array $columns The existing columns
179
+ * @return array $columns the column data
180
+ */
181
+ public function add_column( $columns ) {
182
+
183
+ $seocolumn = array( 'ad_seo' => 'SEO' );
184
+
185
+ $column_keys = array_keys( $columns );
186
+
187
+ //* Column keys to look for, in order of appearance.
188
+ $order_keys = array(
189
+ 'comments',
190
+ 'posts',
191
+ 'date',
192
+ 'tags',
193
+ 'bbp_topic_freshness',
194
+ 'bbp_forum_freshness',
195
+ 'bbp_reply_created',
196
+ );
197
+
198
+ foreach ( $order_keys as $key ) {
199
+ //* Put value in $offset, if not false, break loop.
200
+ if ( false !== ( $offset = array_search( $key, $column_keys ) ) )
201
+ break;
202
+ }
203
+
204
+ //* I tried but found nothing
205
+ if ( false === $offset ) {
206
+ //* Add SEO bar at the end of columns.
207
+ $columns = array_merge( $columns, $seocolumn );
208
+ } else {
209
+ //* Add seo bar between columns.
210
+
211
+ //* Cache columns.
212
+ $columns_before = $columns;
213
+
214
+ $columns = array_merge(
215
+ array_splice( $columns, 0, $offset ),
216
+ $seocolumn,
217
+ array_splice( $columns_before, $offset )
218
+ );
219
+ }
220
+
221
+ return $columns;
222
+ }
223
+
224
+ /**
225
+ * Echo's the SEO Bar.
226
+ *
227
+ * @since 2.6.0
228
+ * @staticvar string $type
229
+ *
230
+ * @param string $column the current column : If it's a taxonomy, this is empty
231
+ * @param int $post_id the post id : If it's a taxonomy, this is the column name
232
+ * @param string $tax_id this is empty : If it's a taxonomy, this is the taxonomy id
233
+ */
234
+ public function seo_bar( $column, $post_id, $tax_id = '' ) {
235
+
236
+ static $type = null;
237
+
238
+ if ( ! isset( $type ) ) {
239
+ $type = get_post_type( $post_id );
240
+
241
+ if ( false === $type || '' !== $tax_id ) {
242
+ $screen = (object) get_current_screen();
243
+
244
+ if ( isset( $screen->taxonomy ) )
245
+ $type = $screen->taxonomy;
246
+ }
247
+ }
248
+
249
+ /**
250
+ * Params are shifted.
251
+ * @link https://core.trac.wordpress.org/ticket/33521
252
+ */
253
+ if ( '' !== $tax_id ) {
254
+ $column = $post_id;
255
+ $post_id = $tax_id;
256
+ }
257
+
258
+ if ( 'ad_seo' === $column )
259
+ echo $this->post_status( $post_id, $type, true );
260
+
261
+ }
262
+
263
+ /**
264
+ * Echo's the SEO column in edit screens on Ajax call.
265
+ *
266
+ * @since 2.1.9
267
+ *
268
+ * @param string $column the current column : If it's a taxonomy, this is empty
269
+ * @param int $post_id the post id : If it's a taxonomy, this is the column name
270
+ * @param string $tax_id this is empty : If it's a taxonomy, this is the taxonomy id
271
+ */
272
+ public function seo_bar_ajax( $column, $post_id, $tax_id = '' ) {
273
+
274
+ $is_term = false;
275
+
276
+ /**
277
+ * Params are shifted.
278
+ * @link https://core.trac.wordpress.org/ticket/33521
279
+ */
280
+ if ( '' !== $tax_id ) {
281
+ $is_term = true;
282
+ $column = $post_id;
283
+ $post_id = $tax_id;
284
+ }
285
+
286
+ if ( 'ad_seo' === $column ) {
287
+ $context = __( 'Refresh to see the SEO Bar status.', 'autodescription' );
288
+
289
+ $ajax_id = $column . $post_id;
290
+
291
+ echo $this->post_status_special( $context, '?', 'unknown', $is_term, $ajax_id );
292
+ }
293
+
294
+ }
295
+
296
+ /**
297
+ * Wrap a single-line block for the SEO bar, showing special statuses.
298
+ *
299
+ * @since 2.6.0
300
+ *
301
+ * @param string $context The hover/screenreader context.
302
+ * @param string $symbol The single-character symbol.
303
+ * @param string $class The SEO block color code. : 'bad', 'okay', 'good', 'unknown'.
304
+ * @param int|null $ajax_id The unique Ajax ID to generate a small on-hover script for this ID. May be Arbitrary.
305
+ * @return string The special block with wrap.
306
+ */
307
+ protected function post_status_special( $context, $symbol = '?', $color = 'unknown', $is_term = '', $ajax_id = null ) {
308
+
309
+ $classes = $this->get_the_seo_bar_classes();
310
+
311
+ $args = array();
312
+ $args['class'] = $classes[$color];
313
+ $args['width'] = $classes['100%'];
314
+ $args['notice'] = $context;
315
+ $args['indicator'] = $symbol;
316
+
317
+ $block = $this->wrap_the_seo_bar_block( $args );
318
+
319
+ if ( empty( $is_term ) )
320
+ $is_term = $this->is_archive();
321
+
322
+ return $this->get_the_seo_bar_wrap( $block, $is_term, $ajax_id );
323
+ }
324
+
325
+ /**
326
+ * Renders post status. Caches the output.
327
+ *
328
+ * @since 2.1.9
329
+ * @staticvar string $post_i18n The post type slug.
330
+ * @staticvar bool $is_term If we're dealing with TT pages.
331
+ *
332
+ * @param int $post_id The Post ID or taxonomy ID.
333
+ * @param string $type Is fetched on edit.php, inpost, taxonomies, etc.
334
+ * @param bool $html return the status in html or string.
335
+ * @return string $content the post SEO status
336
+ */
337
+ public function post_status( $post_id = '', $type = 'inpost', $html = true ) {
338
+
339
+ $content = '';
340
+
341
+ //* Fetch Post ID if it hasn't been provided.
342
+ if ( empty( $post_id ) )
343
+ $post_id = $this->get_the_real_ID();
344
+
345
+ //* Only run when post ID is found.
346
+ if ( isset( $post_id ) && $post_id ) {
347
+
348
+ //* Fetch Post Type.
349
+ if ( 'inpost' === $type || '' === $type )
350
+ $type = get_post_type( $post_id );
351
+
352
+ //* No need to re-evalute these.
353
+ static $post_i18n = null;
354
+ static $is_term = null;
355
+
356
+ $term = false;
357
+ /**
358
+ * Static caching.
359
+ * @since 2.3.8
360
+ */
361
+ if ( ! isset( $post_i18n ) && ! isset( $is_term ) ) {
362
+
363
+ //* Setup i18n values for posts and pages.
364
+ if ( 'post' === $type ) {
365
+ $post_i18n = __( 'Post', 'autodescription' );
366
+ $is_term = false;
367
+ $term = false;
368
+ } else if ( 'page' === $type ) {
369
+ $post_i18n = __( 'Page', 'autodescription' );
370
+ $is_term = false;
371
+ $term = false;
372
+ } else {
373
+ /**
374
+ * Because of static caching, $is_term was never assigned.
375
+ * @since 2.4.1
376
+ */
377
+ $is_term = true;
378
+ }
379
+ }
380
+
381
+ if ( $is_term ) {
382
+ //* We're on a term or taxonomy. Try fetching names. Default back to "Page".
383
+ $term = get_term_by( 'id', $post_id, $type, OBJECT );
384
+ $post_i18n = $this->get_the_term_name( $term );
385
+
386
+ /**
387
+ * Check if current post type is a page or taxonomy.
388
+ * Only check if is_term is not yet changed to false. To save processing power.
389
+ *
390
+ * @since 2.3.1
391
+ */
392
+ if ( $is_term && $this->is_post_type_page( $type ) )
393
+ $is_term = false;
394
+ }
395
+
396
+ $post_low = $this->maybe_lowercase_noun( $post_i18n );
397
+
398
+ $args = array(
399
+ 'is_term' => $is_term,
400
+ 'term' => $term,
401
+ 'post_id' => $post_id,
402
+ 'post_i18n' => $post_i18n,
403
+ 'post_low' => $post_low,
404
+ 'type' => $type,
405
+ );
406
+
407
+ if ( $is_term ) {
408
+ return $this->the_seo_bar_term( $args );
409
+ } else {
410
+ return $this->the_seo_bar_page( $args );
411
+ }
412
+ } else {
413
+ $context = __( 'Failed to fetch post ID.', 'autodescription' );
414
+
415
+ return $this->post_status_special( $context, '!', 'bad' );
416
+ }
417
+ }
418
+
419
+ /**
420
+ * Outputs a part of the SEO Bar based on parameters.
421
+ *
422
+ * @since 2.6.0
423
+ *
424
+ * @param array $args : {
425
+ * string $indicator
426
+ * string $notice
427
+ * string $width
428
+ * string $class
429
+ * }
430
+ * @return string The SEO Bar block part.
431
+ */
432
+ protected function wrap_the_seo_bar_block( $args ) {
433
+
434
+ $wrap = '<span class="ad-sec-wrap ' . $args['width'] . '">'
435
+ . '<a onclick="return false;" class="' . $args['class'] . '" aria-label="' . $args['notice'] . '" data-desc="' . $args['notice'] . '">'
436
+ . $args['indicator']
437
+ . '</a>'
438
+ . '</span>';
439
+
440
+ return $wrap;
441
+ }
442
+
443
+ /**
444
+ * Wrap the SEO bar.
445
+ *
446
+ * If Ajax ID is set, a small jQuery script will also be output to reset the
447
+ * DOM element for the status bar hover.
448
+ *
449
+ * @since 2.6.0
450
+ * @staticvar string $class
451
+ *
452
+ * @param string $content The SEO Bar content.
453
+ * @param bool $is_term Whether the bar is for a term.
454
+ * @param int|null $ajax_id The unique Ajax ID to generate a small on-hover script for.
455
+ * @return string The SEO Bar wrapped.
456
+ */
457
+ protected function get_the_seo_bar_wrap( $content, $is_term, $ajax_id = null ) {
458
+
459
+ static $class = null;
460
+
461
+ if ( is_null( $class ) ) {
462
+ $classes = $this->get_the_seo_bar_classes();
463
+
464
+ $width = $is_term ? ' ' . $classes['100%'] : '';
465
+ $pill = $this->pill_the_seo_bar() ? ' ' . $classes['pill'] : '';
466
+
467
+ $class = 'ad-seo clearfix' . $width . $pill;
468
+ }
469
+
470
+ if ( isset( $ajax_id ) ) {
471
+ //* Ajax handler.
472
+ $script = '<script>jQuery("#' . esc_attr( $ajax_id ) . '").on( "hover click", autodescription.statusBarHover );</script>';
473
+
474
+ return sprintf( '<span class="%s" id="%s"><span class="ad-bar-wrap">%s</span></span>', $class, $ajax_id, $content ) . $script;
475
+ }
476
+
477
+ return sprintf( '<span class="%s"><span class="ad-bar-wrap">%s</span></span>', $class, $content );
478
+ }
479
+
480
+ /**
481
+ * Output the SEO bar for Terms and Taxonomies.
482
+ *
483
+ * @since 2.6.0
484
+ *
485
+ * @param array $args {
486
+ * 'is_term' => bool $is_term,
487
+ * 'term' => object $term,
488
+ * 'post_id' => int $post_id,
489
+ * 'post_i18n' => string $post_i18n,
490
+ * 'post_low' => string $post_low,
491
+ * 'type' => string $type,
492
+ * }
493
+ * @return string $content The SEO bar.
494
+ */
495
+ protected function the_seo_bar_term( $args ) {
496
+
497
+ $post_id = $args['post_id'];
498
+ $term = $args['term'];
499
+ $post = $args['post_i18n'];
500
+ $is_term = true;
501
+
502
+ $noindex = isset( $term->admeta['noindex'] ) && $this->is_checked( $term->admeta['noindex'] ) ? true : false;
503
+ $redirect = false; // We don't apply redirect on taxonomies (yet)
504
+
505
+ $ad_savedflag = isset( $term->admeta['saved_flag'] ) && $this->is_checked( $term->admeta['saved_flag'] ) ? true : false;
506
+ $flag = $ad_savedflag;
507
+
508
+ //* Genesis data fetch
509
+ if ( false === $noindex && false === $flag && isset( $term->meta['noindex'] ) )
510
+ $noindex = $this->is_checked( $term->meta['noindex'] ) ? true : false;
511
+
512
+ //* Blocked SEO, return simple bar.
513
+ if ( $redirect || $noindex )
514
+ return $this->the_seo_bar_blocked( array( 'is_term' => $is_term, 'redirect' => $redirect, 'noindex' => $noindex, 'post_i18n' => $post ) );
515
+
516
+ $title_notice = $this->the_seo_bar_title_notice( $args );
517
+ $description_notice = $this->the_seo_bar_description_notice( $args );
518
+ $index_notice = $this->the_seo_bar_index_notice( $args );
519
+ $follow_notice = $this->the_seo_bar_follow_notice( $args );
520
+ $archive_notice = $this->the_seo_bar_archive_notice( $args );
521
+
522
+ $content = $title_notice . $description_notice . $index_notice . $follow_notice . $archive_notice;
523
+
524
+ return $this->get_the_seo_bar_wrap( $content, $is_term );
525
+ }
526
+
527
+ /**
528
+ * Output the SEO bar for Terms and Taxonomies.
529
+ *
530
+ * @since 2.6.0
531
+ *
532
+ * @param array $args {
533
+ * 'is_term' => $is_term,
534
+ * 'term' => $term,
535
+ * 'post_id' => $post_id,
536
+ * 'post_i18n' => $post_i18n,
537
+ * 'post_low' => $post_low,
538
+ * 'type' => $type,
539
+ * }
540
+ * @return string $content The SEO bar.
541
+ */
542
+ protected function the_seo_bar_page( $args ) {
543
+
544
+ $post_id = $args['post_id'];
545
+ $post = $args['post_i18n'];
546
+ $is_term = false;
547
+ $is_front_page = $this->is_static_frontpage( $post_id );
548
+
549
+ $redirect = $this->get_custom_field( 'redirect', $post_id );
550
+ $redirect = empty( $redirect ) ? false : true;
551
+
552
+ $noindex = $this->get_custom_field( '_genesis_noindex', $post_id );
553
+ $noindex = $this->is_checked( $noindex );
554
+
555
+ if ( $is_front_page )
556
+ $noindex = $this->is_option_checked( 'homepage_noindex' ) ? true : $noindex;
557
+
558
+ if ( $redirect || $noindex )
559
+ return $this->the_seo_bar_blocked( array( 'is_term' => $is_term, 'redirect' => $redirect, 'noindex' => $noindex, 'post_i18n' => $post ) );
560
+
561
+ $title_notice = $this->the_seo_bar_title_notice( $args );
562
+ $description_notice = $this->the_seo_bar_description_notice( $args );
563
+ $index_notice = $this->the_seo_bar_index_notice( $args );
564
+ $follow_notice = $this->the_seo_bar_follow_notice( $args );
565
+ $archive_notice = $this->the_seo_bar_archive_notice( $args );
566
+ $redirect_notice = $this->the_seo_bar_redirect_notice( $args );
567
+
568
+ $content = $title_notice . $description_notice . $index_notice . $follow_notice . $archive_notice . $redirect_notice;
569
+
570
+ return $this->get_the_seo_bar_wrap( $content, $is_term );
571
+ }
572
+
573
+ /**
574
+ * Fetch the post or term data for The SEO Bar, structured and cached.
575
+ *
576
+ * @since 2.6.0
577
+ * @staticvar array $data
578
+ *
579
+ * @param array $args The term/post args.
580
+ * @return array $data {
581
+ * 'title' => $title,
582
+ * 'title_is_from_custom_field' => $title_is_from_custom_field,
583
+ * 'description' => $description,
584
+ * 'description_is_from_custom_field' => $description_is_from_custom_field,
585
+ * 'nofollow' => $nofollow,
586
+ * 'noarchive' => $noarchive
587
+ * }
588
+ */
589
+ protected function the_seo_bar_data( $args ) {
590
+
591
+ $post_id = $args['post_id'];
592
+
593
+ static $data = array();
594
+
595
+ if ( isset( $data[$post_id] ) )
596
+ return $data[$post_id];
597
+
598
+ if ( $args['is_term'] ) {
599
+ return $data[$post_id] = $this->the_seo_bar_term_data( $args );
600
+ } else {
601
+ return $data[$post_id] = $this->the_seo_bar_post_data( $args );
602
+ }
603
+ }
604
+
605
+ /**
606
+ * Fetch the term data for The SEO Bar.
607
+ *
608
+ * @since 2.6.0
609
+ * @staticvar array $data
610
+ *
611
+ * @param array $args The term args.
612
+ * @return array $data {
613
+ * 'title' => $title,
614
+ * 'title_is_from_custom_field' => $title_is_from_custom_field,
615
+ * 'description' => $description,
616
+ * 'description_is_from_custom_field' => $description_is_from_custom_field,
617
+ * 'nofollow' => $nofollow,
618
+ * 'noarchive' => $noarchive
619
+ * }
620
+ */
621
+ protected function the_seo_bar_term_data( $args ) {
622
+
623
+ $term = $args['term'];
624
+ $post_id = $args['post_id'];
625
+ $taxonomy = $args['type'];
626
+
627
+ $flag = isset( $term->admeta['saved_flag'] ) && $this->is_checked( $term->admeta['saved_flag'] ) ? true : false;
628
+
629
+ $title_custom_field = isset( $term->admeta['doctitle'] ) ? $term->admeta['doctitle'] : '';
630
+ $description_custom_field = isset( $term->admeta['description'] ) ? $term->admeta['description'] : '';
631
+ $nofollow = isset( $term->admeta['nofollow'] ) ? $term->admeta['nofollow'] : '';
632
+ $noarchive = isset( $term->admeta['noarchive'] ) ? $term->admeta['noarchive'] : '';
633
+
634
+ //* Genesis data fetch
635
+ if ( false === $flag && isset( $term->meta ) ) {
636
+ if ( empty( $title_custom_field ) && isset( $term->meta['doctitle'] ) )
637
+ $title_custom_field = $term->meta['doctitle'];
638
+
639
+ if ( empty( $description_custom_field ) && isset( $term->meta['description'] ) )
640
+ $description_custom_field = $term->meta['description'];
641
+
642
+ if ( empty( $nofollow ) && isset( $term->meta['nofollow'] ) )
643
+ $nofollow = $term->meta['nofollow'];
644
+
645
+ if ( empty( $noarchive ) && isset( $term->meta['noarchive'] ) )
646
+ $noarchive = $term->meta['noarchive'];
647
+ }
648
+
649
+ $title_is_from_custom_field = (bool) $title_custom_field;
650
+ if ( $title_is_from_custom_field ) {
651
+ $title = $this->title( '', '', '', array( 'term_id' => $post_id, 'taxonomy' => $taxonomy, 'get_custom_field' => true ) );
652
+ } else {
653
+ $title = $this->title( '', '', '', array( 'term_id' => $post_id, 'taxonomy' => $taxonomy, 'get_custom_field' => false ) );
654
+ }
655
+
656
+ $description_is_from_custom_field = (bool) $description_custom_field;
657
+ if ( $description_is_from_custom_field ) {
658
+ $taxonomy = isset( $term->taxonomy ) && $term->taxonomy ? $term->taxonomy : '';
659
+ $description_args = $taxonomy ? array( 'id' => $post_id, 'taxonomy' => $term->taxonomy, 'get_custom_field' => true ) : array( 'get_custom_field' => true );
660
+
661
+ $description = $this->generate_description( '', $description_args );
662
+ } else {
663
+ $taxonomy = isset( $term->taxonomy ) && $term->taxonomy ? $term->taxonomy : '';
664
+ $description_args = $taxonomy ? array( 'id' => $post_id, 'taxonomy' => $term->taxonomy, 'get_custom_field' => false ) : array( 'get_custom_field' => false );
665
+
666
+ $description = $this->generate_description( '', $description_args );
667
+ }
668
+
669
+ $nofollow = $this->is_checked( $nofollow );
670
+ $noarchive = $this->is_checked( $noarchive );
671
+
672
+ return array(
673
+ 'title' => $title,
674
+ 'title_is_from_custom_field' => $title_is_from_custom_field,
675
+ 'description' => $description,
676
+ 'description_is_from_custom_field' => $description_is_from_custom_field,
677
+ 'nofollow' => $nofollow,
678
+ 'noarchive' => $noarchive
679
+ );
680
+ }
681
+
682
+ /**
683
+ * Fetch the post data for The SEO Bar.
684
+ *
685
+ * @since 2.6.0
686
+ * @staticvar array $data
687
+ *
688
+ * @param array $args The post args.
689
+ * @return array $data {
690
+ * 'title' => $title,
691
+ * 'title_is_from_custom_field' => $title_is_from_custom_field,
692
+ * 'description' => $description,
693
+ * 'description_is_from_custom_field' => $description_is_from_custom_field,
694
+ * 'nofollow' => $nofollow,
695
+ * 'noarchive' => $noarchive
696
+ * }
697
+ */
698
+ protected function the_seo_bar_post_data( $args ) {
699
+
700
+ $post_id = $args['post_id'];
701
+ $page_on_front = $this->is_static_frontpage( $post_id );
702
+
703
+ $title_custom_field = $this->get_custom_field( '_genesis_title', $post_id );
704
+ $description_custom_field = $this->get_custom_field( '_genesis_description', $post_id );
705
+ $nofollow = $this->get_custom_field( '_genesis_nofollow', $post_id );
706
+ $noarchive = $this->get_custom_field( '_genesis_noarchive', $post_id );
707
+
708
+ if ( $page_on_front ) {
709
+ $title_custom_field = $this->get_option( 'homepage_title' ) ? $this->get_option( 'homepage_title' ) : $title_custom_field;
710
+ $description_custom_field = $this->get_option( 'homepage_description' ) ? $this->get_option( 'homepage_description' ) : $description_custom_field;
711
+ $nofollow = $this->get_option( 'homepage_nofollow' ) ? $this->get_option( 'homepage_nofollow' ) : $nofollow;
712
+ $noarchive = $this->get_option( 'homepage_noarchive' ) ? $this->get_option( 'homepage_noarchive' ) : $noarchive;
713
+ }
714
+
715
+ $title_is_from_custom_field = (bool) $title_custom_field;
716
+ if ( $title_is_from_custom_field )
717
+ $title = $this->title( '', '', '', array( 'term_id' => $post_id, 'page_on_front' => $page_on_front, 'get_custom_field' => true ) );
718
+ else
719
+ $title = $this->title( '', '', '', array( 'term_id' => $post_id, 'page_on_front' => $page_on_front, 'get_custom_field' => false ) );
720
+
721
+ $description_is_from_custom_field = (bool) $description_custom_field;
722
+ if ( $description_is_from_custom_field )
723
+ $description = $this->generate_description( '', array( 'id' => $post_id, 'get_custom_field' => true ) );
724
+ else
725
+ $description = $this->generate_description( '', array( 'id' => $post_id, 'get_custom_field' => false ) );
726
+
727
+ $nofollow = $this->is_checked( $nofollow );
728
+ $noarchive = $this->is_checked( $noarchive );
729
+
730
+ return array(
731
+ 'title' => $title,
732
+ 'title_is_from_custom_field' => $title_is_from_custom_field,
733
+ 'description' => $description,
734
+ 'description_is_from_custom_field' => $description_is_from_custom_field,
735
+ 'nofollow' => $nofollow,
736
+ 'noarchive' => $noarchive,
737
+ );
738
+ }
739
+
740
+ /**
741
+ * Render the SEO bar title block and notice.
742
+ *
743
+ * @since 2.6.0
744
+ *
745
+ * @param array $args
746
+ * @return string The SEO Bar Title Block
747
+ */
748
+ protected function the_seo_bar_title_notice( $args ) {
749
+
750
+ //* Fetch data
751
+ $data = $this->the_seo_bar_data( $args );
752
+ $title = $data['title'];
753
+ $title_is_from_custom_field = $data['title_is_from_custom_field'];
754
+
755
+ //* Fetch CSS classes.
756
+ $classes = $this->get_the_seo_bar_classes();
757
+ $ad25 = $classes['25%'];
758
+
759
+ //* Fetch i18n and put in vars
760
+ $i18n = $this->get_the_seo_bar_i18n();
761
+ $title_short = $i18n['title_short'];
762
+ $generated = $i18n['generated_short'];
763
+ $and_i18n = $i18n['and'];
764
+ $but_i18n = $i18n['but'];
765
+
766
+ //* Initialize notice.
767
+ $notice = $i18n['title'];
768
+ $class = $classes['good'];
769
+
770
+ //* Generated notice.
771
+ $generated_notice = '<br>' . $i18n['generated'];
772
+ $gen_t = $title_is_from_custom_field ? '' : $generated;
773
+ $gen_t_notice = $title_is_from_custom_field ? '' : $generated_notice;
774
+
775
+ //* Title length. Convert &#8230; to a single character as well.
776
+ $tit_len = mb_strlen( html_entity_decode( $title ) );
777
+
778
+ //* Length notice.
779
+ $title_length_warning = $this->get_the_seo_bar_title_length_warning( $tit_len, $class );
780
+ $notice .= $title_length_warning ? ' ' . $title_length_warning['notice'] : '';
781
+ $class = $title_length_warning['class'];
782
+
783
+ $title_duplicated = false;
784
+ //* Check if title is duplicated from blogname.
785
+ if ( $this->add_title_additions() ) {
786
+ //* We are using blognames in titles.
787
+
788
+ $blogname = $this->get_blogname();
789
+
790
+ $first = stripos( $title, $blogname );
791
+ $last = strripos( $title, $blogname );
792
+
793
+ if ( $first !== $last )
794
+ $title_duplicated = true;
795
+ }
796
+
797
+ if ( $title_duplicated ) {
798
+ //* If the title is good, we should use And. Otherwise 'But'.
799
+ $but_and = $title_length_warning['but'] ? $but_i18n : $and_i18n;
800
+
801
+ /* translators: %s = But or And */
802
+ $notice .= '<br>' . sprintf( __( '%s the Title contains the Blogname multiple times.', 'autodescription' ), $but_and );
803
+ $class = $classes['bad'];
804
+ }
805
+
806
+ //* Put everything together.
807
+ $notice = $notice . $gen_t_notice;
808
+ $title_short = $title_short . $gen_t;
809
+
810
+ $tit_wrap_args = array(
811
+ 'indicator' => $title_short,
812
+ 'notice' => $notice,
813
+ 'width' => $ad25,
814
+ 'class' => $class,
815
+ );
816
+
817
+ $title_notice = $this->wrap_the_seo_bar_block( $tit_wrap_args );
818
+
819
+ return $title_notice;
820
+ }
821
+
822
+ /**
823
+ * Render the SEO bar description block and notice.
824
+ *
825
+ * @since 2.6.0
826
+ *
827
+ * @param array $args
828
+ * @return string The SEO Bar Description Block
829
+ */
830
+ protected function the_seo_bar_description_notice( $args ) {
831
+
832
+ //* Fetch data
833
+ $data = $this->the_seo_bar_data( $args );
834
+ $description = $data['description'];
835
+ $description_is_from_custom_field = $data['description_is_from_custom_field'];
836
+
837
+ //* Fetch i18n and put in vars
838
+ $i18n = $this->get_the_seo_bar_i18n();
839
+ $description_short = $i18n['description_short'];
840
+ $generated_short = $i18n['generated_short'];
841
+
842
+ //* Description length. Convert &#8230; to a single character as well.
843
+ $desc_len = mb_strlen( html_entity_decode( $description ) );
844
+
845
+ //* Fetch CSS classes.
846
+ $classes = $this->get_the_seo_bar_classes();
847
+ $ad25 = $classes['25%'];
848
+
849
+ //* Initialize notice.
850
+ $notice = $i18n['description'];
851
+ $class = $classes['good'];
852
+
853
+ //* Length notice.
854
+ $desc_length_warning = $this->get_the_seo_bar_description_length_warning( $desc_len, $class );
855
+ $notice .= $desc_length_warning['notice'] ? $desc_length_warning['notice'] . '<br>' : '';
856
+ $class = $desc_length_warning['class'];
857
+
858
+ //* Duplicated Words notice.
859
+ $desc_too_many = $this->get_the_seo_bar_description_words_warning( $description, $class );
860
+ $notice .= $desc_too_many['notice'] ? $desc_too_many['notice'] . '<br>' : '';
861
+ $class = $desc_too_many['class'];
862
+
863
+ //* Generation notice.
864
+ $generated_notice = $i18n['generated'] . ' ';
865
+ $gen_d = $description_is_from_custom_field ? '' : $generated_short;
866
+ $gen_d_notice = $description_is_from_custom_field ? '' : $generated_notice;
867
+
868
+ //* Put everything together.
869
+ $notice = $notice . $gen_d_notice;
870
+ $description_short = $description_short . $gen_d;
871
+
872
+ $desc_wrap_args = array(
873
+ 'indicator' => $description_short,
874
+ 'notice' => $notice,
875
+ 'width' => $ad25,
876
+ 'class' => $class,
877
+ );
878
+
879
+ $description_notice = $this->wrap_the_seo_bar_block( $desc_wrap_args );
880
+
881
+ return $description_notice;
882
+ }
883
+
884
+ /**
885
+ * Description Length notices.
886
+ *
887
+ * @since 2.6.0
888
+ *
889
+ * @param int $desc_len The Title length
890
+ * @param string $class The current color class.
891
+ * @return array {
892
+ * notice => The notice,
893
+ * class => The class,
894
+ * }
895
+ */
896
+ protected function get_the_seo_bar_description_length_warning( $desc_len, $class ) {
897
+
898
+ $classes = $this->get_the_seo_bar_classes();
899
+ $bad = $classes['bad'];
900
+ $okay = $classes['okay'];
901
+ $good = $classes['good'];
902
+
903
+ if ( $desc_len < 100 ) {
904
+ $notice = ' ' . __( 'Length is far too short.', 'autodescription' );
905
+ $class = $bad;
906
+ } else if ( $desc_len < 137 ) {
907
+ $notice = ' ' . __( 'Length is too short.', 'autodescription' );
908
+
909
+ // Don't make it okay if it's already bad.
910
+ $class = $bad === $class ? $class : $okay;
911
+ } else if ( $desc_len > 155 && $desc_len < 175 ) {
912
+ $notice = ' ' . __( 'Length is too long.', 'autodescription' );
913
+
914
+ // Don't make it okay if it's already bad.
915
+ $class = $bad === $class ? $class : $okay;
916
+ } else if ( $desc_len >= 175 ) {
917
+ $notice = ' ' . __( 'Length is far too long.', 'autodescription' );
918
+ $class = $bad;
919
+ } else {
920
+ $notice = ' ' . __( 'Length is good.', 'autodescription' );
921
+
922
+ // Don't make it good if it's already bad or okay.
923
+ $class = $good !== $class ? $class : $good;
924
+ }
925
+
926
+ return array(
927
+ 'notice' => $notice,
928
+ 'class' => $class
929
+ );
930
+ }
931
+
932
+ /**
933
+ * Calculates the word count and returns a warning with the words used.
934
+ * Only when count is over 3.
935
+ *
936
+ * @since 2.6.0
937
+ *
938
+ * @param string $description The Description with maybe words too many.
939
+ * @param string $class The current color class.
940
+ * @return string The warning notice.
941
+ */
942
+ protected function get_the_seo_bar_description_words_warning( $description, $class ) {
943
+
944
+ $notice = '';
945
+ $desc_too_many = '';
946
+
947
+ //* Convert description's special characters into PHP readable words.
948
+ $description = htmlentities( $description, ENT_COMPAT, "UTF-8" );
949
+
950
+ //* Because we've converted all characters to XHTML codes, the odd ones should be only numerical.
951
+ $html_special_chars = '&0123456789;';
952
+
953
+ //* Count the words.
954
+ $desc_words = str_word_count( strtolower( $description ), 2, $html_special_chars );
955
+
956
+ static $bother_me_length = null;
957
+ /**
958
+ * Applies filters 'the_seo_framework_bother_me_desc_length' : int Min Character length to bother you with.
959
+ * @since 2.6.0
960
+ */
961
+ if ( is_null( $bother_me_length ) )
962
+ $bother_me_length = (int) apply_filters( 'the_seo_framework_bother_me_desc_length', 3 );
963
+
964
+ if ( is_array( $desc_words ) ) {
965
+ //* We're going to fetch word based on key, and the last element (as first)
966
+ $word_keys = array_flip( array_reverse( $desc_words, true ) );
967
+
968
+ $desc_word_count = array_count_values( $desc_words );
969
+
970
+ //* Parse word counting.
971
+ if ( is_array( $desc_word_count ) ) {
972
+ foreach ( $desc_word_count as $desc_word => $desc_word_count ) {
973
+
974
+ if ( mb_strlen( html_entity_decode( $desc_word ) ) < $bother_me_length ) {
975
+ $run = $desc_word_count >= 5 ? true : false;
976
+ } else {
977
+ $run = $desc_word_count >= 3 ? true : false;
978
+ }
979
+
980
+ if ( $run ) {
981
+ //* The encoded word is longer or equal to the bother lenght.
982
+
983
+ $word_len = mb_strlen( $desc_word );
984
+
985
+ $position = $word_keys[$desc_word];
986
+ $first_word_original = mb_substr( $description, $position, $word_len );
987
+
988
+ //* Found words that are used too frequently.
989
+ $desc_too_many[] = array( $first_word_original => $desc_word_count );
990
+ }
991
+ }
992
+ }
993
+ }
994
+
995
+ if ( '' !== $desc_too_many && is_array( $desc_too_many ) ) {
996
+
997
+ $classes = $this->get_the_seo_bar_classes();
998
+ $bad = $classes['bad'];
999
+ $okay = $classes['okay'];
1000
+
1001
+ $words_count = count( $desc_too_many );
1002
+ //* Don't make it okay if it's already bad.
1003
+ $class = $bad !== $class && $words_count <= 1 ? $okay : $bad;
1004
+
1005
+ $i = 1;
1006
+ $count = count( $desc_too_many );
1007
+ foreach ( $desc_too_many as $desc_array ) {
1008
+ foreach ( $desc_array as $desc_value => $desc_count ) {
1009
+ $notice .= ' ';
1010
+
1011
+ /**
1012
+ * Don't ucfirst abbrivations.
1013
+ * @since 2.4.1
1014
+ */
1015
+ $desc_value = ctype_upper( $desc_value ) ? $desc_value : ucfirst( $desc_value );
1016
+
1017
+ $notice .= sprintf( __( '%s is used %d times.', 'autodescription' ), '<span>' . $desc_value . '</span>', $desc_count );
1018
+
1019
+ //* Don't add break at last occurence.
1020
+ $notice .= $i === $count ? '' : '<br>';
1021
+ $i++;
1022
+ }
1023
+ }
1024
+ }
1025
+
1026
+ return array(
1027
+ 'notice' => $notice,
1028
+ 'class' => $class
1029
+ );
1030
+ }
1031
+
1032
+ /**
1033
+ * Render the SEO bar index block and notice.
1034
+ *
1035
+ * @since 2.6.0
1036
+ *
1037
+ * @param array $args
1038
+ * @return string The SEO Bar Index Block
1039
+ */
1040
+ protected function the_seo_bar_index_notice( $args ) {
1041
+
1042
+ $term = $args['term'];
1043
+ $is_term = $args['is_term'];
1044
+ $post_i18n = $args['post_i18n'];
1045
+
1046
+ $data = $this->the_seo_bar_data( $args );
1047
+
1048
+ $classes = $this->get_the_seo_bar_classes();
1049
+ $unknown = $classes['unknown'];
1050
+ $bad = $classes['bad'];
1051
+ $okay = $classes['okay'];
1052
+ $good = $classes['good'];
1053
+ $ad_125 = $classes['12.5%'];
1054
+
1055
+ $i18n = $this->get_the_seo_bar_i18n();
1056
+ $index_short = $i18n['index_short'];
1057
+ $but_i18n = $i18n['but'];
1058
+ $and_i18n = $i18n['and'];
1059
+ $ind_notice = $i18n['index'];
1060
+
1061
+ $ind_notice .= ' ' . sprintf( __( "%s is being indexed.", 'autodescription' ), $post_i18n );
1062
+ $ind_class = $good;
1063
+
1064
+ /**
1065
+ * Get noindex site option
1066
+ *
1067
+ * @since 2.2.2
1068
+ */
1069
+ if ( $this->is_option_checked( 'site_noindex' ) ) {
1070
+ $ind_notice .= '<br>' . __( "But you've disabled indexing for the whole site.", 'autodescription' );
1071
+ $ind_class = $unknown;
1072
+ $ind_but = true;
1073
+ }
1074
+
1075
+ //* Adds notice for global archive indexing options.
1076
+ if ( $is_term ) {
1077
+
1078
+ /**
1079
+ * @staticvar bool $checked
1080
+ * @staticvar string $label
1081
+ */
1082
+ static $checked = null;
1083
+
1084
+ if ( ! isset( $checked ) ) {
1085
+ //* Fetch whether it's checked.
1086
+ $checked = $this->the_seo_bar_archive_robots_options( 'noindex' );
1087
+ }
1088
+
1089
+ if ( $checked ) {
1090
+ $but_and = isset( $ind_but ) ? $and_i18n : $but_i18n;
1091
+ $label = $this->get_the_term_name( $term, false );
1092
+
1093
+ /* translators: 1: But or And, 2: Current taxonomy term plural label */
1094
+ $ind_notice .= '<br>' . sprintf( __( '%1$s indexing for %2$s have been disabled.', 'autodescription' ), $but_and, $label );
1095
+ $ind_class = $unknown;
1096
+ $ind_but = true;
1097
+ }
1098
+ }
1099
+
1100
+ //* Adds notice for WordPress blog public indexing.
1101
+ if ( false === $this->is_blog_public() ) {
1102
+ $but_and = isset( $ind_but ) ? $and_i18n : $but_i18n;
1103
+ /* translators: %s = But or And */
1104
+ $ind_notice .= '<br>' . sprintf( __( "%s the blog isn't set to public. This means WordPress discourages indexing.", 'autodescription' ), $but_and );
1105
+ $ind_class = $bad;
1106
+ $ind_but = true;
1107
+ }
1108
+
1109
+ /**
1110
+ * Check if archive is empty, and therefore has set noindex for those.
1111
+ *
1112
+ * @since 2.2.8
1113
+ */
1114
+ if ( $is_term && isset( $term->count ) && 0 === $term->count ) {
1115
+ $but_and = isset( $ind_but ) ? $and_i18n : $but_i18n;
1116
+
1117
+ /* translators: %s = But or And */
1118
+ $ind_notice .= '<br>' . sprintf( __( "%s there are no posts in this term; therefore, indexing has been disabled.", 'autodescription' ), $but_and );
1119
+ //* Don't make it unknown if it's not good.
1120
+ $ind_class = $ind_class !== $good ? $ind_class : $unknown;
1121
+ }
1122
+
1123
+ $ind_wrap_args = array(
1124
+ 'indicator' => $index_short,
1125
+ 'notice' => $ind_notice,
1126
+ 'width' => $ad_125,
1127
+ 'class' => $ind_class,
1128
+ );
1129
+
1130
+ $index_notice = $this->wrap_the_seo_bar_block( $ind_wrap_args );
1131
+
1132
+ return $index_notice;
1133
+ }
1134
+
1135
+ /**
1136
+ * Checks whether global index/archive/follow options are checked for archives.
1137
+ *
1138
+ * @since 2.6.0
1139
+ * @staticvar bool $cache
1140
+ *
1141
+ * @param string $type : 'noindex', 'nofollow', 'noarchive'
1142
+ * @return bool
1143
+ */
1144
+ protected function the_seo_bar_archive_robots_options( $type ) {
1145
+
1146
+ $taxonomy = false;
1147
+
1148
+ if ( $this->is_category() )
1149
+ $taxonomy = 'category';
1150
+
1151
+ if ( $this->is_tag() )
1152
+ $taxonomy = 'tag';
1153
+
1154
+ if ( $taxonomy ) {
1155
+ static $cache = array();
1156
+
1157
+ if ( isset( $cache[$type][$taxonomy] ) )
1158
+ return $cache[$type][$taxonomy];
1159
+
1160
+ if ( $this->is_option_checked( $taxonomy . '_' . $type ) )
1161
+ return $cache[$type][$taxonomy] = true;
1162
+
1163
+ return $cache[$type][$taxonomy] = false;
1164
+ }
1165
+
1166
+ return false;
1167
+ }
1168
+
1169
+ /**
1170
+ * Render the SEO bar follow block and notice.
1171
+ *
1172
+ * @since 2.6.0
1173
+ *
1174
+ * @param array $args
1175
+ * @return string The SEO Bar Follow Block
1176
+ */
1177
+ protected function the_seo_bar_follow_notice( $args ) {
1178
+
1179
+ $followed = true;
1180
+
1181
+ $term = $args['term'];
1182
+ $is_term = $args['is_term'];
1183
+ $post_i18n = $args['post_i18n'];
1184
+
1185
+ $data = $this->the_seo_bar_data( $args );
1186
+ $nofollow = $data['nofollow'];
1187
+
1188
+ $classes = $this->get_the_seo_bar_classes();
1189
+ $unknown = $classes['unknown'];
1190
+ $bad = $classes['bad'];
1191
+ $okay = $classes['okay'];
1192
+ $good = $classes['good'];
1193
+ $ad_125 = $classes['12.5%'];
1194
+
1195
+ $i18n = $this->get_the_seo_bar_i18n();
1196
+ $follow_i18n = $i18n['follow'];
1197
+ $but_i18n = $i18n['but'];
1198
+ $and_i18n = $i18n['and'];
1199
+ $follow_short = $i18n['follow_short'];
1200
+
1201
+ if ( $nofollow ) {
1202
+ $fol_notice = $follow_i18n . ' ' . sprintf( __( "%s links aren't being followed.", 'autodescription' ), $post_i18n );
1203
+ $fol_class = $unknown;
1204
+ $fol_but = true;
1205
+
1206
+ $followed = false;
1207
+ } else {
1208
+ $fol_notice = $follow_i18n . ' ' . sprintf( __( '%s links are being followed.', 'autodescription' ), $post_i18n );
1209
+ $fol_class = $good;
1210
+ }
1211
+
1212
+ /**
1213
+ * Get nofolow site option
1214
+ *
1215
+ * @since 2.2.2
1216
+ */
1217
+ if ( $this->is_option_checked( 'site_nofollow' ) ) {
1218
+ $but_and = isset( $fol_but ) ? $and_i18n : $but_i18n;
1219
+ /* translators: %s = But or And */
1220
+ $fol_notice .= '<br>' . sprintf( __( "%s you've disabled the following of links for the whole site.", 'autodescription' ), $but_and );
1221
+ $fol_class = $unknown;
1222
+ $fol_but = true;
1223
+
1224
+ $followed = false;
1225
+ }
1226
+
1227
+ //* Adds notice for global archive indexing options.
1228
+ if ( $is_term ) {
1229
+
1230
+ /**
1231
+ * @staticvar bool $checked
1232
+ * @staticvar string $label
1233
+ */
1234
+ static $checked = null;
1235
+
1236
+ if ( ! isset( $checked ) ) {
1237
+ //* Fetch whether it's checked.
1238
+ $checked = $this->the_seo_bar_archive_robots_options( 'nofollow' );
1239
+ }
1240
+
1241
+ if ( $checked ) {
1242
+ $but_and = isset( $fol_but ) ? $and_i18n : $but_i18n;
1243
+ $label = $this->get_the_term_name( $term, false );
1244
+
1245
+ /* translators: 1: But or And, 2: Current taxonomy term plural label */
1246
+ $fol_notice .= '<br>' . sprintf( __( '%1$s following for %2$s have been disabled.', 'autodescription' ), $but_and, $label );
1247
+ $fol_class = $unknown;
1248
+
1249
+ $followed = false;
1250
+ }
1251
+ }
1252
+
1253
+ if ( false === $this->is_blog_public() ) {
1254
+ //* Make it "and" if following has not been disabled otherwise.
1255
+ $but_and = $followed || ! isset( $fol_but ) ? $and_i18n : $but_i18n;
1256
+
1257
+ /* translators: %s = But or And */
1258
+ $fol_notice .= '<br>' . sprintf( __( "%s the blog isn't set to public. This means WordPress allows the links to be followed regardless.", 'autodescription' ), $but_and );
1259
+ $fol_class = $followed ? $fol_class : $okay;
1260
+ $fol_but = true;
1261
+
1262
+ $followed = false;
1263
+ }
1264
+
1265
+ $fol_wrap_args = array(
1266
+ 'indicator' => $follow_short,
1267
+ 'notice' => $fol_notice,
1268
+ 'width' => $ad_125,
1269
+ 'class' => $fol_class,
1270
+ );
1271
+
1272
+ $follow_notice = $this->wrap_the_seo_bar_block( $fol_wrap_args );
1273
+
1274
+ return $follow_notice;
1275
+ }
1276
+
1277
+ /**
1278
+ * Render the SEO bar archive block and notice.
1279
+ *
1280
+ * @since 2.6.0
1281
+ *
1282
+ * @param array $args
1283
+ * @return string The SEO Bar Follow Block
1284
+ */
1285
+ protected function the_seo_bar_archive_notice( $args ) {
1286
+
1287
+ $archived = true;
1288
+
1289
+ $term = $args['term'];
1290
+ $is_term = $args['is_term'];
1291
+ $post_low = $args['post_low'];
1292
+
1293
+ $data = $this->the_seo_bar_data( $args );
1294
+ $noarchive = $data['noarchive'];
1295
+
1296
+ $classes = $this->get_the_seo_bar_classes();
1297
+ $unknown = $classes['unknown'];
1298
+ $bad = $classes['bad'];
1299
+ $okay = $classes['okay'];
1300
+ $good = $classes['good'];
1301
+ $ad_125 = $classes['12.5%'];
1302
+
1303
+ $i18n = $this->get_the_seo_bar_i18n();
1304
+ $archive_i18n = $i18n['archive'];
1305
+ $but_i18n = $i18n['but'];
1306
+ $and_i18n = $i18n['and'];
1307
+ $archive_short = $i18n['archive_short'];
1308
+
1309
+ if ( $noarchive ) {
1310
+ $arc_notice = $archive_i18n . ' ' . sprintf( __( "Search Engine aren't allowed to archive this %s.", 'autodescription' ), $post_low );
1311
+ $arc_class = $unknown;
1312
+ $archived = false;
1313
+ $arc_but = true;
1314
+ } else {
1315
+ $arc_notice = $archive_i18n . ' ' . sprintf( __( 'Search Engine are allowed to archive this %s.', 'autodescription' ), $post_low );
1316
+ $arc_class = $good;
1317
+ }
1318
+
1319
+ /**
1320
+ * Get noarchive site option
1321
+ *
1322
+ * @since 2.2.2
1323
+ */
1324
+ if ( $this->is_option_checked( 'site_noarchive' ) ) {
1325
+ $but_and = isset( $arc_but ) ? $and_i18n : $but_i18n;
1326
+
1327
+ $arc_notice .= '<br>' . sprintf( __( "But you've disabled archiving for the whole site.", 'autodescription' ), $but_and );
1328
+ $arc_class = $unknown;
1329
+ $arc_but = true;
1330
+
1331
+ $archived = false;
1332
+ }
1333
+
1334
+ //* Adds notice for global archive indexing options.
1335
+ if ( $is_term ) {
1336
+
1337
+ /**
1338
+ * @staticvar bool $checked
1339
+ * @staticvar string $label
1340
+ */
1341
+ static $checked = null;
1342
+
1343
+ if ( ! isset( $checked ) ) {
1344
+ //* Fetch whether it's checked.
1345
+ $checked = $this->the_seo_bar_archive_robots_options( 'noarchive' );
1346
+ }
1347
+
1348
+ if ( $checked ) {
1349
+ $but_and = isset( $arc_but ) ? $and_i18n : $but_i18n;
1350
+ $label = $this->get_the_term_name( $term, false );
1351
+
1352
+ /* translators: 1: But or And, 2: Current taxonomy term plural label */
1353
+ $arc_notice .= '<br>' . sprintf( __( '%1$s archiving for %2$s have been disabled.', 'autodescription' ), $but_and, $label );
1354
+ $arc_class = $unknown;
1355
+ $arc_but = true;
1356
+
1357
+ $archived = false;
1358
+ }
1359
+ }
1360
+
1361
+ if ( false === $this->is_blog_public() ) {
1362
+ //* Make it "and" if archiving has not been disabled otherwise.
1363
+ $but_and = $archived || ! isset( $arc_but ) ? $and_i18n : $but_i18n;
1364
+
1365
+ /* translators: %s = But or And */
1366
+ $arc_notice .= '<br>' . sprintf( __( "%s the blog isn't set to public. This means WordPress allows the blog to be archived regardless.", 'autodescription' ), $but_and );
1367
+ $arc_but = true;
1368
+
1369
+ $arc_class = $archived ? $arc_class : $okay;
1370
+ $archived = true;
1371
+ }
1372
+
1373
+ $arc_wrap_args = array(
1374
+ 'indicator' => $archive_short,
1375
+ 'notice' => $arc_notice,
1376
+ 'width' => $ad_125,
1377
+ 'class' => $arc_class,
1378
+ );
1379
+
1380
+ $archive_notice = $this->wrap_the_seo_bar_block( $arc_wrap_args );
1381
+
1382
+ return $archive_notice;
1383
+ }
1384
+
1385
+ /**
1386
+ * Render the SEO bar redirect block and notice.
1387
+ *
1388
+ * @since 2.6.0
1389
+ *
1390
+ * @param array $args
1391
+ * @return string The SEO Bar Redirect Block
1392
+ */
1393
+ protected function the_seo_bar_redirect_notice( $args ) {
1394
+
1395
+ $is_term = $args['is_term'];
1396
+
1397
+ if ( $is_term ) {
1398
+ //* No redirection on taxonomies (yet).
1399
+ $redirect_notice = '';
1400
+ } else {
1401
+ //* Pretty much outputs that it's not being redirected.
1402
+
1403
+ $post = $args['post_i18n'];
1404
+
1405
+ $classes = $this->get_the_seo_bar_classes();
1406
+ $ad_125 = $classes['12.5%'];
1407
+
1408
+ $i18n = $this->get_the_seo_bar_i18n();
1409
+ $redirect_i18n = $i18n['redirect'];
1410
+ $redirect_short = $i18n['redirect_short'];
1411
+
1412
+ $red_notice = $redirect_i18n . ' ' . sprintf( __( "%s isn't being redirected.", 'autodescription' ), $post );
1413
+ $red_class = $classes['good'];
1414
+
1415
+ $red_wrap_args = array(
1416
+ 'indicator' => $redirect_short,
1417
+ 'notice' => $red_notice,
1418
+ 'width' => $ad_125,
1419
+ 'class' => $red_class,
1420
+ );
1421
+
1422
+ $redirect_notice = $this->wrap_the_seo_bar_block( $red_wrap_args );
1423
+ }
1424
+
1425
+ return $redirect_notice;
1426
+ }
1427
+
1428
+ /**
1429
+ * Render the SEO bar when the page/term is blocked.
1430
+ *
1431
+ * @since 2.6.0
1432
+ *
1433
+ * @param array $args {
1434
+ * $is_term => bool,
1435
+ * $redirect => bool,
1436
+ * $noindex => bool,
1437
+ * $post_i18n => string
1438
+ * }
1439
+ * @return string The SEO Bar
1440
+ */
1441
+ protected function the_seo_bar_blocked( $args ) {
1442
+
1443
+ $classes = $this->get_the_seo_bar_classes();
1444
+ $i18n = $this->get_the_seo_bar_i18n();
1445
+
1446
+ $is_term = $args['is_term'];
1447
+ $redirect = $args['redirect'];
1448
+ $noindex = $args['noindex'];
1449
+ $post = $args['post_i18n'];
1450
+
1451
+ if ( $redirect && $noindex ) {
1452
+ //* Redirect and noindex found, why bother showing SEO info?
1453
+
1454
+ $red_notice = $i18n['redirect'] . ' ' . sprintf( __( "%s is being redirected. This means no SEO values have to be set.", 'autodescription' ), $post );
1455
+ $red_class = $classes['unknown'];
1456
+
1457
+ $noi_notice = $i18n['index'] . ' ' . sprintf( __( "%s is not being indexed. This means no SEO values have to be set.", 'autodescription' ), $post );
1458
+ $noi_class = $classes['unknown'];
1459
+
1460
+ $red_wrap_args = array(
1461
+ 'indicator' => $i18n['redirect_short'],
1462
+ 'notice' => $red_notice,
1463
+ 'width' => $classes['50%'],
1464
+ 'class' => $red_class,
1465
+ );
1466
+
1467
+ $noi_wrap_args = array(
1468
+ 'indicator' => $i18n['index_short'],
1469
+ 'notice' => $noi_notice,
1470
+ 'width' => $classes['50%'],
1471
+ 'class' => $noi_class,
1472
+ );
1473
+
1474
+ $redirect_notice = $this->wrap_the_seo_bar_block( $red_wrap_args );
1475
+ $noindex_notice = $this->wrap_the_seo_bar_block( $noi_wrap_args );
1476
+
1477
+ $content = $redirect_notice . $noindex_notice;
1478
+
1479
+ return $this->get_the_seo_bar_wrap( $content, $is_term );
1480
+ } else if ( $redirect && false === $noindex ) {
1481
+ //* Redirect found, why bother showing SEO info?
1482
+
1483
+ $red_notice = $i18n['redirect'] . ' ' . sprintf( __( "%s is being redirected. This means no SEO values have to be set.", 'autodescription' ), $post );
1484
+ $red_class = $classes['unknown'];
1485
+
1486
+ $red_wrap_args = array(
1487
+ 'indicator' => $i18n['redirect_short'],
1488
+ 'notice' => $red_notice,
1489
+ 'width' => $classes['100%'],
1490
+ 'class' => $red_class,
1491
+ );
1492
+
1493
+ $redirect_notice = $this->wrap_the_seo_bar_block( $red_wrap_args );
1494
+
1495
+ return $this->get_the_seo_bar_wrap( $redirect_notice, $is_term );
1496
+ } else if ( $noindex && false === $redirect ) {
1497
+ //* Noindex found, why bother showing SEO info?
1498
+
1499
+ $noi_notice = $i18n['index'] . ' ' . sprintf( __( "%s is not being indexed. This means no SEO values have to be set.", 'autodescription' ), $post );
1500
+ $noi_class = $classes['unknown'];
1501
+
1502
+ $noi_wrap_args = array(
1503
+ 'indicator' => $i18n['index_short'],
1504
+ 'notice' => $noi_notice,
1505
+ 'width' => $classes['100%'],
1506
+ 'class' => $noi_class,
1507
+ );
1508
+
1509
+ $noindex_notice = $this->wrap_the_seo_bar_block( $noi_wrap_args );
1510
+
1511
+ return $this->get_the_seo_bar_wrap( $noindex_notice, $is_term );
1512
+ }
1513
+
1514
+ return '';
1515
+ }
1516
+
1517
+ /**
1518
+ * Title Length notices.
1519
+ *
1520
+ * @since 2.6.0
1521
+ *
1522
+ * @param int $tit_len The Title length
1523
+ * @param string $class The Current Title notification class.
1524
+ * @return array {
1525
+ * string $notice => The notice,
1526
+ * string $class => The class,
1527
+ * bool $but => Whether we should use but or and,
1528
+ * }
1529
+ */
1530
+ protected function get_the_seo_bar_title_length_warning( $tit_len, $class ) {
1531
+
1532
+ $classes = $this->get_the_seo_bar_classes();
1533
+ $bad = $classes['bad'];
1534
+ $okay = $classes['okay'];
1535
+ $good = $classes['good'];
1536
+
1537
+ $but = false;
1538
+
1539
+ if ( $tit_len < 25 ) {
1540
+ $notice = ' ' . __( 'Length is far too short.', 'autodescription' );
1541
+ $class = $bad;
1542
+ } else if ( $tit_len < 42 ) {
1543
+ $notice = ' ' . __( 'Length is too short.', 'autodescription' );
1544
+ $class = $okay;
1545
+ } else if ( $tit_len > 55 && $tit_len < 75 ) {
1546
+ $notice = ' ' . __( 'Length is too long.', 'autodescription' );
1547
+ $class = $okay;
1548
+ } else if ( $tit_len >= 75 ) {
1549
+ $notice = ' ' . __( 'Length is far too long.', 'autodescription' );
1550
+ $class = $bad;
1551
+ } else {
1552
+ $notice = ' ' . __( 'Length is good.', 'autodescription' );
1553
+ $class = $good;
1554
+ $but = true;
1555
+ }
1556
+
1557
+ return array(
1558
+ 'notice' => $notice,
1559
+ 'class' => $class,
1560
+ 'but' => $but
1561
+ );
1562
+ }
1563
+
1564
+ /**
1565
+ * Returns an array of the classes used for CSS within The SEO Bar.
1566
+ *
1567
+ * @since 2.6.0
1568
+ *
1569
+ * @return array The class names.
1570
+ */
1571
+ public function get_the_seo_bar_classes() {
1572
+ return array(
1573
+ 'bad' => 'ad-seo-bad',
1574
+ 'okay' => 'ad-seo-okay',
1575
+ 'good' => 'ad-seo-good',
1576
+ 'unknown' => 'ad-seo-unknown',
1577
+
1578
+ 'pill' => 'pill',
1579
+
1580
+ '100%' => 'ad-100',
1581
+ '60%' => 'ad-60',
1582
+ '50%' => 'ad-50',
1583
+ '40%' => 'ad-40',
1584
+ '33%' => 'ad-33',
1585
+ '25%' => 'ad-25',
1586
+ '25%' => 'ad-25',
1587
+ '20%' => 'ad-20',
1588
+ '16%' => 'ad-16',
1589
+ '12.5%' => 'ad-12-5',
1590
+ '11%' => 'ad-11',
1591
+ '10%' => 'ad-10',
1592
+ );
1593
+ }
1594
+
1595
+ /**
1596
+ * Returns an array of the i18n notices for The SEO Bar.
1597
+ *
1598
+ * @staticvar array $i18n
1599
+ * @since 2.6.0
1600
+ *
1601
+ * @return array The i18n sentences.
1602
+ */
1603
+ public function get_the_seo_bar_i18n() {
1604
+
1605
+ static $i18n = null;
1606
+
1607
+ if ( isset( $i18n ) )
1608
+ return $i18n;
1609
+
1610
+ return $i18n = array(
1611
+ 'title' => __( 'Title:', 'autodescription' ),
1612
+ 'description' => __( 'Description:', 'autodescription' ),
1613
+ 'index' => __( 'Index:', 'autodescription' ),
1614
+ 'follow' => __( 'Follow:', 'autodescription' ),
1615
+ 'archive' => __( 'Archive:', 'autodescription' ),
1616
+ 'redirect' => __( 'Redirect:', 'autodescription' ),
1617
+
1618
+ 'generated' => __( 'Generated: Automatically generated.', 'autodescription'),
1619
+
1620
+ 'generated_short' => _x( 'G', 'Generated', 'autodescription' ),
1621
+ 'title_short' => _x( 'T', 'Title', 'autodescription' ),
1622
+ 'description_short' => _x( 'D', 'Description', 'autodescription' ),
1623
+ 'index_short' => _x( 'I', 'no-Index', 'autodescription' ),
1624
+ 'follow_short' => _x( 'F', 'no-Follow', 'autodescription' ),
1625
+ 'archive_short' => _x( 'A', 'no-Archive', 'autodescription' ),
1626
+ 'redirect_short' => _x( 'R', 'Redirect', 'autodescription' ),
1627
+
1628
+ 'but' => _x( 'But', 'But there are...', 'autodescription' ),
1629
+ 'and' => _x( 'And', 'And there are...', 'autodescription' ),
1630
+ );
1631
+ }
1632
+
1633
+ /**
1634
+ * Whether to square or pill the seo bar.
1635
+ *
1636
+ * Applies filters 'the_seo_framework_seo_bar_pill' : boolean
1637
+ *
1638
+ * @staticvar bool $cache
1639
+ * @since 2.6.0
1640
+ *
1641
+ * @return bool
1642
+ */
1643
+ protected function pill_the_seo_bar() {
1644
+
1645
+ static $cache = null;
1646
+
1647
+ if ( isset( $cache ) )
1648
+ return $cache;
1649
+
1650
+ //* TODO add option.
1651
+ $filter = (bool) apply_filters( 'the_seo_framework_seo_bar_pill', false );
1652
+
1653
+ return $cache = $filter ? true : false;
1654
+ }
1655
+
1656
+ }
inc/classes/feed.class.php ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The SEO Framework plugin
4
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License version 3 as published
8
+ * by the Free Software Foundation.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ /**
20
+ * Class AutoDescription_Feed
21
+ *
22
+ * Influences WordPress feeds.
23
+ * "Report Cybr for Feeding." - General EUW League of Legends player.
24
+ *
25
+ * @since 2.5.2
26
+ */
27
+ class AutoDescription_Feed extends AutoDescription_Transients {
28
+
29
+ /**
30
+ * Constructor, load parent constructor and run functions.
31
+ */
32
+ public function __construct() {
33
+ parent::__construct();
34
+
35
+ add_action( 'template_redirect', array( $this, 'init_feed' ) );
36
+ }
37
+
38
+ /**
39
+ * Initializes feed actions and hooks.
40
+ *
41
+ * @since 2.6.0
42
+ */
43
+ public function init_feed() {
44
+
45
+ if ( false === $this->is_feed() )
46
+ return;
47
+
48
+ add_filter( 'the_content_feed', array( $this, 'the_content_feed' ), 10, 2 );
49
+
50
+ //* Only add the feed link to the excerpt if we're only building excerpts.
51
+ if ( $this->rss_uses_excerpt() )
52
+ add_filter( 'the_excerpt_rss', array( $this, 'the_content_feed' ), 10, 1 );
53
+
54
+ }
55
+
56
+ /**
57
+ * Determines whether the WordPress excerpt RSS feed option is used.
58
+ *
59
+ * @since 2.6.0
60
+ *
61
+ * @return bool
62
+ */
63
+ public function rss_uses_excerpt() {
64
+ return (bool) get_option( 'rss_use_excerpt' );
65
+ }
66
+
67
+ /**
68
+ * Changes feed's content.
69
+ *
70
+ * @param $content The feed's content.
71
+ * @param $feed_type The feed type (not used in excerpted content)
72
+ *
73
+ * @since 2.5.2
74
+ */
75
+ public function the_content_feed( $content, $feed_type = null ) {
76
+
77
+ if ( $content ) {
78
+
79
+ /**
80
+ * Don't alter already-excerpts or descriptions.
81
+ * $feed_type is only set on 'the_content_feed' filter.
82
+ */
83
+ if ( isset( $feed_type ) && $this->get_option( 'excerpt_the_feed' ) ) {
84
+ //* Strip all code and lines.
85
+ $excerpt = $this->get_excerpt_by_id( $content );
86
+
87
+ $excerpt_len = (int) mb_strlen( $excerpt );
88
+ /**
89
+ * Applies filters the_seo_framework_max_content_feed_length : The max excerpt length.
90
+ * @since 2.5.2
91
+ */
92
+ $max_len = (int) apply_filters( 'the_seo_framework_max_content_feed_length', 400 );
93
+
94
+ //* Generate excerpt.
95
+ $excerpt = $this->trim_excerpt( $excerpt, $excerpt_len, $max_len );
96
+
97
+ $h2_output = '';
98
+
99
+ if ( 0 === strpos( $content, '<h2>' ) ) {
100
+ //* Add the h2 title back
101
+ $h2_end = mb_strpos( $content, '</h2>' );
102
+
103
+ if ( false !== $h2_end ) {
104
+ //* Start of content, plus <h2>
105
+ $h2_start = 4;
106
+ //* Remove the length of <h2>, again.
107
+ $h2_end = $h2_end - $h2_start;
108
+
109
+ //* Fetch h2 content.
110
+ $h2_content = mb_substr( $content, $h2_start, $h2_end );
111
+
112
+ //* Remove the H2 content from the excerpt.
113
+ $count = 1;
114
+ $excerpt = str_replace( $h2_content, '', $excerpt, $count );
115
+
116
+ //* Wrap h2 content in h2 tags.
117
+ $h2_output = '<h2>' . $h2_content . "</h2>\r\n";
118
+ }
119
+ }
120
+
121
+ $content = $h2_output . '<p>' . trim( $excerpt ) . '</p>';
122
+ }
123
+
124
+ if ( $this->get_option( 'source_the_feed' ) ) {
125
+
126
+ //* Fetch permalink and add it to the content.
127
+ $permalink = $this->the_url();
128
+
129
+ /**
130
+ * Applies filters 'the_seo_framework_feed_source_link' : string
131
+ * @since 2.6.0
132
+ */
133
+ $source_i18n = (string) apply_filters( 'the_seo_framework_feed_source_link_text', _x( 'Source', 'The content source', 'autodescription' ) );
134
+ $content .= "\r\n" . '<p><a href="' . $permalink . '" rel="external nofollow">' . $source_i18n . '</a></p>';
135
+ }
136
+
137
+ }
138
+
139
+ return $content;
140
+ }
141
+
142
+ }
inc/classes/generate-description.class.php ADDED
@@ -0,0 +1,812 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The SEO Framework plugin
4
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License version 3 as published
8
+ * by the Free Software Foundation.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ /**
20
+ * Class AutoDescription_Generate_Description
21
+ *
22
+ * Generates Description SEO data based on content.
23
+ *
24
+ * @since 2.6.0
25
+ */
26
+ class AutoDescription_Generate_Description extends AutoDescription_Generate {
27
+
28
+ /**
29
+ * Determines whether we're parsing the manual content Excerpt for the automated description.
30
+ *
31
+ * @since 2.6.0
32
+ *
33
+ * @var bool Using manual excerpt.
34
+ */
35
+ protected $using_manual_excerpt = false;
36
+
37
+ /**
38
+ * Constructor, loads parent constructor.
39
+ */
40
+ public function __construct() {
41
+ parent::__construct();
42
+ }
43
+
44
+ /**
45
+ * Creates description. Base function.
46
+ *
47
+ * @since 1.0.0
48
+ *
49
+ * @param string $description The optional description to simply parse.
50
+ * @param array $args description args : {
51
+ * @param int $id the term or page id.
52
+ * @param string $taxonomy taxonomy name.
53
+ * @param bool $is_home We're generating for the home page.
54
+ * @param bool $get_custom_field Do not fetch custom title when false.
55
+ * @param bool $social Generate Social Description when true.
56
+ * }
57
+ * @return string The description
58
+ */
59
+ public function generate_description( $description = '', $args = array() ) {
60
+
61
+ /**
62
+ * Parse args.
63
+ * @since 2.5.0
64
+ */
65
+ $args = $this->reparse_description_args( $args );
66
+
67
+ if ( $args['get_custom_field'] && empty( $description ) ) {
68
+ //* Fetch from options, if any.
69
+ $description = (string) $this->description_from_custom_field( $args, false );
70
+
71
+ //* We've already checked the custom fields, so let's remove the check in the generation.
72
+ $args['get_custom_field'] = false;
73
+ }
74
+
75
+ //* Still no description found? Create an auto description based on content.
76
+ if ( empty( $description ) || ! is_scalar( $description ) )
77
+ $description = $this->generate_description_from_id( $args, false );
78
+
79
+ /**
80
+ * Applies filters 'the_seo_framework_do_shortcodes_in_description' : Boolean
81
+ * @since 2.6.6
82
+ */
83
+ if ( apply_filters( 'the_seo_framework_do_shortcodes_in_description', false ) )
84
+ $description = do_shortcode( $description );
85
+
86
+ /**
87
+ * Sanitize.
88
+ * @since 2.3.4 Beautifies too.
89
+ */
90
+ $description = $this->escape_description( $description );
91
+
92
+ return $description;
93
+ }
94
+
95
+ /**
96
+ * Escapes and beautifies description.
97
+ *
98
+ * @since 2.5.2
99
+ *
100
+ * @param string $description The description to escape and beautify.
101
+ * @return string Escaped and beautified description.
102
+ */
103
+ public function escape_description( $description = '' ) {
104
+
105
+ $description = wptexturize( $description );
106
+ $description = convert_chars( $description );
107
+ $description = esc_html( $description );
108
+ $description = capital_P_dangit( $description );
109
+ $description = trim( $description );
110
+
111
+ return $description;
112
+ }
113
+
114
+ /**
115
+ * Parses and sanitizes description arguments.
116
+ *
117
+ * @since 2.5.0
118
+ *
119
+ * @applies filters the_seo_framework_description_args : {
120
+ * @param int $id the term or page id.
121
+ * @param string $taxonomy taxonomy name.
122
+ * @param bool $is_home We're generating for the home page.
123
+ * @param bool $get_custom_field Do not fetch custom title when false.
124
+ * @param bool $social Generate Social Description when true.
125
+ * }
126
+ *
127
+ * @param array $args required The passed arguments.
128
+ * @param array $defaults The default arguments.
129
+ * @param bool $get_defaults Return the default arguments. Ignoring $args.
130
+ * @return array $args parsed args.
131
+ */
132
+ public function parse_description_args( $args = array(), $defaults = array(), $get_defaults = false ) {
133
+
134
+ //* Passing back the defaults reduces the memory usage.
135
+ if ( empty( $defaults ) ) {
136
+ $defaults = array(
137
+ 'id' => $this->get_the_real_ID(),
138
+ 'taxonomy' => '',
139
+ 'is_home' => false,
140
+ 'get_custom_field' => true,
141
+ 'social' => false,
142
+ );
143
+
144
+ $defaults = (array) apply_filters( 'the_seo_framework_description_args', $defaults, $args );
145
+ }
146
+
147
+ //* Return early if it's only a default args request.
148
+ if ( $get_defaults )
149
+ return $defaults;
150
+
151
+ //* Array merge doesn't support sanitation. We're simply type casting here.
152
+ $args['id'] = isset( $args['id'] ) ? (int) $args['id'] : $defaults['id'];
153
+ $args['taxonomy'] = isset( $args['taxonomy'] ) ? (string) $args['taxonomy'] : $defaults['taxonomy'];
154
+ $args['is_home'] = isset( $args['is_home'] ) ? (bool) $args['is_home'] : $defaults['is_home'];
155
+ $args['get_custom_field'] = isset( $args['get_custom_field'] ) ? (bool) $args['get_custom_field'] : $defaults['get_custom_field'];
156
+ $args['social'] = isset( $args['social'] ) ? (bool) $args['social'] : $defaults['social'];
157
+
158
+ return $args;
159
+ }
160
+
161
+ /**
162
+ * Reparses description args.
163
+ *
164
+ * @param array $args required The passed arguments.
165
+ *
166
+ * @since 2.6.0
167
+ * @return array $args parsed args.
168
+ */
169
+ public function reparse_description_args( $args = array() ) {
170
+
171
+ $default_args = $this->parse_description_args( '', '', true );
172
+
173
+ if ( is_array( $args ) ) {
174
+ if ( empty( $args ) ) {
175
+ $args = $default_args;
176
+ } else {
177
+ $args = $this->parse_description_args( $args, $default_args );
178
+ }
179
+ } else {
180
+ //* Old style parameters are used. Doing it wrong.
181
+ $this->_doing_it_wrong( __CLASS__ . '::' . __FUNCTION__, 'Use $args = array() for parameters.', '2.5.0' );
182
+ $args = $default_args;
183
+ }
184
+
185
+ return $args;
186
+ }
187
+
188
+ /**
189
+ * Creates description from custom fields.
190
+ *
191
+ * @since 2.4.1
192
+ *
193
+ * @param array $args description args : {
194
+ * @param int $id the term or page id.
195
+ * @param string $taxonomy taxonomy name.
196
+ * @param bool $is_home We're generating for the home page.
197
+ * }
198
+ * @param bool $escape Escape the output if true.
199
+ * @return string|mixed The description, might be unsafe for html output.
200
+ */
201
+ public function description_from_custom_field( $args = array(), $escape = true ) {
202
+
203
+ /**
204
+ * Parse args.
205
+ * @since 2.5.0
206
+ */
207
+ $args = $this->reparse_description_args( $args );
208
+
209
+ //* HomePage Description.
210
+ $description = $this->get_custom_homepage_description( $args );
211
+
212
+ if ( empty( $description ) ) {
213
+ if ( $this->is_archive() )
214
+ $description = $this->get_custom_archive_description( $args );
215
+ else
216
+ $description = $this->get_custom_singular_description( $args['id'] );
217
+ }
218
+
219
+ if ( $escape && $description )
220
+ $description = $this->escape_description( $description );
221
+
222
+ return $description;
223
+ }
224
+
225
+ /**
226
+ * Fetches HomePage Description from custom field.
227
+ *
228
+ * @since 2.6.0
229
+ * @access protected
230
+ * Use $this->description_from_custom_field() instead.
231
+ *
232
+ * @param array $args Description args.
233
+ * @return string The Description
234
+ */
235
+ protected function get_custom_homepage_description( $args ) {
236
+
237
+ $description = '';
238
+
239
+ if ( $args['is_home'] || $this->is_front_page() || ( empty( $args['taxonomy'] ) && $this->is_static_frontpage( $args['id'] ) ) ) {
240
+ $homedesc = $this->get_option( 'homepage_description' );
241
+ $description = $homedesc ? $homedesc : '';
242
+ }
243
+
244
+ return $description;
245
+ }
246
+
247
+ /**
248
+ * Fetches Singular Description from custom field.
249
+ *
250
+ * @since 2.6.0
251
+ * @access protected
252
+ * Use $this->description_from_custom_field() instead.
253
+ *
254
+ * @param int $id The page ID.
255
+ * @return string The Description
256
+ */
257
+ protected function get_custom_singular_description( $id ) {
258
+
259
+ $description = '';
260
+
261
+ if ( $this->is_singular( $id ) ) {
262
+ $custom_desc = $this->get_custom_field( '_genesis_description', $id );
263
+ $description = $custom_desc ? $custom_desc : $description;
264
+ }
265
+
266
+ return $description;
267
+ }
268
+
269
+ /**
270
+ * Fetch Archive Description from custom field.
271
+ *
272
+ * @since 2.6.0
273
+ * @access protected
274
+ * Use $this->description_from_custom_field() instead.
275
+ *
276
+ * @param array $args
277
+ * @return string The Description
278
+ */
279
+ protected function get_custom_archive_description( $args ) {
280
+
281
+ $description = '';
282
+
283
+ if ( $this->is_archive() ) {
284
+ if ( $this->is_category() || $this->is_tag() || $this->is_tax() ) {
285
+
286
+ $term = $this->fetch_the_term( $args['id'] );
287
+
288
+ if ( isset( $term->admeta['description'] ) ) {
289
+ if ( $this->is_tax() )
290
+ $description = empty( $term->admeta['description'] ) ? $description : wp_kses_stripslashes( wp_kses_decode_entities( $term->admeta['description'] ) );
291
+ else
292
+ $description = empty( $term->admeta['description'] ) ? $description : $term->admeta['description'];
293
+ }
294
+
295
+ $flag = isset( $term->admeta['saved_flag'] ) ? $this->is_checked( $term->admeta['saved_flag'] ) : false;
296
+
297
+ if ( false === $flag && empty( $description ) && isset( $term->meta['description'] ) )
298
+ $description = empty( $term->meta['description'] ) ? $description : $term->meta['description'];
299
+ }
300
+
301
+ /**
302
+ * @TODO add filter.
303
+ * @priority medium 2.7.0
304
+ */
305
+ // if ( $this->is_author() ) {}
306
+ }
307
+
308
+
309
+ return $description;
310
+ }
311
+
312
+ /**
313
+ * Generates description from content while parsing filters.
314
+ *
315
+ * @since 2.3.3
316
+ *
317
+ * @param array $args description args : {
318
+ * @param int $id the term or page id.
319
+ * @param string $taxonomy taxonomy name.
320
+ * @param bool $is_home We're generating for the home page.
321
+ * @param bool $get_custom_field Do not fetch custom title when false.
322
+ * @param bool $social Generate Social Description when true.
323
+ * }
324
+ * @param bool $escape Escape output when true.
325
+ * @return string $output The description.
326
+ */
327
+ public function generate_description_from_id( $args = array(), $escape = true ) {
328
+
329
+ if ( $this->the_seo_framework_debug ) $this->debug_init( __CLASS__, __FUNCTION__, true, $debug_key = microtime(true), get_defined_vars() );
330
+
331
+ /**
332
+ * Applies filters bool 'the_seo_framework_enable_auto_description' : Enable or disable the description.
333
+ *
334
+ * @since 2.5.0
335
+ */
336
+ $autodescription = (bool) apply_filters( 'the_seo_framework_enable_auto_description', true );
337
+ if ( false === $autodescription )
338
+ return '';
339
+
340
+ $description = $this->generate_the_description( $args, false );
341
+
342
+ if ( $escape )
343
+ $description = $this->escape_description( $description );
344
+
345
+ if ( $this->the_seo_framework_debug ) $this->debug_init( __CLASS__, __FUNCTION__, false, $debug_key, array( 'description' => $description, 'transient_key' => $this->auto_description_transient ) );
346
+
347
+ return (string) $description;
348
+ }
349
+
350
+ /**
351
+ * Generates description from content.
352
+ *
353
+ * @since 2.6.0
354
+ * @staticvar string $title
355
+ *
356
+ * @param array $args description args : {
357
+ * @param int $id the term or page id.
358
+ * @param string $taxonomy taxonomy name.
359
+ * @param bool $is_home We're generating for the home page.
360
+ * @param bool $get_custom_field Do not fetch custom title when false.
361
+ * @param bool $social Generate Social Description when true.
362
+ * }
363
+ * @param bool $escape Whether to escape the description.
364
+ * @return string The description.
365
+ */
366
+ protected function generate_the_description( $args, $escape = true ) {
367
+
368
+ /**
369
+ * Parse args.
370
+ * @since 2.5.0
371
+ */
372
+ $args = $this->reparse_description_args( $args );
373
+
374
+ //* Home Page description
375
+ if ( $args['is_home'] || $this->is_front_page() || $this->is_static_frontpage( $args['id'] ) )
376
+ return $this->generate_home_page_description( $args['get_custom_field'] );
377
+
378
+ $term = $this->fetch_the_term( $args['id'] );
379
+
380
+ //* Whether the post ID has a manual excerpt.
381
+ if ( empty( $term ) && has_excerpt( $args['id'] ) )
382
+ $this->using_manual_excerpt = true;
383
+
384
+ $title_on_blogname = $this->generate_description_additions( $args['id'], $term, false );
385
+ $title = $title_on_blogname['title'];
386
+ $on = $title_on_blogname['on'];
387
+ $blogname = $title_on_blogname['blogname'];
388
+ $sep = $title_on_blogname['sep'];
389
+
390
+ /**
391
+ * Setup transient.
392
+ */
393
+ $this->setup_auto_description_transient( $args['id'], $args['taxonomy'] );
394
+
395
+ /**
396
+ * Cache the generated description within a transient.
397
+ * @since 2.3.3
398
+ * @since 2.3.4 Put inside a different function.
399
+ */
400
+ $excerpt = $this->get_transient( $this->auto_description_transient );
401
+ if ( false === $excerpt ) {
402
+
403
+ /**
404
+ * Get max char length.
405
+ * Default to 200 when $args['social'] as there are no additions.
406
+ */
407
+ $additions = trim( $title . " $on " . $blogname );
408
+ //* If there are additions, add a trailing space.
409
+ if ( $additions )
410
+ $additions .= " ";
411
+
412
+ $max_char_length_normal = 155 - mb_strlen( html_entity_decode( $additions ) );
413
+ $max_char_length_social = 200;
414
+
415
+ //* Generate Excerpts.
416
+ $excerpt_normal = $this->generate_excerpt( $args['id'], $term, $max_char_length_normal );
417
+ $excerpt_social = $this->generate_excerpt( $args['id'], $term, $max_char_length_social );
418
+
419
+ //* Put in array to be accessed later.
420
+ $excerpt = array(
421
+ 'normal' => $excerpt_normal,
422
+ 'social' => $excerpt_social
423
+ );
424
+
425
+ /**
426
+ * Transient expiration: 1 week.
427
+ * Keep the description for at most 1 week.
428
+ */
429
+ $expiration = WEEK_IN_SECONDS;
430
+
431
+ $this->set_transient( $this->auto_description_transient, $excerpt, $expiration );
432
+ }
433
+
434
+ /**
435
+ * Check for Social description, don't add blogname then.
436
+ * Also continues normally if it's the front page.
437
+ *
438
+ * @since 2.5.0
439
+ */
440
+ if ( $args['social'] ) {
441
+ if ( $excerpt['social'] ) {
442
+ $description = $excerpt['social'];
443
+ } else {
444
+ //* No social description if nothing is found.
445
+ $description = '';
446
+ }
447
+ } else {
448
+
449
+ if ( empty( $excerpt['normal'] ) ) {
450
+ //* Fetch additions ignoring options.
451
+
452
+ $title_on_blogname = $this->generate_description_additions( $args['id'], $term, true );
453
+ $title = $title_on_blogname['title'];
454
+ $on = $title_on_blogname['on'];
455
+ $blogname = $title_on_blogname['blogname'];
456
+ $sep = $title_on_blogname['sep'];
457
+ }
458
+
459
+ /* translators: 1: Title, 2: on, 3: Blogname */
460
+ $title_on_blogname = trim( sprintf( __( '%1$s %2$s %3$s', 'autodescription' ), $title, $on, $blogname ) );
461
+
462
+ if ( $excerpt['normal'] ) {
463
+ /* translators: 1: Title on Blogname, 2: Separator, 3: Excerpt */
464
+ $description = sprintf( __( '%1$s %2$s %3$s', 'autodescription' ), $title_on_blogname, $sep, $excerpt['normal'] );
465
+ } else {
466
+ //* We still add the additions when no excerpt has been found.
467
+ // i.e. home page or empty/shortcode filled page.
468
+ $description = $title_on_blogname;
469
+ }
470
+ }
471
+
472
+ if ( $escape )
473
+ $description = $this->escape_description( $description );
474
+
475
+ return $description;
476
+ }
477
+
478
+ /**
479
+ * Generates the home page description.
480
+ *
481
+ * @since 2.6.0
482
+ *
483
+ * @param bool $custom_field whether to check the Custom Field.
484
+ * @return string The description.
485
+ */
486
+ public function generate_home_page_description( $custom_field = true ) {
487
+
488
+ $id = $this->get_the_front_page_ID();
489
+
490
+ /**
491
+ * Return early if description is found from Home Page Settings.
492
+ * Only do so when $args['get_custom_field'] is true.
493
+ * @since 2.3.4
494
+ */
495
+ if ( $custom_field ) {
496
+ $description = $this->get_custom_homepage_description( array( 'is_home' => true ) );
497
+ if ( $description )
498
+ return $description;
499
+ }
500
+
501
+ $title_on_blogname = $this->generate_description_additions( $id, '', true );
502
+
503
+ $title = $title_on_blogname['title'];
504
+ $on = $title_on_blogname['on'];
505
+ $blogname = $title_on_blogname['blogname'];
506
+
507
+ return $description = sprintf( '%s %s %s', $title, $on, $blogname );
508
+ }
509
+
510
+ /**
511
+ * Determines whether to add description additions. (╯°□°)╯︵ ┻━┻
512
+ *
513
+ * @since 2.6.0
514
+ * @staticvar bool $cache
515
+ *
516
+ * @param int $id The current page or post ID.
517
+ * @param object|emptystring $term The current Term.
518
+ * @return bool Whether to add description additions.
519
+ */
520
+ public function add_description_additions( $id = '', $term = '' ) {
521
+
522
+ static $cache = null;
523
+
524
+ if ( isset( $cache ) )
525
+ return $cache;
526
+
527
+ /**
528
+ * Applies filters the_seo_framework_add_description_additions : {
529
+ * @param bool true to add prefix.
530
+ * @param int $id The Term object ID or The Page ID.
531
+ * @param object $term The Term object.
532
+ * }
533
+ *
534
+ * @since 2.6.0
535
+ */
536
+ $filter = (bool) apply_filters( 'the_seo_framework_add_description_additions', true, $id, $term );
537
+ $option = (bool) $this->get_option( 'description_additions' );
538
+ $excerpt = ! $this->using_manual_excerpt;
539
+
540
+ return $cache = $option && $filter && $excerpt ? true : false;
541
+ }
542
+
543
+ /**
544
+ * Gets Description Separator.
545
+ *
546
+ * Applies filters 'the_seo_framework_description_separator' : string
547
+ * @since 2.3.9
548
+ * @staticvar string $sep
549
+ *
550
+ * @return string The Separator
551
+ */
552
+ public function get_description_separator() {
553
+
554
+ static $sep = null;
555
+
556
+ if ( isset( $sep ) )
557
+ return $sep;
558
+
559
+ return $sep = (string) apply_filters( 'the_seo_framework_description_separator', $this->get_separator( 'description' ) );
560
+ }
561
+
562
+ /**
563
+ * Generates description additions.
564
+ *
565
+ * @since 2.6.0
566
+ * @staticvar array $title string of titles.
567
+ * @staticvar string $on
568
+ * @access private
569
+ *
570
+ * @param int $id The post or term ID
571
+ * @param object|empty $term The term object
572
+ * @param bool $ignore Whether to ignore options and filters.
573
+ * @return array : {
574
+ * $title => The title
575
+ * $on => The word separator
576
+ * $blogname => The blogname
577
+ * $sep => The separator
578
+ * }
579
+ */
580
+ public function generate_description_additions( $id = '', $term = '', $ignore = false ) {
581
+
582
+ static $title = array();
583
+
584
+ if ( $ignore || $this->add_description_additions( $id, $term ) ) {
585
+
586
+ if ( ! isset( $title[$id] ) )
587
+ $title[$id] = $this->generate_description_title( $id, $term, $ignore );
588
+
589
+ if ( $ignore || $this->is_option_checked( 'description_blogname' ) ) {
590
+
591
+ static $on = null;
592
+ if ( is_null( $on ) ) {
593
+ /* translators: Front-end output. */
594
+ $on = _x( 'on', 'Placement. e.g. Post Title "on" Blog Name', 'autodescription' );
595
+ }
596
+
597
+ //* Already cached.
598
+ $blogname = $this->get_blogname();
599
+ } else {
600
+ $on = '';
601
+ $blogname = '';
602
+ }
603
+
604
+ //* Already cached.
605
+ $sep = $this->get_description_separator();
606
+ } else {
607
+ $title[$id] = '';
608
+ $on = '';
609
+ $blogname = '';
610
+ $sep = '';
611
+ }
612
+
613
+ return array(
614
+ 'title' => $title[$id],
615
+ 'on' => $on,
616
+ 'blogname' => $blogname,
617
+ 'sep' => $sep,
618
+ );
619
+ }
620
+
621
+ /**
622
+ * Generates the Title for description.
623
+ *
624
+ * @since 2.5.2
625
+ *
626
+ * @param int $id The page ID.
627
+ * @param void|object $term The term object.
628
+ * @param bool $page_on_front If front page.
629
+ * @return string The description title.
630
+ */
631
+ public function generate_description_title( $id = '', $term = '', $page_on_front = false ) {
632
+
633
+ if ( '' === $id )
634
+ $id = $this->get_the_real_ID();
635
+
636
+ if ( $page_on_front || $this->is_static_frontpage( $id ) ) {
637
+ $tagline = $this->get_option( 'homepage_title_tagline' );
638
+ $title = $tagline ? $tagline : $this->get_blogdescription();
639
+ } else {
640
+ /**
641
+ * No need to parse these when generating social description.
642
+ *
643
+ * @since 2.5.0
644
+ */
645
+ if ( $this->is_blog_page( $id ) ) {
646
+ /**
647
+ * We're on the blog page now.
648
+ * @since 2.2.8
649
+ */
650
+ $title = $this->title( '', '', '', array( 'term_id' => $id, 'notagline' => true, 'description_title' => true, 'escape' => false ) );
651
+
652
+ /**
653
+ * @TODO create option.
654
+ * @priority medium 2.8.0+
655
+ */
656
+ /* translators: Front-end output. */
657
+ $title = __( 'Latest posts:', 'autodescription' ) . ' ' . $title;
658
+ } else if ( $term && is_object( $term ) ) {
659
+ //* We're on a taxonomy now.
660
+
661
+ if ( isset( $term->admeta['doctitle'] ) && $term->admeta['doctitle'] ) {
662
+ $title = $term->admeta['doctitle'];
663
+ } else if ( isset( $term->name ) && $term->name ) {
664
+ $title = $term->name;
665
+ } else if ( isset( $term->slug ) && $term->slug ) {
666
+ $title = $term->slug;
667
+ }
668
+ } else {
669
+ //* We're on a page now.
670
+ $title = $this->title( '', '', '', array( 'term_id' => $id, 'notagline' => true, 'description_title' => true, 'escape' => false ) );
671
+ }
672
+ }
673
+
674
+ /**
675
+ * Use Untitled on empty titles.
676
+ * @since 2.2.8
677
+ */
678
+ /* translators: Front-end output. */
679
+ $title = empty( $title ) ? $this->untitled() : trim( $title );
680
+
681
+ return $title;
682
+ }
683
+
684
+ /**
685
+ * Generates the excerpt.
686
+ * @NOTE Supply calculated $max_char_length to reflect actual output.
687
+ *
688
+ * @since 2.3.4
689
+ * @staticvar array $excerpt_cache Holds the excerpt
690
+ * @staticvar array $excerptlength_cache Holds the excerpt length
691
+ *
692
+ * @param int|string $page_id required : The Page ID
693
+ * @param object|null $term The Taxonomy Term.
694
+ * @param int $max_char_length The maximum excerpt char length.
695
+ */
696
+ public function generate_excerpt( $page_id, $term = '', $max_char_length = 154 ) {
697
+
698
+ static $excerpt_cache = array();
699
+ static $excerptlength_cache = array();
700
+
701
+ $term_id = isset( $term->term_id ) ? $term->term_id : false;
702
+
703
+ //* Put excerpt in cache.
704
+ if ( ! isset( $excerpt_cache[$page_id][$term_id] ) ) {
705
+ if ( $this->is_singular( $page_id ) ) {
706
+ //* We're on the blog page now.
707
+ $excerpt = $this->get_excerpt_by_id( '', $page_id );
708
+ } else if ( $term && is_object( $term ) ) {
709
+ //* We're on a taxonomy now.
710
+ $excerpt = empty( $term->description ) ? $this->get_excerpt_by_id( '', '', $page_id ) : $this->s_description( $term->description );
711
+ } else if ( $this->is_author() ) {
712
+ $excerpt = $this->s_description( get_the_author_meta( 'description', (int) get_query_var( 'author' ) ) );
713
+ } else {
714
+ $excerpt = '';
715
+ }
716
+
717
+ $excerpt_cache[$page_id][$term_id] = $excerpt;
718
+ }
719
+
720
+ //* Fetch excerpt from cache.
721
+ $excerpt = $excerpt_cache[$page_id][$term_id];
722
+
723
+ /**
724
+ * Put excerptlength in cache.
725
+ * Why cache? My tests have shown that mb_strlen is 1.03x faster than cache fetching.
726
+ * However, _mb_strlen (compat) is about 1740x slower. And this is the reason it's cached!
727
+ */
728
+ if ( ! isset( $excerptlength_cache[$page_id][$term_id] ) )
729
+ $excerptlength_cache[$page_id][$term_id] = mb_strlen( $excerpt );
730
+
731
+ //* Fetch the length from cache.
732
+ $excerpt_length = $excerptlength_cache[$page_id][$term_id];
733
+
734
+ //* Trunculate if the excerpt is longer than the max char length
735
+ $excerpt = $this->trim_excerpt( $excerpt, $excerpt_length, $max_char_length );
736
+
737
+ return (string) $excerpt;
738
+ }
739
+
740
+ /**
741
+ * Trims the excerpt by word and determines sentence stops.
742
+ *
743
+ * @since 2.6.0
744
+ *
745
+ * @param string $excerpt The untrimmed excerpt.
746
+ * @param int $excerpt_length The current excerpt length.
747
+ * @param int $max_char_length At what point to shave off the excerpt.
748
+ * @return string The trimmed excerpt.
749
+ */
750
+ public function trim_excerpt( $excerpt, $excerpt_length, $max_char_length ) {
751
+
752
+ if ( $excerpt_length > $max_char_length ) {
753
+
754
+ //* Cut string to fit $max_char_length.
755
+ $sub_ex = mb_substr( $excerpt, 0, $max_char_length );
756
+ $sub_ex = trim( html_entity_decode( $sub_ex ) );
757
+
758
+ //* Split words in array separated by delimiter.
759
+ $ex_words = explode( ' ', $sub_ex );
760
+
761
+ //* Count to total words in the excerpt.
762
+ $ex_total = count( $ex_words );
763
+
764
+ //* Slice the complete excerpt and count the amount of words.
765
+ $extra_ex_words = explode( ' ', trim( $excerpt ), $ex_total + 1 );
766
+ $extra_ex_total = count( $extra_ex_words ) - 1;
767
+ unset( $extra_ex_words[ $extra_ex_total ] );
768
+
769
+ //* Calculate if last word exceeds.
770
+ if ( $extra_ex_total >= $ex_total ) {
771
+ $ex_cut = mb_strlen( $ex_words[ $ex_total - 1 ] );
772
+
773
+ if ( $extra_ex_total > $ex_total ) {
774
+ /**
775
+ * There are more words in the trimmed excerpt than the compared total excerpt.
776
+ * Remove the exceeding word.
777
+ */
778
+ $excerpt = mb_substr( $sub_ex, 0, - $ex_cut );
779
+ } else {
780
+ /**
781
+ * The amount of words are the same in the comparison.
782
+ * Calculate if the chacterers are exceeding.
783
+ */
784
+ $ex_extra_cut = mb_strlen( $extra_ex_words[ $extra_ex_total - 1 ] );
785
+
786
+ if ( $ex_extra_cut > $ex_cut ) {
787
+ //* Final word is falling off. Remove it.
788
+ $excerpt = mb_substr( $sub_ex, 0, - $ex_cut );
789
+ } else {
790
+ //* We're all good here, continue.
791
+ $excerpt = $sub_ex;
792
+ }
793
+ }
794
+ }
795
+
796
+ //* Remove trailing/leading comma's and spaces.
797
+ $excerpt = trim( $excerpt, ' ,' );
798
+
799
+ //* Fetch last character.
800
+ $last_char = substr( $excerpt, -1 );
801
+
802
+ $stops = array( '.', '?', '!' );
803
+ //* Add three dots if there's no full stop at the end of the excerpt.
804
+ if ( ! in_array( $last_char, $stops ) )
805
+ $excerpt .= '...';
806
+
807
+ }
808
+
809
+ return trim( $excerpt );
810
+ }
811
+
812
+ }
inc/classes/generate-image.class.php ADDED
@@ -0,0 +1,363 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The SEO Framework plugin
4
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License version 3 as published
8
+ * by the Free Software Foundation.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ /**
20
+ * Class AutoDescription_Generate_Image
21
+ *
22
+ * Generates Image SEO data based on content.
23
+ *
24
+ * @since 2.6.0
25
+ */
26
+ class AutoDescription_Generate_Image extends AutoDescription_Generate_Url {
27
+
28
+ /**
29
+ * Constructor, loads parent constructor.
30
+ */
31
+ public function __construct() {
32
+ parent::__construct();
33
+ }
34
+
35
+ /**
36
+ * Fetches og:image URL.
37
+ *
38
+ * @since 2.5.2 Applies filters string the_seo_framework_og_image_after_featured
39
+ * @since 2.5.2 Applies filters string the_seo_framework_og_image_after_header
40
+ *
41
+ * @todo listen to attached images within post.
42
+ * @priority medium 2.7.0+
43
+ *
44
+ * @param string $post_id The post ID.
45
+ * @param array $args The image arguments.
46
+ * @param bool $escape Whether to escape the image URL.
47
+ * @return string the Open Graph Image URL.
48
+ */
49
+ public function get_image( $post_id = '', $args = array(), $escape = true ) {
50
+
51
+ if ( empty( $post_id ) )
52
+ $post_id = $this->get_the_real_ID();
53
+
54
+ if ( empty( $post_id ) )
55
+ return '';
56
+
57
+ /**
58
+ * Backwards compat with parse args.
59
+ * @since 2.5.0
60
+ */
61
+ if ( ! isset( $args['post_id'] ) )
62
+ $args['post_id'] = $post_id;
63
+
64
+ $args = $this->reparse_image_args( $args );
65
+
66
+ //* 0. Image from argument.
67
+ $image = $args['image'];
68
+
69
+ //* Check if there are no disallowed arguments.
70
+ $all_allowed = empty( $args['disallowed'] );
71
+
72
+ //* 1. Fetch image from featured
73
+ if ( empty( $image ) && ( $all_allowed || ! in_array( 'featured', $args['disallowed'] ) ) )
74
+ $image = $this->get_image_from_post_thumbnail( $args );
75
+
76
+ //* 2. Fetch image from fallback filter 1
77
+ if ( empty( $image ) )
78
+ $image = (string) apply_filters( 'the_seo_framework_og_image_after_featured', '', $args['post_id'] );
79
+
80
+ //* 3. Fallback: Get header image if exists
81
+ if ( empty( $image ) && ( $all_allowed || ! in_array( 'header', $args['disallowed'] ) ) && current_theme_supports( 'custom-header', 'default-image' ) )
82
+ $image = get_header_image();
83
+
84
+ //* 4. Fetch image from fallback filter 2
85
+ if ( empty( $image ) )
86
+ $image = (string) apply_filters( 'the_seo_framework_og_image_after_header', '', $args['post_id'] );
87
+
88
+ //* 5. Get the WP 4.3.0 Site Icon
89
+ if ( empty( $image ) && ( $all_allowed || ! in_array( 'icon', $args['disallowed'] ) ) )
90
+ $image = $this->site_icon();
91
+
92
+ /**
93
+ * Escape in Generation.
94
+ * @since 2.5.2
95
+ */
96
+ if ( $escape && $image )
97
+ return esc_url( $image );
98
+
99
+ return $image;
100
+ }
101
+
102
+ /**
103
+ * Parse and sanitize image args.
104
+ *
105
+ * @since 2.5.0
106
+ *
107
+ * @since 2.0.1 Applies filters the_seo_framework_og_image_args : {
108
+ * @param string image The image url
109
+ * @param mixed size The image size
110
+ * @param bool icon Fetch Image icon
111
+ * @param array attr Image attributes
112
+ * @param array disallowed Disallowed image types : {
113
+ * array (
114
+ * string 'featured'
115
+ * string 'header'
116
+ * string 'icon'
117
+ * )
118
+ * }
119
+ * }
120
+ * The image set in the filter will always be used as fallback
121
+ *
122
+ * @param array $args required The passed arguments.
123
+ * @param array $defaults The default arguments.
124
+ * @param bool $get_defaults Return the default arguments. Ignoring $args.
125
+ * @return array $args parsed args.
126
+ */
127
+ public function parse_image_args( $args = array(), $defaults = array(), $get_defaults = false ) {
128
+
129
+ //* Passing back the defaults reduces the memory usage.
130
+ if ( empty( $defaults ) ) {
131
+ $defaults = array(
132
+ 'post_id' => $this->get_the_real_ID(),
133
+ 'image' => '',
134
+ 'size' => 'full',
135
+ 'icon' => false,
136
+ 'attr' => array(),
137
+ 'disallowed' => array(),
138
+ );
139
+
140
+ $defaults = (array) apply_filters( 'the_seo_framework_og_image_args', $defaults, $args );
141
+ }
142
+
143
+ //* Return early if it's only a default args request.
144
+ if ( $get_defaults )
145
+ return $defaults;
146
+
147
+ //* Array merge doesn't support sanitation. We're simply type casting here.
148
+ $args['post_id'] = isset( $args['post_id'] ) ? (int) $args['post_id'] : $defaults['post_id'];
149
+ $args['image'] = isset( $args['image'] ) ? (string) $args['image'] : $defaults['image'];
150
+ $args['size'] = isset( $args['size'] ) ? $args['size'] : $defaults['size']; // Mixed.
151
+ $args['icon'] = isset( $args['icon'] ) ? (bool) $args['icon'] : $defaults['icon'];
152
+ $args['attr'] = isset( $args['attr'] ) ? (array) $args['attr'] : $defaults['attr'];
153
+ $args['disallowed'] = isset( $args['disallowed'] ) ? (array) $args['disallowed'] : $defaults['disallowed'];
154
+
155
+ return $args;
156
+ }
157
+
158
+ /**
159
+ * Reparses image args.
160
+ *
161
+ * @since 2.6.6
162
+ *
163
+ * @param array $args required The passed arguments.
164
+ * @return array $args parsed args.
165
+ */
166
+ public function reparse_image_args( $args = array() ) {
167
+
168
+ $default_args = $this->parse_image_args( '', '', true );
169
+
170
+ if ( is_array( $args ) ) {
171
+ if ( empty( $args ) ) {
172
+ $args = $default_args;
173
+ } else {
174
+ $args = $this->parse_image_args( $args, $default_args );
175
+ }
176
+ } else {
177
+ //* Old style parameters are used. Doing it wrong.
178
+ $this->_doing_it_wrong( __CLASS__ . '::' . __FUNCTION__, 'Use $args = array() for parameters.', '2.5.0' );
179
+ $args = $default_args;
180
+ }
181
+
182
+ return $args;
183
+ }
184
+
185
+ /**
186
+ * Fetches image from post thumbnail.
187
+ * Resizes the image between 1500px if bigger. Then it saves the image and
188
+ * Keeps dimensions relative.
189
+ *
190
+ * @since 2.3.0
191
+ *
192
+ * @param array $args Image arguments.
193
+ * @return string|null the image url.
194
+ */
195
+ public function get_image_from_post_thumbnail( $args = array() ) {
196
+
197
+ if ( empty( $args ) )
198
+ $args = $this->reparse_image_args( $args );
199
+
200
+ if ( ! isset( $args['post_id'] ) )
201
+ $args['post_id'] = $this->get_the_real_ID();
202
+
203
+ $id = get_post_thumbnail_id( $args['post_id'] );
204
+
205
+ $image = $id ? $this->parse_og_image( $id, $args ) : '';
206
+
207
+ return $image;
208
+ }
209
+
210
+ /**
211
+ * Fetches images id's from WooCommerce gallery
212
+ *
213
+ * @since 2.5.0
214
+ * @staticvar array $ids The image ids
215
+ *
216
+ * @param array $args Image arguments.
217
+ * @return array The image URL's.
218
+ */
219
+ public function get_image_from_woocommerce_gallery() {
220
+
221
+ static $ids = null;
222
+
223
+ if ( isset( $ids ) )
224
+ return $ids;
225
+
226
+ $attachment_ids = '';
227
+
228
+ $post_id = $this->get_the_real_ID();
229
+
230
+ if ( metadata_exists( 'post', $post_id, '_product_image_gallery' ) ) {
231
+ $product_image_gallery = get_post_meta( $post_id, '_product_image_gallery', true );
232
+
233
+ $attachment_ids = array_filter( explode( ',', $product_image_gallery ) );
234
+ }
235
+
236
+ return $ids = $attachment_ids;
237
+ }
238
+
239
+ /**
240
+ * Parses OG image to correct size.
241
+ *
242
+ * @since 2.5.0
243
+ * @staticvar string $called Checks if image ID has already been fetched (to prevent duplicate output on WooCommerce).
244
+ *
245
+ * @todo create formula to fetch transient.
246
+ * @priority high 2.7.0
247
+ *
248
+ * @param int $id The attachment ID.
249
+ * @param array $args The image args
250
+ * @return string|empty Parsed image url or empty if already called
251
+ */
252
+ public function parse_og_image( $id, $args = array() ) {
253
+
254
+ //* Don't do anything if $id isn't given.
255
+ if ( ! isset( $id ) || empty( $id ) )
256
+ return;
257
+
258
+ static $called = array();
259
+ //* Don't parse image twice. Return empty on second run.
260
+ if ( isset( $called[$id] ) )
261
+ return '';
262
+
263
+ if ( empty( $args ) )
264
+ $args = $this->reparse_image_args( $args );
265
+
266
+ $src = wp_get_attachment_image_src( $id, $args['size'], $args['icon'], $args['attr'] );
267
+
268
+ $i = $src[0]; // Source URL
269
+ $w = $src[1]; // Width
270
+ $h = $src[2]; // Height
271
+
272
+ //* Prefered 1500px, resize it
273
+ if ( $w > 1500 || $h > 1500 ) {
274
+
275
+ if ( $w === $h ) {
276
+ //* Square
277
+ $w = 1500;
278
+ $h = 1500;
279
+ } else if ( $w > $h ) {
280
+ //* Landscape
281
+ $h = $this->proportionate_dimensions( $h, $w, $w = 1500 );
282
+ } else if ( $h > $w ) {
283
+ //* Portrait
284
+ $w = $this->proportionate_dimensions( $w, $h, $h = 1500 );
285
+ }
286
+
287
+ //* Get path of image and load it into the wp_get_image_editor
288
+ $i_file_path = get_attached_file( $id );
289
+
290
+ $i_file_old_name = basename( get_attached_file( $id ) );
291
+ $i_file_ext = pathinfo( $i_file_path, PATHINFO_EXTENSION );
292
+
293
+ if ( $i_file_ext ) {
294
+ $i_file_dir_name = pathinfo( $i_file_path, PATHINFO_DIRNAME );
295
+ //* Add trailing slash.
296
+ $i_file_dir_name = '/' === substr( $i_file_dir_name, -1 ) ? $i_file_dir_name : $i_file_dir_name . '/';
297
+
298
+ $i_file_file_name = pathinfo( $i_file_path, PATHINFO_FILENAME );
299
+
300
+ //* Yes I know, I should use generate_filename(), but it's slower.
301
+ //* Will look at that later. This is already 100 lines of correctly working code.
302
+ $new_image_dirfile = $i_file_dir_name . $i_file_file_name . '-' . $w . 'x' . $h . '.' . $i_file_ext;
303
+
304
+ //* Generate image URL.
305
+ $upload_dir = wp_upload_dir();
306
+ $upload_url = $upload_dir['baseurl'];
307
+ $upload_basedir = $upload_dir['basedir'];
308
+
309
+ //* We've got our image path.
310
+ $i = str_ireplace( $upload_basedir, '', $new_image_dirfile );
311
+ $i = $upload_url . $i;
312
+
313
+ // Generate file if it doesn't exists yet.
314
+ if ( ! file_exists( $new_image_dirfile ) ) {
315
+
316
+ $image_editor = wp_get_image_editor( $i_file_path );
317
+
318
+ if ( ! is_wp_error( $image_editor ) ) {
319
+ $image_editor->resize( $w, $h, false );
320
+ $image_editor->set_quality( 82 ); // Let's save some bandwidth, Facebook compresses it even further anyway.
321
+ $image_editor->save( $new_image_dirfile );
322
+ } else {
323
+ //* Image has failed to create.
324
+ $i = '';
325
+ }
326
+ }
327
+ }
328
+ }
329
+
330
+ return $called[$id] = $i;
331
+ }
332
+
333
+ /**
334
+ * Fetches site icon brought in WordPress 4.3.0
335
+ *
336
+ * @since 2.2.1
337
+ *
338
+ * @param string $size The icon size, accepts 'full' and pixel values
339
+ * @return string url site icon, not escaped.
340
+ */
341
+ public function site_icon( $size = 'full' ) {
342
+
343
+ $icon = '';
344
+
345
+ if ( 'full' === $size ) {
346
+ $site_icon_id = get_option( 'site_icon' );
347
+
348
+ if ( $site_icon_id ) {
349
+ $url_data = '';
350
+ $url_data = wp_get_attachment_image_src( $site_icon_id, $size );
351
+
352
+ $icon = $url_data ? $url_data[0] : '';
353
+ }
354
+
355
+ } else if ( is_int( $size ) && function_exists( 'has_site_icon' ) && $this->wp_version( '4.3', '>=' ) ) {
356
+ //* Also applies (MultiSite) filters.
357
+ $icon = get_site_icon_url( $size );
358
+ }
359
+
360
+ return $icon;
361
+ }
362
+
363
+ }
inc/classes/generate-ldjson.class.php ADDED
@@ -0,0 +1,933 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The SEO Framework plugin
4
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License version 3 as published
8
+ * by the Free Software Foundation.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ /**
20
+ * Class AutoDescription_Generate
21
+ *
22
+ * Generates SEO data based on content
23
+ * Returns strings/arrays
24
+ *
25
+ * @since 2.6.0
26
+ */
27
+ class AutoDescription_Generate_Ldjson extends AutoDescription_Generate_Image {
28
+
29
+ /**
30
+ * Constructor, load parent constructor
31
+ */
32
+ public function __construct() {
33
+ parent::__construct();
34
+ }
35
+
36
+ /**
37
+ * Render the LD+Json scripts.
38
+ *
39
+ * @since 2.6.0
40
+ *
41
+ * @return string The LD+Json scripts.
42
+ */
43
+ public function render_ld_json_scripts() {
44
+
45
+ if ( $this->has_json_ld_plugin() )
46
+ return '';
47
+
48
+ $this->setup_ld_json_transient( $this->get_the_real_ID() );
49
+
50
+ if ( $this->the_seo_framework_debug ) $this->debug_init( __CLASS__, __FUNCTION__, false, $debug_key = microtime(true), array( 'LD Json transient' => $this->ld_json_transient, 'Output from transient' => false !== $this->get_transient( $this->ld_json_transient ) ) );
51
+
52
+ $output = $this->get_transient( $this->ld_json_transient );
53
+ if ( false === $output ) {
54
+
55
+ $output = '';
56
+
57
+ //* Only display search helper and knowledge graph on front page.
58
+ if ( $this->is_front_page() ) {
59
+
60
+ $sitename = $this->ld_json_name();
61
+ $sitelinks = $this->ld_json_search();
62
+ $knowledgegraph = $this->ld_json_knowledge();
63
+
64
+ if ( $sitename )
65
+ $output .= $sitename;
66
+
67
+ if ( $sitelinks )
68
+ $output .= $sitelinks;
69
+
70
+ if ( $knowledgegraph )
71
+ $output .= $knowledgegraph;
72
+
73
+ } else {
74
+ $breadcrumbhelper = $this->ld_json_breadcrumbs();
75
+
76
+ //* No wrapper, is done within script generator.
77
+ if ( $breadcrumbhelper )
78
+ $output .= $breadcrumbhelper;
79
+ }
80
+
81
+ /**
82
+ * Transient expiration: 1 week.
83
+ * Keep the script for at most 1 week.
84
+ */
85
+ $expiration = WEEK_IN_SECONDS;
86
+
87
+ $this->set_transient( $this->ld_json_transient, $output, $expiration );
88
+ }
89
+
90
+ /**
91
+ * Debug output.
92
+ * @since 2.4.2
93
+ */
94
+ if ( $this->the_seo_framework_debug ) $this->debug_init( __CLASS__, __FUNCTION__, false, $debug_key, array( 'LD Json transient output' => $output ) );
95
+
96
+ return $output;
97
+ }
98
+
99
+ /**
100
+ * Returns http://schema.org json encoded context URL.
101
+ *
102
+ * @staticvar string $context
103
+ * @since 2.6.0
104
+ *
105
+ * @return string The json encoded context url.
106
+ */
107
+ public function schema_context() {
108
+
109
+ static $context;
110
+
111
+ if ( isset( $context ) )
112
+ return $context;
113
+
114
+ return $context = json_encode( 'http://schema.org' );
115
+ }
116
+
117
+ /**
118
+ * Returns 'WebSite' json encoded type name.
119
+ *
120
+ * @staticvar string $context
121
+ * @since 2.6.0
122
+ *
123
+ * @return string The json encoded type name.
124
+ */
125
+ public function schema_type() {
126
+
127
+ static $type;
128
+
129
+ if ( isset( $type ) )
130
+ return $type;
131
+
132
+ return $type = json_encode( 'WebSite' );
133
+ }
134
+
135
+ /**
136
+ * Returns json encoded home url.
137
+ *
138
+ * @staticvar string $url
139
+ * @since 2.6.0
140
+ *
141
+ * @return string The json encoded home url.
142
+ */
143
+ public function schema_home_url() {
144
+
145
+ static $type;
146
+
147
+ if ( isset( $type ) )
148
+ return $type;
149
+
150
+ return $type = json_encode( $this->the_home_url_from_cache() );
151
+ }
152
+
153
+ /**
154
+ * Returns json encoded blogname.
155
+ *
156
+ * @staticvar string $name
157
+ * @since 2.6.0
158
+ *
159
+ * @return string The json encoded blogname.
160
+ */
161
+ public function schema_blog_name() {
162
+
163
+ static $name;
164
+
165
+ if ( isset( $name ) )
166
+ return $name;
167
+
168
+ return $name = json_encode( $this->get_blogname() );
169
+ }
170
+
171
+ /**
172
+ * Returns 'BreadcrumbList' json encoded type name.
173
+ *
174
+ * @staticvar string $crumblist
175
+ * @since 2.6.0
176
+ *
177
+ * @return string The json encoded 'BreadcrumbList'.
178
+ */
179
+ public function schema_breadcrumblist() {
180
+
181
+ static $crumblist;
182
+
183
+ if ( isset( $crumblist ) )
184
+ return $crumblist;
185
+
186
+ return $crumblist = json_encode( 'BreadcrumbList' );
187
+ }
188
+
189
+ /**
190
+ * Returns 'ListItem' json encoded type name.
191
+ *
192
+ * @staticvar string $crumblist
193
+ * @since 2.6.0
194
+ *
195
+ * @return string The json encoded 'ListItem'.
196
+ */
197
+ public function schema_listitem() {
198
+
199
+ static $listitem;
200
+
201
+ if ( isset( $listitem ) )
202
+ return $listitem;
203
+
204
+ return $listitem = json_encode( 'ListItem' );
205
+ }
206
+
207
+ /**
208
+ * Generate LD+Json search helper.
209
+ *
210
+ * @since 2.2.8
211
+ *
212
+ * @return escaped LD+json search helper string.
213
+ */
214
+ public function ld_json_search() {
215
+
216
+ if ( false === $this->enable_ld_json_searchbox() )
217
+ return '';
218
+
219
+ $context = $this->schema_context();
220
+ $webtype = $this->schema_type();
221
+ $url = $this->schema_home_url();
222
+ $name = $this->schema_blog_name();
223
+ $actiontype = json_encode( 'SearchAction' );
224
+
225
+ // Remove trailing quote and add it back.
226
+ $target = mb_substr( json_encode( $this->the_home_url_from_cache( true ) . '?s=' ), 0, -1 ) . '{search_term_string}"';
227
+
228
+ $queryaction = json_encode( 'required name=search_term_string' );
229
+
230
+ $json = sprintf( '{"@context":%s,"@type":%s,"url":%s,"name":%s,"potentialAction":{"@type":%s,"target":%s,"query-input":%s}}', $context, $webtype, $url, $name, $actiontype, $target, $queryaction );
231
+
232
+ $output = '';
233
+ if ( $json )
234
+ $output = '<script type="application/ld+json">' . $json . "</script>" . "\r\n";
235
+
236
+ return $output;
237
+ }
238
+
239
+ /**
240
+ * Generate LD+Json breadcrumb helper.
241
+ *
242
+ * @since 2.4.2
243
+ *
244
+ * @return escaped LD+json search helper string.
245
+ */
246
+ public function ld_json_breadcrumbs() {
247
+
248
+ if ( false === $this->enable_ld_json_breadcrumbs() )
249
+ return '';
250
+
251
+ //* Used to count ancestors and categories.
252
+ $output = '';
253
+
254
+ if ( $this->is_single() || $this->is_wc_product() ) {
255
+ $output = $this->ld_json_breadcrumbs_post();
256
+ } else if ( false === $this->is_front_page() && $this->is_page() ) {
257
+ $output = $this->ld_json_breadcrumbs_page();
258
+ }
259
+
260
+ return $output;
261
+ }
262
+
263
+ /**
264
+ * Generate post breadcrumb.
265
+ *
266
+ * @since 2.6.0
267
+ *
268
+ * @return string $output The breadcrumb script.
269
+ */
270
+ public function ld_json_breadcrumbs_post() {
271
+
272
+ $output = '';
273
+
274
+ $post_id = $this->get_the_real_ID();
275
+
276
+ $cat_type = 'category';
277
+
278
+ //* WooCommerce support.
279
+ if ( $this->is_wc_product() )
280
+ $cat_type = 'product_cat';
281
+
282
+ //* Test categories.
283
+ $r = is_object_in_term( $post_id, $cat_type, '' );
284
+
285
+ if ( ! $r || is_wp_error( $r ) )
286
+ return '';
287
+
288
+ //* Get categories.
289
+ $cats = wp_get_object_terms( $post_id, $cat_type, array( 'fields' => 'all_with_object_id', 'orderby' => 'parent' ) );
290
+
291
+ if ( empty( $cats ) || is_wp_error( $cats ) )
292
+ return '';
293
+
294
+ $assigned_ids = array();
295
+ $kittens = array();
296
+ $parents = array();
297
+ $parents_merge = array();
298
+
299
+ //* Fetch cats children id's, if any.
300
+ foreach ( $cats as $cat ) {
301
+ //* The category objects. The cats.
302
+ $cat_id = $cat->term_id;
303
+
304
+ //* Store to filter unused Cat ID's from the post.
305
+ $assigned_ids[] = $cat_id;
306
+
307
+ // Check if they have kittens.
308
+ $children = get_term_children( $cat_id, $cat->taxonomy );
309
+ $ancestors = get_ancestors( $cat_id, $cat->taxonomy );
310
+
311
+ //* Save children id's as kittens.
312
+ $kittens[$cat_id] = $children;
313
+ $parents[$cat_id] = $ancestors;
314
+ }
315
+
316
+ foreach ( $kittens as $kit_id => $child_id ) {
317
+ foreach ( $child_id as $ckey => $cid ) {
318
+
319
+ /**
320
+ * Seed out children that aren't assigned.
321
+ * (from levels too deep as get_term_children gets them all).
322
+ */
323
+ if ( $cid && false === in_array( $cid, $assigned_ids ) )
324
+ unset( $kittens[$kit_id][$ckey] );
325
+
326
+ /**
327
+ * Make the tree count down multiple children are assigned.
328
+ * This fetches the array from the ancestors.
329
+ *
330
+ * What we want is that the latest child ID gets its own single tree.
331
+ * All ancestors should be a representation of the previous assigned trees.
332
+ *
333
+ * E.g. We have this structure, all assigned:
334
+ * - Cat 1
335
+ * - Cat 2
336
+ * - Cat 3
337
+ *
338
+ * We want a tree for "Cat 1+2+3", "Cat 1+2", and "Cat 3".
339
+ *
340
+ * We could add Cat 1, but that's will give two single category lines, which could be misinterperted.
341
+ * So we only use what we know: The kittens (child tree).
342
+ */
343
+ if ( isset( $parents[$cid] ) && ! empty( $parents[$kit_id] ) ) {
344
+ $parents_merge[$kit_id] = $parents[$kit_id];
345
+ unset( $kittens[$kit_id] );
346
+ }
347
+ }
348
+ }
349
+
350
+ /**
351
+ * Build category ID trees for kittens.
352
+ */
353
+ $trees = $this->build_breadcrumb_trees( $kittens );
354
+
355
+ //* Empty parents.
356
+ $parents = array();
357
+
358
+ if ( ! empty( $parents_merge ) ) {
359
+ foreach ( $parents_merge as $child_id => $parents_ids ) {
360
+
361
+ //* Reset kitten.
362
+ $kitten = array();
363
+
364
+ //* Last element should be parent.
365
+ $pid = array_pop( $parents_ids );
366
+
367
+ if ( isset( $pid ) ) {
368
+ //* Parents are reversed children. Let's fix that.
369
+ $parents_ids = array_reverse( $parents_ids );
370
+
371
+ //* Add previous parent at the end of the rest.
372
+ array_push( $parents_ids, $child_id );
373
+
374
+ //* Temporarily array.
375
+ $kitten[$pid] = $parents_ids;
376
+
377
+ $trees = $this->build_breadcrumb_trees( $kitten, $trees );
378
+ } else {
379
+ //* Parents are reversed children. Let's fix that.
380
+ $parents_ids = array_reverse( $parents_ids );
381
+
382
+ $trees = $this->build_breadcrumb_trees( $parents_ids, $trees );
383
+ }
384
+ }
385
+ }
386
+
387
+ /**
388
+ * Sort by number of id's. Provides a cleaner layout, better Search Engine understanding and more consistent cache.
389
+ */
390
+ if ( count( $trees ) > 1 )
391
+ array_multisort( array_map( 'count', $trees ), SORT_DESC, $trees );
392
+
393
+ $context = $this->schema_context();
394
+ $context_type = $this->schema_breadcrumblist();
395
+ $item_type = $this->schema_listitem();
396
+
397
+ //* For each of the tree items, create a separated script.
398
+ if ( $trees ) {
399
+ foreach ( $trees as $tree_ids ) {
400
+
401
+ if ( is_array( $tree_ids ) ) {
402
+ //* Term has assigned children.
403
+
404
+ /**
405
+ * @staticvar int $item_cache : Used to prevent duplicated item re-generation.
406
+ */
407
+ static $item_cache = array();
408
+
409
+ $items = '';
410
+
411
+ //* Put the children in the right order.
412
+ $tree_ids = array_reverse( $tree_ids, false );
413
+
414
+ foreach ( $tree_ids as $position => $child_id ) {
415
+ if ( in_array( $child_id, $assigned_ids ) ) {
416
+ //* Cat has been assigned, continue.
417
+
418
+ //* Fetch item from cache if available.
419
+ if ( isset( $item_cache[$child_id] ) ) {
420
+ $pos = $position + 2;
421
+ $item_cache[$child_id]['pos'] = $pos;
422
+ $items .= $this->make_breadcrumb( $item_cache[$child_id], true );
423
+
424
+ } else {
425
+
426
+ $pos = $position + 2;
427
+
428
+ $cat = get_term_by( 'id', $child_id, $cat_type, OBJECT, 'raw' );
429
+
430
+ $id = json_encode( $this->the_url( '', array( 'get_custom_field' => false, 'external' => true, 'is_term' => true, 'term' => $cat ) ) );
431
+
432
+ $custom_field_name = isset( $cat->admeta['doctitle'] ) ? $cat->admeta['doctitle'] : '';
433
+ $cat_name = $custom_field_name ? $custom_field_name : $cat->name;
434
+ $name = json_encode( $cat_name );
435
+
436
+ //* Store in cache.
437
+ $item_cache[$child_id] = array(
438
+ 'type' => $item_type,
439
+ 'pos' => (string) $pos,
440
+ 'id' => $id,
441
+ 'name' => $name
442
+ );
443
+
444
+ $items .= $this->make_breadcrumb( $item_cache[$child_id], true );
445
+ }
446
+ }
447
+ }
448
+
449
+ if ( $items ) {
450
+
451
+ $items = $this->ld_json_breadcrumb_first( $item_type ) . $items . $this->ld_json_breadcrumb_last( $item_type, $pos, $post_id );
452
+
453
+ //* Put it all together.
454
+ $breadcrumbhelper = sprintf( '{"@context":%s,"@type":%s,"itemListElement":[%s]}', $context, $context_type, $items );
455
+ $output .= '<script type="application/ld+json">' . $breadcrumbhelper . '</script>' . "\r\n";
456
+ }
457
+ } else {
458
+ //* No assigned children, single term item.
459
+
460
+ $items = '';
461
+
462
+ //* The position of the current item is always static here.
463
+ $pos = '2';
464
+
465
+ //* $tree_ids is a single ID here.
466
+ $cat = get_term_by( 'id', $tree_ids, $cat_type, OBJECT, 'raw' );
467
+
468
+ $id = json_encode( $this->the_url( '', array( 'get_custom_field' => false, 'is_term' => true, 'external' => true, 'term' => $cat ) ) );
469
+
470
+ $custom_field_name = isset( $cat->admeta['doctitle'] ) ? $cat->admeta['doctitle'] : '';
471
+ $cat_name = $custom_field_name ? $custom_field_name : $cat->name;
472
+ $name = json_encode( $cat_name );
473
+
474
+ $items .= sprintf( '{"@type":%s,"position":%s,"item":{"@id":%s,"name":%s}},', $item_type, (string) $pos, $id, $name );
475
+
476
+ if ( $items ) {
477
+
478
+ $items = $this->ld_json_breadcrumb_first( $item_type ) . $items . $this->ld_json_breadcrumb_last( $item_type, $pos, $post_id );
479
+
480
+ //* Put it all together.
481
+ $breadcrumbhelper = sprintf( '{"@context":%s,"@type":%s,"itemListElement":[%s]}', $context, $context_type, $items );
482
+ $output .= '<script type="application/ld+json">' . $breadcrumbhelper . '</script>' . "\r\n";
483
+ }
484
+ }
485
+
486
+ }
487
+ }
488
+
489
+ return $output;
490
+ }
491
+
492
+ /**
493
+ * Generate page breadcrumb.
494
+ *
495
+ * @since 2.6.0
496
+ *
497
+ * @return string $output The breadcrumb script.
498
+ */
499
+ public function ld_json_breadcrumbs_page() {
500
+
501
+ $output = '';
502
+
503
+ $page_id = $this->get_the_real_ID();
504
+
505
+ //* Get ancestors.
506
+ $parents = get_post_ancestors( $page_id );
507
+
508
+ if ( $parents ) {
509
+
510
+ $context = $this->schema_context();
511
+ $context_type = $this->schema_breadcrumblist();
512
+ $item_type = $this->schema_listitem();
513
+
514
+ $items = '';
515
+
516
+ $parents = array_reverse( $parents );
517
+
518
+ foreach ( $parents as $position => $parent_id ) {
519
+ $pos = $position + 2;
520
+
521
+ $id = json_encode( $this->the_url( '', array( 'get_custom_field' => false, 'external' => true, 'id' => $parent_id ) ) );
522
+
523
+ $custom_field_name = $this->get_custom_field( '_genesis_title', $parent_id );
524
+ $parent_name = $custom_field_name ? $custom_field_name : $this->title( '', '', '', array( 'term_id' => $parent_id, 'meta' => true, 'get_custom_field' => false, 'placeholder' => true, 'notagline' => true, 'description_title' => true ) );
525
+
526
+ $name = json_encode( $parent_name );
527
+
528
+ $breadcrumb = array(
529
+ 'type' => $item_type,
530
+ 'pos' => (string) $pos,
531
+ 'id' => $id,
532
+ 'name' => $name
533
+ );
534
+
535
+ $items .= $this->make_breadcrumb( $breadcrumb, true );
536
+ }
537
+
538
+ if ( $items ) {
539
+
540
+ $items = $this->ld_json_breadcrumb_first( $item_type ) . $items . $this->ld_json_breadcrumb_last( $item_type, $pos, $page_id );
541
+
542
+ //* Put it all together.
543
+ $breadcrumbhelper = sprintf( '{"@context":%s,"@type":%s,"itemListElement":[%s]}', $context, $context_type, $items );
544
+ $output = '<script type="application/ld+json">' . $breadcrumbhelper . '</script>' . "\r\n";
545
+ }
546
+ }
547
+
548
+ return $output;
549
+ }
550
+
551
+ /**
552
+ * Return home page item for LD Json Breadcrumbs.
553
+ *
554
+ * @staticvar string $first_item.
555
+ *
556
+ * @since 2.4.2
557
+ *
558
+ * @param string|null $item_type the breadcrumb item type.
559
+ *
560
+ * @return string Home Breadcrumb item
561
+ */
562
+ public function ld_json_breadcrumb_first( $item_type = null ) {
563
+
564
+ static $first_item = null;
565
+
566
+ if ( isset( $first_item ) )
567
+ return $first_item;
568
+
569
+ if ( is_null( $item_type ) )
570
+ $item_type = json_encode( 'ListItem' );
571
+
572
+ $id = json_encode( $this->the_home_url_from_cache() );
573
+
574
+ $home_title = $this->get_option( 'homepage_title' );
575
+
576
+ if ( $home_title ) {
577
+ $custom_name = $home_title;
578
+ } else if ( $this->has_page_on_front() ) {
579
+ $home_id = (int) get_option( 'page_on_front' );
580
+
581
+ $custom_name = $this->get_custom_field( '_genesis_title', $home_id );
582
+ $custom_name = $custom_name ? $custom_name : $this->get_blogname();
583
+ } else {
584
+ $custom_name = $this->get_blogname();
585
+ }
586
+
587
+ $custom_name = json_encode( $custom_name );
588
+
589
+ $breadcrumb = array(
590
+ 'type' => $item_type,
591
+ 'pos' => '1',
592
+ 'id' => $id,
593
+ 'name' => $custom_name
594
+ );
595
+
596
+ return $first_item = $this->make_breadcrumb( $breadcrumb, true );
597
+ }
598
+
599
+ /**
600
+ * Return current page item for LD Json Breadcrumbs.
601
+ *
602
+ * @staticvar string $last_item.
603
+ *
604
+ * @since 2.4.2
605
+ *
606
+ * @param string $item_type the breadcrumb item type.
607
+ * @param int $pos Last known position.
608
+ * @param int $post_id The current Post ID
609
+ *
610
+ * @staticvar string $type The breadcrumb item type.
611
+ * @staticvar string $id The current post/page/archive url.
612
+ * @staticvar string $name The current post/page/archive title.
613
+ *
614
+ * @return string Last Breadcrumb item
615
+ */
616
+ public function ld_json_breadcrumb_last( $item_type = null, $pos = null, $post_id = null ) {
617
+
618
+ /**
619
+ * 2 (becomes 3) holds mostly true for single term items.
620
+ * This shouldn't run anyway. Pos should always be provided.
621
+ */
622
+ if ( is_null( $pos ) )
623
+ $pos = '2';
624
+
625
+ //* Add current page.
626
+ $pos = $pos + 1;
627
+
628
+ if ( is_null( $item_type ) ) {
629
+ static $type = null;
630
+
631
+ if ( ! isset( $type ) )
632
+ $type = json_encode( 'ListItem' );
633
+
634
+ $item_type = $type;
635
+ }
636
+
637
+ if ( empty( $post_id ) )
638
+ $post_id = $this->get_the_real_ID();
639
+
640
+ static $id = null;
641
+ static $name = null;
642
+
643
+ if ( ! isset( $id ) )
644
+ $id = json_encode( $this->the_url_from_cache() );
645
+
646
+ if ( ! isset( $name ) ) {
647
+ $custom_field = $this->get_custom_field( '_genesis_title', $post_id );
648
+ $name = $custom_field ? $custom_field : $this->title( '', '', '', array( 'term_id' => $post_id, 'placeholder' => true, 'meta' => true, 'notagline' => true, 'description_title' => true ) );
649
+ $name = json_encode( $name );
650
+ }
651
+
652
+ $breadcrumb = array(
653
+ 'type' => $item_type,
654
+ 'pos' => (string) $pos,
655
+ 'id' => $id,
656
+ 'name' => $name
657
+ );
658
+
659
+ return $this->make_breadcrumb( $breadcrumb, false );
660
+ }
661
+
662
+ /**
663
+ * Builds a breadcrumb.
664
+ *
665
+ * @since 2.6.0
666
+ * @param array $item : {
667
+ * 'type',
668
+ * 'pos',
669
+ * 'id',
670
+ * 'name'
671
+ * }
672
+ * @param bool $comma Whether to add a trailing comma.
673
+ *
674
+ * @return string The LD+Json breadcrumb.
675
+ */
676
+ public function make_breadcrumb( $item, $comma = true ) {
677
+ $comma = $comma ? ',' : '';
678
+ return sprintf( '{"@type":%s,"position":%s,"item":{"@id":%s,"name":%s}}%s', $item['type'], $item['pos'], $item['id'], $item['name'], $comma );
679
+ }
680
+
681
+ /**
682
+ * Build breadcrumb trees.
683
+ *
684
+ * @since 2.6.0
685
+ *
686
+ * @param array The breadcrumb trees, with the key as parent.
687
+ * @param array $previous_tree A previous set tree to compare to, if set.
688
+ *
689
+ * @return trees in order.
690
+ */
691
+ protected function build_breadcrumb_trees( $kittens, array $previous_tree = array() ) {
692
+
693
+ $trees = $previous_tree;
694
+
695
+ foreach ( $kittens as $parent => $kitten ) {
696
+ if ( empty( $kitten ) ) {
697
+ //* Final cat.
698
+ $trees[] = $parent;
699
+ } else {
700
+ if ( 1 === count( $kitten ) ) {
701
+ //* Single tree.
702
+ $trees[] = array( reset( $kitten ), $parent );
703
+ } else {
704
+ //* Nested categories.
705
+ $add = array();
706
+
707
+ foreach ( $kitten as $kit => $kat ) {
708
+ //* Only add if non-existent in $trees.
709
+ if ( ! in_array( $kat, $trees ) )
710
+ $add[] = $kat;
711
+ }
712
+
713
+ //* Put children in right order.
714
+ $add = array_reverse( $add );
715
+
716
+ $trees[] = array_merge( $add, array( $parent ) );
717
+ }
718
+ }
719
+ }
720
+
721
+ return $trees;
722
+ }
723
+
724
+ /**
725
+ * Return LD+Json Knowledge Graph helper.
726
+ *
727
+ * @since 2.2.8
728
+ *
729
+ * @return string LD+json Knowledge Graph helper.
730
+ */
731
+ public function ld_json_knowledge() {
732
+
733
+ if ( false === $this->enable_ld_json_knowledge() )
734
+ return '';
735
+
736
+ $knowledge_type = $this->get_option( 'knowledge_type' );
737
+
738
+ /**
739
+ * Forgot to add this.
740
+ * @since 2.4.3
741
+ */
742
+ $knowledge_name = $this->get_option( 'knowledge_name' );
743
+ $knowledge_name = $knowledge_name ? $knowledge_name : $this->get_blogname();
744
+
745
+ $context = $this->schema_context();
746
+ $type = json_encode( ucfirst( $knowledge_type ) );
747
+ $name = json_encode( $knowledge_name );
748
+ $url = json_encode( esc_url( home_url( '/' ) ) );
749
+
750
+ $logo = '';
751
+
752
+ if ( $this->get_option( 'knowledge_logo' ) && 'organization' === $knowledge_type ) {
753
+ $icon = $this->site_icon();
754
+
755
+ if ( $icon ) {
756
+ $logourl = esc_url_raw( $icon );
757
+
758
+ //* Add trailing comma
759
+ $logo = '"logo":' . json_encode( $logourl ) . ',';
760
+ }
761
+ }
762
+
763
+ /**
764
+ * Applies filters 'the_seo_framework_json_options' : array The option names
765
+ * @since ???
766
+ * @todo Document.
767
+ */
768
+ $options = (array) apply_filters( 'the_seo_framework_json_options', array(
769
+ 'knowledge_facebook',
770
+ 'knowledge_twitter',
771
+ 'knowledge_gplus',
772
+ 'knowledge_instagram',
773
+ 'knowledge_youtube',
774
+ 'knowledge_linkedin',
775
+ 'knowledge_pinterest',
776
+ 'knowledge_soundcloud',
777
+ 'knowledge_tumblr',
778
+ ) );
779
+
780
+ $sameurls = '';
781
+ $comma = ',';
782
+
783
+ //* Put the urls together from the options.
784
+ if ( is_array( $options ) ) {
785
+ foreach ( $options as $option ) {
786
+ $the_option = $this->get_option( $option );
787
+
788
+ if ( '' !== $the_option )
789
+ $sameurls .= json_encode( $the_option ) . $comma;
790
+ }
791
+ }
792
+
793
+ //* Remove trailing comma
794
+ $sameurls = rtrim( $sameurls, $comma );
795
+ $json = '';
796
+
797
+ if ( $sameurls )
798
+ $json = sprintf( '{"@context":%s,"@type":%s,"name":%s,"url":%s,%s"sameAs":[%s]}', $context, $type, $name, $url, $logo, $sameurls );
799
+
800
+ $output = '';
801
+ if ( $json )
802
+ $output = '<script type="application/ld+json">' . $json . '</script>' . "\r\n";
803
+
804
+ return $output;
805
+ }
806
+
807
+ /**
808
+ * Generate Site Name LD+Json script.
809
+ *
810
+ * @since 2.6.0
811
+ *
812
+ * @return string The LD+JSon Site Name script.
813
+ */
814
+ public function ld_json_name() {
815
+
816
+ if ( false === $this->enable_ld_json_sitename() )
817
+ return '';
818
+
819
+ $context = $this->schema_context();
820
+ $webtype = $this->schema_type();
821
+ $url = $this->schema_home_url();
822
+ $name = $this->schema_blog_name();
823
+
824
+ $json = sprintf( '{"@context":%s,"@type":%s,"name":%s,"url":%s}', $context, $webtype, $name, $url );
825
+
826
+ $output = '';
827
+ if ( $json )
828
+ $output = '<script type="application/ld+json">' . $json . '</script>' . "\r\n";
829
+
830
+ return $output;
831
+ }
832
+
833
+ /**
834
+ * Determines if breadcrumbs scripts are enabled.
835
+ *
836
+ * @since 2.6.0
837
+ * @staticvar bool $cache
838
+ *
839
+ * @return bool
840
+ */
841
+ public function enable_ld_json_breadcrumbs() {
842
+
843
+ static $cache = null;
844
+
845
+ if ( isset( $cache ) )
846
+ return $cache;
847
+
848
+ /**
849
+ * Applies filters the_seo_framework_json_breadcrumb_output
850
+ * @since 2.4.2
851
+ */
852
+ $filter = (bool) apply_filters( 'the_seo_framework_json_breadcrumb_output', true );
853
+ $option = $this->is_option_checked( 'ld_json_breadcrumbs' );
854
+
855
+ return $cache = $filter && $option ? true : false;
856
+ }
857
+
858
+ /**
859
+ * Determines if sitename script is enabled.
860
+ *
861
+ * @since 2.6.0
862
+ * @staticvar bool $cache
863
+ *
864
+ * @return bool
865
+ */
866
+ public function enable_ld_json_sitename() {
867
+
868
+ static $cache = null;
869
+
870
+ if ( isset( $cache ) )
871
+ return $cache;
872
+
873
+ /**
874
+ * Applies filters the_seo_framework_json_sitename_output
875
+ * @since 2.6.0
876
+ */
877
+ $filter = (bool) apply_filters( 'the_seo_framework_json_sitename_output', true );
878
+ $option = $this->is_option_checked( 'ld_json_sitename' );
879
+
880
+ return $cache = $filter && $option ? true : false;
881
+ }
882
+
883
+ /**
884
+ * Determines if searchbox script is enabled.
885
+ *
886
+ * @since 2.6.0
887
+ * @staticvar bool $cache
888
+ *
889
+ * @return bool
890
+ */
891
+ public function enable_ld_json_searchbox() {
892
+
893
+ static $cache = null;
894
+
895
+ if ( isset( $cache ) )
896
+ return $cache;
897
+
898
+ /**
899
+ * Applies filters 'the_seo_framework_json_search_output'
900
+ * @since 2.3.9
901
+ */
902
+ $filter = (bool) apply_filters( 'the_seo_framework_json_search_output', true );
903
+ $option = $this->is_option_checked( 'ld_json_searchbox' );
904
+
905
+ return $cache = $filter && $option ? true : false;
906
+ }
907
+
908
+ /**
909
+ * Determines if Knowledge Graph Script is enabled.
910
+ *
911
+ * @since 2.6.5
912
+ * @staticvar bool $cache
913
+ *
914
+ * @return bool
915
+ */
916
+ public function enable_ld_json_knowledge() {
917
+
918
+ static $cache = null;
919
+
920
+ if ( isset( $cache ) )
921
+ return $cache;
922
+
923
+ /**
924
+ * Applies filters 'the_seo_framework_json_search_output'
925
+ * @since 2.6.5
926
+ */
927
+ $filter = (bool) apply_filters( 'the_seo_framework_json_knowledge_output', true );
928
+ $option = $this->is_option_checked( 'knowledge_output' );
929
+
930
+ return $cache = $filter && $option ? true : false;
931
+ }
932
+
933
+ }
inc/classes/generate-title.class.php ADDED
@@ -0,0 +1,1394 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The SEO Framework plugin
4
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License version 3 as published
8
+ * by the Free Software Foundation.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ /**
20
+ * Class AutoDescription_Generate_Title
21
+ *
22
+ * Generates title SEO data based on content.
23
+ *
24
+ * @since 2.6.0
25
+ */
26
+ class AutoDescription_Generate_Title extends AutoDescription_Generate_Description {
27
+
28
+ /**
29
+ * Constructor, load parent constructor
30
+ */
31
+ public function __construct() {
32
+ parent::__construct();
33
+ }
34
+
35
+ /**
36
+ * Get the title. God function.
37
+ * Always use this function for the title unless you're absolutely sure what you're doing.
38
+ *
39
+ * This function is used for all these: Taxonomies and Terms, Posts, Pages, Blog, front page, front-end, back-end.
40
+ *
41
+ * @since 1.0.0
42
+ *
43
+ * Params required wp_title filter :
44
+ * @param string $title The Title to return
45
+ * @param string $sep The Title sepeartor
46
+ * @param string $seplocation The Title sepeartor location ( accepts 'left' or 'right' )
47
+ *
48
+ * @since 2.4.0:
49
+ * @param array $args : accepted args : {
50
+ * @param int term_id The Taxonomy Term ID when taxonomy is also filled in. Else post ID.
51
+ * @param string taxonomy The Taxonomy name.
52
+ * @param bool page_on_front Page on front condition for example generation.
53
+ * @param bool placeholder Generate placeholder, ignoring options.
54
+ * @param bool notagline Generate title without tagline.
55
+ * @param bool meta Ignore doing_it_wrong. Used in og:title/twitter:title
56
+ * @param bool get_custom_field Do not fetch custom title when false.
57
+ * @param bool description_title Fetch title for description.
58
+ * @param bool is_front_page Fetch front page title.
59
+ * }
60
+ *
61
+ * @return string $title Title
62
+ */
63
+ public function title( $title = '', $sep = '', $seplocation = '', $args = array() ) {
64
+
65
+ //* Use WordPress default feed title.
66
+ if ( $this->is_feed() )
67
+ return trim( $title );
68
+
69
+ $args = $this->reparse_title_args( $args );
70
+
71
+ /**
72
+ * Return early if the request is the Title only (without tagline/blogname).
73
+ */
74
+ if ( $args['notagline'] )
75
+ return $this->build_title_notagline( $args );
76
+
77
+ /**
78
+ * Add doing it wrong notice for better SEO consistency.
79
+ * Only when in wp_title.
80
+ *
81
+ * @since 2.2.5
82
+ */
83
+ if ( false === $args['meta'] && false === $this->is_admin() ) {
84
+ if ( false === $this->current_theme_supports_title_tag() && doing_filter( 'wp_title' ) ) {
85
+ if ( $seplocation ) {
86
+ //* Set doing it wrong parameters.
87
+ $this->tell_title_doing_it_wrong( $title, $sep, $seplocation, false );
88
+ //* And echo them.
89
+ add_action( 'wp_footer', array( $this, 'tell_title_doing_it_wrong' ), 20 );
90
+
91
+ //* Notify cache.
92
+ $this->title_doing_it_wrong = true;
93
+
94
+ //* Notify transients
95
+ $this->set_theme_dir_transient( false );
96
+
97
+ return $this->build_title_doingitwrong( $title, $sep, $seplocation, $args );
98
+ } else if ( $sep ) {
99
+ //* Set doing it wrong parameters.
100
+ $this->tell_title_doing_it_wrong( $title, $sep, $seplocation, false );
101
+ //* And echo them.
102
+ add_action( 'wp_footer', array( $this, 'tell_title_doing_it_wrong' ), 20 );
103
+
104
+ //* Notify cache.
105
+ $this->title_doing_it_wrong = true;
106
+
107
+ //* Notify transients
108
+ $this->set_theme_dir_transient( false );
109
+
110
+ //* Title is empty.
111
+ $args['empty_title'] = true;
112
+
113
+ return $this->build_title_doingitwrong( $title, $sep, $seplocation, $args );
114
+ }
115
+ }
116
+ }
117
+
118
+ //* Notify cache to keep using the same output. We're doing it right :).
119
+ if ( ! isset( $this->title_doing_it_wrong ) )
120
+ $this->title_doing_it_wrong = false;
121
+
122
+ //* Set transient to true if the theme is doing it right.
123
+ if ( false === $this->title_doing_it_wrong )
124
+ $this->set_theme_dir_transient( true );
125
+
126
+ //* Empty title and rebuild it.
127
+ return $this->build_title( $title = '', $seplocation, $args );
128
+ }
129
+
130
+ /**
131
+ * Escapes and beautifies title.
132
+ *
133
+ * @param string $title The title to escape and beautify.
134
+ * @param bool $trim Whether to trim the title from whitespaces.
135
+ *
136
+ * @since 2.5.2
137
+ *
138
+ * @return string Escaped and beautified title.
139
+ */
140
+ public function escape_title( $title = '', $trim = true ) {
141
+
142
+ $title = wptexturize( $title );
143
+ $title = convert_chars( $title );
144
+ $title = esc_html( $title );
145
+ $title = capital_P_dangit( $title );
146
+ $title = $trim ? trim( $title ) : $title;
147
+
148
+ return $title;
149
+ }
150
+
151
+ /**
152
+ * Parse and sanitize title args.
153
+ *
154
+ * @param array $args required The passed arguments.
155
+ * @param array $defaults The default arguments.
156
+ * @param bool $get_defaults Return the default arguments. Ignoring $args.
157
+ *
158
+ * @applies filters the_seo_framework_title_args : {
159
+ * @param int term_id The Taxonomy Term ID when taxonomy is also filled in. Else post ID.
160
+ * @param string taxonomy The Taxonomy name.
161
+ * @param bool page_on_front Page on front condition for example generation.
162
+ * @param bool notagline Generate title without tagline.
163
+ * @param bool meta Ignore doing_it_wrong. Used in og:title/twitter:title
164
+ * @param bool get_custom_field Do not fetch custom title when false.
165
+ * @param bool description_title Fetch title for description.
166
+ * @param bool is_front_page Fetch front page title.
167
+ * }
168
+ *
169
+ * @since 2.4.0
170
+ * @return array $args parsed args.
171
+ */
172
+ public function parse_title_args( $args = array(), $defaults = array(), $get_defaults = false ) {
173
+
174
+ //* Passing back the defaults reduces the memory usage.
175
+ if ( empty( $defaults ) ) {
176
+ $defaults = array(
177
+ 'term_id' => $this->get_the_real_ID(),
178
+ 'taxonomy' => '',
179
+ 'page_on_front' => false,
180
+ 'notagline' => false,
181
+ 'meta' => false,
182
+ 'get_custom_field' => true,
183
+ 'description_title' => false,
184
+ 'is_front_page' => false,
185
+ 'escape' => true,
186
+ );
187
+
188
+ //* @since 2.5.0
189
+ $defaults = (array) apply_filters( 'the_seo_framework_title_args', $defaults, $args );
190
+ }
191
+
192
+ //* Return early if it's only a default args request.
193
+ if ( $get_defaults )
194
+ return $defaults;
195
+
196
+ //* Array merge doesn't support sanitation. We're simply type casting here.
197
+ $args['term_id'] = isset( $args['term_id'] ) ? (int) $args['term_id'] : $defaults['term_id'];
198
+ $args['taxonomy'] = isset( $args['taxonomy'] ) ? (string) $args['taxonomy'] : $defaults['taxonomy'];
199
+ $args['page_on_front'] = isset( $args['page_on_front'] ) ? (bool) $args['page_on_front'] : $defaults['page_on_front'];
200
+ $args['notagline'] = isset( $args['notagline'] ) ? (bool) $args['notagline'] : $defaults['notagline'];
201
+ $args['meta'] = isset( $args['meta'] ) ? (bool) $args['meta'] : $defaults['meta'];
202
+ $args['get_custom_field'] = isset( $args['get_custom_field'] ) ? (bool) $args['get_custom_field'] : $defaults['get_custom_field'];
203
+ $args['description_title'] = isset( $args['description_title'] ) ? (bool) $args['description_title'] : $defaults['description_title'];
204
+ $args['is_front_page'] = isset( $args['is_front_page'] ) ? (bool) $args['is_front_page'] : $defaults['is_front_page'];
205
+ $args['escape'] = isset( $args['escape'] ) ? (bool) $args['escape'] : $defaults['escape'];
206
+
207
+ return $args;
208
+ }
209
+
210
+ /**
211
+ * Reparse title args.
212
+ *
213
+ * @param array $args required The passed arguments.
214
+ *
215
+ * @since 2.6.0
216
+ * @return array $args parsed args.
217
+ */
218
+ public function reparse_title_args( $args = array() ) {
219
+
220
+ $default_args = $this->parse_title_args( '', '', true );
221
+
222
+ if ( is_array( $args ) ) {
223
+ if ( empty( $args ) ) {
224
+ $args = $default_args;
225
+ } else {
226
+ $args = $this->parse_title_args( $args, $default_args );
227
+ }
228
+ } else {
229
+ //* Old style parameters are used. Doing it wrong.
230
+ $this->_doing_it_wrong( __CLASS__ . '::' . __FUNCTION__, 'Use $args = array() for parameters.', '2.5.0' );
231
+ $args = $default_args;
232
+ }
233
+
234
+ return $args;
235
+ }
236
+
237
+ /**
238
+ * Build the title based on input, without tagline.
239
+ *
240
+ * @param array $args : accepted args : {
241
+ * @param int term_id The Taxonomy Term ID
242
+ * @param bool placeholder Generate placeholder, ignoring options.
243
+ * @param bool page_on_front Page on front condition for example generation
244
+ * }
245
+ *
246
+ * @since 2.4.0
247
+ *
248
+ * @return string Title without tagline.
249
+ */
250
+ protected function build_title_notagline( $args = array() ) {
251
+
252
+ $title = $this->do_title_pre_filter( '', $args, false );
253
+
254
+ if ( empty( $title ) )
255
+ $title = $this->get_notagline_title( $args );
256
+
257
+ if ( empty( $title ) )
258
+ $title = $this->untitled();
259
+
260
+ $title = $this->do_title_pro_filter( $title, $args, false );
261
+
262
+ if ( $args['escape'] )
263
+ $title = $this->escape_title( $title );
264
+
265
+ return $title;
266
+ }
267
+
268
+ /**
269
+ * Build the title based on input, without tagline.
270
+ * Note: Not escaped.
271
+ *
272
+ * @param array $args : accepted args : {
273
+ * @param int term_id The Taxonomy Term ID
274
+ * @param bool placeholder Generate placeholder, ignoring options.
275
+ * @param bool page_on_front Page on front condition for example generation
276
+ * }
277
+ *
278
+ * @since 2.6.0
279
+ *
280
+ * @return string Title without tagline.
281
+ */
282
+ protected function get_notagline_title( $args = array() ) {
283
+
284
+ $title = '';
285
+
286
+ //* Fetch title from custom fields.
287
+ if ( $args['get_custom_field'] )
288
+ $title = $this->get_custom_field_title( $title, $args['term_id'], $args['taxonomy'] );
289
+
290
+ //* Generate the Title if empty or if home.
291
+ if ( empty( $title ) )
292
+ $title = (string) $this->generate_title( $args, false );
293
+
294
+ return $title;
295
+ }
296
+
297
+ /**
298
+ * Build the title based on input for themes that are doing it wrong.
299
+ * Pretty much a duplicate of build_title but contains many more variables.
300
+ * Keep this in mind.
301
+ *
302
+ * @param string $title The Title to return
303
+ * @param string $sep The Title sepeartor
304
+ * @param string $seplocation The Title sepeartor location ( accepts 'left' or 'right' )
305
+ * @param array $args : accepted args : {
306
+ * @param int term_id The Taxonomy Term ID
307
+ * @param string taxonomy The Taxonomy name
308
+ * @param bool placeholder Generate placeholder, ignoring options.
309
+ * @param bool get_custom_field Do not fetch custom title when false.
310
+ * }
311
+ *
312
+ * @since 2.4.0
313
+ *
314
+ * @return string $title Title
315
+ */
316
+ public function build_title_doingitwrong( $title = '', $sep = '', $seplocation = '', $args = array() ) {
317
+
318
+ if ( $this->the_seo_framework_debug ) $this->debug_init( __CLASS__, __FUNCTION__, true, $debug_key = microtime(true), get_defined_vars() );
319
+
320
+ /**
321
+ * Empty the title, because most themes think they 'know' how to SEO the front page.
322
+ * Because, most themes know how to make the title 'pretty'.
323
+ * And therefor add all kinds of stuff.
324
+ *
325
+ * Moved up and return early to reduce processing.
326
+ * @since 2.3.8
327
+ */
328
+ if ( $this->is_front_page() )
329
+ return $title = '';
330
+
331
+ $args = $this->reparse_title_args( $args );
332
+
333
+ /**
334
+ * When using an empty wp_title() function, outputs are unexpected.
335
+ * This small piece of code will fix all that.
336
+ * By removing the separator from the title and adding the blog name always to the right.
337
+ * Which is always the case with doing_it_wrong.
338
+ *
339
+ * @thanks JW_ https://wordpress.org/support/topic/wp_title-problem-bug
340
+ * @since 2.4.3
341
+ */
342
+ if ( isset( $args['empty_title'] ) ) {
343
+ $title = trim( str_replace( $sep, '', $title ) );
344
+ $seplocation = 'right';
345
+ }
346
+
347
+ $blogname = $this->get_blogname();
348
+
349
+ /**
350
+ * Don't add/replace separator when false.
351
+ *
352
+ * @applies filters the_seo_framework_doingitwrong_add_sep
353
+ *
354
+ * @since 2.4.2
355
+ */
356
+ $add_sep = (bool) apply_filters( 'the_seo_framework_doingitwrong_add_sep', true );
357
+
358
+ $sep_replace = false;
359
+ //* Maybe remove separator.
360
+ if ( $add_sep && ( $sep || $title ) ) {
361
+ $sep_replace = true;
362
+ $sep_to_replace = (string) $sep;
363
+ }
364
+
365
+ //* Fetch the title as is.
366
+ $title = $this->get_notagline_title( $args );
367
+
368
+ /**
369
+ * Applies filters the_seo_framework_title_separator : String The title separator
370
+ */
371
+ if ( $add_sep )
372
+ $sep = $this->get_title_separator();
373
+
374
+ /**
375
+ * Add $sep_to_replace
376
+ *
377
+ * @since 2.3.8
378
+ */
379
+ if ( $sep_replace ) {
380
+ //* Title always contains something at this point.
381
+ $tit_len = mb_strlen( $title );
382
+
383
+ /**
384
+ * Prevent double separator on date archives.
385
+ * This will cause manual titles with the same separator at the end to be removed.
386
+ * Then again, update your theme. D:
387
+ *
388
+ * A separator is at least 2 long (space + separator).
389
+ *
390
+ * @param string $sep_to_replace Already confirmed to contain the old sep string.
391
+ *
392
+ * Now also considers seplocation.
393
+ * @since 2.4.1
394
+ */
395
+ if ( $sep_to_replace ) {
396
+
397
+ $sep_to_replace_length = mb_strlen( $sep_to_replace );
398
+
399
+ if ( 'right' === $seplocation ) {
400
+ if ( $tit_len > $sep_to_replace_length && ! mb_strpos( $title, $sep_to_replace, $tit_len - $sep_to_replace_length ) )
401
+ $title = $title . ' ' . $sep_to_replace;
402
+ } else {
403
+ if ( $tit_len > $sep_to_replace_length && ! mb_strpos( $title, $sep_to_replace, $sep_to_replace_length ) )
404
+ $title = $sep_to_replace . ' ' . $title;
405
+ }
406
+ }
407
+
408
+ /**
409
+ * Convert characters to easier match and prevent removal of matching entities and title characters.
410
+ * Reported by Riccardo: https://wordpress.org/support/topic/problem-with-post-titles
411
+ * @since 2.5.2
412
+ */
413
+ $sep_to_replace = html_entity_decode( $sep_to_replace );
414
+ $title = html_entity_decode( $title );
415
+
416
+ /**
417
+ * Now also considers seplocation.
418
+ * @since 2.4.1
419
+ */
420
+ if ( 'right' === $seplocation ) {
421
+ $title = trim( rtrim( $title, "$sep_to_replace " ) ) . " $sep ";
422
+ } else {
423
+ $title = " $sep " . trim( ltrim( $title, " $sep_to_replace" ) );
424
+ }
425
+
426
+ } else {
427
+ $title = trim( $title ) . " $sep ";
428
+ }
429
+
430
+ if ( ! $args['description_title'] )
431
+ $title = $this->add_title_protection( $title, $args['term_id'] );
432
+
433
+ if ( $args['escape'] )
434
+ $title = $this->escape_title( $title, false );
435
+
436
+ if ( $this->the_seo_framework_debug ) $this->debug_init( __CLASS__, __FUNCTION__, false, $debug_key, array( 'title_output' => $title ) );
437
+
438
+ return $title;
439
+ }
440
+
441
+ /**
442
+ * Build the title based on input.
443
+ *
444
+ * @param string $title The Title to return
445
+ * @param string $seplocation The Title sepeartor location ( accepts 'left' or 'right' )
446
+ * @param array $args : accepted args : {
447
+ * @param int term_id The Taxonomy Term ID
448
+ * @param string taxonomy The Taxonomy name
449
+ * @param bool page_on_front Page on front condition for example generation
450
+ * @param bool placeholder Generate placeholder, ignoring options.
451
+ * @param bool get_custom_field Do not fetch custom title when false.
452
+ * @param bool is_front_page Fetch front page title.
453
+ * }
454
+ *
455
+ * @since 2.4.0
456
+ *
457
+ * @return string $title Title
458
+ */
459
+ public function build_title( $title = '', $seplocation = '', $args = array() ) {
460
+
461
+ if ( $this->the_seo_framework_debug ) $this->debug_init( __CLASS__, __FUNCTION__, true, $debug_key = microtime(true), get_defined_vars() );
462
+
463
+ $args = $this->reparse_title_args( $args );
464
+
465
+ /**
466
+ * Overwrite title here, prevents duplicate title issues, since we're working with a filter.
467
+ * @since 2.2.2
468
+ * Use filter title.
469
+ * @since 2.6.0
470
+ */
471
+ $title = $this->do_title_pre_filter( '', $args, false );
472
+ $blogname = '';
473
+
474
+ $is_front_page = $this->is_front_page() || $args['page_on_front'] || $this->is_static_frontpage( $args['term_id'] ) ? true : false;
475
+
476
+ $seplocation = $this->get_title_seplocation( $seplocation );
477
+
478
+ /**
479
+ * Generate the Title if empty or if home.
480
+ *
481
+ * Generation of title has acquired its own functions.
482
+ * @since 2.3.4
483
+ */
484
+ if ( $is_front_page ) {
485
+ $generated = (array) $this->generate_home_title( $args['get_custom_field'], $seplocation, '', false );
486
+
487
+ if ( $generated && is_array( $generated ) ) {
488
+ if ( empty( $title ) )
489
+ $title = $generated['title'] ? (string) $generated['title'] : $title;
490
+
491
+ $blogname = $generated['blogname'] ? (string) $generated['blogname'] : $blogname;
492
+ $seplocation = $generated['seplocation'] ? (string) $generated['seplocation'] : $seplocation;
493
+ }
494
+ } else {
495
+ //* Fetch the title as is.
496
+ if ( empty( $title ) )
497
+ $title = $this->get_notagline_title( $args );
498
+
499
+ $blogname = $this->get_blogname();
500
+ }
501
+
502
+ /**
503
+ * From WordPress core get_the_title.
504
+ * Bypasses get_post() function object which causes conflict with some themes and plugins.
505
+ *
506
+ * Also bypasses the_title filters.
507
+ * And now also works in admin. It gives you a true representation of its output.
508
+ *
509
+ * Title for the description bypasses sanitation and additions.
510
+ *
511
+ * @since 2.4.1
512
+ */
513
+ if ( ! $args['description_title'] ) {
514
+
515
+ $title = $this->add_title_protection( $title, $args['term_id'] );
516
+ $title = $this->add_title_pagination( $title );
517
+
518
+ if ( $is_front_page ) {
519
+ if ( $this->home_page_add_title_tagline() )
520
+ $title = $this->process_title_additions( $blogname, $title, $seplocation );
521
+ } else {
522
+ if ( $this->add_title_additions() )
523
+ $title = $this->process_title_additions( $title, $blogname, $seplocation );
524
+ }
525
+
526
+ }
527
+
528
+ $title = $this->do_title_pro_filter( $title, $args, false );
529
+
530
+ /**
531
+ * Applies filters 'the_seo_framework_do_shortcodes_in_title' : Boolean
532
+ * @since 2.6.6
533
+ */
534
+ if ( apply_filters( 'the_seo_framework_do_shortcodes_in_title', false ) )
535
+ $title = do_shortcode( $title );
536
+
537
+ if ( $args['escape'] )
538
+ $title = $this->escape_title( $title );
539
+
540
+ if ( $this->the_seo_framework_debug ) $this->debug_init( __CLASS__, __FUNCTION__, false, $debug_key, array( 'title_output' => $title ) );
541
+
542
+ return $title;
543
+ }
544
+
545
+ /**
546
+ * Fetches title from special fields, like other plugins with special queries.
547
+ * Used before and has priority over custom fields.
548
+ * Front end only.
549
+ *
550
+ * @since 2.5.2
551
+ *
552
+ * @return string $title Title from Special Field.
553
+ */
554
+ public function title_from_special_fields() {
555
+
556
+ $title = '';
557
+
558
+ if ( false === $this->is_admin() ) {
559
+ if ( $this->is_ultimate_member_user_page() && um_is_core_page( 'user' ) && um_get_requested_user() ) {
560
+ $title = um_user( 'display_name' );
561
+ }
562
+ }
563
+
564
+ return $title;
565
+ }
566
+
567
+ /**
568
+ * Generate the title based on query conditions.
569
+ *
570
+ * @since 2.3.4
571
+ *
572
+ * @param array $args The Title Args.
573
+ * @param bool $escape Parse Title through saninitation calls.
574
+ *
575
+ * @staticvar array $cache : contains $title strings.
576
+ * @since 2.6.0
577
+ *
578
+ * @return string $title The Generated Title.
579
+ */
580
+ public function generate_title( $args = array(), $escape = true ) {
581
+
582
+ $args = $this->reparse_title_args( $args );
583
+
584
+ $title = '';
585
+ $id = $args['term_id'];
586
+ $taxonomny = $args['taxonomy'];
587
+
588
+ static $cache = array();
589
+
590
+ if ( isset( $cache[$id][$taxonomny] ) )
591
+ $title = $cache[$id][$taxonomny];
592
+
593
+ if ( empty( $title ) ) {
594
+
595
+ if ( $this->is_archive() ) {
596
+ if ( ( $id && $taxonomny ) || $this->is_category() || $this->is_tag() || $this->is_tax() ) {
597
+ $title = $this->title_for_terms( $args, false );
598
+ } else {
599
+ $term = get_queried_object();
600
+ /**
601
+ * Get all other archive titles
602
+ * @since 2.5.2
603
+ */
604
+ $title = $this->get_the_real_archive_title( $term, $args );
605
+ }
606
+
607
+ }
608
+
609
+ $title = $this->get_the_404_title( $title );
610
+ $title = $this->get_the_search_title( $title, false );
611
+
612
+ //* Fetch the post title if no title is found.
613
+ if ( empty( $title ) )
614
+ $title = $this->post_title_from_ID( $id );
615
+
616
+ //* You forgot to enter a title "anywhere"!
617
+ if ( empty( $title ) )
618
+ $title = $this->untitled();
619
+
620
+ }
621
+
622
+ if ( $escape )
623
+ $title = $this->escape_title( $title, false );
624
+
625
+ return $title;
626
+ }
627
+
628
+ /**
629
+ * Generate the title based on conditions for the home page.
630
+ *
631
+ * @since 2.3.4
632
+ * @access private
633
+ *
634
+ * @param bool $get_custom_field Fetch Title from Custom Fields.
635
+ * @param string $seplocation The separator location
636
+ * @param string $deprecated Deprecated: The Home Page separator location
637
+ * @param bool $escape Parse Title through saninitation calls.
638
+ * @param bool $get_option Whether to fetch the SEO Settings option.
639
+ *
640
+ * @return array {
641
+ * 'title' => (string) $title : The Generated Title
642
+ * 'blogname' => (string) $blogname : The Generated Blogname
643
+ * 'add_tagline' => (bool) $add_tagline : Whether to add the tagline
644
+ * 'seplocation' => (string) $seplocation : The Separator Location
645
+ * }
646
+ */
647
+ public function generate_home_title( $get_custom_field = true, $seplocation = '', $deprecated = '', $escape = true, $get_option = true ) {
648
+
649
+ $add_tagline = $this->home_page_add_title_tagline();
650
+
651
+ /**
652
+ * Add tagline or not based on option
653
+ *
654
+ * @since 2.2.2
655
+ */
656
+ if ( $add_tagline ) {
657
+ /**
658
+ * Tagline based on option.
659
+ * @since 2.3.8
660
+ */
661
+ $blogname = $this->get_option( 'homepage_title_tagline' );
662
+ $blogname = $blogname ? $blogname : $this->get_blogdescription();
663
+ } else {
664
+ $blogname = '';
665
+ }
666
+
667
+ /**
668
+ * Render from function
669
+ * @since 2.2.8
670
+ */
671
+ $title = $this->title_for_home( '', $get_custom_field, false, $get_option );
672
+ $seplocation = $this->get_home_title_seplocation( $seplocation );
673
+
674
+ if ( $escape ) {
675
+ $title = $this->escape_title( $title, false );
676
+ $blogname = $this->escape_title( $blogname, false );
677
+ }
678
+
679
+ return array(
680
+ 'title' => $title,
681
+ 'blogname' => $blogname,
682
+ 'add_tagline' => $add_tagline,
683
+ 'seplocation' => $seplocation
684
+ );
685
+ }
686
+
687
+ /**
688
+ * Gets the title for the static home page.
689
+ * Essentially falling back to the blogname. Not to be confused with $blogname.
690
+ *
691
+ * @since 2.2.8
692
+ * @access private
693
+ * @see $this->generate_home_title()
694
+ *
695
+ * @param string $home_title The fallback title.
696
+ * @param bool $get_custom_field Fetch Title from InPost Custom Fields.
697
+ * @param bool $escape Parse Title through saninitation calls.
698
+ * @param bool $get_option Whether to fetch the SEO Settings option.
699
+ *
700
+ * @return string The Title.
701
+ */
702
+ public function title_for_home( $home_title = '', $get_custom_field = true, $escape = false, $get_option = true ) {
703
+
704
+ /**
705
+ * Get blogname title based on option
706
+ * @since 2.2.2
707
+ */
708
+ if ( $get_option ) {
709
+ $home_title_option = $this->get_option( 'homepage_title' ) ? $this->get_option( 'homepage_title' ) : $home_title;
710
+ $home_title = $home_title_option ? $home_title_option : $home_title;
711
+ }
712
+
713
+ /**
714
+ * Fetch from Home Page InPost SEO Box if available.
715
+ * Only from page on front.
716
+ */
717
+ if ( $get_custom_field && empty( $home_title ) && $this->has_page_on_front() ) {
718
+ $custom_field = $this->get_custom_field( '_genesis_title', $this->get_the_front_page_ID() );
719
+ $home_title = $custom_field ? $custom_field : $this->get_blogname();
720
+ } else {
721
+ $home_title = $home_title ? $home_title : $this->get_blogname();
722
+ }
723
+
724
+ if ( $escape )
725
+ $home_title = $this->escape_title( $home_title, false );
726
+
727
+ return (string) $home_title;
728
+ }
729
+
730
+ /**
731
+ * Gets the title for Category, Tag or Taxonomy
732
+ *
733
+ * @since 2.2.8
734
+ *
735
+ * @param array $args The Title arguments.
736
+ * @param bool $escape Parse Title through saninitation calls.
737
+ *
738
+ * @todo put args in array.
739
+ *
740
+ * @return string The Title.
741
+ */
742
+ public function title_for_terms( $args = array(), $escape = false ) {
743
+
744
+ $args = $this->reparse_title_args( $args );
745
+
746
+ $title = '';
747
+ $term = null;
748
+
749
+ if ( $args['term_id'] && $args['taxonomy'] )
750
+ $term = get_term( $args['term_id'], $args['taxonomy'], OBJECT, 'raw' );
751
+
752
+ if ( $this->is_category() || $this->is_tag() ) {
753
+
754
+ if ( $args['get_custom_field'] ) {
755
+ if ( ! isset( $term ) )
756
+ $term = $this->fetch_the_term( $args['term_id'] );
757
+
758
+ $title = empty( $term->admeta['doctitle'] ) ? $title : $term->admeta['doctitle'];
759
+
760
+ $flag = isset( $term->admeta['saved_flag'] ) && $this->is_checked( $term->admeta['saved_flag'] ) ? true : false;
761
+ if ( false === $flag && empty( $title ) && isset( $term->meta['doctitle'] ) )
762
+ $title = empty( $term->meta['doctitle'] ) ? $title : $term->meta['doctitle'];
763
+ }
764
+
765
+ if ( empty( $title ) )
766
+ $title = $this->get_the_real_archive_title( $term, $args );
767
+
768
+ } else {
769
+ if ( ! isset( $term ) && $this->is_tax() )
770
+ $term = get_term_by( 'slug', get_query_var( 'term' ), get_query_var( 'taxonomy' ) );
771
+
772
+ if ( $args['get_custom_field'] && isset( $term ) ) {
773
+ $title = empty( $term->admeta['doctitle'] ) ? $title : wp_kses_stripslashes( wp_kses_decode_entities( $term->admeta['doctitle'] ) );
774
+
775
+ $flag = isset( $term->admeta['saved_flag'] ) && $this->is_checked( $term->admeta['saved_flag'] );
776
+ if ( false === $flag && empty( $title ) && isset( $term->meta['doctitle'] ) )
777
+ $title = empty( $term->meta['doctitle'] ) ? $title : wp_kses_stripslashes( wp_kses_decode_entities( $term->meta['doctitle'] ) );
778
+ }
779
+
780
+ if ( empty( $title ) )
781
+ $title = $this->get_the_real_archive_title( $term, $args );
782
+
783
+ }
784
+
785
+ if ( $escape )
786
+ $title = $this->escape_title( $title, false );
787
+
788
+ return (string) $title;
789
+ }
790
+
791
+ /**
792
+ * Gets the title from custom field
793
+ *
794
+ * @since 2.2.8
795
+ *
796
+ * @param string $title the fallback title.
797
+ * @param bool $escape Parse Title through saninitation calls.
798
+ * @param int $id The Post ID.
799
+ * @param string $taxonomy The term name.
800
+ *
801
+ * @return string The Title.
802
+ */
803
+ public function title_from_custom_field( $title = '', $escape = false, $id = null, $taxonomy = null ) {
804
+
805
+ $id = isset( $id ) ? $id : null;
806
+
807
+ /**
808
+ * Create something special for blog page. Only if it's not the home page.
809
+ * @since 2.2.8
810
+ */
811
+ if ( $this->is_singular() ) {
812
+ //* Get title from custom field, empty it if it's not there to override the default title
813
+ $title = $this->get_custom_field( '_genesis_title', $id ) ? $this->get_custom_field( '_genesis_title', $id ) : $title;
814
+ } else if ( $this->is_blog_page( $id ) ) {
815
+ //* Posts page title.
816
+ $title = $this->get_custom_field( '_genesis_title', $id ) ? $this->get_custom_field( '_genesis_title', $id ) : get_the_title( $id );
817
+ } else if ( $this->is_archive() || ( $id && $taxonomy ) ) {
818
+ //* Get the custom title for terms.
819
+ $term = get_term( $id, $taxonomy, OBJECT, 'raw' );
820
+ $title = isset( $term->admeta['doctitle'] ) ? $term->admeta['doctitle'] : $title;
821
+ }
822
+
823
+ if ( $escape )
824
+ $title = $this->escape_title( $title, false );
825
+
826
+ return (string) $title;
827
+ }
828
+
829
+ /**
830
+ * Get the archive Title, including filter. Also works in admin.
831
+ * @NOTE Taken from WordPress core. Altered to work in the Admin area.
832
+ *
833
+ * @param object $term The Term object.
834
+ * @param array $args The Title arguments.
835
+ *
836
+ * @since 2.6.0
837
+ */
838
+ public function get_the_real_archive_title( $term = null, $args = array() ) {
839
+
840
+ if ( empty( $term ) )
841
+ $term = get_queried_object();
842
+
843
+ /**
844
+ * Applies filters the_seo_framework_the_archive_title : {
845
+ * @param string empty short circuit the function.
846
+ * @param object $term The Term object.
847
+ * }
848
+ *
849
+ * @since 2.6.0
850
+ */
851
+ $title = (string) apply_filters( 'the_seo_framework_the_archive_title', '', $term );
852
+
853
+ if ( $title )
854
+ return $title;
855
+
856
+ /**
857
+ * @since 2.6.0
858
+ */
859
+ $use_prefix = $this->use_archive_prefix( $term, $args );
860
+
861
+ if ( $this->is_category() || $this->is_tag() || $this->is_tax() ) {
862
+ $title = $this->single_term_title( '', false, $term );
863
+ /* translators: Front-end output. 1: Taxonomy singular name, 2: Current taxonomy term */
864
+ $title = $use_prefix ? sprintf( __( '%1$s: %2$s', 'autodescription' ), $this->get_the_term_name( $term ), $title ) : $title;
865
+ } else if ( $this->is_author() ) {
866
+ $title = get_the_author();
867
+ /* translators: Front-end output. */
868
+ $title = $use_prefix ? sprintf( __( 'Author: %s', 'autodescription' ), $title ) : $title;
869
+ } else if ( $this->is_date() ) {
870
+ if ( $this->is_year() ) {
871
+ /* translators: Front-end output. */
872
+ $title = get_the_date( _x( 'Y', 'yearly archives date format', 'autodescription' ) );
873
+ /* translators: Front-end output. */
874
+ $title = $use_prefix ? sprintf( __( 'Year: %s', 'autodescription' ), $title ) : $title;
875
+ } else if ( $this->is_month() ) {
876
+ /* translators: Front-end output. */
877
+ $title = get_the_date( _x( 'F Y', 'monthly archives date format', 'autodescription' ) );
878
+ /* translators: Front-end output. */
879
+ $title = $use_prefix ? sprintf( __( 'Month: %s', 'autodescription' ), $title ) : $title;
880
+ } else if ( $this->is_day() ) {
881
+ /* translators: Front-end output. */
882
+ $title = get_the_date( _x( 'F j, Y', 'daily archives date format', 'autodescription' ) );
883
+ /* translators: Front-end output. */
884
+ $title = $use_prefix ? sprintf( __( 'Day: %s', 'autodescription' ), $title ) : $title;
885
+ }
886
+ } else if ( $this->is_tax( 'post_format' ) ) {
887
+ if ( is_tax( 'post_format', 'post-format-aside' ) ) {
888
+ /* translators: Front-end output. */
889
+ $title = _x( 'Asides', 'post format archive title', 'autodescription' );
890
+ } else if ( $this->is_tax( 'post_format', 'post-format-gallery' ) ) {
891
+ /* translators: Front-end output. */
892
+ $title = _x( 'Galleries', 'post format archive title', 'autodescription' );
893
+ } else if ( $this->is_tax( 'post_format', 'post-format-image' ) ) {
894
+ /* translators: Front-end output. */
895
+ $title = _x( 'Images', 'post format archive title', 'autodescription' );
896
+ } else if ( $this->is_tax( 'post_format', 'post-format-video' ) ) {
897
+ /* translators: Front-end output. */
898
+ $title = _x( 'Videos', 'post format archive title', 'autodescription' );
899
+ } else if ( $this->is_tax( 'post_format', 'post-format-quote' ) ) {
900
+ /* translators: Front-end output. */
901
+ $title = _x( 'Quotes', 'post format archive title', 'autodescription' );
902
+ } else if ( $this->is_tax( 'post_format', 'post-format-link' ) ) {
903
+ /* translators: Front-end output. */
904
+ $title = _x( 'Links', 'post format archive title', 'autodescription' );
905
+ } else if ( $this->is_tax( 'post_format', 'post-format-status' ) ) {
906
+ /* translators: Front-end output. */
907
+ $title = _x( 'Statuses', 'post format archive title', 'autodescription' );
908
+ } else if ( $this->is_tax( 'post_format', 'post-format-audio' ) ) {
909
+ /* translators: Front-end output. */
910
+ $title = _x( 'Audio', 'post format archive title', 'autodescription' );
911
+ } else if ( $this->is_tax( 'post_format', 'post-format-chat' ) ) {
912
+ /* translators: Front-end output. */
913
+ $title = _x( 'Chats', 'post format archive title', 'autodescription' );
914
+ }
915
+ } else if ( is_post_type_archive() ) {
916
+ $title = post_type_archive_title( '', false );
917
+ /* translators: Front-end output. */
918
+ $title = $use_prefix ? sprintf( __( 'Archives: %s' ), $title ) : $title;
919
+ } else if ( isset( $term ) ) {
920
+ $title = $this->single_term_title( '', false, $term );
921
+
922
+ if ( $use_prefix ) {
923
+ /* translators: Front-end output. 1: Taxonomy singular name, 2: Current taxonomy term */
924
+ $title = sprintf( __( '%1$s: %2$s', 'autodescription' ), $this->get_the_term_name( $term, true, false ), $title );
925
+ }
926
+ } else {
927
+ /* translators: Front-end output. */
928
+ $title = __( 'Archives', 'autodescription' );
929
+ }
930
+
931
+ return $title;
932
+ }
933
+
934
+ /**
935
+ * Fetch single term title.
936
+ * @NOTE Taken from WordPress core. Altered to work in the Admin area.
937
+ *
938
+ * @since 2.6.0
939
+ *
940
+ * @return string.
941
+ */
942
+ public function single_term_title( $prefix = '', $display = true, $term = null ) {
943
+
944
+ if ( is_null( $term ) )
945
+ $term = get_queried_object();
946
+
947
+ if ( ! $term )
948
+ return;
949
+
950
+ $term_name = '';
951
+
952
+ if ( isset( $term->name ) ) {
953
+ if ( $this->is_category() ) {
954
+ /**
955
+ * Filter the category archive page title.
956
+ *
957
+ * @since 2.0.10 WP CORE
958
+ *
959
+ * @param string $term_name Category name for archive being displayed.
960
+ */
961
+ $term_name = apply_filters( 'single_cat_title', $term->name );
962
+ } else if ( $this->is_tag() ) {
963
+ /**
964
+ * Filter the tag archive page title.
965
+ *
966
+ * @since 2.3.0 WP CORE
967
+ *
968
+ * @param string $term_name Tag name for archive being displayed.
969
+ */
970
+ $term_name = apply_filters( 'single_tag_title', $term->name );
971
+ } else if ( $this->is_tax() || $this->is_admin() ) {
972
+ /**
973
+ * Filter the custom taxonomy archive page title.
974
+ *
975
+ * @since 3.1.0 WP CORE
976
+ *
977
+ * @param string $term_name Term name for archive being displayed.
978
+ */
979
+ $term_name = apply_filters( 'single_term_title', $term->name );
980
+ } else {
981
+ return '';
982
+ }
983
+ }
984
+
985
+ //* Impossible through WordPress interface. Possible through filters.
986
+ if ( empty( $term_name ) )
987
+ $term_name = $this->untitled();
988
+
989
+ if ( $display )
990
+ echo $prefix . $term_name;
991
+ else
992
+ return $prefix . $term_name;
993
+ }
994
+
995
+ /**
996
+ * Return custom field title.
997
+ *
998
+ * @since 2.6.0
999
+ *
1000
+ * @param string $title The current title.
1001
+ * @param int $id The post or TT ID.
1002
+ * @param string $taxonomy The TT name.
1003
+ *
1004
+ * @return string $title The custom field title.
1005
+ */
1006
+ public function get_custom_field_title( $title = '', $id = '', $taxonomy = '' ) {
1007
+
1008
+ $title_special = '';
1009
+
1010
+ if ( $this->is_singular() )
1011
+ $title_special = $this->title_from_special_fields();
1012
+
1013
+ if ( $title_special ) {
1014
+ $title = $title_special;
1015
+ } else {
1016
+ $title_from_custom_field = $this->title_from_custom_field( $title, false, $id, $taxonomy );
1017
+ $title = $title_from_custom_field ? $title_from_custom_field : $title;
1018
+ }
1019
+
1020
+ return $title;
1021
+ }
1022
+
1023
+ /**
1024
+ * Untitled title.
1025
+ *
1026
+ * @since 2.6.0
1027
+ *
1028
+ * @return string Untitled.
1029
+ */
1030
+ public function untitled() {
1031
+ /* translators: Front-end output. */
1032
+ return __( 'Untitled', 'autodescription' );
1033
+ }
1034
+
1035
+ /**
1036
+ * Return Post Title from ID.
1037
+ *
1038
+ * @since 2.6.0
1039
+ *
1040
+ * @param int $id The Post ID.
1041
+ * @param string $title Optional. The current Title.
1042
+ *
1043
+ * @return string Post Title
1044
+ */
1045
+ public function post_title_from_ID( $id = 0, $title = '' ) {
1046
+
1047
+ if ( $this->is_archive() )
1048
+ return $title;
1049
+
1050
+ $post = get_post( $id, OBJECT );
1051
+
1052
+ return $title = isset( $post->post_title ) ? $post->post_title : $title;
1053
+ }
1054
+
1055
+ /**
1056
+ * Return search title.
1057
+ *
1058
+ * @since 2.6.0
1059
+ *
1060
+ * @param string $title the current title.
1061
+ * @param bool $escape Whether to escape attributes from query.
1062
+ *
1063
+ * @return string Search Title
1064
+ */
1065
+ public function get_the_search_title( $title, $escape = true ) {
1066
+
1067
+ if ( $this->is_search() ) {
1068
+ /* translators: Front-end output. */
1069
+ $search_title = (string) apply_filters( 'the_seo_framework_search_title', __( 'Search results for:', 'autodescription' ) );
1070
+
1071
+ return $search_title . ' ' . trim( get_search_query( $escape ) );
1072
+ }
1073
+
1074
+ return $title;
1075
+ }
1076
+
1077
+ /**
1078
+ * Return 404 title.
1079
+ *
1080
+ * @since 2.6.0
1081
+ *
1082
+ * Applies filters string the_seo_framework_404_title
1083
+ * @since 2.5.2
1084
+ *
1085
+ * @param string $title The current Title
1086
+ *
1087
+ * @return string 404 Title
1088
+ */
1089
+ public function get_the_404_title( $title ) {
1090
+
1091
+ if ( $this->is_404() )
1092
+ return (string) apply_filters( 'the_seo_framework_404_title', '404' );
1093
+
1094
+ return $title;
1095
+ }
1096
+
1097
+ /**
1098
+ * Get Title Separator.
1099
+ *
1100
+ * @since 2.6.0
1101
+ * @staticvar string $sep
1102
+ *
1103
+ * Applies filters the_seo_framework_title_separator
1104
+ * @since 2.3.9
1105
+ *
1106
+ * @return string The Separator
1107
+ */
1108
+ public function get_title_separator() {
1109
+
1110
+ static $sep = null;
1111
+
1112
+ if ( isset( $sep ) )
1113
+ return $sep;
1114
+
1115
+ return $sep = (string) apply_filters( 'the_seo_framework_title_separator', $this->get_separator( 'title' ) );
1116
+ }
1117
+
1118
+ /**
1119
+ * Get Title Seplocation.
1120
+ *
1121
+ * Applies filters the_seo_framework_title_seplocation : string the title location.
1122
+ * Applies filters the_seo_framework_title_seplocation_front : string the home page title location.
1123
+ * @since 2.3.9
1124
+ *
1125
+ * @access private
1126
+ * @since 2.6.0
1127
+ * @staticvar string $cache
1128
+ *
1129
+ * @param string $seplocation The current seplocation.
1130
+ * @param bool $home The home seplocation.
1131
+ *
1132
+ * @return string The Seplocation
1133
+ */
1134
+ public function get_title_seplocation( $seplocation = '', $home = false ) {
1135
+
1136
+ static $cache = array();
1137
+
1138
+ if ( isset( $cache[$seplocation][$home] ) )
1139
+ return $cache[$seplocation][$home];
1140
+
1141
+ if ( empty( $seplocation ) || 'right' !== $seplocation || 'left' !== $seplocation ) {
1142
+ if ( $home ) {
1143
+ return $cache[$seplocation][$home] = (string) apply_filters( 'the_seo_framework_title_seplocation_front', $this->get_option( 'home_title_location' ) );
1144
+ } else {
1145
+ return $cache[$seplocation][$home] = (string) apply_filters( 'the_seo_framework_title_seplocation', $this->get_option( 'title_location' ) );
1146
+ }
1147
+ }
1148
+
1149
+ return $cache[$seplocation][$home] = $seplocation;
1150
+ }
1151
+
1152
+ /**
1153
+ * Get Title Seplocation for the homepage.
1154
+ *
1155
+ * @since 2.6.0
1156
+ *
1157
+ * @param string $seplocation The current seplocation.
1158
+ *
1159
+ * @return string The Seplocation for the homepage.
1160
+ */
1161
+ public function get_home_title_seplocation( $seplocation = '' ) {
1162
+ return $this->get_title_seplocation( $seplocation, true );
1163
+ }
1164
+
1165
+ /**
1166
+ * Determines whether to add or remove title additions.
1167
+ *
1168
+ * Applies filters the_seo_framework_add_blogname_to_title : boolean
1169
+ * @since 2.4.3
1170
+ *
1171
+ * @since 2.6.0
1172
+ * @staticvar bool $add
1173
+ *
1174
+ * @return bool True when additions are allowed.
1175
+ */
1176
+ public function add_title_additions() {
1177
+
1178
+ static $add = null;
1179
+
1180
+ if ( isset( $add ) )
1181
+ return $add;
1182
+
1183
+ if ( $this->can_manipulate_title() )
1184
+ if ( $this->is_option_checked( 'title_rem_additions' ) || false === (bool) apply_filters( 'the_seo_framework_add_blogname_to_title', true ) )
1185
+ return $add = false;
1186
+
1187
+ return $add = true;
1188
+ }
1189
+
1190
+ /**
1191
+ * Add the title additions to the title.
1192
+ *
1193
+ * @param string $title The tite.
1194
+ * @param string $blogname The blogname.
1195
+ * @param string $seplocation The separator location.
1196
+ *
1197
+ * @since 2.6.0
1198
+ */
1199
+ public function process_title_additions( $title = '', $blogname = '', $seplocation = '' ) {
1200
+
1201
+ $sep = $this->get_title_separator();
1202
+
1203
+ $title = trim( $title );
1204
+ $blogname = trim( $blogname );
1205
+
1206
+ if ( $blogname && $title ) {
1207
+ if ( 'left' === $seplocation ) {
1208
+ $title = $blogname . " $sep " . $title;
1209
+ } else {
1210
+ $title = $title . " $sep " . $blogname;
1211
+ }
1212
+ }
1213
+
1214
+ return $title;
1215
+ }
1216
+
1217
+ /**
1218
+ * Adds title protection prefixes.
1219
+ *
1220
+ * @since 2.6.0
1221
+ *
1222
+ * @param $title The current Title.
1223
+ * @param $id The page ID.
1224
+ *
1225
+ * @return string $title with possible affixes.
1226
+ */
1227
+ public function add_title_protection( $title, $id ) {
1228
+
1229
+ /**
1230
+ * From WordPress core get_the_title.
1231
+ * Bypasses get_post() function object which causes conflict with some themes and plugins.
1232
+ *
1233
+ * Also bypasses the_title filters.
1234
+ * And now also works in admin. It gives you a true representation of its output.
1235
+ *
1236
+ * @since 2.4.1
1237
+ *
1238
+ * @applies filters core : protected_title_format
1239
+ * @applies filters core : private_title_format
1240
+ */
1241
+ $post = get_post( $id, OBJECT );
1242
+
1243
+ if ( isset( $post->post_password ) && '' !== $post->post_password ) {
1244
+ /* translators: Front-end output */
1245
+ $protected_title_format = apply_filters( 'protected_title_format', __( 'Protected: %s', 'autodescription' ), $post );
1246
+ $title = sprintf( $protected_title_format, $title );
1247
+ } else if ( isset( $post->post_status ) && 'private' === $post->post_status ) {
1248
+ /* translators: Front-end output */
1249
+ $private_title_format = apply_filters( 'private_title_format', __( 'Private: %s', 'autodescription' ), $post );
1250
+ $title = sprintf( $private_title_format, $title );
1251
+ }
1252
+
1253
+ return $title;
1254
+ }
1255
+
1256
+ /**
1257
+ * Adds title pagination, if paginated.
1258
+ *
1259
+ * @since 2.6.0
1260
+ *
1261
+ * @param string $title The current Title.
1262
+ *
1263
+ * @return string Title with maybe pagination added.
1264
+ */
1265
+ public function add_title_pagination( $title ) {
1266
+
1267
+ if ( $this->is_404() || $this->is_admin() )
1268
+ return $title;
1269
+
1270
+ $page = $this->page();
1271
+ $paged = $this->paged();
1272
+
1273
+ if ( $page && $paged ) {
1274
+ /**
1275
+ * @since 2.4.3
1276
+ * Adds page numbering within the title.
1277
+ */
1278
+ if ( $paged >= 2 || $page >= 2 ) {
1279
+ $sep = $this->get_title_separator();
1280
+
1281
+ /* translators: Front-end output. */
1282
+ $title .= " $sep " . sprintf( __( 'Page %s', 'autodescription' ), max( $paged, $page ) );
1283
+ }
1284
+ }
1285
+
1286
+ return $title;
1287
+ }
1288
+
1289
+ /**
1290
+ * Whether to use a title prefix or not.
1291
+ *
1292
+ * @since 2.6.0
1293
+ * @staticvar bool $cache
1294
+ *
1295
+ * @param object $term The Term object.
1296
+ * @param array $args The title arguments.
1297
+ *
1298
+ * @return bool
1299
+ */
1300
+ public function use_archive_prefix( $term = null, $args = array() ) {
1301
+
1302
+ //* Don't add prefix in meta.
1303
+ if ( $args['meta'] )
1304
+ return false;
1305
+
1306
+ static $cache = null;
1307
+
1308
+ if ( isset( $cache ) )
1309
+ return $cache;
1310
+
1311
+ /**
1312
+ * Applies filters the_seo_framework_use_archive_title_prefix : {
1313
+ * @param bool true to add prefix.
1314
+ * @param object $term The Term object.
1315
+ * }
1316
+ *
1317
+ * @since 2.6.0
1318
+ */
1319
+ $filter = (bool) apply_filters( 'the_seo_framework_use_archive_title_prefix', true, $term );
1320
+ $option = ! $this->get_option( 'title_rem_prefixes' );
1321
+
1322
+ return $cache = $option && $filter ? true : false;
1323
+ }
1324
+
1325
+ /**
1326
+ * Filter the title prior to output.
1327
+ *
1328
+ * @since 2.6.0
1329
+ * @access private
1330
+ *
1331
+ * @param string $title The current title.
1332
+ * @param array $args The title args.
1333
+ * @param bool $escape Whether to escape the title.
1334
+ *
1335
+ * @return string $title
1336
+ */
1337
+ public function do_title_pre_filter( $title, $args, $escape = true ) {
1338
+
1339
+ /**
1340
+ * Applies filters 'the_seo_framework_pre_add_title' : string
1341
+ * @since 2.6.0
1342
+ * @param string $title
1343
+ * @param array $args
1344
+ * @param bool $escape
1345
+ */
1346
+ $title = (string) apply_filters( 'the_seo_framework_pre_add_title', $title, $args, $escape );
1347
+
1348
+ if ( $escape )
1349
+ $title = $this->escape_title( $title );
1350
+
1351
+ return $title;
1352
+ }
1353
+
1354
+ /**
1355
+ * Filter the title prior to output.
1356
+ *
1357
+ * @since 2.6.0
1358
+ * @access private
1359
+ *
1360
+ * @param string $title The current title.
1361
+ * @param array $args The title args.
1362
+ * @param bool $escape Whether to escape the title.
1363
+ *
1364
+ * @return string $title
1365
+ */
1366
+ public function do_title_pro_filter( $title, $args, $escape = true ) {
1367
+
1368
+ /**
1369
+ * Applies filters 'the_seo_framework_pro_add_title' : string
1370
+ * @since 2.6.0
1371
+ * @param string $title
1372
+ * @param array $args
1373
+ * @param bool $escape
1374
+ */
1375
+ $title = (string) apply_filters( 'the_seo_framework_pro_add_title', $title, $args, $escape );
1376
+
1377
+ if ( $escape )
1378
+ $title = $this->escape_title( $title );
1379
+
1380
+ return $title;
1381
+ }
1382
+
1383
+ /**
1384
+ * Whether to add home page tagline.
1385
+ *
1386
+ * @since 2.6.0
1387
+ *
1388
+ * @return bool
1389
+ */
1390
+ public function home_page_add_title_tagline() {
1391
+ return $this->is_option_checked( 'homepage_tagline' );
1392
+ }
1393
+
1394
+ }
inc/classes/generate-url.class.php ADDED
@@ -0,0 +1,1343 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The SEO Framework plugin
4
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License version 3 as published
8
+ * by the Free Software Foundation.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ /**
20
+ * Class AutoDescription_Generate_Url
21
+ *
22
+ * Generates URL and permalink SEO data based on content.
23
+ *
24
+ * @since 2.6.0
25
+ */
26
+ class AutoDescription_Generate_Url extends AutoDescription_Generate_Title {
27
+
28
+ /**
29
+ * Whether to slash the url or not. Used when query vars are in url.
30
+ *
31
+ * @since 2.6.0
32
+ *
33
+ * @var bool Whether to slash the url.
34
+ */
35
+ protected $url_slashit;
36
+
37
+ /**
38
+ * Whether to add a subdomain to the url if set.
39
+ *
40
+ * @since 2.6.0
41
+ *
42
+ * @var string The subdomain.
43
+ */
44
+ protected $add_subdomain;
45
+
46
+ /**
47
+ * Holds current HTTP host.
48
+ *
49
+ * @since 2.6.5
50
+ *
51
+ * @var string The current HTTP host.
52
+ */
53
+ protected $current_host;
54
+
55
+ /**
56
+ * Holds home HTTP host.
57
+ *
58
+ * @since 2.6.5
59
+ *
60
+ * @var string The home HTTP host.
61
+ */
62
+ protected $home_host;
63
+
64
+ /**
65
+ * Constructor, load parent constructor and set up variables.
66
+ */
67
+ public function __construct() {
68
+ parent::__construct();
69
+
70
+ $this->home_host = parse_url( get_option( 'home' ), PHP_URL_HOST );
71
+ }
72
+
73
+ /**
74
+ * Creates canonical URL.
75
+ *
76
+ * @param string $url the url
77
+ *
78
+ * @since 2.4.2
79
+ * @param array $args : accepted args : {
80
+ * @param bool $paged Return current page URL without pagination if false
81
+ * @param bool $paged_plural Whether to add pagination for the second or later page.
82
+ * @param bool $from_option Get the canonical uri option
83
+ * @param object $post The Post Object.
84
+ * @param bool $external Whether to fetch the current WP Request or get the permalink by Post Object.
85
+ * @param bool $is_term Fetch url for term.
86
+ * @param object $term The term object.
87
+ * @param bool $home Fetch home URL.
88
+ * @param bool $forceslash Fetch home URL and slash it, always.
89
+ * @param int $id The Page or Term ID.
90
+ * }
91
+ *
92
+ * @since 2.0.0
93
+ *
94
+ * @return string Escape url.
95
+ */
96
+ public function the_url( $url = '', $args = array() ) {
97
+
98
+ if ( $this->the_seo_framework_debug && false === $this->doing_sitemap ) $this->debug_init( __CLASS__, __FUNCTION__, true, $debug_key = microtime(true), get_defined_vars() );
99
+
100
+ $args = $this->reparse_url_args( $args );
101
+
102
+ /**
103
+ * Fetch permalink if Feed.
104
+ * @since 2.5.2
105
+ */
106
+ if ( $this->is_feed() )
107
+ $url = get_permalink();
108
+
109
+ //* Reset cache.
110
+ $this->url_slashit = true;
111
+ $this->add_subdomain = '';
112
+ $this->current_host = '';
113
+
114
+ $path = '';
115
+ $scheme = '';
116
+
117
+ /**
118
+ * Trailing slash the post, or not.
119
+ * @since 2.2.4
120
+ */
121
+ $slashit = true;
122
+
123
+ if ( ! $args['home'] && empty( $url ) ) {
124
+ /**
125
+ * Get url from options
126
+ * @since 2.2.9
127
+ */
128
+ if ( $args['get_custom_field'] && $this->is_singular() ) {
129
+ $custom_url = $this->get_custom_field( '_genesis_canonical_uri' );
130
+
131
+ if ( $custom_url ) {
132
+ $url = $custom_url;
133
+ $this->url_slashit = false;
134
+ $scheme = parse_url( $custom_url, PHP_URL_SCHEME );
135
+ }
136
+ }
137
+
138
+ if ( empty( $url ) )
139
+ $path = $this->generate_url_path( $args );
140
+ }
141
+
142
+ //* Translate the URL, when possible.
143
+ $path = $this->get_translation_path( $path, $args['id'], $args['external'] );
144
+
145
+ //* Domain Mapping canonical URL
146
+ if ( empty( $url ) ) {
147
+ $wpmu_url = $this->the_url_wpmudev_domainmap( $path, true );
148
+ if ( $wpmu_url && is_array( $wpmu_url ) ) {
149
+ $url = $wpmu_url[0];
150
+ $scheme = $wpmu_url[1];
151
+ }
152
+ }
153
+
154
+ //* Domain Mapping canonical URL
155
+ if ( empty( $url ) ) {
156
+ $dm_url = $this->the_url_donncha_domainmap( $path, true );
157
+ if ( $dm_url && is_array( $dm_url ) ) {
158
+ $url = $dm_url[0];
159
+ $scheme = $dm_url[1];
160
+ }
161
+ }
162
+
163
+ //* Non-domainmap URL
164
+ if ( empty( $url ) ) {
165
+ if ( $args['home'] ) {
166
+ $this->current_lang = '';
167
+ $this->add_subdomain = '';
168
+ }
169
+
170
+ $url = $this->add_url_host( $path );
171
+ $scheme = is_ssl() ? 'https' : 'http';
172
+
173
+ $url = $this->add_url_subdomain( $url );
174
+ }
175
+
176
+ //* URL has been given manually or $args['home'] is true.
177
+ if ( ! isset( $scheme ) )
178
+ $scheme = is_ssl() ? 'https' : 'http';
179
+
180
+ $url = $this->set_url_scheme( $url, $scheme );
181
+
182
+ if ( $this->url_slashit ) {
183
+ /**
184
+ * Slash it only if $slashit is true
185
+ * @since 2.2.4
186
+ */
187
+ if ( $slashit && ! $args['forceslash'] )
188
+ $url = user_trailingslashit( $url );
189
+
190
+ //* Be careful with the default permalink structure.
191
+ if ( $args['forceslash'] )
192
+ $url = trailingslashit( $url );
193
+ }
194
+
195
+ if ( $this->pretty_permalinks ) {
196
+ $url = esc_url( $url );
197
+ } else {
198
+ //* Keep the &'s more readable.
199
+ $url = esc_url_raw( $url );
200
+ }
201
+
202
+ if ( $this->the_seo_framework_debug && false === $this->doing_sitemap ) $this->debug_init( __CLASS__, __FUNCTION__, false, $debug_key, array( 'url_output' => $url ) );
203
+
204
+ return $url;
205
+ }
206
+
207
+ /**
208
+ * Parse and sanitize url args.
209
+ *
210
+ * @param array $args required The passed arguments.
211
+ * @param array $defaults The default arguments.
212
+ * @param bool $get_defaults Return the default arguments. Ignoring $args.
213
+ *
214
+ * @applies filters the_seo_framework_url_args : {
215
+ * @param bool $paged Return current page URL without pagination if false
216
+ * @param bool $paged_plural Whether to add pagination for the second or later page.
217
+ * @param bool $from_option Get the canonical uri option
218
+ * @param object $post The Post Object.
219
+ * @param bool $external Whether to fetch the current WP Request or get the permalink by Post Object.
220
+ * @param bool $is_term Fetch url for term.
221
+ * @param object $term The term object.
222
+ * @param bool $home Fetch home URL.
223
+ * @param bool $forceslash Fetch home URL and slash it, always.
224
+ * @param int $id The Page or Term ID.
225
+ * }
226
+ *
227
+ * @since 2.4.2
228
+ * @return array $args parsed args.
229
+ */
230
+ public function parse_url_args( $args = array(), $defaults = array(), $get_defaults = false ) {
231
+
232
+ //* Passing back the defaults reduces the memory usage.
233
+ if ( empty( $defaults ) ) {
234
+ $defaults = array(
235
+ 'paged' => false,
236
+ 'paged_plural' => true,
237
+ 'get_custom_field' => true,
238
+ 'external' => false,
239
+ 'is_term' => false,
240
+ 'post' => null,
241
+ 'term' => null,
242
+ 'home' => false,
243
+ 'forceslash' => false,
244
+ 'id' => $this->get_the_real_ID()
245
+ );
246
+
247
+ //* @since 2.5.0
248
+ $defaults = (array) apply_filters( 'the_seo_framework_url_args', $defaults, $args );
249
+ }
250
+
251
+ //* Return early if it's only a default args request.
252
+ if ( $get_defaults )
253
+ return $defaults;
254
+
255
+ //* Array merge doesn't support sanitation. We're simply type casting here.
256
+ $args['paged'] = isset( $args['paged'] ) ? (bool) $args['paged'] : $defaults['paged'];
257
+ $args['paged_plural'] = isset( $args['paged_plural'] ) ? (bool) $args['paged_plural'] : $defaults['paged_plural'];
258
+ $args['get_custom_field'] = isset( $args['get_custom_field'] ) ? (bool) $args['get_custom_field'] : $defaults['get_custom_field'];
259
+ $args['external'] = isset( $args['external'] ) ? (bool) $args['external'] : $defaults['external'];
260
+ $args['is_term'] = isset( $args['is_term'] ) ? (bool) $args['is_term'] : $defaults['is_term'];
261
+ $args['get_custom_field'] = isset( $args['get_custom_field'] ) ? (bool) $args['get_custom_field'] : $defaults['get_custom_field'];
262
+ $args['post'] = isset( $args['post'] ) ? (object) $args['post'] : $defaults['post'];
263
+ $args['term'] = isset( $args['term'] ) ? (object) $args['term'] : $defaults['term'];
264
+ $args['home'] = isset( $args['home'] ) ? (bool) $args['home'] : $defaults['home'];
265
+ $args['forceslash'] = isset( $args['forceslash'] ) ? (bool) $args['forceslash'] : $defaults['forceslash'];
266
+ $args['id'] = isset( $args['id'] ) ? (int) $args['id'] : $defaults['id'];
267
+
268
+ return $args;
269
+ }
270
+
271
+ /**
272
+ * Reparse URL args.
273
+ *
274
+ * @param array $args required The passed arguments.
275
+ *
276
+ * @since 2.6.2
277
+ * @return array $args parsed args.
278
+ */
279
+ public function reparse_url_args( $args = array() ) {
280
+
281
+ $default_args = $this->parse_url_args( '', '', true );
282
+
283
+ if ( is_array( $args ) ) {
284
+ if ( empty( $args ) ) {
285
+ $args = $default_args;
286
+ } else {
287
+ $args = $this->parse_url_args( $args, $default_args );
288
+ }
289
+ } else {
290
+ //* Old style parameters are used. Doing it wrong.
291
+ $this->_doing_it_wrong( __CLASS__ . '::' . __FUNCTION__, 'Use $args = array() for parameters.', '2.4.2' );
292
+ $args = $default_args;
293
+ }
294
+
295
+ return $args;
296
+ }
297
+
298
+ /**
299
+ * Generate URL from arguments.
300
+ *
301
+ * @since 2.6.0
302
+ *
303
+ * @global object $wp
304
+ *
305
+ * @param array $args the URL args.
306
+ * @return string $path
307
+ */
308
+ public function generate_url_path( $args = array() ) {
309
+
310
+ $args = $this->reparse_url_args( $args );
311
+
312
+ if ( $args['is_term'] || $this->is_archive() ) {
313
+ $term = $args['term'];
314
+
315
+ //* Term or Taxonomy.
316
+ if ( ! isset( $term ) )
317
+ $term = get_queried_object();
318
+
319
+ if ( isset( $term->taxonomy ) ) {
320
+ //* Registered Terms and Taxonomies.
321
+ $path = $this->get_relative_term_url( $term, $args );
322
+ } else if ( ! $args['external'] ) {
323
+ //* Everything else.
324
+ global $wp;
325
+ $path = trailingslashit( get_option( 'home' ) ) . $wp->request;
326
+ $path = $this->set_url_scheme( $path, 'relative' );
327
+ } else {
328
+ //* Nothing to see here...
329
+ $path = '';
330
+ }
331
+
332
+ } else {
333
+
334
+ /**
335
+ * Reworked to use the $args['id'] check based on get_the_real_ID.
336
+ * @since 2.6.0 & 2.6.2
337
+ */
338
+ $post_id = isset( $args['post']->ID ) ? $args['post']->ID : $args['id'];
339
+
340
+ if ( $this->pretty_permalinks && $post_id && $this->is_singular() ) {
341
+ $post = get_post( $post_id );
342
+
343
+ //* Don't slash draft links.
344
+ if ( isset( $post->post_status ) && ( 'auto-draft' === $post->post_status || 'draft' === $post->post_status ) )
345
+ $this->url_slashit = false;
346
+ }
347
+
348
+ $path = $this->build_singular_relative_url( $post_id, $args );
349
+ }
350
+
351
+ if ( isset( $path ) )
352
+ return $path;
353
+
354
+ return '';
355
+ }
356
+
357
+ /**
358
+ * Generates relative URL for the Homepage and Singular Posts.
359
+ *
360
+ * @since 2.6.5
361
+ *
362
+ * @global object $wp
363
+ *
364
+ * @param int $post_id The ID.
365
+ * @param array $args The URL arguments.
366
+ * @return relative Post or Page url.
367
+ */
368
+ public function build_singular_relative_url( $post_id = null, $args = array() ) {
369
+
370
+ if ( ! isset( $post_id ) ) {
371
+ if ( ! $args['external'] )
372
+ $post_id = $this->get_the_real_ID();
373
+ else
374
+ return '';
375
+ }
376
+
377
+ $args = $this->reparse_url_args( $args );
378
+
379
+ if ( $args['external'] || ! $this->is_front_page() ) {
380
+ $url = get_permalink( $post_id );
381
+ } else if ( $this->is_front_page() ) {
382
+ $url = get_home_url();
383
+ } else if ( ! $args['external'] ) {
384
+ global $wp;
385
+
386
+ if ( isset( $wp->request ) )
387
+ $url = trailingslashit( get_option( 'home' ) ) . $wp->request;
388
+
389
+ }
390
+
391
+ //* No permalink found.
392
+ if ( ! isset( $url ) )
393
+ return '';
394
+
395
+ if ( $this->is_singular() )
396
+ $paged = $this->maybe_get_paged( $this->page(), $args['paged'], $args['paged_plural'] );
397
+ else
398
+ $paged = $this->maybe_get_paged( $this->paged(), $args['paged'], $args['paged_plural'] );
399
+
400
+ if ( $paged ) {
401
+ if ( $this->pretty_permalinks ) {
402
+ if ( $this->is_singular() )
403
+ $url = trailingslashit( $url ) . $paged;
404
+ else
405
+ $url = trailingslashit( $url ) . 'page/' . $paged;
406
+ } else {
407
+ if ( $this->is_singular() )
408
+ $url = add_query_arg( 'page', $paged, $url );
409
+ else
410
+ $url = add_query_arg( 'paged', $paged, $url );
411
+ }
412
+ }
413
+
414
+ $path = $this->set_url_scheme( $url, 'relative' );
415
+
416
+ return $path;
417
+ }
418
+
419
+ /**
420
+ * Create full valid URL with parsed host.
421
+ * Don't forget to use set_url_scheme() afterwards.
422
+ *
423
+ * @since 2.6.5
424
+ *
425
+ * @param string $path Current path.
426
+ * @return string Full valid URL with http host.
427
+ */
428
+ public function add_url_host( $path = '' ) {
429
+
430
+ $host = $this->current_host ? $this->current_host : $this->home_host;
431
+
432
+ return $url = 'http://' . trailingslashit( $host ) . ltrim( $path, ' \\/' );
433
+ }
434
+
435
+ /**
436
+ * Generates relative URL for current post_ID for translation plugins.
437
+ *
438
+ * @param string $path the current URL path.
439
+ * @param int $post_id The post ID.
440
+ * @param bool $external Whether to fetch the WP Request or get the permalink by Post Object.
441
+ *
442
+ * @since 2.6.0
443
+ *
444
+ * @global object $post
445
+ *
446
+ * @return relative Post or Page url.
447
+ */
448
+ public function get_translation_path( $path = '', $post_id = null, $external = false ) {
449
+
450
+ if ( is_object( $post_id ) )
451
+ $post_id = isset( $post_id->ID ) ? $post_id->ID : $this->get_the_real_ID();
452
+
453
+ if ( ! isset( $post_id ) )
454
+ $post_id = $this->get_the_real_ID();
455
+
456
+ //* WPML support.
457
+ if ( $this->is_wpml_active() )
458
+ $path = $this->get_relative_wmpl_url( $path, $post_id );
459
+
460
+ //* qTranslate X support. Can't work externally as we can't fetch the post's current language.
461
+ if ( ! $external && $this->is_qtranslate_active() )
462
+ $path = $this->get_relative_qtranslate_url( $path, $post_id );
463
+
464
+ return $path;
465
+ }
466
+
467
+ /**
468
+ * Generates qtranslate URL.
469
+ *
470
+ * @param string $path The current path.
471
+ * @param int $post_id The Post ID. Unused.
472
+ *
473
+ * @global array $q_config
474
+ *
475
+ * @staticvar int $q_config_mode
476
+ *
477
+ * @since 2.6.0
478
+ */
479
+ public function get_relative_qtranslate_url( $path = '', $post_id = '' ) {
480
+
481
+ //* Reset cache.
482
+ $this->url_slashit = true;
483
+ $this->add_subdomain = '';
484
+
485
+ static $q_config_mode = null;
486
+
487
+ if ( ! isset( $q_config ) ) {
488
+ global $q_config;
489
+ $q_config_mode = $q_config['url_mode'];
490
+ }
491
+
492
+ //* If false, change canonical URL for every page.
493
+ $hide = isset( $q_config['hide_default_language'] ) ? $q_config['hide_default_language'] : true;
494
+
495
+ $current_lang = isset( $q_config['language'] ) ? $q_config['language'] : false;
496
+ $default_lang = isset( $q_config['default_language'] ) ? $q_config['default_language'] : false;
497
+
498
+ //* Don't to anything on default language when path is hidden.
499
+ if ( $hide && $current_lang === $default_lang )
500
+ return $path;
501
+
502
+ switch ( $q_config_mode ) {
503
+
504
+ case '1' :
505
+ //* Negotiation type query var.
506
+
507
+ //* Don't slash it further.
508
+ $this->url_slashit = false;
509
+
510
+ /**
511
+ * Path must have trailing slash for pagination permalinks to work.
512
+ * So we remove the query string and add it back with slash.
513
+ */
514
+ if ( strpos( $path, '?lang=' . $current_lang ) !== false )
515
+ $path = str_replace( '?lang=' . $current_lang, '', $path );
516
+
517
+ return user_trailingslashit( $path ) . '?lang=' . $current_lang;
518
+ break;
519
+
520
+ case '2' :
521
+ //* Subdirectory
522
+ if ( 0 === strpos( $path, '/' . $current_lang . '/' ) )
523
+ return $path;
524
+ else
525
+ return $path = trailingslashit( $current_lang ) . ltrim( $path, ' \\/' );
526
+ break;
527
+
528
+ case '3' :
529
+ //* Notify cache of subdomain addition.
530
+ $this->add_subdomain = $current_lang;
531
+
532
+ //* No need to alter the path.
533
+ return $path;
534
+ break;
535
+
536
+ }
537
+
538
+ return $path;
539
+ }
540
+
541
+ /**
542
+ * Generate relative WPML url.
543
+ *
544
+ * @since 2.4.3
545
+ * @staticvar bool $gli_exists
546
+ * @staticvar string $default_lang
547
+ *
548
+ * @global object $sitepress
549
+ *
550
+ * @param string $path The current path.
551
+ * @param int $post_id The Post ID.
552
+ * @return relative path for WPML urls.
553
+ */
554
+ public function get_relative_wmpl_url( $path = '', $post_id = '' ) {
555
+ global $sitepress;
556
+
557
+ //* Reset cache.
558
+ $this->url_slashit = true;
559
+ $this->add_subdomain = '';
560
+
561
+ if ( ! isset( $sitepress ) )
562
+ return $path;
563
+
564
+ static $gli_exists = null;
565
+ if ( is_null( $gli_exists ) )
566
+ $gli_exists = function_exists( 'wpml_get_language_information' );
567
+
568
+ if ( ! $gli_exists )
569
+ return $path;
570
+
571
+ if ( empty( $post_id ) )
572
+ $post_id = $this->get_the_real_ID();
573
+
574
+ //* Cache default language.
575
+ static $default_lang = null;
576
+ if ( is_null( $default_lang ) )
577
+ $default_lang = $sitepress->get_default_language();
578
+
579
+ /**
580
+ * Applies filters wpml_post_language_details : array|wp_error
581
+ *
582
+ * ... Somehow WPML thought this would be great and understandable.
583
+ * This should be put inside a callable function.
584
+ * @since 2.6.0
585
+ */
586
+ $lang_info = apply_filters( 'wpml_post_language_details', NULL, $post_id );
587
+
588
+ if ( is_wp_error( $lang_info ) ) {
589
+ //* Terms and Taxonomies.
590
+ $lang_info = array();
591
+
592
+ //* Cache the code.
593
+ static $lang_code = null;
594
+ if ( is_null( $lang_code ) && defined( 'ICL_LANGUAGE_CODE' ) )
595
+ $lang_code = ICL_LANGUAGE_CODE;
596
+
597
+ $lang_info['language_code'] = $lang_code;
598
+ }
599
+
600
+ //* If filter isn't used, bail.
601
+ if ( ! isset( $lang_info['language_code'] ) )
602
+ return $path;
603
+
604
+ $current_lang = $lang_info['language_code'];
605
+
606
+ //* No need to alter URL if we're on default lang.
607
+ if ( $current_lang === $default_lang )
608
+ return $path;
609
+
610
+ //* Cache negotiation type.
611
+ static $negotiation_type = null;
612
+ if ( ! isset( $negotiation_type ) )
613
+ $negotiation_type = $sitepress->get_setting( 'language_negotiation_type' );
614
+
615
+ switch ( $negotiation_type ) {
616
+
617
+ case '1' :
618
+ //* Subdirectory
619
+
620
+ /**
621
+ * Might not always work.
622
+ * @TODO Fix.
623
+ * @priority OMG WTF BBQ
624
+ */
625
+ $contains_path = strpos( $path, '/' . $current_lang . '/' );
626
+ if ( false !== $contains_path && 0 === $contains_path )
627
+ return $path;
628
+ else
629
+ return $path = trailingslashit( $current_lang ) . ltrim( $path, ' \\/' );
630
+ break;
631
+
632
+ case '2' :
633
+ //* Custom domain.
634
+
635
+ $langsettings = $sitepress->get_setting( 'language_domains' );
636
+ $current_lang_setting = isset( $langsettings[ $current_lang ] ) ? $langsettings[ $current_lang ] : '';
637
+
638
+ if ( empty( $current_lang_setting ) )
639
+ return $path;
640
+
641
+ $current_lang_setting = $this->make_fully_qualified_url( $current_lang_setting );
642
+ $parsed = parse_url( $current_lang_setting );
643
+
644
+ $this->current_host = isset( $parsed['host'] ) ? $parsed['host'] : '';
645
+ $current_path = isset( $parsed['path'] ) ? trailingslashit( $parsed['path'] ) : '';
646
+
647
+ return $current_path . $path;
648
+ break;
649
+
650
+ case '3' :
651
+ //* Negotiation type query var.
652
+
653
+ //* Don't slash it further.
654
+ $this->url_slashit = false;
655
+
656
+ /**
657
+ * Path must have trailing slash for pagination permalinks to work.
658
+ * So we remove the query string and add it back with slash.
659
+ */
660
+ if ( false !== strpos( $path, '?lang=' . $current_lang ) )
661
+ $path = str_replace( '?lang=' . $current_lang, '', $path );
662
+
663
+ return user_trailingslashit( $path ) . '?lang=' . $current_lang;
664
+ break;
665
+
666
+ }
667
+
668
+ return $path;
669
+ }
670
+
671
+ /**
672
+ * Generates relative URL for current term.
673
+ *
674
+ * @global WP_Rewrite object $wp_rewrite
675
+ *
676
+ * @param object $term The term object.
677
+ * @param array|bool $args {
678
+ * 'external' : Whether to fetch the WP Request or get the permalink by Post Object.
679
+ * 'paged' : Whether to add pagination for all types.
680
+ * 'paged_plural' : Whether to add pagination for the second or later page.
681
+ * }
682
+ *
683
+ * @since 2.4.2
684
+ *
685
+ * @return Relative term or taxonomy URL.
686
+ */
687
+ public function get_relative_term_url( $term = null, $args = array() ) {
688
+ global $wp_rewrite;
689
+
690
+ if ( ! is_array( $args ) ) {
691
+ /**
692
+ * @since 2.6.0
693
+ * '$args = array()' replaced '$no_request = false'.
694
+ */
695
+ $this->_doing_it_wrong( __CLASS__ . '::' . __FUNCTION__, 'Use $args = array() for parameters.', '2.6.0' );
696
+
697
+ $no_request = (bool) $args;
698
+ $args = $this->parse_url_args( '', '', true );
699
+ $args['external'] = $no_request;
700
+ }
701
+
702
+ // We can't fetch the Term object within sitemaps.
703
+ if ( $args['external'] && is_null( $term ) )
704
+ return '';
705
+
706
+ if ( is_null( $term ) )
707
+ $term = get_queried_object();
708
+
709
+ $taxonomy = $term->taxonomy;
710
+ $path = $wp_rewrite->get_extra_permastruct( $taxonomy );
711
+
712
+ $slug = $term->slug;
713
+ $t = get_taxonomy( $taxonomy );
714
+
715
+ $paged = $this->maybe_get_paged( $this->paged(), $args['paged'], $args['paged_plural'] );
716
+
717
+ if ( empty( $path ) ) {
718
+ //* Default permalink structure.
719
+
720
+ if ( 'category' === $taxonomy ) {
721
+ $path = '?cat=' . $term->term_id;
722
+ } else if ( isset( $t->query_var ) && '' !== $t->query_var ) {
723
+ $path = '?' . $t->query_var . '=' . $slug;
724
+ } else {
725
+ $path = '?taxonomy=' . $taxonomy . '&term=' . $slug;
726
+ }
727
+
728
+ if ( $paged )
729
+ $path .= '&paged=' . $paged;
730
+
731
+ //* Don't slash it.
732
+ $this->url_slashit = false;
733
+
734
+ } else {
735
+ if ( $t->rewrite['hierarchical'] ) {
736
+ $hierarchical_slugs = array();
737
+ $ancestors = get_ancestors( $term->term_id, $taxonomy, 'taxonomy' );
738
+
739
+ foreach ( (array) $ancestors as $ancestor ) {
740
+ $ancestor_term = get_term( $ancestor, $taxonomy );
741
+ $hierarchical_slugs[] = $ancestor_term->slug;
742
+ }
743
+
744
+ $hierarchical_slugs = array_reverse( $hierarchical_slugs );
745
+ $hierarchical_slugs[] = $slug;
746
+
747
+ $path = str_replace( "%$taxonomy%", implode( '/', $hierarchical_slugs ), $path );
748
+ } else {
749
+ $path = str_replace( "%$taxonomy%", $slug, $path );
750
+ }
751
+
752
+ if ( $paged )
753
+ $path = trailingslashit( $path ) . 'page/' . $paged;
754
+
755
+ $path = user_trailingslashit( $path, 'category' );
756
+ }
757
+
758
+ //* Leading Slash it..
759
+ $path = '/' . ltrim( $path, ' \\/' );
760
+
761
+ return $path;
762
+ }
763
+
764
+ /**
765
+ * Set url scheme.
766
+ * WordPress core function, without filter.
767
+ *
768
+ * @param string $url Absolute url that includes a scheme.
769
+ * @param string $scheme optional. Scheme to give $url. Currently 'http', 'https', 'login', 'login_post', 'admin', or 'relative'.
770
+ * @param bool $use_filter Whether to parse filters.
771
+ *
772
+ * @since 2.4.2
773
+ * @return string url with chosen scheme.
774
+ */
775
+ public function set_url_scheme( $url, $scheme = null, $use_filter = true ) {
776
+
777
+ /**
778
+ * Core should uphold the coding standards (Yoda code). Open issue @link Github.com?
779
+ * @todo yoda-fy
780
+ */
781
+ if ( ! isset( $scheme ) ) {
782
+ $scheme = is_ssl() ? 'https' : 'http';
783
+ } else if ( $scheme === 'admin' || $scheme === 'login' || $scheme === 'login_post' || $scheme === 'rpc' ) {
784
+ $scheme = is_ssl() || force_ssl_admin() ? 'https' : 'http';
785
+ } else if ( $scheme !== 'http' && $scheme !== 'https' && $scheme !== 'relative' ) {
786
+ $scheme = is_ssl() ? 'https' : 'http';
787
+ }
788
+
789
+ $url = $this->make_fully_qualified_url( $url );
790
+
791
+ if ( 'relative' === $scheme ) {
792
+ $url = ltrim( preg_replace( '#^\w+://[^/]*#', '', $url ) );
793
+ if ( $url !== '' && $url[0] === '/' )
794
+ $url = '/' . ltrim( $url , "/ \t\n\r\0\x0B" );
795
+
796
+ } else {
797
+ //* This will break if $scheme is set to false.
798
+ $url = preg_replace( '#^\w+://#', $scheme . '://', $url );
799
+ }
800
+
801
+ if ( $use_filter )
802
+ return $this->set_url_scheme_filter( $url, $scheme );
803
+
804
+ return $url;
805
+ }
806
+
807
+ /**
808
+ * Set URL scheme based on filter.
809
+ *
810
+ * @since 2.6.0
811
+ *
812
+ * @param string $url The url with scheme.
813
+ * @param string $scheme The current scheme.
814
+ *
815
+ * @return $url with applied filters.
816
+ */
817
+ public function set_url_scheme_filter( $url, $current_scheme ) {
818
+
819
+ /**
820
+ * Applies filters the_seo_framework_canonical_force_scheme : Changes scheme.
821
+ *
822
+ * Accepted variables:
823
+ * (string) 'https' : Force https
824
+ * (bool) true : Force https
825
+ * (bool) false : Force http
826
+ * (string) 'http' : Force http
827
+ * (string) 'relative' : Scheme relative
828
+ * (void) null : Do nothing
829
+ *
830
+ * @param string $current_scheme the current used scheme.
831
+ *
832
+ * @since 2.4.2
833
+ */
834
+ $scheme_settings = apply_filters( 'the_seo_framework_canonical_force_scheme', null, $current_scheme );
835
+
836
+ /**
837
+ * @TODO add options metabox.
838
+ * @priority medium 2.6.5+
839
+ */
840
+
841
+ if ( isset( $scheme_settings ) ) {
842
+ if ( 'https' === $scheme_settings || 'http' === $scheme_settings || 'relative' === $scheme_settings ) {
843
+ $url = $this->set_url_scheme( $url, $scheme_settings, false );
844
+ } else if ( ! $scheme_settings ) {
845
+ $url = $this->set_url_scheme( $url, 'http', false );
846
+ } else if ( $scheme_setting ) {
847
+ $url = $this->set_url_scheme( $url, 'https', false );
848
+ }
849
+ }
850
+
851
+ return $url;
852
+ }
853
+
854
+ /**
855
+ * Try to get an canonical URL when WPMUdev Domain Mapping is active.
856
+ *
857
+ * @since 2.3.0
858
+ *
859
+ * @param string $path The post relative path.
860
+ *
861
+ * @param bool $get_scheme Output array with scheme.
862
+ * @since 2.4.0
863
+ *
864
+ * @return string|array|void The unescaped URL, the scheme
865
+ */
866
+ public function the_url_wpmudev_domainmap( $path, $get_scheme = false ) {
867
+
868
+ if ( false === $this->is_domainmapping_active() )
869
+ return '';
870
+
871
+ global $wpdb, $blog_id;
872
+
873
+ /**
874
+ * Cache revisions. Hexadecimal.
875
+ * @since 2.6.0
876
+ */
877
+ $revision = '1';
878
+
879
+ $cache_key = 'wpmudev_mapped_domain_' . $revision . '_' . $blog_id;
880
+
881
+ //* Check if the domain is mapped
882
+ $mapped_domain = $this->object_cache_get( $cache_key );
883
+ if ( false === $mapped_domain ) {
884
+
885
+ $mapped_domains = $wpdb->get_results( $wpdb->prepare( "SELECT id, domain, is_primary, scheme FROM {$wpdb->base_prefix}domain_mapping WHERE blog_id = %d", $blog_id ), OBJECT );
886
+
887
+ $primary_key = 0;
888
+ $domain_ids = array();
889
+ foreach ( $mapped_domains as $key => $domain ) {
890
+ if ( isset( $domain->is_primary ) && '1' === $domain->is_primary ) {
891
+ $primary_key = $key;
892
+
893
+ //* We've found the primary key, break loop.
894
+ break;
895
+ } else {
896
+ //* Save IDs.
897
+ if ( isset( $domain->id ) && $domain->id )
898
+ $domain_ids[$key] = $domain->id;
899
+ }
900
+ }
901
+
902
+ if ( 0 === $primary_key && ! empty( $domain_ids ) ) {
903
+ //* No primary ID has been found. Get the one with the lowest ID, which has been added first.
904
+ $primary_key = array_keys( $domain_ids, min( $domain_ids ), true );
905
+ $primary_key = reset( $primary_key );
906
+ }
907
+
908
+ //* Set 0, as we check for false to begin with.
909
+ $mapped_domain = isset( $mapped_domains[$primary_key] ) ? $mapped_domains[$primary_key] : 0;
910
+
911
+ $this->object_cache_set( $cache_key, $mapped_domain, 3600 );
912
+ }
913
+
914
+ if ( $mapped_domain ) {
915
+
916
+ $domain = isset( $mapped_domain->domain ) ? $mapped_domain->domain : '0';
917
+ $scheme = isset( $mapped_domain->scheme ) ? $mapped_domain->scheme : '';
918
+
919
+ //* Fallback to is_ssl if no scheme has been found.
920
+ if ( '' === $scheme )
921
+ $scheme = is_ssl() ? '1' : '0';
922
+
923
+ if ( '1' === $scheme ) {
924
+ $scheme_full = 'https://';
925
+ $scheme = 'https';
926
+ } else {
927
+ $scheme_full = 'http://';
928
+ $scheme = 'http';
929
+ }
930
+
931
+ //* Put it all together.
932
+ $url = trailingslashit( $scheme_full . $domain ) . ltrim( $path, ' \\/' );
933
+
934
+ if ( ! $get_scheme ) {
935
+ return $url;
936
+ } else {
937
+ return array( $url, $scheme );
938
+ }
939
+ }
940
+
941
+ return '';
942
+ }
943
+
944
+ /**
945
+ * Try to get an canonical URL when Donncha Domain Mapping is active.
946
+ *
947
+ * @param string $path The post relative path.
948
+ * @param bool $get_scheme Output array with scheme.
949
+ *
950
+ * @since 2.4.0
951
+ *
952
+ * @return string|array|void The unescaped URL, the scheme
953
+ */
954
+ public function the_url_donncha_domainmap( $path, $get_scheme = false ) {
955
+
956
+ if ( false === $this->is_donncha_domainmapping_active() )
957
+ return '';
958
+
959
+ global $current_blog;
960
+
961
+ $scheme = is_ssl() ? 'https' : 'http';
962
+ $url = function_exists( 'domain_mapping_siteurl' ) ? domain_mapping_siteurl( false ) : false;
963
+
964
+ $request_uri = '';
965
+
966
+ if ( $url && $url !== untrailingslashit( $scheme . '://' . $current_blog->domain . $current_blog->path ) ) {
967
+ if ( ( defined( 'VHOST' ) && 'yes' !== VHOST ) || ( defined( 'SUBDOMAIN_INSTALL' ) && false === SUBDOMAIN_INSTALL ) ) {
968
+ $request_uri = str_replace( $current_blog->path, '/', $_SERVER['REQUEST_URI'] );
969
+ }
970
+
971
+ $url = trailingslashit( $url . $request_uri ) . ltrim( $path, '\\/ ' );
972
+
973
+ if ( $get_scheme ) {
974
+ return array( $url, $scheme );
975
+ } else {
976
+ return $url;
977
+ }
978
+ }
979
+
980
+ return '';
981
+ }
982
+
983
+ /**
984
+ * Generates shortlink URL.
985
+ *
986
+ * @since 2.2.2
987
+ * @global object $wp_query
988
+ *
989
+ * @param int $post_id The post ID
990
+ *
991
+ * @return string|null Escaped site Shortlink URL
992
+ */
993
+ public function get_shortlink( $post_id = 0 ) {
994
+
995
+ if ( $this->get_option( 'shortlink_tag' ) ) {
996
+
997
+ $path = null;
998
+
999
+ if ( false === $this->is_front_page() ) {
1000
+ if ( $this->is_singular( $post_id ) ) {
1001
+
1002
+ if ( 0 === $post_id )
1003
+ $post_id = $this->get_the_real_ID();
1004
+
1005
+ if ( $post_id ) {
1006
+ if ( $this->is_static_frontpage( $post_id ) ) {
1007
+ $path = '';
1008
+ } else {
1009
+ //* This will be converted to '?p' later.
1010
+ $path = '?page_id=' . $post_id;
1011
+ }
1012
+ }
1013
+ } else if ( $this->is_archive() ) {
1014
+
1015
+ if ( $this->is_category() ) {
1016
+ $id = get_queried_object_id();
1017
+ $path = '?cat=' . $id;
1018
+ } else if ( $this->is_tag() ) {
1019
+ $id = get_queried_object_id();
1020
+ $path = '?post_tag=' . $id;
1021
+ } else if ( $this->is_date() ) {
1022
+ // This isn't exactly "short" for a shortlink...
1023
+ global $wp_query;
1024
+
1025
+ $query = $wp_query->query;
1026
+ $var = '';
1027
+
1028
+ $first = true;
1029
+ foreach ( $query as $key => $val ) {
1030
+ $var .= $first ? '?' : '&';
1031
+ $var .= $key . '=' . $val;
1032
+ $first = false;
1033
+ }
1034
+
1035
+ $path = $var;
1036
+ } else if ( $this->is_author() ) {
1037
+ $id = get_queried_object_id();
1038
+ $path = '?author=' . $id;
1039
+ } else if ( $this->is_tax() ) {
1040
+ //* Generate shortlink for object type and slug.
1041
+ $object = get_queried_object();
1042
+
1043
+ $t = isset( $object->taxonomy ) ? urlencode( $object->taxonomy ) : '';
1044
+
1045
+ if ( $t ) {
1046
+ $slug = isset( $object->slug ) ? urlencode( $object->slug ) : '';
1047
+
1048
+ if ( $slug )
1049
+ $path = '?' . $t . '=' . $slug;
1050
+ }
1051
+ }
1052
+
1053
+ }
1054
+ }
1055
+
1056
+ if ( isset( $path ) ) {
1057
+ //* Path always has something. So we can safely use .='&' instead of add_query_arg().
1058
+
1059
+ if ( 0 === $post_id )
1060
+ $post_id = $this->get_the_real_ID();
1061
+
1062
+ $url = $this->the_url_from_cache( '', $post_id, false, false, false );
1063
+ $query = parse_url( $url, PHP_URL_QUERY );
1064
+
1065
+ $additions = '';
1066
+ if ( isset( $query ) ) {
1067
+ if ( false !== strpos( $query, '&' ) )
1068
+ $query = explode( '&', $query );
1069
+ else
1070
+ $query = array( $query );
1071
+
1072
+ foreach ( $query as $arg ) {
1073
+ if ( false === strpos( $path, $arg ) )
1074
+ $additions .= '&' . $arg;
1075
+ }
1076
+ }
1077
+
1078
+ //* We used 'page_id' to determine duplicates. Now we can convert it to a shorter form.
1079
+ $path = str_replace( 'page_id=', 'p=', $path );
1080
+
1081
+ if ( $this->is_archive() || $this->is_home() ) {
1082
+ $paged = $this->maybe_get_paged( $this->paged(), false, true );
1083
+ if ( $paged )
1084
+ $path .= '&paged=' . $paged;
1085
+ } else {
1086
+ $page = $this->maybe_get_paged( $this->page(), false, true );
1087
+ if ( $page )
1088
+ $path .= '&page=' . $page;
1089
+ }
1090
+
1091
+ $home_url = $this->the_home_url_from_cache( true );
1092
+ $url = $home_url . $path . $additions;
1093
+
1094
+ return esc_url_raw( $url );
1095
+ }
1096
+ }
1097
+
1098
+ return '';
1099
+ }
1100
+
1101
+ /**
1102
+ * Generates Previous and Next links
1103
+ *
1104
+ * @since 2.2.4
1105
+ *
1106
+ * @param string $prev_next Previous or next page link
1107
+ * @param int $post_id The post ID
1108
+ *
1109
+ * @return string|null Escaped site Pagination URL
1110
+ */
1111
+ public function get_paged_url( $prev_next = 'next', $post_id = 0 ) {
1112
+
1113
+ if ( ! $this->get_option( 'prev_next_posts' ) && ! $this->get_option( 'prev_next_archives' ) && ! $this->get_option( 'prev_next_frontpage' ) )
1114
+ return '';
1115
+
1116
+ $prev = '';
1117
+ $next = '';
1118
+
1119
+ if ( $this->is_singular() ) {
1120
+
1121
+ $output_singular_paged = $this->is_front_page() ? $this->is_option_checked( 'prev_next_frontpage' ) : $this->is_option_checked( 'prev_next_posts' );
1122
+
1123
+ if ( $output_singular_paged ) {
1124
+
1125
+ $page = $this->page();
1126
+ $numpages = substr_count( $this->get_post_content( $post_id ), '<!--nextpage-->' ) + 1;
1127
+
1128
+ if ( ! $page )
1129
+ $page = 1;
1130
+
1131
+ if ( 'prev' === $prev_next ) {
1132
+ $prev = $page > 1 ? $this->get_paged_post_url( $page - 1, $post_id, 'prev' ) : '';
1133
+ } else if ( 'next' === $prev_next ) {
1134
+ $next = $page < $numpages ? $this->get_paged_post_url( $page + 1, $post_id, 'next' ) : '';
1135
+ }
1136
+
1137
+ }
1138
+ } else if ( $this->is_archive() || $this->is_home() ) {
1139
+
1140
+ $output_archive_paged = false;
1141
+ if ( $this->is_front_page() ) {
1142
+ //* Only home.
1143
+ $output_archive_paged = $this->is_option_checked( 'prev_next_frontpage' );
1144
+ } else {
1145
+ //* Both home and archives.
1146
+ $output_archive_paged = $this->is_option_checked( 'prev_next_archives' );
1147
+ }
1148
+
1149
+ if ( $output_archive_paged ) {
1150
+ $paged = $this->paged();
1151
+
1152
+ if ( 'prev' === $prev_next && $paged > 1 ) {
1153
+ $paged = intval( $paged ) - 1;
1154
+
1155
+ if ( $paged < 1 )
1156
+ $paged = 1;
1157
+
1158
+ $prev = get_pagenum_link( $paged, false );
1159
+ } else if ( 'next' === $prev_next && $paged < $GLOBALS["wp_query"]->max_num_pages ) {
1160
+
1161
+ if ( ! $paged )
1162
+ $paged = 1;
1163
+ $paged = intval( $paged ) + 1;
1164
+
1165
+ $next = get_pagenum_link( $paged, false );
1166
+ }
1167
+ }
1168
+ }
1169
+
1170
+ if ( $prev )
1171
+ return esc_url_raw( $prev );
1172
+
1173
+ if ( $next )
1174
+ return esc_url_raw( $next );
1175
+
1176
+ return '';
1177
+ }
1178
+
1179
+ /**
1180
+ * Return the special URL of a paged post.
1181
+ *
1182
+ * Taken from _wp_link_page() in WordPress core, but instead of anchor markup, just return the URL.
1183
+ * Also adds WPMUdev Domain Mapping support and is optimized for speed.
1184
+ *
1185
+ * @uses $this->the_url_from_cache();
1186
+ * @since 2.2.4
1187
+ *
1188
+ * @param int $i The page number to generate the URL from.
1189
+ * @param int $post_id The post ID
1190
+ * @param string $pos Which url to get, accepts next|prev
1191
+ *
1192
+ * @return string Unescaped URL
1193
+ */
1194
+ public function get_paged_post_url( $i, $post_id = 0, $pos = 'prev' ) {
1195
+
1196
+ $from_option = false;
1197
+
1198
+ if ( empty( $post_id ) )
1199
+ $post_id = $this->get_the_real_ID();
1200
+
1201
+ if ( $i === 1 ) {
1202
+ $url = $this->the_url_from_cache( '', $post_id, false, $from_option, false );
1203
+ } else {
1204
+ $post = get_post( $post_id );
1205
+
1206
+ $urlfromcache = $this->the_url_from_cache( '', $post_id, false, $from_option, false );
1207
+
1208
+ if ( $i >= 2 ) {
1209
+ //* Fix adding pagination url.
1210
+
1211
+ //* Parse query arg, put in var and remove from current URL.
1212
+ $query_arg = parse_url( $urlfromcache, PHP_URL_QUERY );
1213
+ if ( isset( $query_arg ) )
1214
+ $urlfromcache = str_replace( '?' . $query_arg, '', $urlfromcache );
1215
+
1216
+ // Calculate current page number.
1217
+ $current = 'next' === $pos ? ( $i - 1 ) : ( $i + 1 );
1218
+ $current = (string) $current;
1219
+
1220
+ //* Continue if still bigger than or equal to 2.
1221
+ if ( $i >= 2 ) {
1222
+ //* We're adding a page.
1223
+ $last_occurence = strrpos( $urlfromcache, '/' . $current . '/' );
1224
+
1225
+ if ( $last_occurence !== false )
1226
+ $urlfromcache = substr_replace( $urlfromcache, '/', $last_occurence, strlen( '/' . $current . '/' ) );
1227
+ }
1228
+ }
1229
+
1230
+ if ( ! $this->pretty_permalinks || in_array( $post->post_status, array( 'draft', 'auto-draft', 'pending' ) ) ) {
1231
+
1232
+ //* Put removed query arg back prior to adding pagination.
1233
+ if ( isset( $query_arg ) )
1234
+ $urlfromcache = $urlfromcache . '?' . $query_arg;
1235
+
1236
+ $url = add_query_arg( 'page', $i, $urlfromcache );
1237
+ } else if ( $this->is_static_frontpage( $post_id ) ) {
1238
+ global $wp_rewrite;
1239
+
1240
+ $url = trailingslashit( $urlfromcache ) . user_trailingslashit( $wp_rewrite->pagination_base . "/" . $i, 'single_paged' );
1241
+
1242
+ //* Add back query arg if removed.
1243
+ if ( isset( $query_arg ) )
1244
+ $url = $url . '?' . $query_arg;
1245
+ } else {
1246
+ $url = trailingslashit( $urlfromcache ) . user_trailingslashit( $i, 'single_paged' );
1247
+
1248
+ //* Add back query arg if removed.
1249
+ if ( isset( $query_arg ) )
1250
+ $url = $url . '?' . $query_arg;
1251
+ }
1252
+ }
1253
+
1254
+ return $url;
1255
+ }
1256
+
1257
+ /**
1258
+ * Adds subdomain to URL.
1259
+ *
1260
+ * @since 2.6.5
1261
+ *
1262
+ * @param string $url The current URL without subdomain.
1263
+ * @return string $url Fully qualified URL with possible subdomain.
1264
+ */
1265
+ public function add_url_subdomain( $url = '' ) {
1266
+
1267
+ $url = $this->make_fully_qualified_url( $url );
1268
+
1269
+ //* Add subdomain, if set.
1270
+ if ( $this->add_subdomain ) {
1271
+ $scheme = parse_url( $url, PHP_URL_SCHEME );
1272
+ $url = str_replace( $scheme . '://', '', $url );
1273
+
1274
+ //* Put it together.
1275
+ $url = $scheme . '://' . $this->add_subdomain . '.' . $url;
1276
+ }
1277
+
1278
+ return $url;
1279
+ }
1280
+
1281
+ /**
1282
+ * Cached WordPress permalink structure settings.
1283
+ *
1284
+ * @since 2.6.0
1285
+ * @staticvar string $structure
1286
+ *
1287
+ * @return string permalink structure.
1288
+ */
1289
+ public function permalink_structure() {
1290
+
1291
+ static $structure = null;
1292
+
1293
+ if ( isset( $structure ) )
1294
+ return $structure;
1295
+
1296
+ return $structure = get_option( 'permalink_structure' );
1297
+ }
1298
+
1299
+ /**
1300
+ * Add $paged if Paginated and allowed through arguments.
1301
+ *
1302
+ * @since 2.6.0
1303
+ *
1304
+ * @param int $paged
1305
+ * @param bool $singular Whether to allow plural and singular.
1306
+ * @param bool $plural Whether to allow plural regardless.
1307
+ *
1308
+ * @return int|bool $paged. False if not allowed. Int if allowed.
1309
+ */
1310
+ protected function maybe_get_paged( $paged = 0, $singular = false, $plural = true ) {
1311
+
1312
+ if ( $paged ) {
1313
+ if ( $singular )
1314
+ return $paged;
1315
+
1316
+ if ( $plural && $paged >= 2 )
1317
+ return $paged;
1318
+ }
1319
+
1320
+ return false;
1321
+ }
1322
+
1323
+ /**
1324
+ * Makes a fully qualified URL from input. Always uses http to fix.
1325
+ * @see $this->set_url_scheme()
1326
+ *
1327
+ * @since 2.6.5
1328
+ *
1329
+ * @param string $url Required the current maybe not fully qualified URL.
1330
+ * @return string $url
1331
+ */
1332
+ public function make_fully_qualified_url( $url ) {
1333
+
1334
+ if ( '//' === substr( $url, 0, 2 ) ) {
1335
+ $url = 'http:' . $url;
1336
+ } else if ( 'http' !== substr( $url, 0, 4 ) ) {
1337
+ $url = 'http://' . $url;
1338
+ }
1339
+
1340
+ return $url;
1341
+ }
1342
+
1343
+ }
inc/classes/generate.class.php ADDED
@@ -0,0 +1,313 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The SEO Framework plugin
4
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License version 3 as published
8
+ * by the Free Software Foundation.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ /**
20
+ * Class AutoDescription_Generate
21
+ *
22
+ * Generates general SEO data based on content.
23
+ *
24
+ * @since 2.1.6
25
+ */
26
+ class AutoDescription_Generate extends AutoDescription_TermData {
27
+
28
+ /**
29
+ * Constructor, load parent constructor
30
+ */
31
+ public function __construct() {
32
+ parent::__construct();
33
+ }
34
+
35
+ /**
36
+ * Output the `index`, `follow`, `noodp`, `noydir`, `noarchive` robots meta code in array
37
+ *
38
+ * @since 2.2.2
39
+ *
40
+ * @uses genesis_get_seo_option() Get SEO setting value.
41
+ * @uses genesis_get_custom_field() Get custom field value.
42
+ *
43
+ * @global object $wp_query
44
+ *
45
+ * @return array|null robots
46
+ */
47
+ public function robots_meta() {
48
+
49
+ //* Defaults
50
+ $meta = array(
51
+ 'noindex' => $this->get_option( 'site_noindex' ) ? 'noindex' : '',
52
+ 'nofollow' => $this->get_option( 'site_nofollow' ) ? 'nofollow' : '',
53
+ 'noarchive' => $this->get_option( 'site_noarchive' ) ? 'noarchive' : '',
54
+ 'noodp' => $this->get_option( 'noodp' ) ? 'noodp' : '',
55
+ 'noydir' => $this->get_option( 'noydir' ) ? 'noydir' : '',
56
+ );
57
+
58
+ /**
59
+ * Check the Robots SEO settings, set noindex for paged archives.
60
+ * @since 2.2.4
61
+ */
62
+ if ( $this->is_archive() && $this->paged() > 1 )
63
+ $meta['noindex'] = $this->get_option( 'paged_noindex' ) ? 'noindex' : $meta['noindex'];
64
+
65
+ if ( $this->is_front_page() && ( $this->page() > 1 || $this->paged() > 1 ) )
66
+ $meta['noindex'] = $this->get_option( 'home_paged_noindex' ) ? 'noindex' : $meta['noindex'];
67
+
68
+ //* Check home page SEO settings, set noindex, nofollow and noarchive
69
+ if ( $this->is_front_page() ) {
70
+ $meta['noindex'] = empty( $meta['noindex'] ) && $this->is_option_checked( 'homepage_noindex' ) ? 'noindex' : $meta['noindex'];
71
+ $meta['nofollow'] = empty( $meta['nofollow'] ) && $this->is_option_checked( 'homepage_nofollow' ) ? 'nofollow' : $meta['nofollow'];
72
+ $meta['noarchive'] = empty( $meta['noarchive'] ) && $this->is_option_checked( 'homepage_noarchive' ) ? 'noarchive' : $meta['noarchive'];
73
+ } else {
74
+ global $wp_query;
75
+
76
+ /**
77
+ * Check if archive is empty, set noindex for those.
78
+ * @since 2.2.8
79
+ *
80
+ * @todo maybe create option
81
+ * @priority so low... 3.0.0+
82
+ */
83
+ if ( isset( $wp_query->post_count ) && 0 === $wp_query->post_count )
84
+ $meta['noindex'] = 'noindex';
85
+ }
86
+
87
+ if ( $this->is_category() || $this->is_tag() ) {
88
+ $term = get_queried_object();
89
+
90
+ $meta['noindex'] = empty( $meta['noindex'] ) && $term->admeta['noindex'] ? 'noindex' : $meta['noindex'];
91
+ $meta['nofollow'] = empty( $meta['nofollow'] ) && $term->admeta['nofollow'] ? 'nofollow' : $meta['nofollow'];
92
+ $meta['noarchive'] = empty( $meta['noarchive'] ) && $term->admeta['noarchive'] ? 'noarchive' : $meta['noarchive'];
93
+
94
+ if ( $this->is_category() ) {
95
+ $meta['noindex'] = empty( $meta['noindex'] ) && $this->is_option_checked( 'category_noindex' ) ? 'noindex' : $meta['noindex'];
96
+ $meta['nofollow'] = empty( $meta['nofollow'] ) && $this->is_option_checked( 'category_nofollow' ) ? 'nofollow' : $meta['nofollow'];
97
+ $meta['noarchive'] = empty( $meta['noarchive'] ) && $this->is_option_checked( 'category_noindex' ) ? 'noarchive' : $meta['noarchive'];
98
+ } else if ( $this->is_tag() ) {
99
+ $meta['noindex'] = empty( $meta['noindex'] ) && $this->is_option_checked( 'tag_noindex' ) ? 'noindex' : $meta['noindex'];
100
+ $meta['nofollow'] = empty( $meta['nofollow'] ) && $this->is_option_checked( 'tag_nofollow' ) ? 'nofollow' : $meta['nofollow'];
101
+ $meta['noarchive'] = empty( $meta['noarchive'] ) && $this->is_option_checked( 'tag_noindex' ) ? 'noarchive' : $meta['noarchive'];
102
+ }
103
+
104
+ $flag = isset( $term->admeta['saved_flag'] ) && $this->is_checked( $term->admeta['saved_flag'] );
105
+
106
+ if ( false === $flag && isset( $term->meta ) ) {
107
+ //* Genesis support.
108
+ $meta['noindex'] = empty( $meta['noindex'] ) && $term->meta['noindex'] ? 'noindex' : $meta['noindex'];
109
+ $meta['nofollow'] = empty( $meta['nofollow'] ) && $term->meta['nofollow'] ? 'nofollow' : $meta['nofollow'];
110
+ $meta['noarchive'] = empty( $meta['noarchive'] ) && $term->meta['noarchive'] ? 'noarchive' : $meta['noarchive'];
111
+ }
112
+ }
113
+
114
+ // Is custom Taxonomy page. But not a category or tag. Should've recieved specific term SEO settings.
115
+ if ( $this->is_tax() ) {
116
+ $term = get_term_by( 'slug', get_query_var( 'term' ), get_query_var( 'taxonomy' ) );
117
+
118
+ $meta['noindex'] = empty( $meta['noindex'] ) && $term->admeta['noindex'] ? 'noindex' : $meta['noindex'];
119
+ $meta['nofollow'] = empty( $meta['nofollow'] ) && $term->admeta['nofollow'] ? 'nofollow' : $meta['nofollow'];
120
+ $meta['noarchive'] = empty( $meta['noarchive'] ) && $term->admeta['noarchive'] ? 'noarchive' : $meta['noarchive'];
121
+ }
122
+
123
+ if ( $this->is_author() ) {
124
+ // $author_id = (int) get_query_var( 'author' );
125
+
126
+ /**
127
+ * @todo
128
+ * @priority high 2.6.x
129
+ */
130
+ // $meta['noindex'] = empty( $meta['noindex'] ) && get_the_author_meta( 'noindex', $author_id ) ? 'noindex' : $meta['noindex'];
131
+ // $meta['nofollow'] = empty( $meta['nofollow'] ) && get_the_author_meta( 'nofollow', $author_id ) ? 'nofollow' : $meta['nofollow'];
132
+ // $meta['noarchive'] = empty( $meta['noarchive'] ) && get_the_author_meta( 'noarchive', $author_id ) ? 'noarchive' : $meta['noarchive'];
133
+
134
+ $meta['noindex'] = empty( $meta['noindex'] ) && $this->is_option_checked( 'author_noindex' ) ? 'noindex' : $meta['noindex'];
135
+ $meta['nofollow'] = empty( $meta['nofollow'] ) && $this->is_option_checked( 'author_nofollow' ) ? 'nofollow' : $meta['nofollow'];
136
+ $meta['noarchive'] = empty( $meta['noarchive'] ) && $this->is_option_checked( 'author_noarchive' ) ? 'noarchive' : $meta['noarchive'];
137
+ }
138
+
139
+ if ( $this->is_date() ) {
140
+ $meta['noindex'] = empty( $meta['noindex'] ) && $this->is_option_checked( 'date_noindex' ) ? 'noindex' : $meta['noindex'];
141
+ $meta['nofollow'] = empty( $meta['nofollow'] ) && $this->is_option_checked( 'date_nofollow' ) ? 'nofollow' : $meta['nofollow'];
142
+ $meta['noarchive'] = empty( $meta['noarchive'] ) && $this->is_option_checked( 'date_noarchive' ) ? 'noarchive' : $meta['noarchive'];
143
+ }
144
+
145
+ if ( $this->is_search() ) {
146
+ $meta['noindex'] = empty( $meta['noindex'] ) && $this->is_option_checked( 'search_noindex' ) ? 'noindex' : $meta['noindex'];
147
+ $meta['nofollow'] = empty( $meta['nofollow'] ) && $this->is_option_checked( 'search_nofollow' ) ? 'nofollow' : $meta['nofollow'];
148
+ $meta['noarchive'] = empty( $meta['noarchive'] ) && $this->is_option_checked( 'search_noarchive' ) ? 'noarchive' : $meta['noarchive'];
149
+ }
150
+
151
+ if ( $this->is_attachment() ) {
152
+ $meta['noindex'] = empty( $meta['noindex'] ) && $this->is_option_checked( 'attachment_noindex' ) ? 'noindex' : $meta['noindex'];
153
+ $meta['nofollow'] = empty( $meta['nofollow'] ) && $this->is_option_checked( 'attachment_nofollow' ) ? 'nofollow' : $meta['nofollow'];
154
+ $meta['noarchive'] = empty( $meta['noarchive'] ) && $this->is_option_checked( 'attachment_noarchive' ) ? 'noarchive' : $meta['noarchive'];
155
+ }
156
+
157
+ if ( $this->is_singular() ) {
158
+ $meta['noindex'] = empty( $meta['noindex'] ) && $this->get_custom_field( '_genesis_noindex' ) ? 'noindex' : $meta['noindex'];
159
+ $meta['nofollow'] = empty( $meta['nofollow'] ) && $this->get_custom_field( '_genesis_nofollow' ) ? 'nofollow' : $meta['nofollow'];
160
+ $meta['noarchive'] = empty( $meta['noarchive'] ) && $this->get_custom_field( '_genesis_noarchive' ) ? 'noarchive' : $meta['noarchive'];
161
+ }
162
+
163
+ /**
164
+ * Applies filters the_seo_framework_robots_meta_array : array
165
+ * @since 2.6.0
166
+ */
167
+ $meta = (array) apply_filters( 'the_seo_framework_robots_meta_array', $meta );
168
+
169
+ //* Strip empty array items
170
+ $meta = array_filter( $meta );
171
+
172
+ return $meta;
173
+ }
174
+
175
+ /**
176
+ * Returns cached and parsed separator option.
177
+ *
178
+ * @param string $type The separator type. Used to fetch option.
179
+ * @param bool $escape Escape the separator.
180
+ *
181
+ * @staticvar array $sepcache The separator cache.
182
+ * @staticvar array $sep_esc The escaped separator cache.
183
+ *
184
+ * @since 2.3.9
185
+ */
186
+ public function get_separator( $type = 'title', $escape = false ) {
187
+
188
+ static $sepcache = array();
189
+ static $sep_esc = array();
190
+
191
+ if ( isset( $sep_esc[$type][$escape] ) )
192
+ return $sep_esc[$type][$escape];
193
+
194
+ if ( ! isset( $sepcache[$type] ) ) {
195
+ if ( 'title' === $type ) {
196
+ $sep_option = $this->get_option( 'title_seperator' ); // Note: typo.
197
+ } else {
198
+ $sep_option = $this->get_option( $type . '_separator' );
199
+ }
200
+
201
+ if ( 'pipe' === $sep_option ) {
202
+ $sep = '|';
203
+ } else if ( 'dash' === $sep_option ) {
204
+ $sep = '-';
205
+ } else if ( '' !== $sep_option ) {
206
+ //* Encapsulate within html entities.
207
+ $sep = '&' . $sep_option . ';';
208
+ } else {
209
+ //* Nothing found.
210
+ $sep = '|';
211
+ }
212
+
213
+ $sepcache[$type] = $sep;
214
+ }
215
+
216
+ if ( $escape ) {
217
+ return $sep_esc[$type][$escape] = esc_html( $sepcache[$type] );
218
+ } else {
219
+ return $sep_esc[$type][$escape] = $sepcache[$type];
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Fetch blogname
225
+ *
226
+ * @staticvar string $blogname
227
+ *
228
+ * @since 2.5.2
229
+ * @return string $blogname The trimmed and sanitized blogname
230
+ */
231
+ public function get_blogname() {
232
+
233
+ static $blogname = null;
234
+
235
+ if ( isset( $blogname ) )
236
+ return $blogname;
237
+
238
+ return $blogname = trim( get_bloginfo( 'name', 'display' ) );
239
+ }
240
+
241
+ /**
242
+ * Fetch blog description.
243
+ *
244
+ * @staticvar string $description
245
+ *
246
+ * @since 2.5.2
247
+ * @return string $blogname The trimmed and sanitized blog description.
248
+ */
249
+ public function get_blogdescription() {
250
+
251
+ static $description = null;
252
+
253
+ if ( isset( $description ) )
254
+ return $description;
255
+
256
+ $description = trim( get_bloginfo( 'description', 'display' ) );
257
+
258
+ return $description = $description ? $description : $this->untitled();
259
+ }
260
+
261
+ /**
262
+ * Matches WordPress locales.
263
+ * If not matched, it will calculate a locale.
264
+ *
265
+ * @param $match the locale to match. Defaults to WordPress locale.
266
+ *
267
+ * @since 2.5.2
268
+ *
269
+ * @return string Facebook acceptable OG locale.
270
+ */
271
+ public function fetch_locale( $match = '' ) {
272
+
273
+ if ( empty( $match ) )
274
+ $match = get_locale();
275
+
276
+ $match_len = strlen( $match );
277
+ $valid_locales = (array) $this->fb_locales();
278
+ $default = 'en_US';
279
+
280
+ if ( $match_len > 5 ) {
281
+ //* More than full is used. Make it just full.
282
+ $match = substr( $match, 0, 5 );
283
+ $match_len = 5;
284
+ }
285
+
286
+ if ( 5 === $match_len ) {
287
+ //* Full locale is used.
288
+
289
+ //* Return the match if found.
290
+ if ( in_array( $match, $valid_locales ) )
291
+ return $match;
292
+
293
+ //* Convert to only language portion.
294
+ $match = substr( $match, 0, 2 );
295
+ $match_len = 2;
296
+ }
297
+
298
+ if ( 2 === $match_len ) {
299
+ //* Language key is provided.
300
+
301
+ $locale_keys = (array) $this->language_keys();
302
+
303
+ //* No need to do for each loop. Just match the keys.
304
+ if ( $key = array_search( $match, $locale_keys ) ) {
305
+ //* Fetch the corresponding value from key within the language array.
306
+ return $valid_locales[$key];
307
+ }
308
+ }
309
+
310
+ return $default;
311
+ }
312
+
313
+ }
inc/classes/init.class.php ADDED
@@ -0,0 +1,404 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The SEO Framework plugin
4
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License version 3 as published
8
+ * by the Free Software Foundation.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ /**
20
+ * Class AutoDescription_Init
21
+ *
22
+ * Outputs all data in front-end header
23
+ *
24
+ * @since 2.1.6
25
+ */
26
+ class AutoDescription_Init extends AutoDescription_Query {
27
+
28
+ /**
29
+ * Allow object caching through a filter.
30
+ *
31
+ * @since 2.4.3
32
+ *
33
+ * @var bool Enable object caching.
34
+ */
35
+ protected $use_object_cache = true;
36
+
37
+ /**
38
+ * Constructor. Initializes actions and loads parent constructor.
39
+ */
40
+ public function __construct() {
41
+ parent::__construct();
42
+
43
+ add_action( 'init', array( $this, 'autodescription_run' ), 1 );
44
+ add_action( 'template_redirect', array( $this, 'custom_field_redirect' ) );
45
+
46
+ /**
47
+ * Applies filters : the_seo_framework_use_object_cache
48
+ *
49
+ * @since 2.4.3
50
+ */
51
+ $this->use_object_cache = (bool) apply_filters( 'the_seo_framework_use_object_cache', true );
52
+
53
+ }
54
+
55
+ /**
56
+ * Runs the plugin on the front-end.
57
+ *
58
+ * @since 1.0.0
59
+ */
60
+ public function autodescription_run() {
61
+
62
+ /**
63
+ * Don't run in admin.
64
+ * Don't do anything on preview either.
65
+ * @since 2.2.4
66
+ */
67
+ if ( $this->is_admin() || $this->is_preview() )
68
+ return;
69
+
70
+ $this->init_actions();
71
+ $this->init_filters();
72
+
73
+ }
74
+
75
+ /**
76
+ * Initializes front-end actions.
77
+ *
78
+ * @since 2.5.2
79
+ */
80
+ protected function init_actions() {
81
+
82
+ //* Remove canonical header tag from WP
83
+ remove_action( 'wp_head', 'rel_canonical' );
84
+ //* Remove shortlink.
85
+ remove_action( 'wp_head', 'wp_shortlink_wp_head' );
86
+ //* Remove adjecent rel tags.
87
+ remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head' );
88
+ //* Earlier removal of the generator tag. Doesn't require filter.
89
+ remove_action( 'wp_head', 'wp_generator' );
90
+
91
+ if ( $this->is_theme( 'genesis', false ) ) {
92
+ add_action( 'genesis_meta', array( $this, 'html_output' ), 5 );
93
+ } else {
94
+ add_action( 'wp_head', array( $this, 'html_output' ), 1 );
95
+ }
96
+
97
+ }
98
+
99
+ /**
100
+ * Runs front-end filters.
101
+ *
102
+ * @since 2.5.2
103
+ */
104
+ protected function init_filters() {
105
+
106
+ //* Removes all pre_get_document_title filters.
107
+ remove_all_filters( 'pre_get_document_title', false );
108
+
109
+ //* New WordPress 4.4.0 filter. Hurray! It's also much faster :)
110
+ add_filter( 'pre_get_document_title', array( $this, 'title_from_cache' ), 10 );
111
+ //* Override AnsPress Theme Title
112
+ add_filter( 'ap_title', array( $this, 'title_from_cache' ), 99, 1 );
113
+ //* Override bbPress title
114
+ add_filter( 'bbp_title', array( $this, 'title_from_cache' ), 99, 3 );
115
+ //* Override Woo Themes Title
116
+ add_filter( 'woo_title', array( $this, 'title_from_cache'), 99 );
117
+
118
+ /**
119
+ * Applies filters 'the_seo_framework_manipulate_title' : boolean
120
+ * Disables the title tag manipulation on old themes.
121
+ * @since 2.4.1
122
+ */
123
+ if ( (bool) apply_filters( 'the_seo_framework_manipulate_title', true ) ) {
124
+ //* Override WordPress Title
125
+ add_filter( 'wp_title', array( $this, 'title_from_cache' ), 9, 3 );
126
+ }
127
+
128
+ }
129
+
130
+ /**
131
+ * Runs header actions.
132
+ *
133
+ * @since 2.2.6
134
+ * @uses The_SEO_Framework_Load::call_function()
135
+ *
136
+ * @param string|array $args the arguments that will be passed
137
+ * @param bool $before if the header actions should be before or after the SEO Frameworks output
138
+ * @return string|empty The filter output.
139
+ */
140
+ public function header_actions( $args = '', $before = true ) {
141
+
142
+ $output = '';
143
+
144
+ //* Placeholder callback and args.
145
+ $functions = array();
146
+
147
+ /**
148
+ * Applies filters 'the_seo_framework_before_output' : array after functions output
149
+ * Applies filters 'the_seo_framework_after_output' : array after functions output
150
+ */
151
+ $filter_tag = $before ? 'the_seo_framework_before_output' : 'the_seo_framework_after_output';
152
+ $filter = (array) apply_filters( $filter_tag, $functions );
153
+
154
+ $functions = wp_parse_args( $args, $filter );
155
+
156
+ if ( $functions && is_array( $functions ) ) {
157
+ foreach ( $functions as $function ) {
158
+ $arguments = isset( $function['args'] ) ? $function['args'] : '';
159
+
160
+ if ( isset( $function['callback'] ) )
161
+ $output .= $this->call_function( $function['callback'], '2.2.6', $arguments );
162
+
163
+ }
164
+ }
165
+
166
+ return $output;
167
+ }
168
+
169
+ /**
170
+ * Echos the header meta and scripts.
171
+ *
172
+ * @since 1.0.0
173
+ */
174
+ public function html_output() {
175
+
176
+ do_action( 'the_seo_framework_do_before_output' );
177
+
178
+ /**
179
+ * Start the timer here. I know it doesn't calculate the initiation of
180
+ * the plugin, but it will make the code smelly if I were to do so.
181
+ * A static array cache counter function would make it possible, but meh.
182
+ * This function takes the most time anyway.
183
+ */
184
+ $init_start = microtime( true );
185
+ $memory_start = $this->the_seo_framework_debug ? $this->profile( false, false, 'memory', 'html_output' ) : 0;
186
+
187
+ /**
188
+ * Cache key buster
189
+ * Hexadecimal revision, e.g. 0, 1, 2, e, f,
190
+ *
191
+ * @busted to '2' @version 2.5.2.1
192
+ */
193
+ $revision = '2';
194
+ $the_id = $this->get_the_real_ID();
195
+ $key = $this->generate_cache_key( $the_id ) . $revision;
196
+
197
+ /**
198
+ * Give each paged pages/archives a different cache key.
199
+ * @since 2.2.6
200
+ */
201
+ $page = (string) $this->page();
202
+ $paged = (string) $this->paged();
203
+
204
+ $cache_key = 'seo_framework_output_' . $key . '_' . $paged . '_' . $page;
205
+
206
+ $output = $this->object_cache_get( $cache_key );
207
+ if ( false === $output ) {
208
+
209
+ $robots = $this->robots();
210
+
211
+ /**
212
+ * Applies filters 'the_seo_framework_pre' : string
213
+ * Adds content before the output.
214
+ * @since 2.6.0
215
+ */
216
+ $before = (string) apply_filters( 'the_seo_framework_pre', '' );
217
+
218
+ $before_actions = $this->header_actions( '', true );
219
+
220
+ //* Limit processing on 404 or search
221
+ if ( $this->is_404() || $this->is_search() ) {
222
+ $output = $this->og_locale()
223
+ . $this->og_type()
224
+ . $this->og_title()
225
+ . $this->og_url()
226
+ . $this->og_sitename()
227
+ . $this->canonical()
228
+ . $this->google_site_output()
229
+ . $this->bing_site_output()
230
+ . $this->yandex_site_output()
231
+ . $this->pint_site_output()
232
+ ;
233
+ } else {
234
+ $output = $this->the_description()
235
+ . $this->og_image()
236
+ . $this->og_locale()
237
+ . $this->og_type()
238
+ . $this->og_title()
239
+ . $this->og_description()
240
+ . $this->og_url()
241
+ . $this->og_sitename()
242
+ . $this->facebook_publisher()
243
+ . $this->facebook_author()
244
+ . $this->facebook_app_id()
245
+ . $this->article_published_time()
246
+ . $this->article_modified_time()
247
+ . $this->twitter_card()
248
+ . $this->twitter_site()
249
+ . $this->twitter_creator()
250
+ . $this->twitter_title()
251
+ . $this->twitter_description()
252
+ . $this->twitter_image()
253
+ . $this->shortlink()
254
+ . $this->canonical()
255
+ . $this->paged_urls()
256
+ . $this->ld_json()
257
+ . $this->google_site_output()
258
+ . $this->bing_site_output()
259
+ . $this->yandex_site_output()
260
+ . $this->pint_site_output()
261
+ ;
262
+ }
263
+
264
+ $after_actions = $this->header_actions( '', false );
265
+
266
+ /**
267
+ * Applies filters 'the_seo_framework_pro' : string
268
+ * Adds content before the output.
269
+ * @since 2.6.0
270
+ */
271
+ $after = (string) apply_filters( 'the_seo_framework_pro', '' );
272
+
273
+ /**
274
+ * Applies filters 'the_seo_framework_generator_tag' : String generator tag content.
275
+ * @since 2.0.1
276
+ * @see https://wordpress.org/plugins/generator-the-seo-framework/ For an alternative.
277
+ */
278
+ $generator = (string) apply_filters( 'the_seo_framework_generator_tag', '' );
279
+
280
+ if ( '' !== $generator )
281
+ $generator = '<meta name="generator" content="' . esc_attr( $generator ) . '" />' . "\r\n";
282
+
283
+ $output = $robots . $before . $before_actions . $output . $after_actions . $after . $generator;
284
+
285
+ $this->object_cache_set( $cache_key, $output, 86400 );
286
+ }
287
+
288
+ /**
289
+ * Applies filters 'the_seo_framework_indicator' : Boolean
290
+ * Whether to show the indicator in HTML.
291
+ * @since 2.0.0
292
+ */
293
+ $indicator = (bool) apply_filters( 'the_seo_framework_indicator', true );
294
+
295
+ $indicatorbefore = '';
296
+ $indicatorafter = '';
297
+
298
+ if ( $indicator ) {
299
+
300
+ /**
301
+ * Applies filters 'the_seo_framework_indicator_timing' : Boolean
302
+ * Whether to show the hidden generation time in HTML.
303
+ * @since 2.4.0
304
+ */
305
+ $timer = (bool) apply_filters( 'the_seo_framework_indicator_timing', true );
306
+
307
+ /**
308
+ * Applies filters 'sybre_waaijer_<3' : Boolean
309
+ * Whether to show the hidden author name in HTML.
310
+ * @since 2.4.0
311
+ */
312
+ $sybre = (bool) apply_filters( 'sybre_waaijer_<3', true );
313
+
314
+ $start = __( 'Start The Seo Framework', 'autodescription' );
315
+ $end = __( 'End The Seo Framework', 'autodescription' );
316
+ $me = $sybre ? ' ' . __( 'by Sybre Waaijer', 'autodescription' ) : '';
317
+
318
+ $indicatorbefore = '<!-- ' . $start . $me . ' -->' . "\r\n";
319
+
320
+ /**
321
+ * Calculate the plugin load time.
322
+ * @since 2.4.0
323
+ */
324
+ if ( $timer ) {
325
+ $memory = $this->the_seo_framework_debug ? ' | ' . number_format( $this->profile( false, true, 'memory', 'html_output' ) / 1024, 2 ) . ' kiB' : '';
326
+ $indicatorafter = '<!-- ' . $end . $me . ' | ' . number_format( microtime( true ) - $init_start, 5 ) . 's' . $memory . ' -->' . "\r\n";
327
+ } else {
328
+ $indicatorafter = '<!-- ' . $end . $me . ' -->' . "\r\n";
329
+ }
330
+ }
331
+
332
+ $output = "\r\n" . $indicatorbefore . $output . $indicatorafter . "\r\n";
333
+
334
+ do_action( 'the_seo_framework_do_after_output' );
335
+
336
+ echo $output;
337
+ }
338
+
339
+ /**
340
+ * Redirects singular page to an alternate URL.
341
+ *
342
+ * @since 2.0.9
343
+ *
344
+ * @return void early on non-singular pages.
345
+ */
346
+ public function custom_field_redirect() {
347
+
348
+ //* Prevent redirect from options on uneditable pages.
349
+ if ( ! $this->is_singular() || $this->is_admin() )
350
+ return;
351
+
352
+ $url = $this->get_custom_field( 'redirect' );
353
+
354
+ if ( $url ) {
355
+
356
+ $allow_external = $this->allow_external_redirect();
357
+ $scheme = null;
358
+
359
+ if ( false === $allow_external ) {
360
+ $url = $this->set_url_scheme( $url, 'relative' );
361
+ $url = $this->add_url_host( $url );
362
+ $scheme = is_ssl() ? 'https' : 'http';
363
+ }
364
+
365
+ wp_redirect( esc_url_raw( $url, $scheme ), 301 );
366
+ exit;
367
+ }
368
+
369
+ }
370
+
371
+ /**
372
+ * Well, this is annoying.
373
+ *
374
+ * @since 2.4.2
375
+ * @return something that will make your head explode.
376
+ */
377
+ public function explode() {
378
+ add_action( 'wp_head', array( $this, 'roll' ) );
379
+
380
+ /* the code to run this :
381
+ add_action( 'init', 'tsf_explode' );
382
+ function tsf_explode() {
383
+ if ( function_exists( 'the_seo_framework' ) ) {
384
+ $the_seo_framework = the_seo_framework();
385
+ if ( isset( $the_seo_framework ) )
386
+ $the_seo_framework->explode();
387
+ }
388
+ }
389
+ */
390
+ }
391
+
392
+ /**
393
+ * After using explosions, you tend to roll away.
394
+ *
395
+ * @since 2.5.2
396
+ */
397
+ public function roll() {
398
+ ?>
399
+ <style>div:hover>div{-webkit-animation:troll 5s infinite cubic-bezier(0,1.5,.5,1)1s;animation:troll 5s infinite cubic-bezier(0,1.5,.5,1)1s}@-webkit-keyframes troll{100%{-webkit-transform:rotate(0)}75%{-webkit-transform:rotate(30deg)}25%{-webkit-transorm:rotate(0)}0%{-webkit-transorm:rotate(30deg)}}@keyframes troll{100%,25%{transform:rotate(0)}0%,75%{transform:rotate(30deg)}}#container:hover,.site-container:hover{-webkit-animation:none;animation:none}</style>
400
+ <?php
401
+ echo "\r\n";
402
+ }
403
+
404
+ }
inc/classes/inpost.class.php ADDED
@@ -0,0 +1,664 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The SEO Framework plugin
4
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License version 3 as published
8
+ * by the Free Software Foundation.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ /**
20
+ * Class AutoDescription_Inpost
21
+ *
22
+ * Outputs Taxonomy, Post and Page meta boxes
23
+ *
24
+ * @since 2.2.2
25
+ */
26
+ class AutoDescription_Inpost extends AutoDescription_DoingItRight {
27
+
28
+ /**
29
+ * Add inpost SEO Bar through a filter.
30
+ *
31
+ * @since 2.5.2
32
+ *
33
+ * @var bool|string Whether and where to show the inpost SEO bar.
34
+ */
35
+ protected $inpost_seo_bar = false;
36
+
37
+ /**
38
+ * Constructor, load parent constructor
39
+ */
40
+ public function __construct() {
41
+ parent::__construct();
42
+
43
+ //* Enqueue Inpost meta boxes.
44
+ add_action( 'add_meta_boxes', array( $this, 'add_inpost_seo_box_init' ), 5 );
45
+
46
+ //* Enqueue Taxonomy meta output.
47
+ add_action( 'current_screen', array( $this, 'add_taxonomy_seo_box_init' ), 10 );
48
+
49
+ /**
50
+ * Applies filters bool|string the_seo_framework_inpost_seo_bar :
51
+ * Whether to output the SEO bar within the inpost SEO Settings metabox.
52
+ * @param : string 'above' Outputs it above the Settings
53
+ * : string 'below' Outputs it below the Settings
54
+ * : bool false No output.
55
+ * @since 2.5.2
56
+ */
57
+ $this->inpost_seo_bar = apply_filters( 'the_seo_framework_inpost_seo_bar', false );
58
+
59
+ }
60
+
61
+ /**
62
+ * Render the SEO meta box
63
+ *
64
+ * Called outside autodescription_run
65
+ *
66
+ * Applies filters the_seo_framework_seobox_output : bool
67
+ *
68
+ * @since 2.0.0
69
+ */
70
+ public function add_inpost_seo_box_init() {
71
+
72
+ if ( $this->detect_seo_plugins() )
73
+ return;
74
+
75
+ $show_seobox = (bool) apply_filters( 'the_seo_framework_seobox_output', true );
76
+
77
+ if ( $show_seobox )
78
+ add_action( 'add_meta_boxes', array( $this, 'add_inpost_seo_box' ), 10, 1 );
79
+
80
+ }
81
+
82
+ /**
83
+ * Adds SEO Meta boxes within Taxonomy screens.
84
+ *
85
+ * @since 2.1.8
86
+ */
87
+ public function add_taxonomy_seo_box_init() {
88
+
89
+ //* @since 2.6.0
90
+ if ( $this->detect_seo_plugins() )
91
+ return;
92
+
93
+ //* @since 2.6.0
94
+ if ( $this->is_term_edit() ) {
95
+
96
+ /**
97
+ * High priority, this box is seen right below the post/page edit screen.
98
+ * Applies filters 'the_seo_framework_term_metabox_priority' : int
99
+ *
100
+ * @since 2.6.0
101
+ */
102
+ $priority = (int) apply_filters( 'the_seo_framework_term_metabox_priority', 0 );
103
+
104
+ //* Add taxonomy meta boxes
105
+ foreach ( get_taxonomies( array( 'public' => true ) ) as $tax_name )
106
+ add_action( $tax_name . '_edit_form', array( $this, 'pre_seo_box' ), $priority, 2 );
107
+
108
+ }
109
+
110
+ }
111
+
112
+ /**
113
+ * Adds SEO Meta boxes beneath every page/post edit screen.
114
+ *
115
+ * @param string $post_type The current Post Type.
116
+ *
117
+ * @since 2.0.0
118
+ */
119
+ public function add_inpost_seo_box( $post_type ) {
120
+
121
+ /**
122
+ * @uses $this->post_type_supports_custom_seo()
123
+ * @since 2.3.9
124
+ */
125
+ if ( $this->post_type_supports_custom_seo( $post_type ) ) {
126
+
127
+ $post = get_post_type_object( $post_type );
128
+
129
+ if ( is_object( $post ) ) {
130
+ $labels = isset( $post->labels ) ? $post->labels : '';
131
+
132
+ if ( $labels ) {
133
+ //* Title and type are used interchangeably.
134
+ $title = isset( $labels->singular_name ) ? $labels->singular_name : $labels->name;
135
+ $args = array( $title, 'is_post_page' );
136
+
137
+ /**
138
+ * Applies filters the_seo_framework_metabox_id : string The metabox priority and class ID.
139
+ * @since 2.6.0
140
+ * @NOTE warning: might cause CSS and JS conflicts.
141
+ * @TODO solve note.
142
+ * @priority medium 2.7.0
143
+ */
144
+ $id = (string) apply_filters( 'the_seo_framework_metabox_id', 'theseoframework-inpost-box' );
145
+ $context = 'normal';
146
+
147
+ /**
148
+ * High priority, this box is seen right below the post/page edit screen.
149
+ * Applies filters 'the_seo_framework_metabox_priority' : string
150
+ * Accepts 'high', 'default', 'low'
151
+ * @since 2.6.0
152
+ */
153
+ $priority = (string) apply_filters( 'the_seo_framework_metabox_priority', 'high' );
154
+
155
+ add_meta_box( $id, sprintf( __( '%s SEO Settings', 'autodescription' ), $title ), array( $this, 'pre_seo_box' ), $post_type, $context, $priority, $args );
156
+ }
157
+ }
158
+ }
159
+
160
+ }
161
+
162
+ /**
163
+ * Determines which arguments should be used
164
+ *
165
+ * @since 2.1.8
166
+ *
167
+ * @used by add_inpost_seo_box
168
+ *
169
+ * @param $object the page/post/taxonomy object
170
+ * @param $args the page/post arguments or taxonomy slug.
171
+ *
172
+ * @return string Inpost SEO box.
173
+ */
174
+ public function pre_seo_box( $object, $args ) {
175
+
176
+ if ( is_array( $args ) && isset( $args['args'] ) ) {
177
+ $args_split = $args['args'];
178
+
179
+ $page = $args_split[1];
180
+
181
+ // Return $args as array on post/page
182
+ if ( 'is_post_page' === $page ) {
183
+ // Note: Passes through object.
184
+ return $this->inpost_seo_box( $object, (array) $args );
185
+ }
186
+ } else {
187
+ //* Note: Passes object.
188
+ // Empty the arguments, if any.
189
+ return $this->inpost_seo_box( $object, $args = '' );
190
+ }
191
+
192
+ return '';
193
+ }
194
+
195
+ /**
196
+ * Callback for in-post SEO meta box.
197
+ *
198
+ * @since 2.0.0
199
+ * @access private
200
+ *
201
+ * @param array $post The post object
202
+ *
203
+ * @param object $object the page/post/taxonomy object
204
+ * @param array $args the page/post arguments or taxonomy slug
205
+ *
206
+ * @uses $this->get_custom_field() Get custom field value.
207
+ *
208
+ * Note: Passed through object $object by reference
209
+ */
210
+ public function inpost_seo_box( $object, $args ) {
211
+
212
+ //* Determines if it's inside a meta box or within a taxonomy page.
213
+ $is_term = false;
214
+
215
+ // Args are passed.
216
+ if ( is_array( $args ) && isset( $args['args'] ) ) {
217
+ $args_split = $args['args'];
218
+
219
+ //* The post type callback arg (translated)
220
+ $type = $args_split[0];
221
+ //* The kind of page we're on.
222
+ $page = $args_split[1];
223
+
224
+ // Only add nonce on post/page edit screen
225
+ if ( 'is_post_page' === $page ) {
226
+ wp_nonce_field( 'inpost_seo_save', 'hmpl_ad_inpost_seo_nonce' );
227
+ } else {
228
+ // This shouldn't happen.
229
+ return;
230
+ }
231
+ } else if ( is_object( $object ) ) {
232
+
233
+ //* Singular name.
234
+ $type = $this->get_the_term_name( $object, true, false );
235
+
236
+ //* Plural name.
237
+ if ( empty( $type ) )
238
+ $type = $this->get_the_term_name( $object, false, false );
239
+
240
+ if ( empty( $type ) ) {
241
+ // Fallback to Page as it is generic.
242
+ $type = __( 'Page', 'autodescription' );
243
+ }
244
+
245
+ $is_term = true;
246
+ }
247
+
248
+ //* Echo output.
249
+ if ( $is_term ) {
250
+ $this->tt_inpost_box( $type, $object );
251
+ } else {
252
+ $this->page_inpost_box( $type );
253
+ }
254
+
255
+ }
256
+
257
+ /**
258
+ * Callback function for Taxonomy and Terms inpost box.
259
+ *
260
+ * @since 2.3.5
261
+ * @access private
262
+ *
263
+ * @param string $type The TT type name.
264
+ * @param object $object The TT object.
265
+ */
266
+ public function tt_inpost_box( $type, $object ) {
267
+
268
+ do_action( 'the_seo_framework_pre_tt_inpost_box' );
269
+
270
+ //* Get the language the Google page should assume.
271
+ $language = $this->google_language();
272
+
273
+ $data = $this->get_term_data( $object );
274
+
275
+ $title = isset( $data['title'] ) ? $data['title'] : '';
276
+ $description = isset( $data['description'] ) ? $data['description'] : '';
277
+ $noindex = isset( $data['noindex'] ) ? $data['noindex'] : '';
278
+ $nofollow = isset( $data['nofollow'] ) ? $data['nofollow'] : '';
279
+ $noarchive = isset( $data['noarchive'] ) ? $data['noarchive'] : '';
280
+
281
+ //* Fetch Term ID and taxonomy.
282
+ $term_id = $object->term_id;
283
+ $taxonomy = $object->taxonomy;
284
+
285
+ $generated_doctitle_args = array(
286
+ 'term_id' => $term_id,
287
+ 'taxonomy' => $taxonomy,
288
+ 'placeholder' => true,
289
+ 'get_custom_field' => false,
290
+ );
291
+
292
+ $generated_description_args = array(
293
+ 'id' => $term_id,
294
+ 'taxonomy' => $taxonomy,
295
+ 'get_custom_field' => false,
296
+ );
297
+
298
+ //* Generate title and description.
299
+ $generated_doctitle = $this->title( '', '', '', $generated_doctitle_args );
300
+ $generated_description = $this->generate_description( '', $generated_description_args );
301
+
302
+ $blog_name = $this->get_blogname();
303
+ $add_additions = $this->add_title_additions();
304
+
305
+ /**
306
+ * Separator doesn't matter. Since html_entity_decode is used.
307
+ * Order doesn't matter either. Since it's just used for length calculation.
308
+ *
309
+ * @since 2.3.4
310
+ */
311
+ $doc_pre_rem = $add_additions ? $title . " | " . $blog_name : $title;
312
+ $title_len = $title ? $doc_pre_rem : $generated_doctitle;
313
+ $description_len = $description ? $description : $generated_description;
314
+
315
+ /**
316
+ * Convert to what Google outputs.
317
+ *
318
+ * This will convert e.g. &raquo; to a single length character.
319
+ * @since 2.3.4
320
+ */
321
+ $tit_len_parsed = html_entity_decode( $title_len );
322
+ $desc_len_parsed = html_entity_decode( $description_len );
323
+
324
+ /**
325
+ * Generate static placeholder for when title or description is emptied
326
+ *
327
+ * @since 2.2.4
328
+ */
329
+ $title_placeholder = $generated_doctitle;
330
+ $description_placeholder = $generated_description;
331
+
332
+ ?>
333
+ <h3><?php printf( __( '%s SEO Settings', 'autodescription' ), $type ); ?></h3>
334
+
335
+ <table class="form-table">
336
+ <tbody>
337
+
338
+ <?php if ( 'above' === $this->inpost_seo_bar ) : ?>
339
+ <tr>
340
+ <th scope="row" valign="top"><?php _e( 'Doing it Right', 'autodescription' ); ?></th>
341
+ <td>
342
+ <?php echo $this->post_status( $term_id, $taxonomy, true ); ?>
343
+ </td>
344
+ </tr>
345
+ <?php endif; ?>
346
+
347
+ <tr class="form-field">
348
+ <th scope="row" valign="top">
349
+ <label for="autodescription-meta[doctitle]">
350
+ <strong><?php printf( __( '%s Title', 'autodescription' ), $type ); ?></strong>
351
+ <a href="https://support.google.com/webmasters/answer/35624?hl=<?php echo $language; ?>#3" target="_blank" title="<?php _e( 'Recommended Length: 50 to 55 characters', 'autodescription' ) ?>">[?]</a>
352
+ </label>
353
+ </th>
354
+ <td>
355
+ <div id="autodescription-title-wrap">
356
+ <input name="autodescription-meta[doctitle]" id="autodescription-meta[doctitle]" type="text" placeholder="<?php echo $title_placeholder ?>" value="<?php echo esc_attr( $title ); ?>" size="40" />
357
+ <span id="autodescription-title-offset" class="hide-if-no-js"></span><span id="autodescription-title-placeholder" class="hide-if-no-js"></span>
358
+ </div>
359
+ <p class="description theseoframework-counter"><?php printf( __( 'Characters Used: %s', 'autodescription' ), '<span id="autodescription-meta[doctitle]_chars">'. mb_strlen( $tit_len_parsed ) .'</span>' ); ?></p>
360
+ </td>
361
+ </tr>
362
+
363
+ <tr class="form-field">
364
+ <th scope="row" valign="top">
365
+ <label for="autodescription-meta[description]">
366
+ <strong><?php printf( __( '%s Meta Description', 'autodescription' ), $type ); ?></strong>
367
+ <a href="https://support.google.com/webmasters/answer/35624?hl=<?php echo $language; ?>#1" target="_blank" title="<?php _e( 'Recommended Length: 145 to 155 characters', 'autodescription' ) ?>">[?]</a>
368
+ </label>
369
+ </th>
370
+ <td>
371
+ <textarea name="autodescription-meta[description]" id="autodescription-meta[description]" placeholder="<?php echo $description_placeholder ?>" rows="5" cols="50" class="large-text"><?php echo esc_html( $description ); ?></textarea>
372
+ <p class="description theseoframework-counter"><?php printf( __( 'Characters Used: %s', 'autodescription' ), '<span id="autodescription-meta[description]_chars">'. mb_strlen( $desc_len_parsed ) .'</span>' ); ?></p>
373
+ </td>
374
+ </tr>
375
+
376
+ <tr>
377
+ <th scope="row" valign="top"><?php _e( 'Robots Meta Settings', 'autodescription' ); ?></th>
378
+ <td>
379
+ <label for="autodescription-meta[noindex]"><input name="autodescription-meta[noindex]" id="autodescription-meta[noindex]" type="checkbox" value="1" <?php checked( $noindex ); ?> />
380
+ <?php printf( __( 'Apply %s to this term', 'autodescription' ), $this->code_wrap( 'noindex' ) ); ?>
381
+ <a href="https://support.google.com/webmasters/answer/93710?hl=<?php echo $language; ?>" target="_blank" title="<?php printf( __( 'Tell Search Engines not to show this page in their search results', 'autodescription' ) ) ?>">[?]</a>
382
+ </label>
383
+
384
+ <br>
385
+
386
+ <label for="autodescription-meta[nofollow]"><input name="autodescription-meta[nofollow]" id="autodescription-meta[nofollow]" type="checkbox" value="1" <?php checked( $nofollow ); ?> />
387
+ <?php printf( __( 'Apply %s to this term', 'autodescription' ), $this->code_wrap( 'nofollow' ) ); ?>
388
+ <a href="https://support.google.com/webmasters/answer/96569?hl=<?php echo $language; ?>" target="_blank" title="<?php printf( __( 'Tell Search Engines not to follow links on this page', 'autodescription' ) ) ?>">[?]</a>
389
+ </label>
390
+
391
+ <br>
392
+
393
+ <label for="autodescription-meta[noarchive]"><input name="autodescription-meta[noarchive]" id="autodescription-meta[noarchive]" type="checkbox" value="1" <?php checked( $noarchive ); ?> />
394
+ <?php printf( __( 'Apply %s to this term', 'autodescription' ), $this->code_wrap( 'noarchive' ) ); ?>
395
+ <a href="https://support.google.com/webmasters/answer/79812?hl=<?php echo $language; ?>" target="_blank" title="<?php printf( __( 'Tell Search Engines not to save a cached copy of this page', 'autodescription' ) ) ?>">[?]</a>
396
+ </label>
397
+
398
+ <?php // Saved flag, if set then it won't fetch for Genesis meta anymore ?>
399
+ <label class="hidden" for="autodescription-meta[saved_flag]">
400
+ <input name="autodescription-meta[saved_flag]" id="autodescription-meta[saved_flag]" type="checkbox" value="1" checked='checked' />
401
+ </label>
402
+ </td>
403
+ </tr>
404
+
405
+ <?php if ( 'below' === $this->inpost_seo_bar ) : ?>
406
+ <tr>
407
+ <th scope="row" valign="top"><?php _e( 'Doing it Right', 'autodescription' ); ?></th>
408
+ <td>
409
+ <?php echo $this->post_status( $term_id, $taxonomy, true ); ?>
410
+ </td>
411
+ </tr>
412
+ <?php endif; ?>
413
+
414
+ </tbody>
415
+ </table>
416
+ <?php
417
+
418
+ do_action( 'the_seo_framework_pro_tt_inpost_box' );
419
+
420
+ }
421
+
422
+ /**
423
+ * Callback function for Post and Pages inpost metabox.
424
+ *
425
+ * @since 2.3.5
426
+ * @access private
427
+ *
428
+ * @param string $type The post type name.
429
+ */
430
+ public function page_inpost_box( $type ) {
431
+
432
+ do_action( 'the_seo_framework_pre_page_inpost_box' );
433
+
434
+ //* Get the language the Google page should assume.
435
+ $language = $this->google_language();
436
+
437
+ $post_id = $this->get_the_real_ID();
438
+ $is_static_frontpage = $this->is_static_frontpage( $post_id );
439
+
440
+ $title = $this->get_custom_field( '_genesis_title', $post_id );
441
+
442
+ /**
443
+ * Generate static placeholder for when title or description is emptied
444
+ *
445
+ * @since 2.2.4
446
+ *
447
+ * Fetch description from Home Page SEO Settings placeholder if it exists.
448
+ * @since 2.2.5
449
+ *
450
+ * Generate description for Posts Page if selected in customizer.
451
+ * @since 2.2.8
452
+ */
453
+ if ( $is_static_frontpage ) {
454
+ //* Front page.
455
+ $generated_doctitle_args = array(
456
+ 'page_on_front' => true,
457
+ 'placeholder' => true,
458
+ 'meta' => true,
459
+ 'get_custom_field' => false,
460
+ );
461
+
462
+ $generated_description_args = array(
463
+ 'id' => $post_id,
464
+ 'is_home' => true,
465
+ 'get_custom_field' => true,
466
+ );
467
+ } else if ( $this->is_blog_page( $post_id ) ) {
468
+ //* Page for posts.
469
+ $generated_doctitle_args = array(
470
+ 'placeholder' => true,
471
+ 'meta' => true,
472
+ 'get_custom_field' => false,
473
+ );
474
+
475
+ $generated_description_args = array(
476
+ 'id' => $post_id,
477
+ 'page_for_posts' => true,
478
+ );
479
+ } else {
480
+ $generated_doctitle_args = array(
481
+ 'placeholder' => true,
482
+ 'meta' => true,
483
+ 'get_custom_field' => false,
484
+ );
485
+
486
+ $generated_description_args = array(
487
+ 'id' => $post_id,
488
+ );
489
+ }
490
+ $generated_doctitle = $this->title( '', '', '', $generated_doctitle_args );
491
+ $generated_description = $this->generate_description_from_id( $generated_description_args );
492
+
493
+ /**
494
+ * Special check for home page.
495
+ *
496
+ * @since 2.3.4
497
+ */
498
+ if ( $is_static_frontpage ) {
499
+ if ( $this->get_option( 'homepage_tagline' ) ) {
500
+ $tit_len_pre = $title ? $title . " | " . $this->get_blogdescription() : $generated_doctitle;
501
+ } else {
502
+ $tit_len_pre = $title ? $title : $generated_doctitle;
503
+ }
504
+ } else {
505
+ /**
506
+ * Separator doesn't matter. Since html_entity_decode is used.
507
+ * Order doesn't matter either. Since it's just used for length calculation.
508
+ *
509
+ * @since 2.3.4
510
+ */
511
+ if ( $this->add_title_additions() ) {
512
+ $tit_len_pre = $title ? $title . " | " . $this->get_blogname() : $generated_doctitle;
513
+ } else {
514
+ $tit_len_pre = $title ? $title : $generated_doctitle;
515
+ }
516
+ }
517
+
518
+ //* Fetch description from option.
519
+ $description = $this->get_custom_field( '_genesis_description' );
520
+
521
+ /**
522
+ * Calculate current description length
523
+ *
524
+ * Reworked.
525
+ * @since 2.3.4
526
+ */
527
+ if ( $is_static_frontpage ) {
528
+ //* The homepage description takes precedence.
529
+ $homepage_description = $this->get_option( 'homepage_description' );
530
+
531
+ if ( $description )
532
+ $desc_len_pre = $homepage_description ? $homepage_description : $description;
533
+ else
534
+ $desc_len_pre = $homepage_description ? $homepage_description : $generated_description;
535
+ } else {
536
+ $desc_len_pre = $description ? $description : $generated_description;
537
+ }
538
+
539
+ /**
540
+ * Convert to what Google outputs.
541
+ *
542
+ * This will convert e.g. &raquo; to a single length character.
543
+ * @since 2.3.4
544
+ */
545
+ $tit_len_parsed = html_entity_decode( $tit_len_pre );
546
+ $desc_len_parsed = html_entity_decode( $desc_len_pre );
547
+
548
+ /**
549
+ * Generate static placeholder for when title or description is emptied
550
+ *
551
+ * Now within aptly named vars.
552
+ * @since 2.3.4
553
+ */
554
+ $doctitle_placeholder = $generated_doctitle;
555
+ $description_placeholder = $generated_description;
556
+
557
+ //* Fetch Canonical URL.
558
+ $canonical = $this->get_custom_field( '_genesis_canonical_uri' );
559
+ //* Fetch Canonical URL Placeholder.
560
+ $canonical_placeholder = $this->the_url_from_cache( '', $post_id, false, false );
561
+
562
+ ?>
563
+ <?php if ( 'above' === $this->inpost_seo_bar ) : ?>
564
+ <p>
565
+ <strong><?php _e( 'Doing it Right', 'autodescription' ); ?></strong>
566
+ <div><?php echo $this->post_status( $post_id, 'inpost', true ); ?></div>
567
+ </p>
568
+ <?php endif; ?>
569
+
570
+ <p>
571
+ <label for="autodescription_title"><strong><?php printf( __( 'Custom %s Title', 'autodescription' ), $type ); ?></strong>
572
+ <a href="https://support.google.com/webmasters/answer/35624?hl=<?php echo $language; ?>#3" target="_blank" title="<?php _e( 'Recommended Length: 50 to 55 characters', 'autodescription' ); ?>">[?]</a>
573
+ <span class="description theseoframework-counter"><?php printf( __( 'Characters Used: %s', 'autodescription' ), '<span id="autodescription_title_chars">'. mb_strlen( $tit_len_parsed ) .'</span>' ); ?></span>
574
+ </label>
575
+ </p>
576
+ <p>
577
+ <div id="autodescription-title-wrap">
578
+ <input class="large-text" type="text" name="autodescription[_genesis_title]" id="autodescription_title" placeholder="<?php echo $doctitle_placeholder ?>" value="<?php echo esc_attr( $this->get_custom_field( '_genesis_title' ) ); ?>" />
579
+ <span id="autodescription-title-offset" class="hide-if-no-js"></span><span id="autodescription-title-placeholder" class="hide-if-no-js"></span>
580
+ </div>
581
+ </p>
582
+
583
+ <p>
584
+ <label for="autodescription_description">
585
+ <strong><?php printf( __( 'Custom %s Description', 'autodescription' ), $type ); ?></strong>
586
+ <a href="https://support.google.com/webmasters/answer/35624?hl=<?php echo $language; ?>#1" target="_blank" title="<?php _e( 'Recommended Length: 145 to 155 characters', 'autodescription' ); ?>">[?]</a>
587
+ <span class="description theseoframework-counter"><?php printf( __( 'Characters Used: %s', 'autodescription' ), '<span id="autodescription_description_chars">'. mb_strlen( $desc_len_parsed ) .'</span>' ); ?></span>
588
+ </label>
589
+ </p>
590
+ <p>
591
+ <textarea class="large-text" name="autodescription[_genesis_description]" id="autodescription_description" placeholder="<?php echo $description_placeholder ?>" rows="4" cols="4"><?php echo esc_textarea( $this->get_custom_field( '_genesis_description' ) ); ?></textarea>
592
+ </p>
593
+
594
+ <p>
595
+ <label for="autodescription_canonical">
596
+ <strong><?php _e( 'Custom Canonical URL', 'autodescription' ); ?></strong>
597
+ <a href="https://support.google.com/webmasters/answer/139066?hl=<?php echo $language; ?>" target="_blank" title="<?php printf( __( 'Preferred %s URL location', 'autodescription' ), $type ); ?>">[?]</a>
598
+ </label>
599
+ </p>
600
+ <p>
601
+ <input class="large-text" type="text" name="autodescription[_genesis_canonical_uri]" id="autodescription_canonical" placeholder="<?php echo $canonical_placeholder ?>" value="<?php echo esc_url( $this->get_custom_field( '_genesis_canonical_uri' ) ); ?>" />
602
+ </p>
603
+
604
+ <p><strong><?php _e( 'Robots Meta Settings', 'autodescription' ); ?></strong></p>
605
+ <p>
606
+ <label for="autodescription_noindex"><input type="checkbox" name="autodescription[_genesis_noindex]" id="autodescription_noindex" value="1" <?php checked( $this->get_custom_field( '_genesis_noindex' ) ); ?> />
607
+ <?php
608
+ /* translators: 1: Option, 2: Post or Page */
609
+ printf( __( 'Apply %1$s to this %2$s', 'autodescription' ), $this->code_wrap( 'noindex' ), $type );
610
+ ?>
611
+ <a href="https://support.google.com/webmasters/answer/93710?hl=<?php echo $language; ?>" target="_blank" title="<?php printf( __( 'Tell Search Engines not to show this %s in their search results', 'autodescription' ), $type ); ?>">[?]</a>
612
+ </label>
613
+
614
+ <br>
615
+
616
+ <label for="autodescription_nofollow"><input type="checkbox" name="autodescription[_genesis_nofollow]" id="autodescription_nofollow" value="1" <?php checked( $this->get_custom_field( '_genesis_nofollow' ) ); ?> />
617
+ <?php
618
+ /* translators: 1: Option, 2: Post or Page */
619
+ printf( __( 'Apply %1$s to this %2$s', 'autodescription' ), $this->code_wrap( 'nofollow' ), $type );
620
+ ?>
621
+ <a href="https://support.google.com/webmasters/answer/96569?hl=<?php echo $language; ?>" target="_blank" title="<?php printf( __( 'Tell Search Engines not to follow links on this %s', 'autodescription' ), $type ); ?>">[?]</a>
622
+ </label>
623
+
624
+ <br>
625
+
626
+ <label for="autodescription_noarchive"><input type="checkbox" name="autodescription[_genesis_noarchive]" id="autodescription_noarchive" value="1" <?php checked( $this->get_custom_field( '_genesis_noarchive' ) ); ?> />
627
+ <?php
628
+ /* translators: 1: Option, 2: Post or Page */
629
+ printf( __( 'Apply %1$s to this %2$s', 'autodescription' ), $this->code_wrap( 'noarchive' ), $type );
630
+ ?>
631
+ <a href="https://support.google.com/webmasters/answer/79812?hl=<?php echo $language; ?>" target="_blank" title="<?php printf( __( 'Tell Search Engines not to save a cached copy of this %s', 'autodescription' ), $type ); ?>">[?]</a>
632
+ </label>
633
+ </p>
634
+
635
+ <p><strong><?php _e( 'Local Search Settings', 'autodescription' ); ?></strong></p>
636
+ <p>
637
+ <label for="autodescription_exclude_local_search"><input type="checkbox" name="autodescription[exclude_local_search]" id="autodescription_exclude_local_search" value="1" <?php checked( $this->get_custom_field( 'exclude_local_search' ) ); ?> />
638
+ <?php printf( __( 'Exclude this %s from local search', 'autodescription' ), $type ); ?>
639
+ <span title="<?php printf( __( 'This excludes this %s from local on-site search results', 'autodescription' ), $type ); ?>">[?]</span>
640
+ </label>
641
+ </p>
642
+
643
+ <p>
644
+ <label for="autodescription_redirect">
645
+ <strong><?php _e( 'Custom 301 Redirect URL', 'autodescription' ); ?></strong>
646
+ <a href="https://support.google.com/webmasters/answer/93633?hl=<?php echo $language; ?>" target="_blank" title="<?php _e( 'This will force visitors to go to another URL', 'autodescription' ); ?>">[?]</a>
647
+ </label>
648
+ </p>
649
+ <p>
650
+ <input class="large-text" type="text" name="autodescription[redirect]" id="genesis_redirect" value="<?php echo esc_url( $this->get_custom_field( 'redirect' ) ); ?>" />
651
+ </p>
652
+
653
+ <?php if ( 'below' === $this->inpost_seo_bar ) : ?>
654
+ <p>
655
+ <strong><?php _e( 'Doing it Right', 'autodescription' ); ?></strong>
656
+ <div><?php echo $this->post_status( $post_id, 'inpost', true ); ?></div>
657
+ </p>
658
+ <?php endif;
659
+
660
+ do_action( 'the_seo_framework_pro_page_inpost_box' );
661
+
662
+ }
663
+
664
+ }
inc/classes/metaboxes.class.php ADDED
@@ -0,0 +1,2424 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The SEO Framework plugin
4
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License version 3 as published
8
+ * by the Free Software Foundation.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ /**
20
+ * Class AutoDescription_Metaboxes
21
+ *
22
+ * Outputs Network and Site SEO settings meta boxes
23
+ *
24
+ * @since 2.2.2
25
+ */
26
+ class AutoDescription_Metaboxes extends AutoDescription_Siteoptions {
27
+
28
+ /**
29
+ * Constructor, load parent constructor.
30
+ */
31
+ public function __construct() {
32
+ parent::__construct();
33
+ }
34
+
35
+ /**
36
+ * List of title separators.
37
+ *
38
+ * @since 2.6.0
39
+ *
40
+ * @return array Title separators.
41
+ */
42
+ public function get_separator_list() {
43
+ return array(
44
+ 'pipe' => '|',
45
+ 'dash' => '-',
46
+ 'ndash' => '&ndash;',
47
+ 'mdash' => '&mdash;',
48
+ 'bull' => '&bull;',
49
+ 'middot' => '&middot;',
50
+ 'lsaquo' => '&lsaquo;',
51
+ 'rsaquo' => '&rsaquo;',
52
+ 'frasl' => '&frasl;',
53
+ 'laquo' => '&laquo;',
54
+ 'raquo' => '&raquo;',
55
+ 'le' => '&le;',
56
+ 'ge' => '&ge;',
57
+ 'lt' => '&lt;',
58
+ 'gt' => '&gt;',
59
+ );
60
+ }
61
+
62
+ /**
63
+ * Returns array of Twitter Card Types
64
+ *
65
+ * @since 2.6.0
66
+ *
67
+ * @return array Twitter Card types.
68
+ */
69
+ public function get_twitter_card_types() {
70
+ return array(
71
+ 'summary' => 'summary',
72
+ 'summary_large_image' => 'summary-large-image',
73
+ 'photo' => 'photo',
74
+ );
75
+ }
76
+
77
+ /**
78
+ * Setting nav tab wrappers.
79
+ * Outputs Tabs and settings content.
80
+ *
81
+ * @param string $id The Nav Tab ID
82
+ * @param array $tabs the tab content {
83
+ * $tabs = tab ID key = array(
84
+ * $tabs['name'] => tab name
85
+ * $tabs['callback'] => string|array callback function
86
+ * $tabs['dashicon'] => string Dashicon
87
+ * $tabs['args'] => mixed optional callback function args
88
+ * )
89
+ * }
90
+ * @param string $version the The SEO Framework version for debugging. May be emptied.
91
+ * @param bool $use_tabs Whether to output tabs, only works when $tabs only has one count.
92
+ *
93
+ * @since 2.3.6
94
+ *
95
+ * @refactored
96
+ * @since 2.6.0
97
+ */
98
+ public function nav_tab_wrapper( $id, $tabs = array(), $version = '2.3.6', $use_tabs = true ) {
99
+
100
+ //* Whether tabs are active.
101
+ $use_tabs = $use_tabs && count( $tabs ) > 1 ? true : false;
102
+
103
+ /**
104
+ * Start navigation.
105
+ *
106
+ * Don't output navigation if $use_tabs is false and the amount of tabs is 1 or lower.
107
+ */
108
+ if ( $use_tabs ) {
109
+ ?>
110
+ <div class="seoframework-nav-tab-wrapper hide-if-no-js" id="<?php echo $id; ?>-tabs-wrapper">
111
+ <?php
112
+ $count = 1;
113
+ foreach ( $tabs as $tab => $value ) {
114
+
115
+ $dashicon = isset( $value['dashicon'] ) ? $value['dashicon'] : '';
116
+ $name = isset( $value['name'] ) ? $value['name'] : '';
117
+
118
+ $checked = 1 === $count ? 'checked' : '';
119
+ $the_id = $id . '-tab-' . $tab;
120
+ $the_name = $id . '-tabs';
121
+
122
+ $label_class = $checked ? ' seoframework-active-tab' : ''; // maybe
123
+
124
+ ?>
125
+ <div class="seoframework-tab">
126
+ <input type="radio" class="seoframework-tabs-radio" id="<?php echo $the_id ?>" name="<?php echo $the_name ?>" <?php echo $checked ?>>
127
+ <label for="<?php echo $the_id; ?>" class="seoframework-nav-tab">
128
+ <?php echo $dashicon ? '<span class="dashicons dashicons-' . esc_attr( $dashicon ) . ' seoframework-dashicons-tabs"></span>' : ''; ?>
129
+ <?php echo $name ? '<span class="seoframework-nav-desktop">' . esc_attr( $name ) . '</span>' : ''; ?>
130
+ </label>
131
+ </div>
132
+ <?php
133
+
134
+ $count++;
135
+ }
136
+ ?>
137
+ </div>
138
+ <?php
139
+ }
140
+
141
+ /**
142
+ * Start Content.
143
+ *
144
+ * The content is relative to the navigation, and uses CSS to become visible.
145
+ */
146
+ $count = 1;
147
+ foreach ( $tabs as $tab => $value ) {
148
+
149
+ $the_id = $id . '-tab-' . $tab . '-content';
150
+ $the_name = $id . '-tabs-content';
151
+
152
+ //* Current tab for JS.
153
+ $current = 1 === $count ? ' seoframework-active-tab-content' : '';
154
+
155
+ ?>
156
+ <div class="seoframework-tabs-content <?php echo $the_name . $current; ?>" id="<?php echo $the_id; ?>" >
157
+ <?php
158
+
159
+ //* No-JS tabs.
160
+ if ( $use_tabs ) {
161
+ $dashicon = isset( $value['dashicon'] ) ? $value['dashicon'] : '';
162
+ $name = isset( $value['name'] ) ? $value['name'] : '';
163
+
164
+ ?>
165
+ <div class="hide-if-js seoframework-content-no-js">
166
+ <div class="seoframework-tab seoframework-tab-no-js">
167
+ <span class="seoframework-nav-tab seoframework-active-tab">
168
+ <?php echo $dashicon ? '<span class="dashicons dashicons-' . esc_attr( $dashicon ) . ' seoframework-dashicons-tabs"></span>' : ''; ?>
169
+ <?php echo $name ? '<span>' . esc_attr( $name ) . '</span>' : ''; ?>
170
+ </span>
171
+ </div>
172
+ </div>
173
+ <?php
174
+ }
175
+
176
+ $callback = isset( $value['callback'] ) ? $value['callback'] : '';
177
+
178
+ if ( $callback ) {
179
+ $params = isset( $value['args'] ) ? $value['args'] : '';
180
+ $output = $this->call_function( $callback, $version, $params );
181
+ echo $output;
182
+ }
183
+
184
+ ?>
185
+ </div>
186
+ <?php
187
+
188
+ $count++;
189
+ }
190
+
191
+ }
192
+
193
+ /**
194
+ * Title meta box on the Site SEO Settings page.
195
+ *
196
+ * @since 2.2.2
197
+ *
198
+ * @param array $args The metabox arguments.
199
+ *
200
+ * @see $this->title_metabox() Callback for Title Settings box.
201
+ */
202
+ public function title_metabox( $args = array() ) {
203
+
204
+ do_action( 'the_seo_framework_title_metabox_before' );
205
+
206
+ $latest_post_id = $this->get_latest_post_id();
207
+
208
+ if ( $latest_post_id ) {
209
+ $post = get_post( $latest_post_id, OBJECT );
210
+ $title = esc_attr( $post->post_title );
211
+ } else {
212
+ $title = esc_attr__( 'Example Post Title', 'autodescription' );
213
+ }
214
+
215
+ $blogname = $this->get_blogname();
216
+ $sep = $this->get_separator( 'title', true );
217
+
218
+ $additions_left = '<span class="title-additions-js">' . $blogname . '<span class="autodescription-sep-js">' . " $sep " . '</span></span>';
219
+ $additions_right = '<span class="title-additions-js"><span class="autodescription-sep-js">' . " $sep " . '</span>' . $blogname . '</span>';
220
+
221
+ $example_left = '<em>' . $additions_left . $title . '</em>';
222
+ $example_right = '<em>' . $title . $additions_right . '</em>';
223
+
224
+ $showleft = 'left' === $this->get_option( 'title_location' ) ? true : false;
225
+ //* Check left first, as right is default (and thus fallback).
226
+ $example_nojs = $showleft ? $example_left : $example_right;
227
+
228
+ ?>
229
+ <h4><?php printf( __( 'Automated Title Settings', 'autodescription' ) ); ?></h4>
230
+ <p><span class="description"><?php printf( __( "The page title is prominently shown within the browser tab as well as within the Search Engine results pages.", 'autodescription' ) ); ?></span></p>
231
+
232
+ <h4><?php _e( 'Example Automated Title Output', 'autodescription' ); ?></h4>
233
+ <p>
234
+ <span class="title-additions-example-left" style="display:<?php echo $showleft ? 'inline' : 'none'; ?>"><?php echo $this->code_wrap_noesc( $example_left ); ?></span>
235
+ <span class="title-additions-example-right" style="display:<?php echo $showleft ? 'none' : 'inline'; ?>"><?php echo $this->code_wrap_noesc( $example_right ); ?></span>
236
+ </p>
237
+
238
+ <hr>
239
+ <?php
240
+
241
+ /**
242
+ * Parse tabs content
243
+ *
244
+ * @param array $default_tabs { 'id' = The identifier =>
245
+ * array(
246
+ * 'name' => The name
247
+ * 'callback' => The callback function, use array for method calling (accepts $this, but isn't used here for optimization purposes)
248
+ * 'dashicon' => Desired dashicon
249
+ * )
250
+ * }
251
+ *
252
+ * @since 2.2.2
253
+ */
254
+ $default_tabs = array(
255
+ 'general' => array(
256
+ 'name' => __( 'General', 'autodescription' ),
257
+ 'callback' => array( $this, 'title_metabox_general_tab' ),
258
+ 'dashicon' => 'admin-generic',
259
+ ),
260
+ 'additions' => array(
261
+ 'name' => __( 'Additions', 'autodescription' ),
262
+ 'callback' => array( $this, 'title_metabox_additions_tab' ),
263
+ 'dashicon' => 'plus',
264
+ 'args' => array(
265
+ 'examples' => array(
266
+ 'left' => $example_left,
267
+ 'right' => $example_right,
268
+ ),
269
+ ),
270
+ ),
271
+ 'prefixes' => array(
272
+ 'name' => __( 'Prefixes', 'autodescription' ),
273
+ 'callback' => array( $this, 'title_metabox_prefixes_tab' ),
274
+ 'dashicon' => 'plus-alt',
275
+ 'args' => array(
276
+ 'additions' => array(
277
+ 'left' => $additions_left,
278
+ 'right' => $additions_right,
279
+ ),
280
+ 'showleft' => $showleft,
281
+ ),
282
+ )
283
+ );
284
+
285
+ /**
286
+ * Applies filters the_seo_framework_title_settings_tabs : array see $default_tabs
287
+ * @since 2.6.0
288
+ *
289
+ * Used to extend Description tabs.
290
+ */
291
+ $defaults = (array) apply_filters( 'the_seo_framework_title_settings_tabs', $default_tabs, $args );
292
+
293
+ $tabs = wp_parse_args( $args, $defaults );
294
+
295
+ $this->nav_tab_wrapper( 'title', $tabs, '2.6.0' );
296
+
297
+ do_action( 'the_seo_framework_title_metabox_after' );
298
+ }
299
+
300
+ /**
301
+ * Title meta box general tab.
302
+ *
303
+ * @since 2.6.0
304
+ *
305
+ * @see $this->title_metabox() : Callback for Title Settings box.
306
+ */
307
+ public function title_metabox_general_tab() {
308
+
309
+ $title_separator = $this->get_separator_list();
310
+
311
+ $recommended = ' class="recommended" title="' . esc_attr__( 'Recommended', 'autodescription' ) . '"';
312
+
313
+ ?>
314
+ <fieldset>
315
+ <legend><h4><?php _e( 'Document Title Separator', 'autodescription' ); ?></h4></legend>
316
+ <p id="title-separator" class="theseoframework-fields">
317
+ <?php foreach ( $title_separator as $name => $html ) { ?>
318
+ <input type="radio" name="<?php $this->field_name( 'title_seperator' ); ?>" id="<?php $this->field_id( 'title_seperator_' . $name ); ?>" value="<?php echo $name ?>" <?php checked( $this->get_field_value( 'title_seperator' ), $name ); ?> />
319
+ <label for="<?php $this->field_id( 'title_seperator_' . $name ); ?>" <?php echo ( $name === 'pipe' || $name === 'dash' ) ? $recommended : ''; ?>><?php echo $html ?></label>
320
+ <?php } ?>
321
+ </p>
322
+ <span class="description"><?php _e( 'If the title consists of two parts (original title and optional addition), then the separator will go in-between them.', 'autodescription' ); ?></span>
323
+ </fieldset>
324
+ <?php
325
+
326
+ }
327
+
328
+ /**
329
+ * Title meta box general tab.
330
+ *
331
+ * @since 2.6.0
332
+ *
333
+ * @param array $examples : array {
334
+ * 'left' => Left Example
335
+ * 'right' => Right Example
336
+ * }
337
+ *
338
+ * @see $this->title_metabox() : Callback for Title Settings box.
339
+ */
340
+ public function title_metabox_additions_tab( $examples = array() ) {
341
+
342
+ $example_left = $examples['left'];
343
+ $example_right = $examples['right'];
344
+
345
+ $language = $this->google_language();
346
+
347
+ $home_page_has_option = __( 'The Home Page has a specific option.', 'autodescription' );
348
+
349
+ ?>
350
+ <fieldset>
351
+ <legend><h4><?php _e( 'Document Title Additions Location', 'autodescription' ); ?></h4></legend>
352
+
353
+ <p>
354
+ <span class="description"><?php _e( 'Determines which side the added title text will go on.', 'autodescription' ); ?></span>
355
+ </p>
356
+ <p id="title-location" class="theseoframework-fields">
357
+ <span class="toblock">
358
+ <input type="radio" name="<?php $this->field_name( 'title_location' ); ?>" id="<?php $this->field_id( 'title_location_left' ); ?>" value="left" <?php checked( $this->get_field_value( 'title_location' ), 'left' ); ?> />
359
+ <label for="<?php $this->field_id( 'title_location_left' ); ?>">
360
+ <span><?php _e( 'Left:', 'autodescription' ); ?></span>
361
+ <?php echo $this->code_wrap_noesc( $example_left ) ?>
362
+ </label>
363
+ </span>
364
+ <span class="toblock">
365
+ <input type="radio" name="<?php $this->field_name( 'title_location' ); ?>" id="<?php $this->field_id( 'title_location_right' ); ?>" value="right" <?php checked( $this->get_field_value( 'title_location' ), 'right' ); ?> />
366
+ <label for="<?php $this->field_id( 'title_location_right' ); ?>">
367
+ <span><?php _e( 'Right:', 'autodescription' ); ?></span>
368
+ <?php echo $this->code_wrap_noesc( $example_right ); ?>
369
+ </label>
370
+ </span>
371
+ </p>
372
+ <span class="description"><?php echo $home_page_has_option; ?></span>
373
+ </fieldset>
374
+ <?php
375
+
376
+ /**
377
+ * @todo use checkbox function
378
+ * @priority low 2.6.x
379
+ */
380
+
381
+ //* Only add this option if the theme is doing it right.
382
+ if ( $this->can_manipulate_title() ) : ?>
383
+ <hr>
384
+
385
+ <h4><?php _e( 'Remove Blogname from Title', 'autodescription' ); ?></h4>
386
+ <p id="title-additions-toggle">
387
+ <label for="<?php $this->field_id( 'title_rem_additions' ); ?>">
388
+ <input type="checkbox" name="<?php $this->field_name( 'title_rem_additions' ); ?>" id="<?php $this->field_id( 'title_rem_additions' ); ?>" <?php $this->is_conditional_checked( 'title_rem_additions' ); ?> value="1" <?php checked( $this->get_field_value( 'title_rem_additions' ) ); ?> />
389
+ <?php _e( 'Remove Blogname from title?', 'autodescription' ); ?>
390
+ </label>
391
+ <a href="<?php echo esc_url( 'https://support.google.com/webmasters/answer/35624?hl=' . $language . '#3' ); ?>" target="_blank" title="<?php _e( 'This might decouple your posts and pages from the rest of the website.', 'autodescription' ); ?>">[?]</a>
392
+ </p>
393
+ <span class="description"><?php _e( 'Only use this option if you are aware of its SEO effects.', 'autodescription' ); ?></span>
394
+ <span class="description"><?php echo $home_page_has_option; ?></span>
395
+ <?php endif;
396
+
397
+ }
398
+
399
+ /**
400
+ * Title meta box prefixes tab.
401
+ *
402
+ * @since 2.6.0
403
+ *
404
+ * @param array $additions : array {
405
+ * 'left' => Left Example Addtitions
406
+ * 'right' => Right Example Additions
407
+ * }
408
+ * @param bool $showleft The example location.
409
+ *
410
+ * @see $this->title_metabox() : Callback for Title Settings box.
411
+ */
412
+ public function title_metabox_prefixes_tab( $additions = array(), $showleft = false ) {
413
+
414
+ $left_additions = $additions['left'];
415
+ $right_additions = $additions['right'];
416
+
417
+ //* Get translated category label, if it exists. Otherwise, fallback to translation.
418
+ $term_labels = $this->get_tax_labels( 'category' );
419
+ $label = isset( $term_labels->singular_name ) ? $term_labels->singular_name : __( 'Category', 'autodescription' );
420
+
421
+ $cats = get_terms( array( 'taxonomy' => 'category', 'fields' => 'ids', 'hide_empty' => false, 'order' => 'ASC', 'number' => 1 ) );
422
+ if ( is_array( $cats ) && ! empty( $cats ) ) {
423
+ //* Category should exist.
424
+ $cat = reset( $cats );
425
+ } else {
426
+ //* Default fallback category.
427
+ $cat = 1;
428
+ }
429
+ //* If cat is found, it will return its name. Otherwise it's an empty string.
430
+ $cat_name = get_cat_name( $cat );
431
+ $cat_name = $cat_name ? $cat_name : __( 'Example Category', 'autodescription' );
432
+
433
+ $display_prefix = $this->is_option_checked( 'title_rem_prefixes' ) ? 'none' : 'inline';
434
+ $title = '<span class="title-prefix-example" style="display:' . $display_prefix . '">' . $label . ': </span>' . $cat_name;
435
+
436
+ $example_left = '<em>' . $left_additions . $title . '</em>';
437
+ $example_right = '<em>' . $title . $right_additions . '</em>';
438
+
439
+ $example_nojs = $showleft ? $example_left : $example_right;
440
+
441
+ $language = $this->google_language();
442
+
443
+ /**
444
+ * @todo use checkbox function
445
+ * @priority low 2.6.x
446
+ */
447
+
448
+ ?>
449
+ <h4><?php _e( 'Title prefix options', 'autodescription' ); ?></h4>
450
+ <p><span class="description"><?php _e( "On archives a descriptive prefix may be added to the title.", 'autodescription' ); ?></span></p>
451
+
452
+ <h4><?php _e( 'Example Automated Archive Title Output' ); ?></h4>
453
+ <p>
454
+ <span class="title-additions-example-left" style="display:<?php echo $showleft ? 'inline' : 'none'; ?>"><?php echo $this->code_wrap_noesc( $example_left ); ?></span>
455
+ <span class="title-additions-example-right" style="display:<?php echo $showleft ? 'none' : 'inline'; ?>"><?php echo $this->code_wrap_noesc( $example_right ); ?></span>
456
+ </p>
457
+
458
+ <hr>
459
+
460
+ <h4><?php _e( 'Remove Archive Title Prefixes', 'autodescription' ); ?></h4>
461
+ <p id="title-prefixes-toggle">
462
+ <label for="<?php $this->field_id( 'title_rem_prefixes' ); ?>">
463
+ <input type="checkbox" name="<?php $this->field_name( 'title_rem_prefixes' ); ?>" id="<?php $this->field_id( 'title_rem_prefixes' ); ?>" <?php $this->is_conditional_checked( 'title_rem_prefixes' ); ?> value="1" <?php checked( $this->get_field_value( 'title_rem_prefixes' ) ); ?> />
464
+ <?php _e( 'Remove Prefixes from title?', 'autodescription' ); ?>
465
+ </label>
466
+ <?php
467
+ $this->make_info(
468
+ __( "The prefix helps visitors and Search Engines determine what kind of page they're visiting", 'autodescription' ),
469
+ 'https://support.google.com/webmasters/answer/35624?hl=' . $language . '#3',
470
+ true
471
+ );
472
+ ?>
473
+ </p>
474
+ <?php
475
+
476
+ }
477
+
478
+ /**
479
+ * Description meta box on the Site SEO Settings page.
480
+ *
481
+ * @since 2.3.4
482
+ *
483
+ * @param array $args The metabox arguments.
484
+ *
485
+ * @see $this->description_metabox() Callback for Description Settings box.
486
+ */
487
+ public function description_metabox( $args = array() ) {
488
+
489
+ do_action( 'the_seo_framework_description_metabox_before' );
490
+
491
+ $blogname = $this->get_blogname();
492
+ $sep = $this->get_separator( 'description', true );
493
+
494
+ /**
495
+ * Generate example.
496
+ */
497
+ $page_title = __( 'Example Title', 'autodescription' );
498
+ $on = _x( 'on', 'Placement. e.g. Post Title "on" Blog Name', 'autodescription' );
499
+ $excerpt = __( 'This is an example description...', 'autodescription' );
500
+
501
+ $page_title = $this->escape_description( $page_title );
502
+ $on = $this->escape_description( $on );
503
+ $excerpt = $this->escape_description( $excerpt );
504
+
505
+ //* Put it together.
506
+ $example = '<span id="description-additions-js">'
507
+ . $page_title
508
+ . '<span id="on-blogname-js">' . " $on " . $blogname . '</span>'
509
+ . '<span id="autodescription-descsep-js">' . " $sep " . '</span>'
510
+ . '</span>'
511
+ . $excerpt
512
+ ;
513
+
514
+ $nojs_additions = '';
515
+ //* Add or remove additions based on option.
516
+ if ( $this->add_description_additions() ) {
517
+ $description_blogname_additions = $this->get_option( 'description_blogname' );
518
+
519
+ $example_nojs_onblog = $description_blogname_additions ? $page_title . " $on " . $blogname : $page_title;
520
+ $nojs_additions = $example_nojs_onblog . " $sep ";
521
+ }
522
+
523
+ $example_nojs = $nojs_additions . $excerpt;
524
+
525
+ ?>
526
+ <h4><?php printf( __( 'Automated Description Settings', 'autodescription' ) ); ?></h4>
527
+ <p><span class="description"><?php printf( __( "The meta description can be used to determine the text used under the title on Search Engine results pages.", 'autodescription' ) ); ?></span></p>
528
+
529
+ <h4><?php _e( 'Example Automated Description Output', 'autodescription' ); ?></h4>
530
+ <p class="hide-if-no-js"><?php echo $this->code_wrap_noesc( $example ); ?></p>
531
+ <p class="hide-if-js"><?php echo $this->code_wrap( $example_nojs ); ?></p>
532
+
533
+ <hr>
534
+ <?php
535
+
536
+ /**
537
+ * Parse tabs content
538
+ *
539
+ * @param array $default_tabs { 'id' = The identifier =>
540
+ * array(
541
+ * 'name' => The name
542
+ * 'callback' => The callback function, use array for method calling (accepts $this, but isn't used here for optimization purposes)
543
+ * 'dashicon' => Desired dashicon
544
+ * )
545
+ * }
546
+ *
547
+ * @since 2.6.0
548
+ */
549
+ $default_tabs = array(
550
+ 'general' => array(
551
+ 'name' => __( 'General', 'autodescription' ),
552
+ 'callback' => array( $this, 'description_metabox_general_tab' ),
553
+ 'dashicon' => 'admin-generic',
554
+ ),
555
+ 'additions' => array(
556
+ 'name' => __( 'Additions', 'autodescription' ),
557
+ 'callback' => array( $this, 'description_metabox_additions_tab' ),
558
+ 'dashicon' => 'plus',
559
+ ),
560
+ );
561
+
562
+ /**
563
+ * Applies filters the_seo_framework_description_settings_tabs : array see $default_tabs
564
+ * @since 2.6.0
565
+ *
566
+ * Used to extend Description tabs.
567
+ */
568
+ $defaults = (array) apply_filters( 'the_seo_framework_description_settings_tabs', $default_tabs, $args );
569
+
570
+ $tabs = wp_parse_args( $args, $defaults );
571
+
572
+ $this->nav_tab_wrapper( 'description', $tabs, '2.6.0' );
573
+
574
+ do_action( 'the_seo_framework_description_metabox_after' );
575
+
576
+ }
577
+
578
+ /**
579
+ * Description meta box general tab.
580
+ *
581
+ * @since 2.6.0
582
+ *
583
+ * @see $this->description_metabox() Callback for Description Settings box.
584
+ */
585
+ public function description_metabox_general_tab() {
586
+
587
+ //* Let's use the same separators as for the title.
588
+ $description_separator = $this->get_separator_list();
589
+ $sep_option = $this->get_option( 'description_separator' );
590
+ $sep_option = $sep_option ? $sep_option : 'pipe';
591
+
592
+ $recommended = ' class="recommended" title="' . __( 'Recommended', 'autodescription' ) . '"';
593
+
594
+ ?>
595
+ <fieldset>
596
+ <legend><h4><?php _e( 'Description Excerpt Separator', 'autodescription' ); ?></h4></legend>
597
+ <p id="description-separator" class="theseoframework-fields">
598
+ <?php foreach ( $description_separator as $name => $html ) { ?>
599
+ <input type="radio" name="<?php $this->field_name( 'description_separator' ); ?>" id="<?php $this->field_id( 'description_separator' . $name ); ?>" value="<?php echo $name ?>" <?php checked( $sep_option, $name ); ?> />
600
+ <label for="<?php $this->field_id( 'description_separator' . $name ); ?>" <?php echo ( 'pipe' === $name || 'dash' === $name ) ? $recommended : ''; ?>><?php echo $html ?></label>
601
+ <?php } ?>
602
+ </p>
603
+ <span class="description"><?php _e( 'If the Automated Description consists of two parts (title and excerpt), then the separator will go in-between them.', 'autodescription' ); ?></span>
604
+ </fieldset>
605
+ <?php
606
+
607
+ }
608
+
609
+ /**
610
+ * Description meta box additions tab.
611
+ *
612
+ * @since 2.6.0
613
+ *
614
+ * @see $this->description_metabox() Callback for Description Settings box.
615
+ */
616
+ public function description_metabox_additions_tab() {
617
+
618
+ $language = $this->google_language();
619
+ $google_explanation = esc_url( 'https://support.google.com/webmasters/answer/35624?hl=' . $language . '#1' );
620
+
621
+ /**
622
+ * @todo use checkbox functions.
623
+ * @priority low 2.6.x
624
+ */
625
+ ?>
626
+ <h4><?php printf( __( 'Description Additions Settings', 'autodescription' ) ); ?></h4>
627
+ <p><span class="description"><?php printf( __( "To create a more organic description, a small introduction can be added before the description.", 'autodescription' ) ); ?></span></p>
628
+ <p><span class="description"><?php printf( __( "The introduction consists of the title and optionally the blogname.", 'autodescription' ) ); ?></span></p>
629
+
630
+ <hr>
631
+
632
+ <h4><?php _e( 'Add descriptive Additions to Description', 'autodescription' ); ?></h4>
633
+ <p id="description-additions-toggle">
634
+ <label for="<?php $this->field_id( 'description_additions' ); ?>" class="toblock">
635
+ <input type="checkbox" name="<?php $this->field_name( 'description_additions' ); ?>" id="<?php $this->field_id( 'description_additions' ); ?>" <?php $this->is_conditional_checked( 'description_additions' ); ?> value="1" <?php checked( $this->get_field_value( 'description_additions' ) ); ?> />
636
+ <?php _e( 'Add Additions to automated description?', 'autodescription' ); ?>
637
+ <a href="<?php echo esc_url( $google_explanation ); ?>" target="_blank" class="description" title="<?php _e( 'This creates good meta descriptions', 'autodescription' ); ?>">[?]</a>
638
+ </label>
639
+ </p>
640
+
641
+ <h4><?php _e( 'Add Blogname to Additions', 'autodescription' ); ?></h4>
642
+ <p id="description-onblogname-toggle">
643
+ <label for="<?php $this->field_id( 'description_blogname' ); ?>" class="toblock">
644
+ <input type="checkbox" name="<?php $this->field_name( 'description_blogname' ); ?>" id="<?php $this->field_id( 'description_blogname' ); ?>" <?php $this->is_conditional_checked( 'description_blogname' ); ?> value="1" <?php checked( $this->get_field_value( 'description_blogname' ) ); ?> />
645
+ <?php _e( 'Add Blogname to automated description additions?', 'autodescription' ); ?>
646
+ </label>
647
+ </p>
648
+ <?php
649
+
650
+ }
651
+
652
+ /**
653
+ * Robots meta box on the Site SEO Settings page.
654
+ *
655
+ * @since 2.2.2
656
+ */
657
+ public function robots_metabox( $args = array() ) {
658
+
659
+ do_action( 'the_seo_framework_robots_metabox_before' );
660
+
661
+ //* Robots types
662
+ $types = array(
663
+ 'category' => __( 'Category', 'autodescription' ),
664
+ 'tag' => __( 'Tag', 'autodescription' ),
665
+ 'author' => __( 'Author', 'autodescription' ),
666
+ 'date' => __( 'Date', 'autodescription' ),
667
+ 'search' => __( 'Search Pages', 'autodescription' ),
668
+ 'attachment' => __( 'Attachment Pages', 'autodescription' ),
669
+ 'site' => _x( 'the entire site', '...for the entire site', 'autodescription' ),
670
+ );
671
+
672
+ //* Robots i18n
673
+ $robots = array(
674
+ 'noindex' => array(
675
+ 'value' => 'noindex',
676
+ 'name' => __( 'NoIndex', 'autodescription' ),
677
+ 'desc' => __( 'These options prevent indexing of the selected archives and pages. If you enable this, the selected archives or pages will be removed from Search Engine results pages.', 'autodescription' ),
678
+ ),
679
+ 'nofollow' => array(
680
+ 'value' => 'nofollow',
681
+ 'name' => __( 'NoFollow', 'autodescription' ),
682
+ 'desc' => __( 'These options prevent links from being followed on the selected archives and pages. If you enable this, the selected archives or pages in-page links will gain no SEO value, including your own links.', 'autodescription' ),
683
+ ),
684
+ 'noarchive' => array(
685
+ 'value' => 'noarchive',
686
+ 'name' => __( 'NoArchive', 'autodescription' ),
687
+ 'desc' => __( 'These options prevent caching of the selected archives and pages. If you enable this, Search Engines will not create a cached copy of the selected archives or pages.', 'autodescription' ),
688
+ ),
689
+ );
690
+
691
+ /**
692
+ * Parse tabs content
693
+ *
694
+ * @param array $default_tabs { 'id' = The identifier =>
695
+ * array(
696
+ * 'name' => The name
697
+ * 'callback' => function callback
698
+ * 'dashicon' => WordPress Dashicon
699
+ * 'args' => function args
700
+ * )
701
+ * }
702
+ *
703
+ * @since 2.2.2
704
+ */
705
+ $default_tabs = array(
706
+ 'general' => array(
707
+ 'name' => __( 'General', 'autodescription' ),
708
+ 'callback' => array( $this, 'robots_metabox_general_tab' ),
709
+ 'dashicon' => 'admin-generic',
710
+ 'args' => '',
711
+ ),
712
+ 'index' => array(
713
+ 'name' => __( 'Indexing', 'autodescription' ),
714
+ 'callback' => array( $this, 'robots_metabox_no_tab' ),
715
+ 'dashicon' => 'filter',
716
+ 'args' => array( $types, $robots['noindex'] ),
717
+ ),
718
+ 'follow' => array(
719
+ 'name' => __( 'Following', 'autodescription' ),
720
+ 'callback' => array( $this, 'robots_metabox_no_tab' ),
721
+ 'dashicon' => 'editor-unlink',
722
+ 'args' => array( $types, $robots['nofollow'] ),
723
+ ),
724
+ 'archive' => array(
725
+ 'name' => __( 'Archiving', 'autodescription' ),
726
+ 'callback' => array( $this, 'robots_metabox_no_tab' ),
727
+ 'dashicon' => 'download',
728
+ 'args' => array( $types, $robots['noarchive'] ),
729
+ ),
730
+ );
731
+
732
+ /**
733
+ * Applies filters 'the_seo_framework_robots_settings_tabs' : array see $default_tabs
734
+ *
735
+ * Used to extend Social tabs
736
+ * @since 2.2.4
737
+ */
738
+ $defaults = (array) apply_filters( 'the_seo_framework_robots_settings_tabs', $default_tabs, $args );
739
+
740
+ $tabs = wp_parse_args( $args, $defaults );
741
+
742
+ $this->nav_tab_wrapper( 'robots', $tabs, '2.2.4' );
743
+
744
+ do_action( 'the_seo_framework_robots_metabox_after' );
745
+
746
+ }
747
+
748
+ /**
749
+ * Robots Metabox General Tab output
750
+ *
751
+ * @since 2.2.4
752
+ *
753
+ * @see $this->robots_metabox() Callback for Robots Settings box.
754
+ */
755
+ protected function robots_metabox_general_tab() {
756
+
757
+ ?>
758
+ <h4><?php _e( 'Open Directory Settings', 'autodescription' ); ?></h4>
759
+ <p class="description"><?php printf( __( "Sometimes, Search Engines use resources from certain Directories to find titles and descriptions for your content. You generally don't want them to. Turn these options on to prevent them from doing so.", 'autodescription' ), $this->code_wrap( 'noodp' ), $this->code_wrap( 'noydir' ) ); ?></p>
760
+ <p class="description"><?php _e( "The Open Directory Project and the Yahoo! Directory may contain outdated SEO values. Therefore, it's best to leave these options checked.", 'autodescription' ); ?></p>
761
+ <?php
762
+
763
+ $this->wrap_fields(
764
+ array(
765
+ $this->make_checkbox(
766
+ 'noodp',
767
+ sprintf( __( 'Apply %s to the entire site?', 'autodescription' ), $this->code_wrap( 'noodp' ) ),
768
+ ''
769
+ ),
770
+ $this->make_checkbox(
771
+ 'noydir',
772
+ sprintf( __( 'Apply %s to the entire site?', 'autodescription' ), $this->code_wrap( 'noydir' ) ),
773
+ ''
774
+ ),
775
+ ),
776
+ true
777
+ );
778
+
779
+ ?>
780
+ <hr>
781
+
782
+ <h4><?php _e( 'Paginated Archive Settings', 'autodescription' ); ?></h4>
783
+ <p class="description"><?php printf( __( "Indexing the second or later page of any archive might cause duplication errors. Search Engines look down upon them; therefore, it's recommended to disable indexing of those pages.", 'autodescription' ), $this->code_wrap( 'noodp' ), $this->code_wrap( 'noydir' ) ); ?></p>
784
+ <?php
785
+
786
+ $this->wrap_fields(
787
+ $this->make_checkbox(
788
+ 'paged_noindex',
789
+ sprintf( __( 'Apply %s to every second or later archive page?', 'autodescription' ), $this->code_wrap( 'noindex' ) ),
790
+ ''
791
+ ),
792
+ true
793
+ );
794
+
795
+ }
796
+
797
+ /**
798
+ * Robots Metabox
799
+ * No-: Index/Follow/Archive
800
+ * Tab output
801
+ *
802
+ * @since 2.2.4
803
+ */
804
+ protected function robots_metabox_no_tab( $types, $robots ) {
805
+
806
+ $ro_value = $robots['value'];
807
+ $ro_name = $robots['name'];
808
+ $ro_i18n = $robots['desc'];
809
+
810
+ ?>
811
+ <h4><?php printf( __( '%s Robots Settings', 'autodescription' ), $ro_name ); ?></h4>
812
+ <p><span class="description"><?php echo $ro_i18n ?></span></p>
813
+ <p class="theseoframework-fields">
814
+ <?php
815
+
816
+ $checkboxes = '';
817
+
818
+ foreach ( $types as $type => $i18n ) {
819
+
820
+ if ( 'site' === $type || 'attachment' === $type || 'search' === $type ) {
821
+ //* Singular.
822
+ /* translators: 1: Option, 2: Post Type */
823
+ $label = sprintf( __( 'Apply %1$s to %2$s?', 'autodescription' ), $this->code_wrap( $ro_name ), $i18n );
824
+ } else {
825
+ //* Archive.
826
+ /* translators: 1: Option, 2: Post Type */
827
+ $label = sprintf( __( 'Apply %1$s to %2$s Archives?', 'autodescription' ), $this->code_wrap( $ro_name ), $i18n );
828
+ }
829
+
830
+ $id = $type . '_' . $ro_value;
831
+
832
+ //* Add <hr> if it's 'site'
833
+ $checkboxes .= ( 'site' === $type ) ? '<hr class="theseoframework-option-spacer">' : '';
834
+
835
+ $checkboxes .= $this->make_checkbox( $id, $label, '' );
836
+ }
837
+
838
+ //* Echo checkboxes.
839
+ echo $this->wrap_fields( $checkboxes );
840
+ ?>
841
+ </p>
842
+ <?php
843
+
844
+ }
845
+
846
+ /**
847
+ * Home Page meta box on the Site SEO Settings page.
848
+ *
849
+ * @param array $args The navigation tabs args.
850
+ *
851
+ * @since 2.2.2
852
+ */
853
+ public function homepage_metabox( $args = array() ) {
854
+
855
+ do_action( 'the_seo_framework_homepage_metabox_before' );
856
+
857
+ ?>
858
+ <p><span class="description"><?php printf( __( 'These settings will take precedence over the settings set within the Home Page edit screen, if any.', 'autodescription' ) ); ?></span></p>
859
+
860
+ <hr>
861
+ <?php
862
+
863
+ /**
864
+ * Parse tabs content
865
+ *
866
+ * @param array $default_tabs { 'id' = The identifier =>
867
+ * array(
868
+ * 'name' => The name
869
+ * 'callback' => The callback function, use array for method calling (accepts $this, but isn't used here for optimization purposes)
870
+ * 'dashicon' => Desired dashicon
871
+ * )
872
+ * }
873
+ *
874
+ * @since 2.6.0
875
+ */
876
+ $default_tabs = array(
877
+ 'general' => array(
878
+ 'name' => __( 'General', 'autodescription' ),
879
+ 'callback' => array( $this, 'homepage_metabox_general' ),
880
+ 'dashicon' => 'admin-generic',
881
+ ),
882
+ 'additions' => array(
883
+ 'name' => __( 'Additions', 'autodescription' ),
884
+ 'callback' => array( $this, 'homepage_metabox_additions' ),
885
+ 'dashicon' => 'plus',
886
+ ),
887
+ 'robots' => array(
888
+ 'name' => __( 'Robots', 'autodescription' ),
889
+ 'callback' => array( $this, 'homepage_metabox_robots' ),
890
+ 'dashicon' => 'visibility',
891
+ ),
892
+ );
893
+
894
+ /**
895
+ * Applies filters the_seo_framework_homepage_settings_tabs : array see $default_tabs
896
+ * @since 2.6.0
897
+ *
898
+ * Used to extend HomePage tabs.
899
+ */
900
+ $defaults = (array) apply_filters( 'the_seo_framework_homepage_settings_tabs', $default_tabs, $args );
901
+
902
+ $tabs = wp_parse_args( $args, $defaults );
903
+
904
+ $this->nav_tab_wrapper( 'homepage', $tabs, '2.6.0' );
905
+
906
+ do_action( 'the_seo_framework_homepage_metabox_after' );
907
+
908
+ }
909
+
910
+ /**
911
+ * HomePage Metabox General Tab Output
912
+ *
913
+ * @since 2.6.0
914
+ *
915
+ * @see $this->homepage_metabox() Callback for HomePage Settings box.
916
+ */
917
+ public function homepage_metabox_general() {
918
+
919
+ /**
920
+ * @param string $language The language for help pages. See $this->google_language();
921
+ */
922
+ $language = $this->google_language();
923
+
924
+ /**
925
+ * @param bool $page_on_front False if homepage is blog, true if single page/post
926
+ * @param bool $home_description_frompost True if home inpost title is filled in. False if not.
927
+ */
928
+ $page_on_front = $this->has_page_on_front();
929
+ $home_description_frompost = false;
930
+
931
+ /**
932
+ * Notify the user that the data is pulled from the post.
933
+ */
934
+ $description_from_post_message = '';
935
+ $title_from_post_message = '';
936
+
937
+ // Setting up often used Translations
938
+ $title_i18n = __( 'Title', 'autodescription' );
939
+ $description_i18n = __( 'Description', 'autodescription' );
940
+ $home_page_i18n = __( 'Home Page', 'autodescription' );
941
+
942
+ //* Get home page ID. If blog on front, it's 0.
943
+ $home_id = $this->get_the_front_page_ID();
944
+
945
+ $home_title = $this->escape_title( $this->get_option( 'homepage_title' ) );
946
+
947
+ //* Get blog tagline
948
+ $blog_description = $this->get_blogdescription();
949
+
950
+ /**
951
+ * Home Page Tagline settings.
952
+ * @since 2.3.8
953
+ *
954
+ * @param string $home_tagline The tagline option.
955
+ * @param string $home_tagline_placeholder The option placeholder. Always defaults to description.
956
+ * @param string|void $home_tagline_value The tagline input value.
957
+ * @param string $blog_description Override blog description with option if applicable.
958
+ */
959
+ $home_tagline = $this->get_field_value( 'homepage_title_tagline' );
960
+ $home_tagline_placeholder = $blog_description;
961
+ $home_tagline_value = $home_tagline ? $home_tagline : '';
962
+ $blog_description = $home_tagline_value ? $home_tagline_value : $blog_description;
963
+
964
+ /**
965
+ * Create a placeholder for when there's no custom HomePage title found.
966
+ * @since 2.2.4
967
+ */
968
+ $home_title_args = $this->generate_home_title( true, '', '', true, false );
969
+ if ( $this->home_page_add_title_tagline() ) {
970
+ $home_title_placeholder = $this->process_title_additions( $home_title_args['blogname'], $home_title_args['title'], $home_title_args['seplocation'] );
971
+ } else {
972
+ $home_title_placeholder = $home_title_args['title'];
973
+ }
974
+
975
+ /**
976
+ * If the home title is fetched from the post, notify about that instead.
977
+ * @since 2.2.4
978
+ *
979
+ * Nesting often used translations
980
+ */
981
+ if ( empty( $home_title ) && $page_on_front && $this->get_custom_field( '_genesis_title', $home_id ) ) {
982
+ /* translators: 1: Option, 2: Page SEO Settings, 3: Home Page */
983
+ $title_from_post_message = sprintf( __( 'Note: The %1$s is fetched from the %2$s on the %3$s.', 'autodescription' ), $title_i18n, __( 'Page SEO Settings', 'autodescription' ), $home_page_i18n );
984
+ }
985
+
986
+ /**
987
+ * Check for options to calculate title length.
988
+ *
989
+ * @since 2.3.4
990
+ */
991
+ if ( $home_title ) {
992
+ $home_title_args = $this->generate_home_title();
993
+ $tit_len_pre = $this->process_title_additions( $home_title_args['title'], $home_title_args['blogname'], $home_title_args['seplocation'] );
994
+ } else {
995
+ $tit_len_pre = $home_title_placeholder;
996
+ }
997
+
998
+ //* Fetch the description from the home page.
999
+ $frompost_description = $page_on_front ? $this->get_custom_field( '_genesis_description', $home_id ) : '';
1000
+
1001
+ //* Fetch the HomePage Description option.
1002
+ $home_description = $this->get_field_value( 'homepage_description' );
1003
+
1004
+ /**
1005
+ * Create a placeholder if there's no custom HomePage description found.
1006
+ * @since 2.2.4
1007
+ *
1008
+ * Reworked. Always create a placeholder.
1009
+ * @since 2.3.4
1010
+ */
1011
+ if ( $frompost_description ) {
1012
+ $description_placeholder = $frompost_description;
1013
+ } else {
1014
+ $description_args = array(
1015
+ 'id' => $home_id,
1016
+ 'is_home' => true,
1017
+ 'get_custom_field' => false
1018
+ );
1019
+
1020
+ $description_placeholder = $this->generate_description( '', $description_args );
1021
+ }
1022
+
1023
+ /**
1024
+ * Checks if the home is blog, the Home Page Metabox description and
1025
+ * the frompost description.
1026
+ * @since 2.3.4
1027
+ */
1028
+ if ( empty( $home_description ) && $page_on_front && $frompost_description )
1029
+ $home_description_frompost = true;
1030
+
1031
+ /**
1032
+ *
1033
+ * If the HomePage Description empty, it will check for the InPost
1034
+ * Description set on the Home Page. And it will set the InPost
1035
+ * Description as placeholder.
1036
+ *
1037
+ * Nesting often used translations.
1038
+ *
1039
+ * Notify that the homepage is a blog.
1040
+ * @since 2.2.2
1041
+ */
1042
+ if ( $home_description_frompost ) {
1043
+ /* translators: 1: Option, 2: Page SEO Settings, 3: Home Page */
1044
+ $description_from_post_message = sprintf( __( 'Note: The %1$s is fetched from the %2$s on the %3$s.', 'autodescription' ), $description_i18n, __( 'Page SEO Settings', 'autodescription' ), $home_page_i18n );
1045
+ }
1046
+
1047
+ $desc_len_pre = $home_description ? $home_description : $description_placeholder;
1048
+
1049
+ /**
1050
+ * Convert to what Google outputs.
1051
+ *
1052
+ * This will convert e.g. &raquo; to a single length character.
1053
+ * @since 2.3.4
1054
+ */
1055
+ $tit_len = html_entity_decode( $this->escape_title( $tit_len_pre ) );
1056
+ $desc_len = html_entity_decode( $this->escape_title( $desc_len_pre ) );
1057
+
1058
+ ?>
1059
+ <p>
1060
+ <label for="<?php $this->field_id( 'homepage_title_tagline' ); ?>" class="toblock">
1061
+ <strong><?php printf( __( 'Custom %s Title Tagline', 'autodescription' ), $home_page_i18n ); ?></strong>
1062
+ </label>
1063
+ </p>
1064
+ <p>
1065
+ <input type="text" name="<?php $this->field_name( 'homepage_title_tagline' ); ?>" class="large-text" id="<?php $this->field_id( 'homepage_title_tagline' ); ?>" placeholder="<?php echo $home_tagline_placeholder ?>" value="<?php echo esc_attr( $home_tagline_value ); ?>" />
1066
+ </p>
1067
+
1068
+ <hr>
1069
+
1070
+ <p>
1071
+ <label for="<?php $this->field_id( 'homepage_title' ); ?>" class="toblock">
1072
+ <strong><?php printf( __( 'Custom %s Title', 'autodescription' ), $home_page_i18n ); ?></strong>
1073
+ <a href="<?php echo esc_url( 'https://support.google.com/webmasters/answer/35624?hl=' . $language . '#3' ); ?>" target="_blank" title="<?php _e( 'Recommended Length: 50 to 55 characters', 'autodescription' ) ?>">[?]</a>
1074
+ <span class="description theseoframework-counter"><?php printf( __( 'Characters Used: %s', 'autodescription' ), '<span id="' . $this->field_id( 'homepage_title', false ) . '_chars">'. mb_strlen( $tit_len ) .'</span>' ); ?></span>
1075
+ </label>
1076
+ </p>
1077
+ <p id="autodescription-title-wrap">
1078
+ <input type="text" name="<?php $this->field_name( 'homepage_title' ); ?>" class="large-text" id="<?php $this->field_id( 'homepage_title' ); ?>" placeholder="<?php echo $home_title_placeholder ?>" value="<?php echo esc_attr( $home_title ); ?>" />
1079
+ <span id="autodescription-title-offset" class="hide-if-no-js"></span><span id="autodescription-title-placeholder" class="hide-if-no-js"></span>
1080
+ </p>
1081
+ <?php
1082
+ if ( $title_from_post_message ) {
1083
+ echo '<p class="description">' . $title_from_post_message . '</p>';
1084
+ }
1085
+ ?>
1086
+ <hr>
1087
+
1088
+ <p>
1089
+ <label for="<?php $this->field_id( 'homepage_description' ); ?>" class="toblock">
1090
+ <strong><?php printf( __( 'Custom %s Description', 'autodescription' ), $home_page_i18n ); ?></strong>
1091
+ <a href="<?php echo esc_url( 'https://support.google.com/webmasters/answer/35624?hl=' . $language . '#1' ); ?>" target="_blank" title="<?php _e( 'Recommended Length: 145 to 155 characters', 'autodescription' ) ?>">[?]</a>
1092
+ <span class="description theseoframework-counter"><?php printf( __( 'Characters Used: %s', 'autodescription' ), '<span id="' . $this->field_id( 'homepage_description', false ) . '_chars">'. mb_strlen( $desc_len ) .'</span>' ); ?></span>
1093
+ </label>
1094
+ </p>
1095
+ <p>
1096
+ <textarea name="<?php $this->field_name( 'homepage_description' ); ?>" class="large-text" id="<?php $this->field_id( 'homepage_description' ); ?>" rows="3" cols="70" placeholder="<?php echo $description_placeholder ?>"><?php echo esc_textarea( $home_description ); ?></textarea>
1097
+ </p>
1098
+ <p class="description">
1099
+ <?php _e( 'The meta description can be used to determine the text used under the title on Search Engine results pages.', 'autodescription' ); ?>
1100
+ </p>
1101
+ <?php
1102
+ if ( $description_from_post_message ) {
1103
+ echo '<p class="description">' . $description_from_post_message . '</p>';
1104
+ }
1105
+
1106
+ }
1107
+
1108
+ /**
1109
+ * HomePage Metabox Additions Tab Output
1110
+ *
1111
+ * @since 2.6.0
1112
+ *
1113
+ * @see $this->homepage_metabox() Callback for HomePage Settings box.
1114
+ */
1115
+ public function homepage_metabox_additions() {
1116
+
1117
+ $home_page_i18n = __( 'Home Page', 'autodescription' );
1118
+
1119
+ /**
1120
+ * Generate example for Title Additions Location.
1121
+ */
1122
+ $title_args = $this->generate_home_title();
1123
+
1124
+ //* I know, brilliant.
1125
+ $title = $title_args['title'];
1126
+ $blogname = $title_args['blogname'];
1127
+
1128
+ // Get title separator
1129
+ $sep = $this->get_separator( 'title', true );
1130
+
1131
+ /**
1132
+ * Generate Examples for both left and right seplocations.
1133
+ */
1134
+ $example_left = '<em><span class="custom-title-js">' . esc_attr( $title ) . '</span><span class="custom-blogname-js"><span class="autodescription-sep-js"> ' . esc_attr( $sep ) . ' </span><span class="custom-tagline-js">' . esc_attr( $blogname ) . '</span></span></span>' . '</em>';
1135
+ $example_right = '<em>' . '<span class="custom-blogname-js"><span class="custom-tagline-js">' . esc_attr( $blogname ) . '</span><span class="autodescription-sep-js"> ' . esc_attr( $sep ) . ' </span></span><span class="custom-title-js">' . esc_attr( $title ) . '</span></em>';
1136
+
1137
+ ?>
1138
+ <fieldset>
1139
+ <legend><h4><?php _e( 'Document Title Additions Location', 'autodescription' ); ?></h4></legend>
1140
+ <p>
1141
+ <span class="description"><?php _e( 'Determines which side the added title text will go on.', 'autodescription' ); ?></span>
1142
+ </p>
1143
+
1144
+ <p id="home-title-location" class="theseoframework-fields">
1145
+ <span class="toblock">
1146
+ <input type="radio" name="<?php $this->field_name( 'home_title_location' ); ?>" id="<?php $this->field_id( 'home_title_location_left' ); ?>" value="left" <?php checked( $this->get_field_value( 'home_title_location' ), 'left' ); ?> />
1147
+ <label for="<?php $this->field_id( 'home_title_location_left' ); ?>">
1148
+ <span><?php _e( 'Left:', 'autodescription' ); ?></span>
1149
+ <?php echo ( $example_left ) ? $this->code_wrap_noesc( $example_left ) : ''; ?>
1150
+ </label>
1151
+ </span>
1152
+ <span class="toblock">
1153
+ <input type="radio" name="<?php $this->field_name( 'home_title_location' ); ?>" id="<?php $this->field_id( 'home_title_location_right' ); ?>" value="right" <?php checked( $this->get_field_value( 'home_title_location' ), 'right' ); ?> />
1154
+ <label for="<?php $this->field_id( 'home_title_location_right' ); ?>">
1155
+ <span><?php _e( 'Right:', 'autodescription' ); ?></span>
1156
+ <?php echo ( $example_right ) ? $this->code_wrap_noesc( $example_right ) : ''; ?>
1157
+ </label>
1158
+ </span>
1159
+ </p>
1160
+ </fieldset>
1161
+
1162
+ <hr>
1163
+ <?php
1164
+ /**
1165
+ * @TODO work on this checkbox.
1166
+ * @priority low 2.6.x
1167
+ */
1168
+ ?>
1169
+ <h4><?php printf( __( '%s Tagline', 'autodescription' ), $home_page_i18n ); ?></h4>
1170
+ <p id="title-tagline-toggle">
1171
+ <label for="<?php $this->field_id( 'homepage_tagline' ); ?>" class="toblock">
1172
+ <input type="checkbox" name="<?php $this->field_name( 'homepage_tagline' ); ?>" id="<?php $this->field_id( 'homepage_tagline' ); ?>" <?php $this->is_conditional_checked( 'homepage_tagline' ); ?> value="1" <?php checked( $this->get_field_value( 'homepage_tagline' ) ); ?> />
1173
+ <?php printf( __( 'Add site description (tagline) to the Title on the %s?', 'autodescription' ), $home_page_i18n ); ?>
1174
+ </label>
1175
+ </p>
1176
+ <?php
1177
+ }
1178
+
1179
+ /**
1180
+ * HomePage Metabox Robots Tab Output
1181
+ *
1182
+ * @since 2.6.0
1183
+ *
1184
+ * @see $this->homepage_metabox() Callback for HomePage Settings box.
1185
+ */
1186
+ public function homepage_metabox_robots() {
1187
+
1188
+ $home_page_i18n = __( 'Home Page', 'autodescription' );
1189
+ $language = $this->google_language();
1190
+
1191
+ //* Get home page ID. If blog on front, it's 0.
1192
+ $home_id = $this->get_the_front_page_ID();
1193
+
1194
+ $noindex_post = $this->get_custom_field( '_genesis_noindex', $home_id );
1195
+ $nofollow_post = $this->get_custom_field( '_genesis_nofollow', $home_id );
1196
+ $noarchive_post = $this->get_custom_field( '_genesis_noarchive', $home_id );
1197
+
1198
+ /**
1199
+ * Shows user that the setting is checked on the home page.
1200
+ * Adds starting - with space to maintain readability.
1201
+ *
1202
+ * @since 2.2.4
1203
+ */
1204
+ if ( $noindex_post || $nofollow_post || $noarchive_post ) {
1205
+ $checked_home = ' - <a href="' . esc_url( admin_url( 'post.php?post=' . $home_id . '&action=edit#theseoframework-inpost-box' ) ) . '" target="_blank" class="attention" title="' . __( 'View Home Page Settings', 'autodescription' ) . '" >' . __( 'Checked in Page', 'autodescription' ) . '</a>';
1206
+ } else {
1207
+ $checked_home = '';
1208
+ }
1209
+
1210
+ ?>
1211
+ <h4><?php _e( 'Home Page Robots Meta Settings', 'autodescription' ); ?></h4>
1212
+ <?php
1213
+
1214
+ $noindex_note = $noindex_post ? $checked_home : '';
1215
+ $nofollow_note = $nofollow_post ? $checked_home : '';
1216
+ $noarchive_note = $noarchive_post ? $checked_home : '';
1217
+
1218
+ /* translators: 1: Option, 2: Location */
1219
+ $i_label = sprintf( __( 'Apply %1$s to the %2$s?', 'autodescription' ), $this->code_wrap( 'noindex' ), $home_page_i18n );
1220
+ $i_label .= ' ';
1221
+ $i_label .= $this->make_info(
1222
+ __( 'Tell Search Engines not to show this page in their search results', 'autodescription' ),
1223
+ 'https://support.google.com/webmasters/answer/93710?hl=' . $language,
1224
+ false
1225
+ )
1226
+ . $noindex_note;
1227
+
1228
+ /* translators: 1: Option, 2: Location */
1229
+ $f_label = sprintf( __( 'Apply %1$s to the %2$s?', 'autodescription' ), $this->code_wrap( 'nofollow' ), $home_page_i18n );
1230
+ $f_label .= ' ';
1231
+ $f_label .= $this->make_info(
1232
+ __( 'Tell Search Engines not to follow links on this page', 'autodescription' ),
1233
+ 'https://support.google.com/webmasters/answer/96569?hl=' . $language,
1234
+ false
1235
+ )
1236
+ . $nofollow_note;
1237
+
1238
+ /* translators: 1: Option, 2: Location */
1239
+ $a_label = sprintf( __( 'Apply %1$s to the %2$s?', 'autodescription' ), $this->code_wrap( 'noarchive' ), $home_page_i18n );
1240
+ $a_label .= ' ';
1241
+ $a_label .= $this->make_info(
1242
+ __( 'Tell Search Engines not to save a cached copy of this page', 'autodescription' ),
1243
+ 'https://support.google.com/webmasters/answer/79812?hl=' . $language,
1244
+ false
1245
+ )
1246
+ . $noarchive_note;
1247
+
1248
+ //* Echo checkboxes.
1249
+ $this->wrap_fields(
1250
+ array(
1251
+ $this->make_checkbox(
1252
+ 'homepage_noindex',
1253
+ $i_label,
1254
+ ''
1255
+ ),
1256
+ $this->make_checkbox(
1257
+ 'homepage_nofollow',
1258
+ $f_label,
1259
+ ''
1260
+ ),
1261
+ $this->make_checkbox(
1262
+ 'homepage_noarchive',
1263
+ $a_label,
1264
+ ''
1265
+ ),
1266
+ ),
1267
+ true
1268
+ );
1269
+
1270
+ // Add notice if any options are checked on the post.
1271
+ if ( $noindex_post || $nofollow_post || $noarchive_post ) {
1272
+ ?><p class="description"><?php printf( __( 'Note: If any of these options are unchecked, but are checked on the Home Page, they will be outputted regardless.', 'autodescription' ) ); ?></p><?php
1273
+ }
1274
+ ?>
1275
+
1276
+ <hr>
1277
+
1278
+ <h4><?php _e( 'Home Page Pagination Robots Settings', 'autodescription' ); ?></h4>
1279
+
1280
+ <p class="description"><?php _e( "If your Home Page is paginated and outputs content that's also found elsewhere on the website, enabling this option might prevent duplicate content.", 'autodescription' ); ?></p>
1281
+
1282
+ <?php
1283
+ //* Echo checkbox.
1284
+ $this->wrap_fields(
1285
+ $this->make_checkbox(
1286
+ 'home_paged_noindex',
1287
+ /* translators: 1: Option, 2: Location */
1288
+ sprintf( __( 'Apply %1$s to every second or later page on the %2$s?', 'autodescription' ), $this->code_wrap( 'noindex' ), $home_page_i18n ),
1289
+ ''
1290
+ ),
1291
+ true
1292
+ );
1293
+
1294
+ }
1295
+
1296
+ /**
1297
+ * Social meta box on the Site SEO Settings page.
1298
+ *
1299
+ * @since 2.2.2
1300
+ *
1301
+ * @uses $this->social_metabox_general_tab()
1302
+ * @uses $this->social_metabox_facebook_tab()
1303
+ * @uses $this->social_metabox_twitter_tab()
1304
+ *
1305
+ * @applies filter 'social_settings_tabs'
1306
+ *
1307
+ * @param array $args the social tabs arguments
1308
+ */
1309
+ public function social_metabox( $args = array() ) {
1310
+
1311
+ do_action( 'the_seo_framework_social_metabox_before' );
1312
+
1313
+ /**
1314
+ * Parse tabs content
1315
+ *
1316
+ * @param array $default_tabs { 'id' = The identifier =>
1317
+ * array(
1318
+ * 'name' => The name
1319
+ * 'callback' => The callback function, use array for method calling (accepts $this, but isn't used here for optimization purposes)
1320
+ * 'dashicon' => Desired dashicon
1321
+ * )
1322
+ * }
1323
+ *
1324
+ * @since 2.2.2
1325
+ */
1326
+ $default_tabs = array(
1327
+ 'general' => array(
1328
+ 'name' => __( 'General', 'autodescription' ),
1329
+ 'callback' => array( $this, 'social_metabox_general_tab' ),
1330
+ 'dashicon' => 'admin-generic',
1331
+ ),
1332
+ 'facebook' => array(
1333
+ 'name' => 'Facebook',
1334
+ 'callback' => array( $this, 'social_metabox_facebook_tab' ),
1335
+ 'dashicon' => 'facebook-alt',
1336
+ ),
1337
+ 'twitter' => array(
1338
+ 'name' => 'Twitter',
1339
+ 'callback' => array( $this, 'social_metabox_twitter_tab' ),
1340
+ 'dashicon' => 'twitter',
1341
+ ),
1342
+ 'postdates' => array(
1343
+ 'name' => __( 'Post Dates', 'autodescription' ),
1344
+ 'callback' => array( $this, 'social_metabox_postdates_tab' ),
1345
+ 'dashicon' => 'backup',
1346
+ ),
1347
+ 'relationships' => array(
1348
+ 'name' => __( 'Link Relationships', 'autodescription' ),
1349
+ 'callback' => array( $this, 'social_metabox_relationships_tab' ),
1350
+ 'dashicon' => 'leftright',
1351
+ ),
1352
+ );
1353
+
1354
+ /**
1355
+ * Applies filters the_seo_framework_social_settings_tabs : array see $default_tabs
1356
+ *
1357
+ * Used to extend Social tabs
1358
+ */
1359
+ $defaults = (array) apply_filters( 'the_seo_framework_social_settings_tabs', $default_tabs, $args );
1360
+
1361
+ $tabs = wp_parse_args( $args, $defaults );
1362
+
1363
+ $this->nav_tab_wrapper( 'social', $tabs, '2.2.2' );
1364
+
1365
+ do_action( 'the_seo_framework_social_metabox_after' );
1366
+
1367
+ }
1368
+
1369
+ /**
1370
+ * Social Metabox General Tab output
1371
+ *
1372
+ * @since 2.2.2
1373
+ *
1374
+ * @see $this->social_metabox() Callback for Social Settings box.
1375
+ */
1376
+ protected function social_metabox_general_tab() {
1377
+
1378
+ ?>
1379
+ <h4><?php _e( 'Site Shortlink Settings', 'autodescription' ); ?></h4>
1380
+ <p class="description"><?php printf( __( 'The shortlink tag might have some use for 3rd party service discoverability, but it has little to no SEO value whatsoever.', 'autodescription' ) ); ?></p>
1381
+ <?php
1382
+
1383
+ //* Echo checkboxes.
1384
+ $this->wrap_fields(
1385
+ $this->make_checkbox(
1386
+ 'shortlink_tag',
1387
+ __( 'Output shortlink tag?', 'autodescription' ),
1388
+ ''
1389
+ ),
1390
+ true
1391
+ );
1392
+
1393
+ ?>
1394
+ <hr>
1395
+
1396
+ <h4><?php _e( 'Social Meta Tags Settings', 'autodescription' ); ?></h4>
1397
+ <p class="description"><?php _e( 'Output various meta tags for social site integration, among other 3rd party services.', 'autodescription' ); ?></p>
1398
+
1399
+ <hr>
1400
+ <?php
1401
+
1402
+ //* Echo Open Graph Tags checkboxes.
1403
+ $this->wrap_fields(
1404
+ $this->make_checkbox(
1405
+ 'og_tags',
1406
+ __( 'Output Open Graph meta tags?', 'autodescription' ),
1407
+ __( 'Facebook, Twitter, Pinterest and many other social sites make use of these tags.', 'autodescription' )
1408
+ ),
1409
+ true
1410
+ );
1411
+
1412
+ if ( $this->detect_og_plugin() )
1413
+ echo '<p class="description">' . __( 'Note: Another Open Graph plugin has been detected.', 'autodescription' ) . '</p>';
1414
+
1415
+ ?><hr><?php
1416
+
1417
+ //* Echo Facebook Tags checkbox.
1418
+ $this->wrap_fields(
1419
+ $this->make_checkbox(
1420
+ 'facebook_tags',
1421
+ __( 'Output Facebook meta tags?', 'autodescription' ),
1422
+ sprintf( __( 'Output various tags targetted at %s.', 'autodescription' ), 'Facebook' )
1423
+ ),
1424
+ true
1425
+ );
1426
+
1427
+ ?><hr><?php
1428
+
1429
+ //* Echo Twitter Tags checkboxes.
1430
+ $this->wrap_fields(
1431
+ $this->make_checkbox(
1432
+ 'twitter_tags',
1433
+ __( 'Output Twitter meta tags?', 'autodescription' ),
1434
+ sprintf( __( 'Output various tags targetted at %s.', 'autodescription' ), 'Twitter' )
1435
+ ),
1436
+ true
1437
+ );
1438
+
1439
+ if ( $this->detect_twitter_card_plugin() )
1440
+ echo '<p class="description">' . __( 'Note: Another Twitter Card plugin has been detected.', 'autodescription' ) . '</p>';
1441
+
1442
+ }
1443
+
1444
+ /**
1445
+ * Social Metabox Facebook Tab Output
1446
+ *
1447
+ * @since 2.2.2
1448
+ *
1449
+ * @see $this->social_metabox() Callback for Social Settings box.
1450
+ */
1451
+ protected function social_metabox_facebook_tab() {
1452
+
1453
+ $fb_author = $this->get_field_value( 'facebook_author' );
1454
+ $fb_author_placeholder = empty( $fb_publisher ) ? _x( 'http://www.facebook.com/YourPersonalProfile', 'Example Facebook Personal URL', 'autodescription' ) : '';
1455
+
1456
+ $fb_publisher = $this->get_field_value( 'facebook_publisher' );
1457
+ $fb_publisher_placeholder = empty( $fb_publisher ) ? _x( 'http://www.facebook.com/YourVerifiedBusinessProfile', 'Example Verified Facebook Business URL', 'autodescription' ) : '';
1458
+
1459
+ $fb_appid = $this->get_field_value( 'facebook_appid' );
1460
+ $fb_appid_placeholder = empty( $fb_appid ) ? '123456789012345' : '';
1461
+
1462
+ ?>
1463
+ <h4><?php _e( 'Default Facebook Integration Settings', 'autodescription' ); ?></h4>
1464
+ <p class="description"><?php _e( 'Facebook post sharing works mostly through Open Graph. However, you can also link your Business and Personal Facebook pages, among various other options.', 'autodescription' ); ?></p>
1465
+ <p class="description"><?php _e( 'When these options are filled in, Facebook might link your Facebook profile to be followed and liked when your post or page is shared.', 'autodescription' ); ?></p>
1466
+
1467
+ <hr>
1468
+
1469
+ <p>
1470
+ <label for="<?php $this->field_id( 'facebook_author' ); ?>">
1471
+ <strong><?php _e( 'Article Author Facebook URL', 'autodescription' ); ?></strong>
1472
+ <a href="<?php echo esc_url( 'https://facebook.com/me' ); ?>" class="description" target="_blank" title="<?php _e( 'Your Facebook Profile', 'autodescription' ); ?>">[?]</a>
1473
+ </label>
1474
+ </p>
1475
+ <p>
1476
+ <input type="text" name="<?php $this->field_name( 'facebook_author' ); ?>" class="large-text" id="<?php $this->field_id( 'facebook_author' ); ?>" placeholder="<?php echo $fb_author_placeholder ?>" value="<?php echo esc_attr( $fb_author ); ?>" />
1477
+ </p>
1478
+
1479
+ <p>
1480
+ <label for="<?php $this->field_id( 'facebook_publisher' ); ?>">
1481
+ <strong><?php _e( 'Article Publisher Facebook URL', 'autodescription' ); ?></strong>
1482
+ <a href="<?php echo esc_url( 'https://instantarticles.fb.com/' ); ?>" class="description" target="_blank" title="<?php _e( 'To use this, you need to be a verified business', 'autodescription' ); ?>">[?]</a>
1483
+ </label>
1484
+ </p>
1485
+ <p>
1486
+ <input type="text" name="<?php $this->field_name( 'facebook_publisher' ); ?>" class="large-text" id="<?php $this->field_id( 'facebook_publisher' ); ?>" placeholder="<?php echo $fb_publisher_placeholder ?>" value="<?php echo esc_attr( $fb_publisher ); ?>" />
1487
+ </p>
1488
+
1489
+ <p>
1490
+ <label for="<?php $this->field_id( 'facebook_appid' ); ?>">
1491
+ <strong><?php _e( 'Facebook App ID', 'autodescription' ); ?></strong>
1492
+ <a href="<?php echo esc_url( 'https://developers.facebook.com/apps' ); ?>" target="_blank" class="description" title="<?php _e( 'Get Facebook App ID', 'autodescription' ); ?>">[?]</a>
1493
+ </label>
1494
+ </p>
1495
+ <p>
1496
+ <input type="text" name="<?php $this->field_name( 'facebook_appid' ); ?>" class="large-text" id="<?php $this->field_id( 'facebook_appid' ); ?>" placeholder="<?php echo $fb_appid_placeholder ?>" value="<?php echo esc_attr( $fb_appid ); ?>" />
1497
+ </p>
1498
+ <?php
1499
+
1500
+ }
1501
+
1502
+ /**
1503
+ * Social Metabox Twitter Tab Output
1504
+ *
1505
+ * @since 2.2.2
1506
+ *
1507
+ * @see $this->social_metabox() Callback for Social Settings box.
1508
+ */
1509
+ protected function social_metabox_twitter_tab() {
1510
+
1511
+ $tw_site = $this->get_field_value( 'twitter_site' );
1512
+ $tw_site_placeholder = empty( $tw_site ) ? _x( '@your-site-username', 'Twitter @username', 'autodescription' ) : '';
1513
+
1514
+ $tw_creator = $this->get_field_value( 'twitter_creator' );
1515
+ $tw_creator_placeholder = empty( $tw_creator ) ? _x( '@your-personal-username', 'Twitter @username', 'autodescription' ) : '';
1516
+
1517
+ $twitter_card = $this->get_twitter_card_types();
1518
+
1519
+ ?>
1520
+ <h4><?php _e( 'Default Twitter Integration Settings', 'autodescription' ); ?></h4>
1521
+ <p class="description"><?php printf( __( 'Twitter post sharing works mostly through Open Graph. However, you can also link your Business and Personal Twitter pages, among various other options.', 'autodescription' ) ); ?></p>
1522
+
1523
+ <hr>
1524
+
1525
+ <fieldset id="twitter-cards">
1526
+ <legend><h4><?php _e( 'Twitter Card Type', 'autodescription' ); ?></h4></legend>
1527
+ <p class="description"><?php printf( __( 'What kind of Twitter card would you like to use? It will default to %s if no image is found.', 'autodescription' ), $this->code_wrap( 'Summary' ) ); ?></p>
1528
+
1529
+ <p class="theseoframework-fields">
1530
+ <?php
1531
+ foreach ( $twitter_card as $type => $name ) {
1532
+ ?>
1533
+ <span class="toblock">
1534
+ <input type="radio" name="<?php $this->field_name( 'twitter_card' ); ?>" id="<?php $this->field_id( 'twitter_card_' . $type ); ?>" value="<?php echo $type ?>" <?php checked( $this->get_field_value( 'twitter_card' ), $type ); ?> />
1535
+ <label for="<?php $this->field_id( 'twitter_card_' . $type ); ?>">
1536
+ <span><?php echo $this->code_wrap( ucfirst( $name ) ); ?></span>
1537
+ <a class="description" href="<?php echo esc_url('https://dev.twitter.com/cards/types/' . $name ); ?>" target="_blank" title="Twitter Card <?php echo ucfirst( $name ) . ' ' . __( 'Example', 'autodescription' ); ?>"><?php _e( 'Example', 'autodescription' ); ?></a>
1538
+ </label>
1539
+ </span>
1540
+ <?php
1541
+ }
1542
+ ?>
1543
+ </p>
1544
+ </fieldset>
1545
+
1546
+ <hr>
1547
+
1548
+ <p class="description"><?php printf( __( 'When the following options are filled in, Twitter might link your Twitter Site or Personal Profile when your post or page is shared.', 'autodescription' ) ); ?></p>
1549
+ <p>
1550
+ <label for="<?php $this->field_id( 'twitter_site' ); ?>" class="toblock">
1551
+ <strong><?php _e( "Your Website's Twitter Profile", 'autodescription' ); ?></strong>
1552
+ <a href="<?php echo esc_url( 'https://twitter.com/home' ); ?>" target="_blank" class="description" title="<?php _e( 'Find your @username', 'autodescription' ); ?>">[?]</a>
1553
+ </label>
1554
+ </p>
1555
+ <p>
1556
+ <input type="text" name="<?php $this->field_name( 'twitter_site' ); ?>" class="large-text" id="<?php $this->field_id( 'twitter_site' ); ?>" placeholder="<?php echo $tw_site_placeholder ?>" value="<?php echo esc_attr( $tw_site ); ?>" />
1557
+ </p>
1558
+
1559
+ <p>
1560
+ <label for="<?php $this->field_id( 'twitter_creator' ); ?>" class="toblock">
1561
+ <strong><?php _e( 'Your Personal Twitter Profile', 'autodescription' ); ?></strong>
1562
+ <a href="<?php echo esc_url( 'https://twitter.com/home' ); ?>" target="_blank" class="description" title="<?php _e( 'Find your @username', 'autodescription' ); ?>">[?]</a>
1563
+ </label>
1564
+ </p>
1565
+ <p>
1566
+ <input type="text" name="<?php $this->field_name( 'twitter_creator' ); ?>" class="large-text" id="<?php $this->field_id( 'twitter_creator' ); ?>" placeholder="<?php echo $tw_creator_placeholder ?>" value="<?php echo esc_attr( $tw_creator ); ?>" />
1567
+ </p>
1568
+ <?php
1569
+
1570
+ }
1571
+
1572
+ /**
1573
+ * Social Metabox PostDates Tab Output
1574
+ *
1575
+ * @since 2.2.4
1576
+ *
1577
+ * @see $this->social_metabox() Callback for Social Settings box.
1578
+ */
1579
+ public function social_metabox_postdates_tab() {
1580
+
1581
+ $pages_i18n = __( 'Pages', 'autodescription' );
1582
+ $posts_i18n = __( 'Posts', 'autodescription' );
1583
+ $home_i18n = __( 'Home Page', 'autodescription' );
1584
+
1585
+ ?>
1586
+ <h4><?php _e( 'Post Date Settings', 'autodescription' ); ?></h4>
1587
+ <p class="description"><?php _e( "Some Search Engines output the publishing date and modified date next to the search results. These help Search Engines find new content and could impact the SEO value.", 'autodescription' ); ?></p>
1588
+ <p class="description"><?php _e( "It's recommended on posts, but it's not recommended on pages unless you modify or create new pages frequently.", 'autodescription' ); ?></p>
1589
+
1590
+ <?php
1591
+ /* translators: 1: Option, 2: Post Type */
1592
+ $post_publish_time_label = sprintf( __( 'Add %1$s to %2$s?', 'autodescription' ), $this->code_wrap( 'article:published_time' ), $posts_i18n );
1593
+ $post_publish_time_checkbox = $this->make_checkbox( 'post_publish_time', $post_publish_time_label, '' );
1594
+
1595
+ /* translators: 1: Option, 2: Post Type */
1596
+ $page_publish_time_label = sprintf( __( 'Add %1$s to %2$s?', 'autodescription' ), $this->code_wrap( 'article:published_time' ), $pages_i18n );
1597
+ $page_publish_time_checkbox = $this->make_checkbox( 'page_publish_time', $page_publish_time_label, '' );
1598
+
1599
+ //* Echo checkboxes.
1600
+ echo $this->wrap_fields( $post_publish_time_checkbox . $page_publish_time_checkbox );
1601
+
1602
+ /* translators: 1: Option, 2: Post Type */
1603
+ $post_modify_time_label = sprintf( __( 'Add %1$s to %2$s?', 'autodescription' ), $this->code_wrap( 'article:modified_time' ), $posts_i18n );
1604
+ $post_modify_time_checkbox = $this->make_checkbox( 'post_modify_time', $post_modify_time_label, '' );
1605
+
1606
+ /* translators: 1: Option, 2: Post Type */
1607
+ $page_modify_time_label = sprintf( __( 'Add %1$s to %2$s?', 'autodescription' ), $this->code_wrap( 'article:modified_time' ), $pages_i18n );
1608
+ $page_modify_time_checkbox = $this->make_checkbox( 'page_modify_time', $page_modify_time_label, '' );
1609
+
1610
+ //* Echo checkboxes.
1611
+ echo $this->wrap_fields( $post_modify_time_checkbox . $page_modify_time_checkbox );
1612
+ ?>
1613
+
1614
+ <hr>
1615
+
1616
+ <h4><?php _e( 'Home Page', 'autodescription' ); ?></h4>
1617
+ <p class="description"><?php _e( "Because you only publish the Home Page once, Search Engines might think your website is outdated. This can be prevented by disabling the following options.", 'autodescription' ); ?></p>
1618
+
1619
+ <?php
1620
+ /* translators: 1: Option, 2: Post Type */
1621
+ $home_publish_time_label = sprintf( __( 'Add %1$s to %2$s?', 'autodescription' ), $this->code_wrap( 'article:published_time' ), $home_i18n );
1622
+ $home_publish_time_checkbox = $this->make_checkbox( 'home_publish_time', $home_publish_time_label, '' );
1623
+
1624
+ /* translators: 1: Option, 2: Post Type */
1625
+ $home_modify_time_label = sprintf( __( 'Add %1$s to %2$s?', 'autodescription' ), $this->code_wrap( 'article:modified_time' ), $home_i18n );
1626
+ $home_modify_time_checkbox = $this->make_checkbox( 'home_modify_time', $home_modify_time_label, '' );
1627
+
1628
+ //* Echo checkboxes.
1629
+ echo $this->wrap_fields( $home_publish_time_checkbox . $home_modify_time_checkbox );
1630
+
1631
+ }
1632
+
1633
+ /**
1634
+ * Social Metabox Relationships Tab Output
1635
+ *
1636
+ * @since 2.2.4
1637
+ *
1638
+ * @see $this->social_metabox() Callback for Social Settings box.
1639
+ */
1640
+ public function social_metabox_relationships_tab() {
1641
+
1642
+ ?>
1643
+ <h4><?php _e( 'Link Relationship Settings', 'autodescription' ); ?></h4>
1644
+ <p class="description"><?php _e( "Some Search Engines look for relations between the content of your pages. If you have multiple pages for a single Post or Page, or have archives indexed, this option will help Search Engines look for the right page to display in the Search Results.", 'autodescription' ); ?></p>
1645
+ <p class="description"><?php _e( "It's recommended to turn this option on for better SEO consistency and to prevent duplicate content errors.", 'autodescription' ); ?></p>
1646
+
1647
+ <hr>
1648
+ <?php
1649
+ /* translators: %s = <code>rel</code> */
1650
+ $prev_next_posts_label = sprintf( __( 'Add %s link tags to Posts and Pages?', 'autodescription' ), $this->code_wrap( 'rel' ) );
1651
+ $prev_next_posts_checkbox = $this->make_checkbox( 'prev_next_posts', $prev_next_posts_label, '' );
1652
+
1653
+ /* translators: %s = <code>rel</code> */
1654
+ $prev_next_archives_label = sprintf( __( 'Add %s link tags to Archives?', 'autodescription' ), $this->code_wrap( 'rel' ) );
1655
+ $prev_next_archives_checkbox = $this->make_checkbox( 'prev_next_archives', $prev_next_archives_label, '' );
1656
+
1657
+ /* translators: %s = <code>rel</code> */
1658
+ $prev_next_frontpage_label = sprintf( __( 'Add %s link tags to the Home Page?', 'autodescription' ), $this->code_wrap( 'rel' ) );
1659
+ $prev_next_frontpage_checkbox = $this->make_checkbox( 'prev_next_frontpage', $prev_next_frontpage_label, '' );
1660
+
1661
+ //* Echo checkboxes.
1662
+ echo $this->wrap_fields( $prev_next_posts_checkbox . $prev_next_archives_checkbox . $prev_next_frontpage_checkbox );
1663
+
1664
+ }
1665
+
1666
+ /**
1667
+ * Webmaster meta box on the Site SEO Settings page.
1668
+ *
1669
+ * @since 2.2.4
1670
+ */
1671
+ public function webmaster_metabox() {
1672
+
1673
+ do_action( 'the_seo_framework_webmaster_metabox_before' );
1674
+
1675
+ $site_url = $this->the_home_url_from_cache();
1676
+ $language = $this->google_language();
1677
+
1678
+ $bing_site_url = "https://www.bing.com/webmaster/configure/verify/ownership?url=" . urlencode( $site_url );
1679
+ $google_site_url = "https://www.google.com/webmasters/verification/verification?hl=" . $language . "&siteUrl=" . $site_url;
1680
+ $pint_site_url = "https://analytics.pinterest.com/";
1681
+ $yandex_site_url = "https://webmaster.yandex.com/site/verification.xml";
1682
+
1683
+ ?>
1684
+ <h4><?php _e( 'Webmaster Integration Settings', 'autodescription' ); ?></h4>
1685
+ <p class="description"><?php _e( "When adding your website to Google, Bing and other Webmaster Tools, you'll be asked to add a code or file to your website for verification purposes. These options will help you easily integrate those codes.", 'autodescription' ); ?></p>
1686
+ <p class="description"><?php _e( "Verifying your website has no SEO value whatsoever. But you might gain added benefits such as search ranking insights to help you improve your website's content.", 'autodescription' ); ?></p>
1687
+
1688
+ <hr>
1689
+
1690
+ <p>
1691
+ <label for="<?php $this->field_id( 'google_verification' ); ?>" class="toblock">
1692
+ <strong><?php _e( "Google Webmaster Verification Code", 'autodescription' ); ?></strong>
1693
+ <a href="<?php echo esc_url( $google_site_url ); ?>" target="_blank" class="description" title="<?php _e( 'Get the Google Verification code', 'autodescription' ); ?>">[?]</a>
1694
+ </label>
1695
+ </p>
1696
+ <p>
1697
+ <input type="text" name="<?php $this->field_name( 'google_verification' ); ?>" class="large-text" id="<?php $this->field_id( 'google_verification' ); ?>" placeholder="ABC1d2eFg34H5iJ6klmNOp7qRstUvWXyZaBc8dEfG9" value="<?php echo esc_attr( $this->get_field_value( 'google_verification' ) ); ?>" />
1698
+ </p>
1699
+
1700
+ <p>
1701
+ <label for="<?php $this->field_id( 'bing_verification' ); ?>" class="toblock">
1702
+ <strong><?php _e( "Bing Webmaster Verification Code", 'autodescription' ); ?></strong>
1703
+ <a href="<?php echo esc_url( $bing_site_url ); ?>" target="_blank" class="description" title="<?php _e( 'Get the Bing Verification Code', 'autodescription' ); ?>">[?]</a>
1704
+ </label>
1705
+ </p>
1706
+ <p>
1707
+ <input type="text" name="<?php $this->field_name( 'bing_verification' ); ?>" class="large-text" id="<?php $this->field_id( 'bing_verification' ); ?>" placeholder="123A456B78901C2D3456E7890F1A234D" value="<?php echo esc_attr( $this->get_field_value( 'bing_verification' ) ); ?>" />
1708
+ </p>
1709
+
1710
+ <p>
1711
+ <label for="<?php $this->field_id( 'yandex_verification' ); ?>" class="toblock">
1712
+ <strong><?php _e( "Yandex Webmaster Verification Code", 'autodescription' ); ?></strong>
1713
+ <a href="<?php echo esc_url( $yandex_site_url ); ?>" target="_blank" class="description" title="<?php _e( 'Get the Yandex Verification Code', 'autodescription' ); ?>">[?]</a>
1714
+ </label>
1715
+ </p>
1716
+ <p>
1717
+ <input type="text" name="<?php $this->field_name( 'yandex_verification' ); ?>" class="large-text" id="<?php $this->field_id( 'yandex_verification' ); ?>" placeholder="12345abc678901d2" value="<?php echo esc_attr( $this->get_field_value( 'yandex_verification' ) ); ?>" />
1718
+ </p>
1719
+
1720
+ <p>
1721
+ <label for="<?php $this->field_id( 'pint_verification' ); ?>" class="toblock">
1722
+ <strong><?php _e( "Pinterest Analytics Verification Code", 'autodescription' ); ?></strong>
1723
+ <a href="<?php echo esc_url( $pint_site_url ); ?>" target="_blank" class="description" title="<?php _e( 'Get the Pinterest Verification Code', 'autodescription' ); ?>">[?]</a>
1724
+ </label>
1725
+ </p>
1726
+ <p>
1727
+ <input type="text" name="<?php $this->field_name( 'pint_verification' ); ?>" class="large-text" id="<?php $this->field_id( 'pint_verification' ); ?>" placeholder="123456a7b8901de2fa34bcdef5a67b98" value="<?php echo esc_attr( $this->get_field_value( 'pint_verification' ) ); ?>" />
1728
+ </p>
1729
+ <?php
1730
+
1731
+ do_action( 'the_seo_framework_webmaster_metabox_after' );
1732
+
1733
+ }
1734
+
1735
+ /**
1736
+ * Knowlegde Graph metabox on the Site SEO Settings page.
1737
+ *
1738
+ * @since 2.2.8
1739
+ *
1740
+ * @see $this->knowledge_metabox() Callback for Social Settings box.
1741
+ */
1742
+ public function knowledge_metabox( $args = array() ) {
1743
+
1744
+ do_action( 'the_seo_framework_knowledge_metabox_before' );
1745
+
1746
+ /**
1747
+ * Parse tabs content
1748
+ *
1749
+ * @param array $default_tabs { 'id' = The identifier =>
1750
+ * array(
1751
+ * 'name' => The name
1752
+ * 'callback' => The callback function, use array for method calling (accepts $this, but isn't used here for optimization purposes)
1753
+ * 'dashicon' => Desired dashicon
1754
+ * )
1755
+ * }
1756
+ *
1757
+ * @since 2.2.8
1758
+ */
1759
+ $default_tabs = array(
1760
+ 'general' => array(
1761
+ 'name' => __( 'General', 'autodescription' ),
1762
+ 'callback' => array( $this, 'knowledge_metabox_general_tab' ),
1763
+ 'dashicon' => 'admin-generic',
1764
+ ),
1765
+ 'website' => array(
1766
+ 'name' => __( 'Website', 'autodescription' ),
1767
+ 'callback' => array( $this, 'knowledge_metabox_about_tab' ),
1768
+ 'dashicon' => 'admin-home',
1769
+ ),
1770
+ 'social' => array(
1771
+ 'name' => 'Social Sites',
1772
+ 'callback' => array( $this, 'knowledge_metabox_social_tab' ),
1773
+ 'dashicon' => 'networking',
1774
+ ),
1775
+ );
1776
+
1777
+ /**
1778
+ * Applies filter knowledgegraph_settings_tabs : Array see $default_tabs
1779
+ *
1780
+ * Used to extend Knowledge Graph tabs
1781
+ */
1782
+ $defaults = (array) apply_filters( 'the_seo_framework_knowledgegraph_settings_tabs', $default_tabs, $args );
1783
+
1784
+ $tabs = wp_parse_args( $args, $defaults );
1785
+
1786
+ $this->nav_tab_wrapper( 'knowledge', $tabs, '2.2.8' );
1787
+
1788
+ do_action( 'the_seo_framework_knowledge_metabox_after' );
1789
+
1790
+ }
1791
+
1792
+ /**
1793
+ * Knowledge Graph Metabox General Tab Output
1794
+ *
1795
+ * @since 2.2.8
1796
+ *
1797
+ * @see $this->knowledge_metabox() Callback for Knowledge Graph Settings box.
1798
+ */
1799
+ public function knowledge_metabox_general_tab() {
1800
+
1801
+ ?>
1802
+ <h4><?php _e( 'Knowledge Graph Settings', 'autodescription' ); ?></h4>
1803
+ <p><span class="description"><?php printf( __( "The Knowledge Graph lets Google and other Search Engines know where to find you or your organization and its relevant content.", 'autodescription' ) ); ?></span></p>
1804
+ <p><span class="description"><?php printf( __( "Google is becoming more of an 'Answer Engine' than a 'Search Engine'. Setting up these options could have a positive impact on the SEO value of your website.", 'autodescription' ) ); ?></span></p>
1805
+
1806
+ <?php
1807
+ $knowledge_output_label = __( 'Output Knowledge tags?', 'autodescription' );
1808
+ $knowledge_output_checkbox = $this->make_checkbox( 'knowledge_output', $knowledge_output_label, '' );
1809
+
1810
+ //* Echo checkbox.
1811
+ echo $this->wrap_fields( $knowledge_output_checkbox );
1812
+
1813
+ if ( $this->wp_version( '4.2.999', '>=' ) ) :
1814
+ ?>
1815
+ <hr>
1816
+
1817
+ <h4><?php printf( _x( "Website logo", 'WordPress Customizer', 'autodescription' ) ); ?></h4>
1818
+ <?php
1819
+ $knowledge_logo_label = __( 'Use the Favicon from Customizer as the Organization Logo?', 'autodescription' );
1820
+ $knowledge_logo_description = __( "This option only has an effect when this site represents an Organization. If left disabled, Search Engines will look elsewhere for a logo, if it exists and is assigned as a logo.", 'autodescription' );
1821
+ $knowledge_logo_checkbox = $this->make_checkbox( 'knowledge_logo', $knowledge_logo_label, $knowledge_logo_description );
1822
+
1823
+ //* Echo checkbox.
1824
+ echo $this->wrap_fields( $knowledge_logo_checkbox );
1825
+ endif;
1826
+
1827
+ }
1828
+
1829
+ /**
1830
+ * Knowledge Graph Metabox About Tab Output
1831
+ *
1832
+ * @since 2.2.8
1833
+ *
1834
+ * @see $this->knowledge_metabox() Callback for Knowledge Graph Settings box.
1835
+ */
1836
+ public function knowledge_metabox_about_tab() {
1837
+
1838
+ $blogname = $this->get_blogname();
1839
+
1840
+ ?>
1841
+ <h4><?php _e( 'About this website', 'autodescription' ); ?></h4>
1842
+ <p><span class="description"><?php printf( __( 'Who or what is your website about?', 'autodescription' ) ); ?></span></p>
1843
+
1844
+ <hr>
1845
+
1846
+ <p>
1847
+ <label for="<?php $this->field_id( 'knowledge_type' ); ?>"><?php _ex( 'This website represents:', '...Organization or Person.', 'autodescription' ); ?></label>
1848
+ <select name="<?php $this->field_name( 'knowledge_type' ); ?>" id="<?php $this->field_id( 'knowledge_type' ); ?>">
1849
+ <?php
1850
+ $knowledge_type = (array) apply_filters(
1851
+ 'the_seo_framework_knowledge_types',
1852
+ array(
1853
+ 'organization' => __( 'An Organization', 'autodescription' ),
1854
+ 'person' => __( 'A Person', 'autodescription' ),
1855
+ )
1856
+ );
1857
+ foreach ( $knowledge_type as $value => $name )
1858
+ echo '<option value="' . esc_attr( $value ) . '"' . selected( $this->get_field_value( 'knowledge_type' ), esc_attr( $value ), false ) . '>' . esc_html( $name ) . '</option>' . "\n";
1859
+ ?>
1860
+ </select>
1861
+ </p>
1862
+
1863
+ <hr>
1864
+
1865
+ <p>
1866
+ <label for="<?php $this->field_id( 'knowledge_name' ); ?>">
1867
+ <strong><?php _e( "The organization or personal name", 'autodescription' ); ?></strong>
1868
+ </label>
1869
+ </p>
1870
+ <p>
1871
+ <input type="text" name="<?php $this->field_name( 'knowledge_name' ); ?>" class="large-text" id="<?php $this->field_id( 'knowledge_name' ); ?>" placeholder="<?php echo esc_attr( $blogname ) ?>" value="<?php echo esc_attr( $this->get_field_value( 'knowledge_name' ) ); ?>" />
1872
+ </p>
1873
+ <?php
1874
+
1875
+ }
1876
+
1877
+ /**
1878
+ * Knowledge Graph Metabox Social Tab Output
1879
+ *
1880
+ * @since 2.2.8
1881
+ *
1882
+ * @see $this->knowledge_metabox() Callback for Knowledge Graph Settings box.
1883
+ */
1884
+ public function knowledge_metabox_social_tab() {
1885
+
1886
+ ?>
1887
+ <h4><?php _e( 'Social Pages connected to this website', 'autodescription' ); ?></h4>
1888
+ <p><span class="description"><?php _e( "Don't have a page at a site or is the profile only privately accessible? Leave that field empty. Unsure? Fill it in anyway.", 'autodescription' ); ?></span></p>
1889
+ <p><span class="description"><?php _e( "Add the link that leads directly to the social page of this website.", 'autodescription' ); ?></span></p>
1890
+
1891
+ <hr>
1892
+
1893
+ <?php
1894
+ $connectedi18n = _x( 'RelatedProfile', 'No spaces. E.g. https://facebook.com/RelatedProfile', 'autodescription' );
1895
+ $profile18n = _x( 'Profile', 'Social Profile', 'autodescription' );
1896
+
1897
+ /**
1898
+ * @todo maybe genericons?
1899
+ */
1900
+
1901
+ $socialsites = array(
1902
+ 'facebook' => array(
1903
+ 'option' => 'knowledge_facebook',
1904
+ 'dashicon' => 'dashicons-facebook',
1905
+ 'desc' => 'Facebook ' . __( 'Page', 'autodescription' ),
1906
+ 'placeholder' => 'http://www.facebook.com/' . $connectedi18n,
1907
+ 'examplelink' => esc_url( 'https://facebook.com/me' ),
1908
+ ),
1909
+ 'twitter' => array(
1910
+ 'option' => 'knowledge_twitter',
1911
+ 'dashicon' => 'dashicons-twitter',
1912
+ 'desc' => 'Twitter ' . $profile18n,
1913
+ 'placeholder' => 'http://www.twitter.com/' . $connectedi18n,
1914
+ 'examplelink' => esc_url( 'https://twitter.com/home' ), // No example link available.
1915
+ ),
1916
+ 'gplus' => array(
1917
+ 'option' => 'knowledge_gplus',
1918
+ 'dashicon' => 'dashicons-googleplus',
1919
+ 'desc' => 'Google+ ' . $profile18n,
1920
+ 'placeholder' => 'https://plus.google.com/' . $connectedi18n,
1921
+ 'examplelink' => esc_url( 'https://plus.google.com/me' ),
1922
+ ),
1923
+ 'instagram' => array(
1924
+ 'option' => 'knowledge_instagram',
1925
+ 'dashicon' => 'genericon-instagram',
1926
+ 'desc' => 'Instagram ' . $profile18n,
1927
+ 'placeholder' => 'http://instagram.com/' . $connectedi18n,
1928
+ 'examplelink' => esc_url( 'https://instagram.com/' ), // No example link available.
1929
+ ),
1930
+ 'youtube' => array(
1931
+ 'option' => 'knowledge_youtube',
1932
+ 'dashicon' => 'genericon-youtube',
1933
+ 'desc' => 'Youtube ' . $profile18n,
1934
+ 'placeholder' => 'http://www.youtube.com/' . $connectedi18n,
1935
+ 'examplelink' => esc_url( 'https://www.youtube.com/user/%2f' ), // Yes a double slash.
1936
+ ),
1937
+ 'linkedin' => array(
1938
+ 'option' => 'knowledge_linkedin',
1939
+ 'dashicon' => 'genericon-linkedin-alt',
1940
+ 'desc' => 'LinkedIn ' . $profile18n . ' ID',
1941
+ 'placeholder' => 'http://www.linkedin.com/profile/view?id=' . $connectedi18n,
1942
+ 'examplelink' => esc_url( 'https://www.linkedin.com/profile/view' ), // This generates a query arg. We should allow that.
1943
+ ),
1944
+ 'pinterest' => array(
1945
+ 'option' => 'knowledge_pinterest',
1946
+ 'dashicon' => 'genericon-pinterest-alt',
1947
+ 'desc' => 'Pinterest ' . $profile18n,
1948
+ 'placeholder' => 'https://www.pinterest.com/' . $connectedi18n . '/',
1949
+ 'examplelink' => esc_url( 'https://www.pinterest.com/me/' ),
1950
+ ),
1951
+ 'soundcloud' => array(
1952
+ 'option' => 'knowledge_soundcloud',
1953
+ 'dashicon' => 'genericon-cloud', // I know, it's not the real one. D:
1954
+ 'desc' => 'SoundCloud ' . $profile18n,
1955
+ 'placeholder' => 'https://soundcloud.com/' . $connectedi18n,
1956
+ 'examplelink' => esc_url( 'https://soundcloud.com/you' ),
1957
+ ),
1958
+ 'tumblr' => array(
1959
+ 'option' => 'knowledge_tumblr',
1960
+ 'dashicon' => 'genericon-tumblr',
1961
+ 'desc' => 'Tumblr ' . __( 'Blog', 'autodescription' ),
1962
+ 'placeholder' => 'https://tumblr.com/blog/' . $connectedi18n,
1963
+ 'examplelink' => esc_url( 'https://www.tumblr.com/dashboard' ), // No example link available.
1964
+ ),
1965
+ );
1966
+
1967
+ foreach ( $socialsites as $key => $value ) {
1968
+ ?>
1969
+ <p>
1970
+ <label for="<?php $this->field_id( $value['option'] ); ?>">
1971
+ <strong><?php echo $value['desc'] ?></strong>
1972
+ <?php
1973
+ if ( $value['examplelink'] ) {
1974
+ ?><a href="<?php echo esc_url( $value['examplelink'] ); ?>" target="_blank">[?]</a><?php
1975
+ }
1976
+ ?>
1977
+ </label>
1978
+ </p>
1979
+ <p>
1980
+ <input type="text" name="<?php $this->field_name( $value['option'] ); ?>" class="large-text" id="<?php $this->field_id( $value['option'] ); ?>" placeholder="<?php echo esc_attr( $value['placeholder'] ) ?>" value="<?php echo esc_attr( $this->get_field_value( $value['option'] ) ); ?>" />
1981
+ </p>
1982
+ <?php
1983
+ }
1984
+
1985
+ }
1986
+
1987
+ /**
1988
+ * Sitemaps meta box on the Site SEO Settings page.
1989
+ *
1990
+ * @since 2.2.9
1991
+ *
1992
+ * @see $this->sitemaps_metabox() Callback for Sitemaps Settings box.
1993
+ */
1994
+ public function sitemaps_metabox( $args = array() ) {
1995
+
1996
+ do_action( 'the_seo_framework_sitemaps_metabox_before' );
1997
+
1998
+ if ( ! $this->pretty_permalinks ) {
1999
+
2000
+ $permalink_settings_url = esc_url( admin_url( 'options-permalink.php' ) );
2001
+ $here = '<a href="' . $permalink_settings_url . '" target="_blank" title="' . __( 'Permalink Settings', 'autodescription' ) . '">' . _x( 'here', 'The sitemap can be found %s.', 'autodescription' ) . '</a>';
2002
+
2003
+ ?>
2004
+ <h4><?php _e( "You're using the plain permalink structure.", 'autodescription' ); ?></h4>
2005
+ <p><span class="description"><?php _e( "This means we can't output the sitemap through the WordPress rewrite rules.", 'autodescription' ); ?></span></p>
2006
+ <hr>
2007
+ <p><span class="description"><?php printf( _x( "Change your Permalink Settings %s (Recommended: 'postname').", '%s = here', 'autodescription' ), $here ); ?></span></p>
2008
+ <?php
2009
+
2010
+ } else {
2011
+
2012
+ /**
2013
+ * Parse tabs content
2014
+ *
2015
+ * @param array $default_tabs { 'id' = The identifier =>
2016
+ * array(
2017
+ * 'name' => The name
2018
+ * 'callback' => The callback function, use array for method calling (accepts $this, but isn't used here for optimization purposes)
2019
+ * 'dashicon' => Desired dashicon
2020
+ * )
2021
+ * }
2022
+ *
2023
+ * @since 2.2.9
2024
+ */
2025
+ $default_tabs = array(
2026
+ 'general' => array(
2027
+ 'name' => __( 'General', 'autodescription' ),
2028
+ 'callback' => array( $this, 'sitemaps_metabox_general_tab' ),
2029
+ 'dashicon' => 'admin-generic',
2030
+ ),
2031
+ 'robots' => array(
2032
+ 'name' => 'Robots.txt',
2033
+ 'callback' => array( $this, 'sitemaps_metabox_robots_tab' ),
2034
+ 'dashicon' => 'share-alt2',
2035
+ ),
2036
+ 'timestamps' => array(
2037
+ 'name' => __( 'Timestamps', 'autodescription' ),
2038
+ 'callback' => array( $this, 'sitemaps_metabox_timestamps_tab' ),
2039
+ 'dashicon' => 'backup',
2040
+ ),
2041
+ 'notify' => array(
2042
+ 'name' => _x( 'Ping', 'Ping or notify Search Engine', 'autodescription' ),
2043
+ 'callback' => array( $this, 'sitemaps_metabox_notify_tab' ),
2044
+ 'dashicon' => 'megaphone',
2045
+ ),
2046
+ );
2047
+
2048
+ /**
2049
+ * Applies filters the_seo_framework_sitemaps_settings_tabs : array see $default_tabs
2050
+ *
2051
+ * Used to extend Knowledge Graph tabs
2052
+ */
2053
+ $defaults = (array) apply_filters( 'the_seo_framework_sitemaps_settings_tabs', $default_tabs, $args );
2054
+
2055
+ $tabs = wp_parse_args( $args, $defaults );
2056
+ $use_tabs = true;
2057
+
2058
+ $sitemap_plugin = $this->detect_sitemap_plugin();
2059
+ $sitemap_detected = $this->has_sitemap_xml();
2060
+ $robots_detected = $this->has_robots_txt();
2061
+
2062
+ /**
2063
+ * Remove the timestamps and notify submenus
2064
+ * @since 2.5.2
2065
+ */
2066
+ if ( $sitemap_plugin || $sitemap_detected ) {
2067
+ unset( $tabs['timestamps'] );
2068
+ unset( $tabs['notify'] );
2069
+ }
2070
+
2071
+ /**
2072
+ * Remove the robots submenu
2073
+ * @since 2.5.2
2074
+ */
2075
+ if ( $robots_detected ) {
2076
+ unset( $tabs['robots'] );
2077
+ }
2078
+
2079
+ if ( $robots_detected && ( $sitemap_plugin || $sitemap_detected ) )
2080
+ $use_tabs = false;
2081
+
2082
+ $this->nav_tab_wrapper( 'sitemaps', $tabs, '2.2.8', $use_tabs );
2083
+
2084
+ }
2085
+
2086
+ do_action( 'the_seo_framework_sitemaps_metabox_after' );
2087
+
2088
+ }
2089
+
2090
+ /**
2091
+ * Sitemaps Metabox General Tab Output
2092
+ *
2093
+ * @since 2.2.9
2094
+ *
2095
+ * @see $this->sitemaps_metabox() Callback for Sitemaps Settings box.
2096
+ */
2097
+ public function sitemaps_metabox_general_tab() {
2098
+
2099
+ $site_url = $this->the_home_url_from_cache( true );
2100
+
2101
+ $sitemap_url = $site_url . 'sitemap.xml';
2102
+ $has_sitemap_plugin = $this->detect_sitemap_plugin();
2103
+ $sitemap_detected = $this->has_sitemap_xml();
2104
+
2105
+ ?>
2106
+ <h4><?php _e( 'Sitemap Integration Settings', 'autodescription' ); ?></h4>
2107
+ <?php
2108
+
2109
+ if ( $has_sitemap_plugin ) {
2110
+ ?>
2111
+ <p class="description"><?php _e( "Another active sitemap plugin has been detected. This means that the sitemap functionality has been replaced.", 'autodescription' ); ?></p>
2112
+ <?php
2113
+ } else if ( $sitemap_detected ) {
2114
+ ?>
2115
+ <p class="description"><?php _e( "A sitemap has been detected in the root folder of your website. This means that the sitemap functionality has no effect.", 'autodescription' ); ?></p>
2116
+ <?php
2117
+ } else {
2118
+ ?>
2119
+ <p class="description"><?php _e( "The Sitemap is an XML file that lists pages and posts for your website along with optional metadata about each post or page. This helps Search Engines crawl your website more easily.", 'autodescription' ); ?></p>
2120
+ <p class="description"><?php _e( "The optional metadata include the post and page modified time and a page priority indication, which is automated.", 'autodescription' ); ?></p>
2121
+
2122
+ <hr>
2123
+
2124
+ <h4><?php _e( 'Sitemap Output', 'autodescription' ); ?></h4>
2125
+ <?php
2126
+ $sitemaps_output_label = __( 'Output Sitemap?', 'autodescription' );
2127
+ $sitemaps_output_checkbox = $this->make_checkbox( 'sitemaps_output', $sitemaps_output_label, '' );
2128
+
2129
+ //* Echo checkbox.
2130
+ echo $this->wrap_fields( $sitemaps_output_checkbox );
2131
+ }
2132
+
2133
+ if ( ! ( $has_sitemap_plugin || $sitemap_detected ) && $this->get_option( 'sitemaps_output' ) ) {
2134
+ $here = '<a href="' . $sitemap_url . '" target="_blank" title="' . __( 'View sitemap', 'autodescription' ) . '">' . _x( 'here', 'The sitemap can be found %s.', 'autodescription' ) . '</a>';
2135
+
2136
+ ?><p class="description"><?php printf( _x( 'The sitemap can be found %s.', '%s = here', 'autodescription' ), $here ); ?></p><?php
2137
+ }
2138
+
2139
+ }
2140
+
2141
+ /**
2142
+ * Sitemaps Metabox Robots Tab Output
2143
+ *
2144
+ * @since 2.2.9
2145
+ *
2146
+ * @see $this->sitemaps_metabox() Callback for Sitemaps Settings box.
2147
+ */
2148
+ public function sitemaps_metabox_robots_tab() {
2149
+
2150
+ $site_url = $this->the_home_url_from_cache( true );
2151
+
2152
+ $robots_url = trailingslashit( $site_url ) . 'robots.txt';
2153
+ $here = '<a href="' . $robots_url . '" target="_blank" title="' . __( 'View robots.txt', 'autodescription' ) . '">' . _x( 'here', 'The sitemap can be found %s.', 'autodescription' ) . '</a>';
2154
+
2155
+ ?>
2156
+ <h4><?php _e( 'Robots.txt Settings', 'autodescription' ); ?></h4>
2157
+ <?php
2158
+ if ( $this->can_do_sitemap_robots() ) :
2159
+ ?>
2160
+ <p class="description"><?php _e( 'The robots.txt file is the first thing Search Engines look for. If you add the sitemap location in the robots.txt file, then Search Engines will look for and index the sitemap.', 'autodescription' ); ?></p>
2161
+ <p class="description"><?php _e( 'If you do not add the sitemap location to the robots.txt file, you will need to notify Search Engines manually through the Webmaster Console provided by the Search Engines.', 'autodescription' ); ?></p>
2162
+
2163
+ <hr>
2164
+
2165
+ <h4><?php _e( 'Add sitemap location in robots.txt', 'autodescription' ); ?></h4>
2166
+ <?php
2167
+ //* Echo checkbox.
2168
+ $this->wrap_fields(
2169
+ $this->make_checkbox(
2170
+ 'sitemaps_robots',
2171
+ __( 'Add sitemap location in robots?', 'autodescription' ),
2172
+ ''
2173
+ ), true
2174
+ );
2175
+ else :
2176
+ ?>
2177
+ <p class="description"><?php _e( 'Another robots.txt sitemap Location addition has been detected.', 'autodescription' ); ?></p>
2178
+ <?php
2179
+ endif;
2180
+
2181
+ ?>
2182
+ <p class="description"><?php printf( _x( 'The robots.txt file can be found %s.', '%s = here', 'autodescription' ), $here ); ?></p>
2183
+ <?php
2184
+
2185
+ }
2186
+
2187
+ /**
2188
+ * Sitemaps Metabox Timestamps Tab Output
2189
+ *
2190
+ * @since 2.2.9
2191
+ *
2192
+ * @see $this->sitemaps_metabox() Callback for Sitemaps Settings box.
2193
+ */
2194
+ public function sitemaps_metabox_timestamps_tab() {
2195
+
2196
+ //* Sets timezone according to WordPress settings.
2197
+ $this->set_timezone();
2198
+
2199
+ $timestamp_0 = date( 'Y-m-d' );
2200
+
2201
+ /**
2202
+ * @link https://www.w3.org/TR/NOTE-datetime
2203
+ * We use the second expression of the time zone offset handling.
2204
+ */
2205
+ $timestamp_1 = date( 'Y-m-d\TH:iP' );
2206
+
2207
+ //* Reset timezone to previous value.
2208
+ $this->reset_timezone();
2209
+
2210
+ ?>
2211
+ <h4><?php _e( 'Timestamps Settings', 'autodescription' ); ?></h4>
2212
+ <p><span class="description"><?php _e( 'The modified time suggests to Search Engines where to look for content changes. It has no impact on the SEO value unless you drastically change pages or posts. It then depends on how well your content is constructed.', 'autodescription' ); ?></span></p>
2213
+ <p><span class="description"><?php _e( "By default, the sitemap only outputs the modified date if you've enabled them within the Social Metabox. This setting overrides those settings for the Sitemap.", 'autodescription' ); ?></span></p>
2214
+
2215
+ <hr>
2216
+
2217
+ <h4><?php _e( 'Output Modified Date', 'autodescription' ); ?></h4>
2218
+ <?php
2219
+ $sitemaps_modified_label = sprintf( __( 'Add %s to the sitemap?', 'autodescription' ), $this->code_wrap( '<lastmod>' ) );
2220
+ $sitemaps_modified_checkbox = $this->make_checkbox( 'sitemaps_modified', $sitemaps_modified_label, '' );
2221
+
2222
+ //* Echo checkbox.
2223
+ echo $this->wrap_fields( $sitemaps_modified_checkbox );
2224
+ ?>
2225
+
2226
+ <hr>
2227
+
2228
+ <fieldset>
2229
+ <legend><h4><?php _e( 'Timestamp Format Settings', 'autodescription' ); ?></h4></legend>
2230
+ <p>
2231
+ <span class="description"><?php _e( 'Determines how specific the modification timestamp is.', 'autodescription' ); ?></span>
2232
+ </p>
2233
+
2234
+ <p id="sitemaps-timestamp-format" class="theseoframework-fields">
2235
+ <span class="toblock">
2236
+ <input type="radio" name="<?php $this->field_name( 'sitemap_timestamps' ); ?>" id="<?php $this->field_id( 'sitemap_timestamps_0' ); ?>" value="0" <?php checked( $this->get_field_value( 'sitemap_timestamps' ), '0' ); ?> />
2237
+ <label for="<?php $this->field_id( 'sitemap_timestamps_0' ); ?>">
2238
+ <span title="<?php _e( 'Complete date', 'autodescription' ); ?>"><?php echo $this->code_wrap( $timestamp_0 ) ?> [?]</span>
2239
+ </label>
2240
+ </span>
2241
+ <span class="toblock">
2242
+ <input type="radio" name="<?php $this->field_name( 'sitemap_timestamps' ); ?>" id="<?php $this->field_id( 'sitemap_timestamps_1' ); ?>" value="1" <?php checked( $this->get_field_value( 'sitemap_timestamps' ), '1' ); ?> />
2243
+ <label for="<?php $this->field_id( 'sitemap_timestamps_1' ); ?>">
2244
+ <span title="<?php _e( 'Complete date plus hours, minutes and timezone', 'autodescription' ); ?>"><?php echo $this->code_wrap( $timestamp_1 ); ?> [?]</span>
2245
+ </label>
2246
+ </span>
2247
+ </p>
2248
+ </fieldset>
2249
+ <?php
2250
+
2251
+ }
2252
+
2253
+ /**
2254
+ * Sitemaps Metabox Notify Tab Output
2255
+ *
2256
+ * @since 2.2.9
2257
+ *
2258
+ * @see $this->sitemaps_metabox() Callback for Sitemaps Settings box.
2259
+ */
2260
+ public function sitemaps_metabox_notify_tab() {
2261
+
2262
+ ?>
2263
+ <h4><?php _e( 'Ping Settings', 'autodescription' ); ?></h4>
2264
+ <p><span class="description"><?php _e( "Notifying Search Engines of a sitemap change is helpful to get your content indexed as soon as possible.", 'autodescription' ); ?></span></p>
2265
+ <p><span class="description"><?php _e( "By default this will happen at most once an hour.", 'autodescription' ); ?></span></p>
2266
+
2267
+ <hr>
2268
+
2269
+ <h4><?php _e( 'Notify Search Engines', 'autodescription' ); ?></h4>
2270
+ <?php
2271
+ $engines = array(
2272
+ 'ping_google' => 'Google',
2273
+ 'ping_bing' => 'Bing',
2274
+ 'ping_yandex' => 'Yandex'
2275
+ );
2276
+
2277
+ $ping_checkbox = '';
2278
+
2279
+ foreach ( $engines as $option => $engine ) {
2280
+ $ping_label = sprintf( __( 'Notify %s about sitemap changes?', 'autodescription' ), $engine );
2281
+ $ping_checkbox .= $this->make_checkbox( $option, $ping_label, '' );
2282
+ }
2283
+
2284
+ //* Echo checkbox.
2285
+ $this->wrap_fields( $ping_checkbox, true );
2286
+
2287
+ }
2288
+
2289
+ /**
2290
+ * Feed meta box on the Site SEO Settings page.
2291
+ *
2292
+ * @since 2.5.2
2293
+ */
2294
+ public function feed_metabox() {
2295
+
2296
+ do_action( 'the_seo_framework_feed_metabox_before' );
2297
+
2298
+ ?>
2299
+ <h4><?php _e( 'Content Feed Settings', 'autodescription' ); ?></h4>
2300
+ <p class="description"><?php _e( "Sometimes, your content can get stolen by robots through the WordPress feeds. This can cause duplicate content issues. To prevent this from happening, it's recommended to convert the feed's content into an excerpt.", 'autodescription' ); ?></p>
2301
+ <p class="description"><?php _e( "Adding a backlink below the feed's content will also let the visitors know where the content came from.", 'autodescription' ); ?></p>
2302
+
2303
+ <hr>
2304
+
2305
+ <h4><?php _e( 'Change Feed Settings', 'autodescription' ); ?></h4>
2306
+ <?php
2307
+ $excerpt_the_feed_label = __( 'Convert feed content into excerpts?', 'autodescription' );
2308
+ $excerpt_the_feed_label .= ' ' . $this->make_info( __( "By default the excerpt will be at most 400 characters long", 'autodescription' ), '', false );
2309
+
2310
+ $source_the_feed_label = __( 'Add backlinks below the feed content?', 'autodescription' );
2311
+ $source_the_feed_label .= ' ' . $this->make_info( __( "This link will not be followed by Search Engines", 'autodescription' ), '', false );
2312
+
2313
+ //* Echo checkboxes.
2314
+ $this->wrap_fields(
2315
+ array(
2316
+ $this->make_checkbox( 'excerpt_the_feed', $excerpt_the_feed_label, '' ),
2317
+ $this->make_checkbox( 'source_the_feed', $source_the_feed_label, '' ),
2318
+ ), true
2319
+ );
2320
+
2321
+ if ( $this->rss_uses_excerpt() ) {
2322
+ $reading_settings_url = esc_url( admin_url( 'options-reading.php' ) );
2323
+ $reading_settings = '<a href="' . $reading_settings_url . '" target="_blank" title="' . __( 'Reading Settings', 'autodescription' ) . '">' . __( 'Reading Settings', 'autodescription' ) . '</a>';
2324
+
2325
+ ?><p><span class="description"><?php
2326
+ printf( _x( "Note: The feed is already converted into an excerpt through the %s.", '%s = Reading Settings', 'autodescription' ), $reading_settings );
2327
+ ?></span></p><?php
2328
+ }
2329
+
2330
+ $feed_url = esc_url( get_feed_link() );
2331
+ $here = '<a href="' . $feed_url . '" target="_blank" title="' . __( 'View feed', 'autodescription' ) . '">' . _x( 'here', 'The feed can be found %s.', 'autodescription' ) . '</a>';
2332
+
2333
+ ?><p class="description"><?php printf( _x( 'The feed can be found %s.', '%s = here', 'autodescription' ), $here ); ?></p><?php
2334
+
2335
+ do_action( 'the_seo_framework_feed_metabox_after' );
2336
+
2337
+ }
2338
+
2339
+ /**
2340
+ * Schema metabox.
2341
+ *
2342
+ * @since 2.6.0
2343
+ */
2344
+ public function schema_metabox() {
2345
+
2346
+ do_action( 'the_seo_framework_schema_metabox_before' );
2347
+
2348
+ ?>
2349
+ <h4><?php _e( 'Schema.org Output Settings', 'autodescription' ); ?></h4>
2350
+
2351
+ <?php if ( $this->has_json_ld_plugin() ) : ?>
2352
+ <p class="description"><?php _e( 'Another Schema.org plugin has been detected.', 'autodescription' ); ?></p>
2353
+ <?php else : ?>
2354
+ <p class="description"><?php _e( "The Schema.org markup is a standard way of annotating structured data for Search Engines. This markup is represented within hidden scripts throughout the website.", 'autodescription' ); ?></p>
2355
+ <p class="description"><?php _e( "When your web pages include structured data markup, Search Engines can use that data to index your content better, present it more prominently in Search Results, and use it in several different applications.", 'autodescription' ); ?></p>
2356
+
2357
+ <hr>
2358
+
2359
+ <?php /* translators: https://developers.google.com/search/docs/data-types/sitelinks-searchbox */ ?>
2360
+ <h4><?php _ex( 'Sitelinks Searchbox', 'Product name', 'autodescription' ); ?></h4>
2361
+ <p class="description"><?php _e( 'When Search users search for your brand name, the following option allows them to search through your website directly from the Search Results.', 'autodescription' ); ?></p>
2362
+ <?php
2363
+ $info = $this->make_info(
2364
+ _x( 'Sitelinks Searchbox', 'Product name', 'autodescription' ),
2365
+ 'https://developers.google.com/search/docs/data-types/sitelinks-searchbox',
2366
+ false
2367
+ );
2368
+ $this->wrap_fields(
2369
+ $this->make_checkbox(
2370
+ 'ld_json_searchbox',
2371
+ _x( 'Enable Sitelinks Searchbox?', 'Product name', 'autodescription' ) . ' ' . $info,
2372
+ ''
2373
+ ),
2374
+ true
2375
+ );
2376
+ ?>
2377
+
2378
+ <hr>
2379
+
2380
+ <h4><?php _e( 'Site Name', 'autodescription' ); ?></h4>
2381
+ <p class="description"><?php _e( "When using breadcrumbs, the first entry is by default your website's address. Using the following option will convert it to the Site Name.", 'autodescription' ); ?></p>
2382
+ <?php
2383
+ $info = $this->make_info(
2384
+ __( 'Include your Site Name in Search Results', 'autodescription' ),
2385
+ 'https://developers.google.com/search/docs/data-types/sitename',
2386
+ false
2387
+ );
2388
+ $description = sprintf( __( "The Site Name is: %s", 'autodescription' ), $this->code_wrap( $this->get_blogname() ) );
2389
+ $this->wrap_fields(
2390
+ $this->make_checkbox(
2391
+ 'ld_json_sitename',
2392
+ __( 'Convert URL to Site Name?', 'autodescription' ) . ' ' . $info,
2393
+ $description
2394
+ ),
2395
+ true
2396
+ );
2397
+ ?>
2398
+
2399
+ <hr>
2400
+
2401
+ <h4><?php _e( 'Breadcrumbs', 'autodescription' ); ?></h4>
2402
+ <p class="description"><?php _e( "Breadcrumb trails indicate the page's position in the site hierarchy. Using the following option will show the hierarchy within the Search Results when available.", 'autodescription' ); ?></p>
2403
+ <?php
2404
+ $info = $this->make_info(
2405
+ __( 'About Breadcrumbs', 'autodescription' ),
2406
+ 'https://developers.google.com/search/docs/data-types/breadcrumbs',
2407
+ false
2408
+ );
2409
+ $description = __( "Multiple trails can be outputted. The longest trail is prioritized.", 'autodescription' );
2410
+ $this->wrap_fields(
2411
+ $this->make_checkbox(
2412
+ 'ld_json_breadcrumbs',
2413
+ __( 'Enable Breadcrumbs?', 'autodescription' ) . ' ' . $info,
2414
+ $description
2415
+ ),
2416
+ true
2417
+ );
2418
+ endif;
2419
+
2420
+ do_action( 'the_seo_framework_schema_metabox_after' );
2421
+
2422
+ }
2423
+
2424
+ }
inc/classes/postdata.class.php ADDED
@@ -0,0 +1,421 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The SEO Framework plugin
4
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License version 3 as published
8
+ * by the Free Software Foundation.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ /**
20
+ * Class AutoDescription_PostData
21
+ *
22
+ * Holds Post data.
23
+ *
24
+ * @since 2.1.6
25
+ */
26
+ class AutoDescription_PostData extends AutoDescription_Detect {
27
+
28
+ /**
29
+ * Constructor, load parent constructor
30
+ */
31
+ public function __construct() {
32
+ parent::__construct();
33
+
34
+ add_action( 'save_post', array( $this, 'inpost_seo_save' ), 1, 2 );
35
+ }
36
+
37
+ /**
38
+ * Save the SEO settings when we save a post or page.
39
+ * Some values get sanitized, the rest are pulled from identically named subkeys in the $_POST['autodescription'] array.
40
+ *
41
+ * @uses $this->save_custom_fields() Perform checks and saves post meta / custom field data to a post or page.
42
+ *
43
+ * @since 2.0.0
44
+ *
45
+ * @param integer $post_id Post ID.
46
+ * @param stdClass $post Post object.
47
+ * @return mixed Returns post id if permissions incorrect, null if doing autosave, ajax or future post, false if update
48
+ * or delete failed, and true on success.
49
+ */
50
+ public function inpost_seo_save( $post_id, $post ) {
51
+
52
+ if ( ! isset( $_POST['autodescription'] ) )
53
+ return;
54
+
55
+ //* Merge user submitted options with fallback defaults
56
+ $data = wp_parse_args( $_POST['autodescription'], array(
57
+ '_genesis_title' => '',
58
+ '_genesis_description' => '',
59
+ '_genesis_canonical_uri' => '',
60
+ 'redirect' => '',
61
+ '_genesis_noindex' => 0,
62
+ '_genesis_nofollow' => 0,
63
+ '_genesis_noarchive' => 0,
64
+ 'exclude_local_search' => 0,
65
+ ) );
66
+
67
+ foreach ( (array) $data as $key => $value ) {
68
+ //* Sanitize the title
69
+ if ( '_genesis_title' === $key )
70
+ $data[$key] = trim( strip_tags( $value ) );
71
+
72
+ //* Sanitize the description
73
+ if ( '_genesis_description' === $key )
74
+ $data[$key] = $this->s_description( $value );
75
+
76
+ //* Sanitize the URL. Make sure it's an absolute URL
77
+ if ( 'redirect' === $key )
78
+ $data[$key] = $this->s_redirect_url( $value );
79
+
80
+ }
81
+
82
+ $this->save_custom_fields( $data, 'inpost_seo_save', 'hmpl_ad_inpost_seo_nonce', $post );
83
+ }
84
+
85
+ /**
86
+ * Save post meta / custom field data for a post or page.
87
+ *
88
+ * It verifies the nonce, then checks we're not doing autosave, ajax or a future post request. It then checks the
89
+ * current user's permissions, before finally* either updating the post meta, or deleting the field if the value was not
90
+ * truthy.
91
+ *
92
+ * By passing an array of fields => values from the same metabox (and therefore same nonce) into the $data argument,
93
+ * repeated checks against the nonce, request and permissions are avoided.
94
+ *
95
+ * @since 2.0.0
96
+ *
97
+ * @thanks StudioPress (http://www.studiopress.com/) for some code.
98
+ *
99
+ * @param array $data Key/Value pairs of data to save in '_field_name' => 'value' format.
100
+ * @param string $nonce_action Nonce action for use with wp_verify_nonce().
101
+ * @param string $nonce_name Name of the nonce to check for permissions.
102
+ * @param WP_Post|integer $post Post object or ID.
103
+ * @return mixed Return null if permissions incorrect, doing autosave, ajax or future post, false if update or delete
104
+ * failed, and true on success.
105
+ */
106
+ public function save_custom_fields( array $data, $nonce_action, $nonce_name, $post ) {
107
+
108
+ //* Verify the nonce
109
+ if ( ! isset( $_POST[ $nonce_name ] ) || ! wp_verify_nonce( $_POST[ $nonce_name ], $nonce_action ) )
110
+ return;
111
+
112
+ //* Don't try to save the data under autosave, ajax, or future post.
113
+ if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
114
+ return;
115
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX )
116
+ return;
117
+ if ( defined( 'DOING_CRON' ) && DOING_CRON )
118
+ return;
119
+
120
+ //* Grab the post object
121
+ $post = get_post( $post );
122
+
123
+ //* Don't save if WP is creating a revision (same as DOING_AUTOSAVE?)
124
+ if ( 'revision' === get_post_type( $post ) )
125
+ return;
126
+
127
+ //* Check that the user is allowed to edit the post
128
+ if ( ! current_user_can( 'edit_post', $post->ID ) )
129
+ return;
130
+
131
+ //* Cycle through $data, insert value or delete field
132
+ foreach ( (array) $data as $field => $value ) {
133
+ //* Save $value, or delete if the $value is empty
134
+ if ( $value )
135
+ update_post_meta( $post->ID, $field, $value );
136
+ else
137
+ delete_post_meta( $post->ID, $field );
138
+ }
139
+
140
+ }
141
+
142
+ /**
143
+ * Fetches or parses the excerpt of the post
144
+ *
145
+ * @since 1.0.0
146
+ *
147
+ * @param string $excerpt the Excerpt
148
+ * @param int $the_id The Post ID.
149
+ * @param int $tt_id The Taxonomy Term ID
150
+ *
151
+ * @return string The Excerpt
152
+ */
153
+ public function get_excerpt_by_id( $excerpt = '', $the_id = '', $tt_id = '' ) {
154
+
155
+ static $cache = array();
156
+
157
+ if ( isset( $cache[$excerpt][$the_id][$tt_id] ) )
158
+ return $cache[$excerpt][$the_id][$tt_id];
159
+
160
+ if ( empty( $excerpt ) )
161
+ $excerpt = $this->fetch_excerpt( $the_id, $tt_id );
162
+
163
+ //* No need to parse an empty excerpt.
164
+ if ( '' === $excerpt )
165
+ return '';
166
+
167
+ $excerpt = wp_strip_all_tags( strip_shortcodes( $excerpt ) );
168
+
169
+ $output = $this->s_description( $excerpt );
170
+
171
+ return $output;
172
+ }
173
+
174
+ /**
175
+ * Fetches excerpt from post excerpt or fetches the full post content.
176
+ * Determines if a page builder is used to return an empty string.
177
+ * Does not sanitize output.
178
+ *
179
+ * @since 2.5.2
180
+ * @since 2.6.6 Detects Page builders.
181
+ *
182
+ * @param int $the_id The Post ID.
183
+ * @param int $tt_id The Taxonomy Term ID.
184
+ * @return string|empty excerpt.
185
+ */
186
+ public function fetch_excerpt( $the_id = '', $tt_id = '' ) {
187
+
188
+ $post = $this->fetch_post_by_id( $the_id, $tt_id, OBJECT );
189
+
190
+ if ( empty( $post ) )
191
+ return '';
192
+
193
+ /**
194
+ * Fetch custom excerpt, if not empty, from the post_excerpt field.
195
+ * @since 2.5.2
196
+ */
197
+ if ( isset( $post->post_excerpt ) && $post->post_excerpt ) {
198
+ $excerpt = $post->post_excerpt;
199
+ } else if ( isset( $post->post_content ) ) {
200
+ $uses_builder = $this->uses_page_builder( $post->ID );
201
+ $excerpt = $uses_builder ? '' : $post->post_content;
202
+ } else {
203
+ $excerpt = '';
204
+ }
205
+
206
+ return $excerpt;
207
+ }
208
+
209
+ /**
210
+ * Returns Post Array from ID.
211
+ * Also returns latest post from blog or archive if applicable.
212
+ *
213
+ * @since 2.6.0
214
+ * @since 2.6.6 Added $output parameter.
215
+ *
216
+ * @param int $the_id The Post ID.
217
+ * @param int $tt_id The Taxonomy Term ID
218
+ * @param mixed $output The value type to return. Accepts OBJECT, ARRAY_A, or ARRAY_N
219
+ * @return empty|array The Post Array.
220
+ */
221
+ protected function fetch_post_by_id( $the_id = '', $tt_id = '', $output = ARRAY_A ) {
222
+
223
+ if ( '' === $the_id && '' === $tt_id ) {
224
+ $the_id = $this->get_the_real_ID();
225
+
226
+ if ( false === $the_id )
227
+ return '';
228
+ }
229
+
230
+ /**
231
+ * @since 2.2.8 Use the 2nd parameter.
232
+ * @since 2.3.3 Now casts to array
233
+ */
234
+ if ( '' !== $the_id ) {
235
+ if ( $this->is_blog_page( $the_id ) ) {
236
+ $args = array(
237
+ 'posts_per_page' => 1,
238
+ 'offset' => 0,
239
+ 'category' => '',
240
+ 'category_name' => '',
241
+ 'orderby' => 'date',
242
+ 'order' => 'DESC',
243
+ 'post_type' => 'post',
244
+ 'post_status' => 'publish',
245
+ 'cache_results' => false,
246
+ );
247
+
248
+ $post = get_posts( $args );
249
+ } else {
250
+ $post = get_post( $the_id );
251
+ }
252
+ } else if ( '' !== $tt_id ) {
253
+ /**
254
+ * @since 2.3.3 Match the descriptions in admin as on the front end.
255
+ */
256
+ $args = array(
257
+ 'posts_per_page' => 1,
258
+ 'offset' => 0,
259
+ 'category' => $tt_id,
260
+ 'category_name' => '',
261
+ 'post_type' => 'post',
262
+ 'post_status' => 'publish',
263
+ 'cache_results' => false,
264
+ );
265
+
266
+ $post = get_posts( $args );
267
+ } else {
268
+ $post = get_post( $the_id );
269
+ }
270
+
271
+ /**
272
+ * @since 2.6.5 Transform post array to object (on Archives).
273
+ */
274
+ if ( is_array( $post ) && isset( $post[0] ) && is_object( $post[0] ) )
275
+ $post = $post[0];
276
+
277
+ //* Something went wrong, nothing to be found. Return empty.
278
+ if ( empty( $post ) )
279
+ return '';
280
+
281
+ //* Stop getting something that doesn't exists. E.g. 404
282
+ if ( isset( $post->ID ) && 0 === $post->ID )
283
+ return '';
284
+
285
+ /**
286
+ * @since 2.6.6
287
+ */
288
+ if ( ARRAY_A === $output || ARRAY_N === $output ) {
289
+ $_post = WP_Post::get_instance( $post );
290
+ $post = $_post->to_array();
291
+
292
+ if ( ARRAY_N === $output )
293
+ $post = array_values( $post );
294
+ }
295
+
296
+ return $post;
297
+ }
298
+
299
+ /**
300
+ * Fetch latest public post ID.
301
+ *
302
+ * @since 2.4.3
303
+ * @staticvar int $page_id
304
+ * @global object $wpdb
305
+ * @global int $blog_id
306
+ *
307
+ * @return int Latest Post ID.
308
+ */
309
+ public function get_latest_post_id() {
310
+ global $wpdb, $blog_id;
311
+
312
+ static $page_id = null;
313
+
314
+ if ( isset( $page_id ) )
315
+ return $page_id;
316
+
317
+ $latest_posts_key = 'latest_post_id_' . $blog_id;
318
+
319
+ //* @TODO consider transient.
320
+ $page_id = $this->object_cache_get( $latest_posts_key );
321
+ if ( false === $page_id ) {
322
+
323
+ // Prepare array
324
+ $post_type = esc_sql( array( 'post', 'page' ) );
325
+ $post_type_in_string = "'" . implode( "','", $post_type ) . "'";
326
+
327
+ // Prepare array
328
+ $post_status = esc_sql( array( 'publish', 'future', 'pending' ) );
329
+ $post_status_in_string = "'" . implode( "','", $post_status ) . "'";
330
+
331
+ $sql = $wpdb->prepare(
332
+ "SELECT ID
333
+ FROM $wpdb->posts
334
+ WHERE post_title <> %s
335
+ AND post_type IN ($post_type_in_string)
336
+ AND post_date < NOW()
337
+ AND post_status IN ($post_status_in_string)
338
+ ORDER BY post_date DESC
339
+ LIMIT %d",
340
+ '', 1 );
341
+
342
+ $page_id = (int) $wpdb->get_var( $sql );
343
+ $this->object_cache_set( $latest_posts_key, $page_id, DAY_IN_SECONDS );
344
+ }
345
+
346
+ return $page_id;
347
+ }
348
+
349
+ /**
350
+ * Fetches Post content.
351
+ *
352
+ * @since 2.6.0
353
+ *
354
+ * @param int $id The post ID.
355
+ * @return string The post content.
356
+ */
357
+ public function get_post_content( $id = 0 ) {
358
+
359
+ $id = $id ? $id : $this->get_the_real_ID();
360
+
361
+ $content = get_post_field( 'post_content', $id );
362
+
363
+ if ( is_string( $content ) )
364
+ return $content;
365
+
366
+ return '';
367
+ }
368
+
369
+ /**
370
+ * Determines whether the post has a page builder attached to it.
371
+ * Doesn't use plugin detection features as some builders might be incorporated within themes.
372
+ *
373
+ * Detects the following builders:
374
+ * - Divi Builder by Elegant Themes
375
+ * - Visual Composer by WPBakery
376
+ * - Page Builder by SiteOrigin
377
+ * - Beaver Builder by Fastline Media
378
+ *
379
+ * @since 2.6.6
380
+ *
381
+ * @param int $post_id
382
+ * @return boolean
383
+ */
384
+ public function uses_page_builder( $post_id ) {
385
+
386
+ $meta = get_post_meta( $post_id );
387
+
388
+ /**
389
+ * Applies filters 'the_seo_framework_detect_page_builder' : boolean
390
+ * Determines whether a page builder has been detected.
391
+ * @since 2.6.6
392
+ *
393
+ * @param boolean The current state.
394
+ * @param int $post_id The current Post ID.
395
+ * @param array $meta The current post meta.
396
+ */
397
+ $detected = (bool) apply_filters( 'the_seo_framework_detect_page_builder', false, $post_id, $meta );
398
+
399
+ if ( $detected )
400
+ return true;
401
+
402
+ if ( empty( $meta ) )
403
+ return false;
404
+
405
+ if ( isset( $meta['_et_pb_use_builder'][0] ) && 'on' === $meta['_et_pb_use_builder'][0] && defined( 'ET_BUILDER_VERSION' ) )
406
+ //* Divi Builder by Elegant Themes
407
+ return true;
408
+ elseif ( isset( $meta['_wpb_vc_js_status'][0] ) && 'true' === $meta['_wpb_vc_js_status'][0] && defined( 'WPB_VC_VERSION' ) )
409
+ //* Visual Composer by WPBakery
410
+ return true;
411
+ elseif ( isset( $meta['panels_data'][0] ) && '' !== $meta['panels_data'][0] && defined( 'SITEORIGIN_PANELS_VERSION' ) )
412
+ //* Page Builder by SiteOrigin
413
+ return true;
414
+ elseif ( isset( $meta['_fl_builder_enabled'][0] ) && '1' === $meta['_fl_builder_enabled'][0] && defined( 'FL_BUILDER_VERSION' ) )
415
+ //* Beaver Builder by Fastline Media
416
+ return true;
417
+
418
+ return false;
419
+ }
420
+
421
+ }
inc/classes/query.class.php ADDED
@@ -0,0 +1,1107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The SEO Framework plugin
4
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License version 2 or later as
8
+ * published by the Free Software Foundation.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ /**
20
+ * Class AutoDescription_Query
21
+ *
22
+ * Caches and organizes the WP Query.
23
+ * Functions are (somewhat) in alphabetical order!
24
+ *
25
+ * @since 2.6.0
26
+ *
27
+ * @license GPLv2+ <http://www.gnu.org/licenses/gpl-2.0.html>
28
+ */
29
+ class AutoDescription_Query extends AutoDescription_Compat {
30
+
31
+ /**
32
+ * Constructor. Load parent constructor.
33
+ */
34
+ public function __construct() {
35
+ parent::__construct();
36
+ }
37
+
38
+ /**
39
+ * Checks whether $wp_query or $current_screen is set.
40
+ *
41
+ * @since 2.6.1
42
+ * @access private
43
+ * @staticvar bool $cache : Always true if set.
44
+ *
45
+ * @global object $wp_query
46
+ * @global object|null $current_screen
47
+ *
48
+ * @return bool True when wp_query or current_screen has been initialized.
49
+ */
50
+ public function can_cache_query() {
51
+
52
+ static $cache = null;
53
+
54
+ if ( isset( $cache ) )
55
+ return $cache;
56
+
57
+ if ( isset( $GLOBALS['wp_query']->query ) || isset( $GLOBALS['current_screen'] ) )
58
+ return $cache = true;
59
+
60
+ return false;
61
+ }
62
+
63
+ /**
64
+ * Get the real page ID, also depending on CPT.
65
+ *
66
+ * @param bool $use_cache Whether to use the cache or not.
67
+ *
68
+ * @staticvar int $id the ID.
69
+ *
70
+ * @since 2.5.0
71
+ *
72
+ * @return int|false The ID.
73
+ */
74
+ public function get_the_real_ID( $use_cache = true ) {
75
+
76
+ $is_admin = $this->is_admin();
77
+ $can_cache = $this->can_cache_query();
78
+
79
+ //* Never use cache for this in admin. Only causes bugs.
80
+ $use_cache = $is_admin || false === $can_cache ? false : $use_cache;
81
+
82
+ if ( $use_cache ) {
83
+ static $id = null;
84
+
85
+ if ( isset( $id ) )
86
+ return $id;
87
+ }
88
+
89
+ //* Try to get ID from plugins.
90
+ $id = $is_admin || false === $can_cache ? 0 : $this->check_the_real_ID();
91
+
92
+ if ( empty( $id ) ) {
93
+ //* The Post ID can be this ID as well.
94
+ $id = get_queried_object_id();
95
+
96
+ //* Never get this when this is an archive. It will always return the wrong value.
97
+ if ( empty( $id ) && false === is_archive() && false === is_home() )
98
+ $id = get_the_ID();
99
+
100
+ //* Determine the Archive ID on term edit.
101
+ if ( empty( $id ) && $is_admin && $this->is_archive_admin() )
102
+ $id = $this->get_admin_term_id();
103
+ }
104
+
105
+ /**
106
+ * Applies filters 'the_seo_framework_current_object_id' : integer
107
+ * Can be either the Post ID, or the Term ID.
108
+ *
109
+ * @param int $id
110
+ * @param bool Whether the globals WP_Query or current_screen are set.
111
+ * @see AutoDescription_Query::can_cache_query()
112
+ *
113
+ * @since 2.6.2
114
+ */
115
+ $id = (int) apply_filters( 'the_seo_framework_current_object_id', $id, $this->can_cache_query() );
116
+
117
+ //* Turn ID into 0 if empty.
118
+ return $id = empty( $id ) ? 0 : $id;
119
+ }
120
+
121
+ /**
122
+ * Get the real ID from plugins.
123
+ *
124
+ * Only works in front-end as there's no need to check for inconsistent
125
+ * functions for the current ID in the admin.
126
+ *
127
+ * @since 2.5.0
128
+ *
129
+ * Applies filters the_seo_framework_real_id : The Real ID for plugins on front-end.
130
+ *
131
+ * @staticvar int $cached_id The cached ID.
132
+ *
133
+ * @return int|empty the ID.
134
+ */
135
+ public function check_the_real_ID() {
136
+
137
+ static $cached_id = null;
138
+
139
+ if ( isset( $cached_id ) && $this->can_cache_query() )
140
+ return $cached_id;
141
+
142
+ $id = '';
143
+
144
+ if ( $this->is_wc_shop() ) {
145
+ //* WooCommerce Shop
146
+ $id = get_option( 'woocommerce_shop_page_id' );
147
+ } else if ( function_exists( 'get_question_id' ) && did_action( 'template_redirect' ) ) {
148
+ //* AnsPress
149
+ $id = get_question_id();
150
+ }
151
+
152
+ $cached_id = (int) apply_filters( 'the_seo_framework_real_id', $id );
153
+
154
+ return $cached_id;
155
+ }
156
+
157
+ /**
158
+ * Fetches the Term ID on admin pages.
159
+ *
160
+ * @since 2.6.0
161
+ * @since 2.6.6 Moved from class AutoDescription_TermData.
162
+ * @staticvar int $term_id The Term ID.
163
+ *
164
+ * @return int Term ID.
165
+ */
166
+ public function get_admin_term_id() {
167
+
168
+ static $term_id = null;
169
+
170
+ if ( isset( $term_id ) )
171
+ return $term_id;
172
+
173
+ if ( isset( $_REQUEST['tag_ID'] ) && $_REQUEST['tag_ID'] ) {
174
+ //* WordPress 4.5+
175
+ $term_id = $_REQUEST['tag_ID'];
176
+ } else if ( isset( $_REQUEST['term_id'] ) && $_REQUEST['term_id'] ) {
177
+ //* Older WordPress versions.
178
+ $term_id = $_REQUEST['term_id'];
179
+ }
180
+
181
+ return $term_id = $term_id ? absint( $term_id ) : 0;
182
+ }
183
+
184
+ /**
185
+ * Detects 404.
186
+ *
187
+ * @since 2.6.0
188
+ * @staticvar bool $cache
189
+ *
190
+ * @return bool
191
+ */
192
+ public function is_404() {
193
+
194
+ static $cache = null;
195
+
196
+ if ( isset( $cache ) && $this->can_cache_query() )
197
+ return $cache;
198
+
199
+ if ( is_404() )
200
+ return $cache = true;
201
+
202
+ return $cache = false;
203
+ }
204
+
205
+ /**
206
+ * Detects admin screen.
207
+ *
208
+ * @since 2.6.0
209
+ * @staticvar bool $cache
210
+ *
211
+ * @return bool
212
+ */
213
+ public function is_admin() {
214
+
215
+ static $cache = null;
216
+
217
+ if ( isset( $cache ) && $this->can_cache_query() )
218
+ return $cache;
219
+
220
+ if ( is_admin() )
221
+ return $cache = true;
222
+
223
+ return $cache = false;
224
+ }
225
+
226
+ /**
227
+ * Detects attachment page.
228
+ *
229
+ * @since 2.6.0
230
+ * @staticvar bool $cache
231
+ * @uses $this->is_singular()
232
+ *
233
+ * @param int|string|array|object $attachment Attachment ID, title, slug, or array of such.
234
+ *
235
+ * @return bool
236
+ */
237
+ public function is_attachment( $attachment = '' ) {
238
+
239
+ static $cache = array();
240
+
241
+ if ( isset( $cache[$attachment] ) && $this->can_cache_query() )
242
+ return $cache[$attachment];
243
+
244
+ if ( $this->is_singular( $attachment ) && is_attachment( $attachment ) )
245
+ return $cache[$attachment] = true;
246
+
247
+ return $cache[$attachment] = false;
248
+ }
249
+
250
+ /**
251
+ * Detects archive pages. Also in admin.
252
+ *
253
+ * @since 2.6.0
254
+ * @staticvar bool $cache
255
+ *
256
+ * @global object $wp_query
257
+ *
258
+ * @return bool
259
+ */
260
+ public function is_archive() {
261
+
262
+ if ( $this->is_admin() )
263
+ return $this->is_archive_admin();
264
+
265
+ static $cache = null;
266
+
267
+ if ( isset( $cache ) && $this->can_cache_query() )
268
+ return $cache;
269
+
270
+ if ( is_archive() && false === $this->is_singular() )
271
+ return $cache = true;
272
+
273
+ if ( $this->can_cache_query() && false === $this->is_singular() ) {
274
+ global $wp_query;
275
+
276
+ if ( $wp_query->is_post_type_archive || $wp_query->is_date || $wp_query->is_author || $wp_query->is_category || $wp_query->is_tag || $wp_query->is_tax )
277
+ return $cache = true;
278
+ }
279
+
280
+ return $cache = false;
281
+ }
282
+
283
+ /**
284
+ * Extends default WordPress is_archive() and determines screen in admin.
285
+ *
286
+ * @since 2.6.0
287
+ * @staticvar bool $cache
288
+ *
289
+ * @global object $current_screen
290
+ *
291
+ * @return bool Post Type is archive
292
+ */
293
+ public function is_archive_admin() {
294
+
295
+ static $cache = null;
296
+
297
+ if ( isset( $cache ) && $this->can_cache_query() )
298
+ return $cache;
299
+
300
+ global $current_screen;
301
+
302
+ if ( isset( $current_screen->base ) && ( 'edit-tags' === $current_screen->base || 'term' === $current_screen->base ) )
303
+ return $cache = true;
304
+
305
+ return $cache = false;
306
+ }
307
+
308
+ /**
309
+ * Detects Term edit screen in WP Admin.
310
+ *
311
+ * @since 2.6.0
312
+ * @staticvar bool $cache
313
+ * @global object $current_screen
314
+ *
315
+ * @return bool We're on Term Edit screen.
316
+ */
317
+ public function is_term_edit() {
318
+
319
+ static $cache = null;
320
+
321
+ if ( isset( $cache ) && $this->can_cache_query() )
322
+ return $cache;
323
+
324
+ global $current_screen;
325
+
326
+ if ( $this->wp_version( '4.4.9999', '>=' ) ) {
327
+ if ( isset( $current_screen->base ) && ( 'term' === $current_screen->base ) )
328
+ return $cache = true;
329
+ } else {
330
+ if ( isset( $current_screen->base ) && ( 'edit-tags' === $current_screen->base ) )
331
+ return $cache = true;
332
+ }
333
+
334
+ return $cache = false;
335
+ }
336
+
337
+ /**
338
+ * Detects Post edit screen in WP Admin.
339
+ *
340
+ * @since 2.6.0
341
+ * @staticvar bool $cache
342
+ * @global object $current_screen
343
+ *
344
+ * @return bool We're on Post Edit screen.
345
+ */
346
+ public function is_post_edit() {
347
+
348
+ static $cache = null;
349
+
350
+ if ( isset( $cache ) && $this->can_cache_query() )
351
+ return $cache;
352
+
353
+ global $current_screen;
354
+
355
+ if ( isset( $current_screen->base ) && 'post' === $current_screen->base )
356
+ return $cache = true;
357
+
358
+ return $cache = false;
359
+ }
360
+
361
+ /**
362
+ * Detects Post or Archive Lists in Admin.
363
+ *
364
+ * @since 2.6.0
365
+ * @staticvar bool $cache
366
+ * @global object $current_screen
367
+ *
368
+ * @return bool We're on the edit screen.
369
+ */
370
+ public function is_wp_lists_edit() {
371
+
372
+ static $cache = null;
373
+
374
+ if ( isset( $cache ) && $this->can_cache_query() )
375
+ return $cache;
376
+
377
+ global $current_screen;
378
+
379
+ //* @NOTE USE WITH CAUTION - WP 4.5 & < 4.5 conflict.
380
+ if ( isset( $current_screen->base ) && ( 'edit' === $current_screen->base || 'edit-tags' === $current_screen->base ) )
381
+ return $cache = true;
382
+
383
+ return $cache = false;
384
+ }
385
+
386
+ /**
387
+ * Detects author archives.
388
+ *
389
+ * @since 2.6.0
390
+ * @staticvar bool $cache
391
+ * @uses $this->is_archive()
392
+ *
393
+ * @param mixed $author Optional. User ID, nickname, nicename, or array of User IDs, nicknames, and nicenames
394
+ *
395
+ * @return bool
396
+ */
397
+ public function is_author( $author = '' ) {
398
+
399
+ static $cache = array();
400
+
401
+ if ( isset( $cache[$author] ) && $this->can_cache_query() )
402
+ return $cache[$author];
403
+
404
+ if ( $this->is_archive() && is_author( $author ) )
405
+ return $cache[$author] = true;
406
+
407
+ return $cache[$author] = false;
408
+ }
409
+
410
+ /**
411
+ * Detect the separated blog page.
412
+ *
413
+ * @since 2.3.4
414
+ * @staticvar bool $is_blog_page
415
+ *
416
+ * @param int $id the Page ID.
417
+ * @return bool true if is blog page. Always false if blog page is homepage.
418
+ */
419
+ public function is_blog_page( $id = '' ) {
420
+
421
+ if ( '' === $id )
422
+ $id = $this->get_the_real_ID();
423
+
424
+ static $is_blog_page = array();
425
+
426
+ if ( isset( $is_blog_page[$id] ) && $this->can_cache_query() )
427
+ return $is_blog_page[$id];
428
+
429
+ $pfp = (int) get_option( 'page_for_posts' );
430
+
431
+ if ( $id === $pfp ) {
432
+ //* Don't use $this->is_archive (will loop).
433
+ if ( $this->has_page_on_front() && false === $this->is_front_page() && false === is_archive() ) {
434
+ return $is_blog_page[$id] = true;
435
+ }
436
+ }
437
+
438
+ return $is_blog_page[$id] = false;
439
+ }
440
+
441
+ /**
442
+ * Detects category archives.
443
+ *
444
+ * @since 2.6.0
445
+ * @staticvar bool $cache
446
+ * @uses $this->is_archive()
447
+ *
448
+ * @param mixed $category Optional. Category ID, name, slug, or array of Category IDs, names, and slugs.
449
+ * @return bool
450
+ */
451
+ public function is_category( $category = '' ) {
452
+
453
+ if ( $this->is_admin() )
454
+ return $this->is_category_admin();
455
+
456
+ static $cache = array();
457
+
458
+ if ( isset( $cache[$category] ) && $this->can_cache_query() )
459
+ return $cache[$category];
460
+
461
+ if ( $this->is_archive() && is_category( $category ) )
462
+ return $cache[$category] = true;
463
+
464
+ return $cache[$category] = false;
465
+ }
466
+
467
+ /**
468
+ * Extends default WordPress is_category() and determines screen in admin.
469
+ *
470
+ * @since 2.6.0
471
+ * @staticvar bool $cache
472
+ * @global object $current_screen
473
+ *
474
+ * @return bool Post Type is category
475
+ */
476
+ public function is_category_admin() {
477
+
478
+ static $cache = null;
479
+
480
+ if ( isset( $cache ) && $this->can_cache_query() )
481
+ return $cache;
482
+
483
+ global $current_screen;
484
+
485
+ if ( $this->is_archive_admin() && isset( $current_screen->taxonomy ) ) {
486
+
487
+ $tax = $current_screen->taxonomy;
488
+ $len = strlen( $tax );
489
+
490
+ if ( $len >= 8 && false !== strrpos( $tax, 'category', -8 ) )
491
+ return $cache = true;
492
+
493
+ if ( $len >= 3 && false !== strrpos( $tax, 'cat', -3 ) )
494
+ return $cache = true;
495
+ }
496
+
497
+ return $cache = false;
498
+ }
499
+
500
+ /**
501
+ * Detects date archives.
502
+ *
503
+ * @since 2.6.0
504
+ * @staticvar bool $cache
505
+ * @uses $this->is_archive()
506
+ *
507
+ * @return bool
508
+ */
509
+ public function is_date() {
510
+
511
+ static $cache = null;
512
+
513
+ if ( isset( $cache ) && $this->can_cache_query() )
514
+ return $cache;
515
+
516
+ if ( $this->is_archive() && is_date() )
517
+ return $cache = true;
518
+
519
+ return $cache = false;
520
+ }
521
+
522
+ /**
523
+ * Detects day archives.
524
+ *
525
+ * @staticvar bool $cache
526
+ * @since 2.6.0
527
+ * @uses $this->is_date()
528
+ *
529
+ * @return bool
530
+ */
531
+ public function is_day() {
532
+
533
+ static $cache = null;
534
+
535
+ if ( isset( $cache ) && $this->can_cache_query() )
536
+ return $cache;
537
+
538
+ if ( is_day() )
539
+ return $cache = true;
540
+
541
+ return $cache = false;
542
+ }
543
+
544
+ /**
545
+ * Detects feed.
546
+ *
547
+ * @since 2.6.0
548
+ * @staticvar bool $cache
549
+ *
550
+ * @param string|array $feeds Optional feed types to check.
551
+ * @return bool
552
+ */
553
+ public function is_feed( $feeds = '' ) {
554
+
555
+ static $cache = array();
556
+
557
+ if ( is_string( $feeds ) && isset( $cache[$feeds] ) && $this->can_cache_query() )
558
+ return $cache[$feeds];
559
+
560
+ if ( is_feed( $feeds ) )
561
+ return $cache[$feeds] = true;
562
+
563
+ return $cache[$feeds] = false;
564
+ }
565
+
566
+ /**
567
+ * Detects front page.
568
+ *
569
+ * @since 2.6.0
570
+ * @staticvar bool $cache
571
+ *
572
+ * @param int $id The page or Post ID.
573
+ * @return bool
574
+ */
575
+ public function is_front_page( $id = 0 ) {
576
+
577
+ static $cache = array();
578
+
579
+ if ( isset( $cache[$id] ) && $this->can_cache_query() )
580
+ return $cache[$id];
581
+
582
+ if ( is_front_page() && empty( $id ) )
583
+ return $cache[$id] = true;
584
+
585
+ //* Elegant Themes Support.
586
+ if ( empty( $id ) && $this->is_home() ) {
587
+ $sof = get_option( 'show_on_front' );
588
+
589
+ if ( 'page' !== $sof && 'posts' !== $sof )
590
+ return $cache[$id] = true;
591
+ }
592
+
593
+ if ( $id ) {
594
+ $sof = get_option( 'show_on_front' );
595
+
596
+ if ( 'page' === $sof && $id === (int) get_option( 'page_on_front' ) )
597
+ return $cache[$id] = true;
598
+
599
+ if ( 'posts' === $sof && $id === (int) get_option( 'page_for_posts' ) )
600
+ return $cache[$id] = true;
601
+ }
602
+
603
+ return $cache[$id] = false;
604
+ }
605
+
606
+ /**
607
+ * Detects home page.
608
+ *
609
+ * @since 2.6.0
610
+ * @staticvar bool $cache
611
+ *
612
+ * @return bool
613
+ */
614
+ public function is_home() {
615
+
616
+ static $cache = null;
617
+
618
+ if ( isset( $cache ) && $this->can_cache_query() )
619
+ return $cache;
620
+
621
+ if ( is_home() )
622
+ return $cache = true;
623
+
624
+ return $cache = false;
625
+ }
626
+
627
+ /**
628
+ * Detects month archives.
629
+ *
630
+ * @since 2.6.0
631
+ * @staticvar bool $cache
632
+ * @uses $this->is_date()
633
+ *
634
+ * @return bool
635
+ */
636
+ public function is_month() {
637
+
638
+ static $cache = null;
639
+
640
+ if ( isset( $cache ) && $this->can_cache_query() )
641
+ return $cache;
642
+
643
+ if ( is_month() )
644
+ return $cache = true;
645
+
646
+ return $cache = false;
647
+ }
648
+
649
+ /**
650
+ * Detects pages.
651
+ *
652
+ * @since 2.6.0
653
+ * @staticvar bool $cache
654
+ * @uses $this->is_singular()
655
+ *
656
+ * @param int|string|array $page Optional. Page ID, title, slug, or array of such. Default empty.
657
+ * @return bool
658
+ */
659
+ public function is_page( $page = '' ) {
660
+
661
+ static $cache = array();
662
+
663
+ if ( isset( $cache[$page] ) && $this->can_cache_query() )
664
+ return $cache[$page];
665
+
666
+ if ( $this->is_singular( $page ) ) {
667
+ if ( is_page( $page ) )
668
+ return $cache[$page] = true;
669
+
670
+ if ( $this->is_admin() )
671
+ return $cache[$page] = $this->is_page_admin( $page );
672
+ }
673
+
674
+ return $cache[$page] = false;
675
+ }
676
+
677
+ /**
678
+ * Detects pages within the admin area.
679
+ *
680
+ * @since 2.6.0
681
+ * @see $this->is_page()
682
+ * @global object $current_screen;
683
+ *
684
+ * @param int|string|array $page Optional. Page ID, title, slug, or array of such. Default empty.
685
+ * @return bool
686
+ */
687
+ public function is_page_admin( $page = '' ) {
688
+ global $current_screen;
689
+
690
+ if ( isset( $current_screen->post_type ) && 'page' === $current_screen->post_type )
691
+ return true;
692
+
693
+ return false;
694
+ }
695
+
696
+ /**
697
+ * Detects preview.
698
+ *
699
+ * @since 2.6.0
700
+ * @staticvar bool $cache
701
+ *
702
+ * @return bool
703
+ */
704
+ public function is_preview() {
705
+
706
+ static $cache = null;
707
+
708
+ if ( isset( $cache ) && $this->can_cache_query() )
709
+ return $cache;
710
+
711
+ if ( is_preview() )
712
+ return $cache = true;
713
+
714
+ return $cache = false;
715
+ }
716
+
717
+ /**
718
+ * Detects preview.
719
+ *
720
+ * @since 2.6.0
721
+ * @staticvar bool $cache
722
+ *
723
+ * @return bool
724
+ */
725
+ public function is_search() {
726
+
727
+ static $cache = null;
728
+
729
+ if ( isset( $cache ) && $this->can_cache_query() )
730
+ return $cache;
731
+
732
+ if ( is_search() )
733
+ return $cache = true;
734
+
735
+ return $cache = false;
736
+ }
737
+
738
+ /**
739
+ * Detects posts.
740
+ *
741
+ * @since 2.6.0
742
+ * @staticvar bool $cache
743
+ * @uses $this->is_singular()
744
+ *
745
+ * @param int|string|array $post Optional. Post ID, title, slug, or array of such. Default empty.
746
+ * @return bool
747
+ */
748
+ public function is_single( $post = '' ) {
749
+
750
+ static $cache = array();
751
+
752
+ if ( isset( $cache[$post] ) && $this->can_cache_query() )
753
+ return $cache[$post];
754
+
755
+ if ( $this->is_singular( $post ) ) {
756
+ if ( is_single( $post ) )
757
+ return $cache[$post] = true;
758
+
759
+ if ( $this->is_admin() )
760
+ return $cache[$post] = $this->is_single_admin( $post );
761
+ }
762
+
763
+ return $cache[$post] = false;
764
+ }
765
+
766
+ /**
767
+ * Detects posts within the admin area.
768
+ *
769
+ * @since 2.6.0
770
+ * @global object $current_screen
771
+ * @see $this->is_single()
772
+ *
773
+ * @param int|string|array $post Optional. Page ID, title, slug, or array of such. Default empty.
774
+ * @return bool
775
+ */
776
+ public function is_single_admin( $post = '' ) {
777
+ global $current_screen;
778
+
779
+ if ( isset( $current_screen->post_type ) && 'post' === $current_screen->post_type )
780
+ return true;
781
+
782
+ return false;
783
+ }
784
+
785
+ /**
786
+ * Determines if the current page is singular is holds singular items within the admin screen.
787
+ * Replaces and expands default WordPress is_singular().
788
+ *
789
+ * @since 2.5.2
790
+ * @staticvar bool $cache
791
+ * @uses $this->is_blog_page()
792
+ * @uses $this->is_wc_shop()
793
+ *
794
+ * @param string|array $post_types Optional. Post type or array of post types. Default empty.
795
+ * @return bool Post Type is singular
796
+ */
797
+ public function is_singular( $post_types = '' ) {
798
+
799
+ static $cache = array();
800
+
801
+ if ( isset( $cache[$post_types] ) && $this->can_cache_query() )
802
+ return $cache[$post_types];
803
+
804
+ //* WP_Query functions require loop, do alternative check.
805
+ if ( $this->is_admin() )
806
+ return $cache[$post_types] = $this->is_singular_admin();
807
+
808
+ if ( is_int( $post_types ) ) {
809
+ //* Cache ID. Core is_singlar() doesn't accept integers.
810
+ $id = $post_types;
811
+ $post_types = '';
812
+ }
813
+
814
+ //* Default check.
815
+ if ( is_singular( $post_types ) )
816
+ return $cache[$post_types] = true;
817
+
818
+ $id = isset( $id ) ? $id : $this->get_the_real_ID();
819
+
820
+ //* Check for somewhat singulars. We need this to adjust Meta data filled in Posts.
821
+ if ( $this->is_blog_page( $id ) || $this->is_wc_shop() )
822
+ return $cache[$post_types] = true;
823
+
824
+ return $cache[$post_types] = false;
825
+ }
826
+
827
+ /**
828
+ * Determines if the page is singular within the admin screen.
829
+ *
830
+ * @since 2.5.2
831
+ * @staticvar bool $cache
832
+ * @global object $current_screen
833
+ *
834
+ * @return bool Post Type is singular
835
+ */
836
+ public function is_singular_admin() {
837
+
838
+ static $cache = null;
839
+
840
+ if ( isset( $cache ) && $this->can_cache_query() )
841
+ return $cache;
842
+
843
+ global $current_screen;
844
+
845
+ if ( isset( $current_screen->base ) && ( 'edit' === $current_screen->base || 'post' === $current_screen->base ) )
846
+ return $cache = true;
847
+
848
+ return $cache = false;
849
+ }
850
+
851
+ /**
852
+ * Detects the static front page.
853
+ *
854
+ * @since 2.3.8
855
+ * @staticvar array $cache
856
+ *
857
+ * @param int $id the Page ID to check. If empty, the current ID will be fetched.
858
+ * @return bool true if is blog page. Always false if the homepage is a blog.
859
+ */
860
+ public function is_static_frontpage( $id = '' ) {
861
+
862
+ if ( empty( $id ) )
863
+ $id = $this->get_the_real_ID();
864
+
865
+ static $cache = array();
866
+
867
+ if ( isset( $cache[$id] ) && $this->can_cache_query() )
868
+ return $cache[$id];
869
+
870
+ $sof = (string) get_option( 'show_on_front' );
871
+
872
+ if ( 'page' === $sof ) {
873
+ $pof = (int) get_option( 'page_on_front' );
874
+
875
+ if ( $id === $pof )
876
+ return $cache[$id] = true;
877
+ }
878
+
879
+ return $cache[$id] = false;
880
+ }
881
+
882
+ /**
883
+ * Detects tag archives.
884
+ *
885
+ * @staticvar bool $cache
886
+ * @since 2.6.0
887
+ * @uses $this->is_archive()
888
+ *
889
+ * @param mixed $tag Optional. Tag ID, name, slug, or array of Tag IDs, names, and slugs.
890
+ * @return bool
891
+ */
892
+ public function is_tag( $tag = '' ) {
893
+
894
+ static $cache = array();
895
+
896
+ if ( isset( $cache[$tag] ) && $this->can_cache_query() )
897
+ return $cache[$tag];
898
+
899
+ //* Admin requires another check.
900
+ if ( $this->is_admin() )
901
+ return $cache[$tag] = $this->is_tag_admin();
902
+
903
+ if ( is_tag( $tag ) )
904
+ return $cache[$tag] = true;
905
+
906
+ return $cache[$tag] = false;
907
+ }
908
+
909
+ /**
910
+ * Determines if the page is a tag within the admin screen.
911
+ *
912
+ * @since 2.6.0
913
+ * @staticvar bool $cache
914
+ * @global object $current_screen
915
+ *
916
+ * @return bool Post Type is category
917
+ */
918
+ public function is_tag_admin() {
919
+
920
+ static $cache = null;
921
+
922
+ if ( isset( $cache ) && $this->can_cache_query() )
923
+ return $cache;
924
+
925
+ if ( $this->is_archive_admin() ) {
926
+ global $current_screen;
927
+
928
+ if ( isset( $current_screen->taxonomy ) && strlen( $current_screen->taxonomy ) >= 3 && false !== strrpos( $current_screen->taxonomy, 'tag', -3 ) )
929
+ return $cache = true;
930
+ }
931
+
932
+ return $cache = false;
933
+ }
934
+
935
+ /**
936
+ * Detects taxonomy archives.
937
+ *
938
+ * @since 2.6.0
939
+ * @staticvar bool $cache
940
+ * @uses $this->is_archive()
941
+ *
942
+ * @param string|array $taxonomy Optional. Taxonomy slug or slugs.
943
+ * @param int|string|array $term Optional. Term ID, name, slug or array of Term IDs, names, and slugs.
944
+ * @return bool
945
+ */
946
+ public function is_tax( $taxonomy = '', $term = '' ) {
947
+
948
+ static $cache = null;
949
+
950
+ if ( isset( $cache[$taxonomy][$term] ) && $this->can_cache_query() )
951
+ return $cache[$taxonomy][$term];
952
+
953
+ if ( is_tax( $taxonomy, $term ) )
954
+ return $cache[$taxonomy][$term] = true;
955
+
956
+ return $cache[$taxonomy][$term] = false;
957
+ }
958
+
959
+ /**
960
+ * Determines if the page is a Ulimate Member's plugin User page.
961
+ * Checks for function availability: um_user, um_is_core_page, um_get_requested_user
962
+ *
963
+ * @since 2.5.2
964
+ * @staticvar bool $cache
965
+ * @uses $this->can_i_use()
966
+ *
967
+ * @return bool Whether we're on a Ultimate Member page.
968
+ */
969
+ public function is_ultimate_member_user_page() {
970
+
971
+ static $cache = null;
972
+
973
+ if ( isset( $cache ) && $this->can_cache_query() )
974
+ return $cache;
975
+
976
+ $caniuse = (bool) $this->can_i_use( array( 'functions' => array( 'um_user', 'um_is_core_page', 'um_get_requested_user' ) ), false );
977
+
978
+ return $cache = $caniuse;
979
+ }
980
+
981
+ /**
982
+ * Determines if the page is the WooCommerce plugin Shop page.
983
+ *
984
+ * @since 2.5.2
985
+ * @staticvar bool $cache
986
+ *
987
+ * @return bool True if on the WooCommerce shop page.
988
+ */
989
+ public function is_wc_shop() {
990
+
991
+ static $cache = null;
992
+
993
+ if ( isset( $cache ) && $this->can_cache_query() )
994
+ return $cache;
995
+
996
+ //* Can't check in admin.
997
+ if ( false === $this->is_admin() && function_exists( 'is_shop' ) && is_shop() )
998
+ return $cache = true;
999
+
1000
+ return $cache = false;
1001
+ }
1002
+
1003
+ /**
1004
+ * Determines if the page is the WooCommerce plugin Product page.
1005
+ *
1006
+ * @since 2.5.2
1007
+ * @staticvar bool $cache
1008
+ *
1009
+ * @return bool True if on a WooCommerce Product page.
1010
+ */
1011
+ public function is_wc_product() {
1012
+
1013
+ static $cache = null;
1014
+
1015
+ if ( isset( $cache ) && $this->can_cache_query() )
1016
+ return $cache;
1017
+
1018
+ //* Can't check in admin.
1019
+ if ( false === $this->is_admin() && function_exists( 'is_product' ) && is_product() )
1020
+ return $cache = true;
1021
+
1022
+ return $cache = false;
1023
+ }
1024
+
1025
+ /**
1026
+ * Detects year archives.
1027
+ *
1028
+ * @since 2.6.0
1029
+ * @staticvar bool $cache
1030
+ * @uses $this->is_date()
1031
+ *
1032
+ * @return bool
1033
+ */
1034
+ public function is_year() {
1035
+
1036
+ static $cache = null;
1037
+
1038
+ if ( isset( $cache ) && $this->can_cache_query() )
1039
+ return $cache;
1040
+
1041
+ if ( is_year() )
1042
+ return $cache = true;
1043
+
1044
+ return $cache = false;
1045
+ }
1046
+
1047
+ /**
1048
+ * Determines whether we're on the SEO settings page.
1049
+ *
1050
+ * @since 2.6.0
1051
+ * @staticvar bool $cache
1052
+ *
1053
+ * @return bool
1054
+ */
1055
+ public function is_seo_settings_page() {
1056
+
1057
+ static $cache = null;
1058
+
1059
+ if ( isset( $cache ) && $this->can_cache_query() )
1060
+ return $cache;
1061
+
1062
+ return $cache = $this->is_menu_page( $this->page_id );
1063
+ }
1064
+
1065
+ /**
1066
+ * Fetches the amount of pages on the screen.
1067
+ * Fetches global $page through Query Var to prevent conflicts.
1068
+ *
1069
+ * @since 2.6.0
1070
+ * @staticvar int $page
1071
+ *
1072
+ * @return int $page Always a positive number.
1073
+ */
1074
+ public function page() {
1075
+
1076
+ static $page = null;
1077
+
1078
+ if ( isset( $page ) && $this->can_cache_query() )
1079
+ return $page;
1080
+
1081
+ $page = get_query_var( 'page' );
1082
+
1083
+ return $page = $page ? (int) $page : 1;
1084
+ }
1085
+
1086
+ /**
1087
+ * Fetches the number of the current page.
1088
+ * Fetches global $paged through Query Var. Determines
1089
+ *
1090
+ * @since 2.6.0
1091
+ * @staticvar int $paged
1092
+ *
1093
+ * @return int $paged
1094
+ */
1095
+ public function paged() {
1096
+
1097
+ static $paged = null;
1098
+
1099
+ if ( isset( $paged ) && $this->can_cache_query() )
1100
+ return $paged;
1101
+
1102
+ $paged = get_query_var( 'paged' );
1103
+
1104
+ return $paged = $paged ? (int) $paged : 1;
1105
+ }
1106
+
1107
+ }
inc/classes/render.class.php ADDED
@@ -0,0 +1,1122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The SEO Framework plugin
4
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License version 3 as published
8
+ * by the Free Software Foundation.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ /**
20
+ * Class AutoDescription_Render
21
+ *
22
+ * Puts all data into HTML valid strings
23
+ * Returns strings
24
+ *
25
+ * @since 2.1.6
26
+ */
27
+ class AutoDescription_Render extends AutoDescription_Admin_Init {
28
+
29
+ /**
30
+ * Theme title doing it wrong boolean.
31
+ *
32
+ * @since 2.4.0
33
+ *
34
+ * @var bool Holds Theme is doing it wrong.
35
+ */
36
+ protected $title_doing_it_wrong = null;
37
+
38
+ /**
39
+ * Constructor, load parent constructor
40
+ */
41
+ public function __construct() {
42
+ parent::__construct();
43
+ }
44
+
45
+ /**
46
+ * Cache description in static variable
47
+ * Must be called inside the loop
48
+ *
49
+ * @staticvar array $description_cache
50
+ *
51
+ * @since 2.2.2
52
+ * @return string The description
53
+ */
54
+ public function description_from_cache( $social = false ) {
55
+
56
+ static $description_cache = array();
57
+
58
+ if ( isset( $description_cache[$social] ) )
59
+ return $description_cache[$social];
60
+
61
+ return $description_cache[$social] = $this->generate_description( '', array( 'social' => $social ) );
62
+ }
63
+
64
+ /**
65
+ * Cache current URL in static variable
66
+ * Must be called inside the loop
67
+ *
68
+ * @param string $url the url
69
+ * @param int $post_id the page id, if empty it will fetch the requested ID, else the page uri
70
+ * @param bool $paged Return current page URL with pagination
71
+ * @param bool $from_option Get the canonical uri option
72
+ * @param bool $paged_plural Whether to allow pagination on second or later pages.
73
+ *
74
+ * @staticvar array $url_cache
75
+ *
76
+ * @since 2.2.2
77
+ * @return string The url
78
+ */
79
+ public function the_url_from_cache( $url = '', $post_id = null, $paged = false, $from_option = true, $paged_plural = true ) {
80
+
81
+ static $url_cache = array();
82
+
83
+ if ( is_null( $post_id ) )
84
+ $post_id = $this->get_the_real_ID();
85
+
86
+ if ( isset( $url_cache[$url][$post_id][$paged][$from_option][$paged_plural] ) )
87
+ return $url_cache[$url][$post_id][$paged][$from_option][$paged_plural];
88
+
89
+ return $url_cache[$url][$post_id][$paged][$from_option][$paged_plural] = $this->the_url( $url, array( 'paged' => $paged, 'get_custom_field' => $from_option, 'id' => $post_id, 'paged_plural' => $paged_plural ) );
90
+ }
91
+
92
+ /**
93
+ * Cache home URL in static variable
94
+ *
95
+ * @param bool $force_slash Force slash
96
+ *
97
+ * @staticvar array $url_cache
98
+ *
99
+ * @since 2.5.0
100
+ * @return string The url
101
+ */
102
+ public function the_home_url_from_cache( $force_slash = false ) {
103
+
104
+ static $url_cache = array();
105
+
106
+ if ( isset( $url_cache[$force_slash] ) )
107
+ return $url_cache[$force_slash];
108
+
109
+ return $url_cache[$force_slash] = $this->the_url( '', array( 'home' => true, 'forceslash' => $force_slash ) );
110
+ }
111
+
112
+ /**
113
+ * Cache current Title in static variable
114
+ * Must be called inside the loop
115
+ *
116
+ * @param string $title The Title to return
117
+ * @param string $sep The Title sepeartor
118
+ * @param string $seplocation The Title sepeartor location ( accepts 'left' or 'right' )
119
+ * @param bool $meta Ignore theme doing it wrong.
120
+ *
121
+ * @staticvar array $title_cache
122
+ *
123
+ * @since 2.2.2
124
+ * @return string The title
125
+ */
126
+ public function title_from_cache( $title = '', $sep = '', $seplocation = '', $meta = false ) {
127
+
128
+ /**
129
+ * Cache the inputs, for when the title is doing it right.
130
+ * Use those values to fetch the cached title.
131
+ *
132
+ * @since 2.4.0
133
+ */
134
+ static $setup_cache = null;
135
+ static $title_param_cache = null;
136
+ static $sep_param_cache = null;
137
+ static $seplocation_param_cache = null;
138
+
139
+ if ( ! isset( $setup_cache ) ) {
140
+ if ( doing_filter( 'pre_get_document_title' ) || doing_filter( 'wp_title' ) ) {
141
+ $title_param_cache = $title;
142
+ $sep_param_cache = $sep;
143
+ $seplocation_param_cache = $seplocation;
144
+
145
+ $setup_cache = 'I like turtles.';
146
+ }
147
+ }
148
+
149
+ /**
150
+ * If the theme is doing it right, override parameters to speed things up.
151
+ *
152
+ * @since 2.4.0
153
+ */
154
+ if ( isset( $this->title_doing_it_wrong ) && false === $this->title_doing_it_wrong ) {
155
+ $title = $title_param_cache;
156
+ $sep = $sep_param_cache;
157
+ $seplocation = $seplocation_param_cache;
158
+ $meta = false;
159
+ }
160
+
161
+ static $title_cache = array();
162
+
163
+ if ( isset( $title_cache[$title][$sep][$seplocation][$meta] ) )
164
+ return $title_cache[$title][$sep][$seplocation][$meta];
165
+
166
+ return $title_cache[$title][$sep][$seplocation][$meta] = $this->title( $title, $sep, $seplocation, array( 'meta' => $meta ) );
167
+ }
168
+
169
+ /**
170
+ * Cache current Image URL in static variable
171
+ * Must be called inside the loop
172
+ *
173
+ * @staticvar string $image_cache
174
+ *
175
+ * @since 2.2.2
176
+ * @return string The image url
177
+ */
178
+ public function get_image_from_cache() {
179
+
180
+ static $image_cache = null;
181
+
182
+ if ( isset( $image_cache ) )
183
+ return $image_cache;
184
+
185
+ $post_id = $this->get_the_real_ID();
186
+
187
+ //* End this madness if there's no ID found (search/404/etc.)
188
+ if ( empty( $post_id ) )
189
+ return '';
190
+
191
+ $image_cache = $this->get_image( $post_id );
192
+
193
+ return $image_cache;
194
+ }
195
+
196
+ /**
197
+ * Render the description
198
+ *
199
+ * @uses $this->description_from_cache()
200
+ * @uses $this->detect_seo_plugins()
201
+ *
202
+ * @since 1.3.0
203
+ */
204
+ public function the_description() {
205
+
206
+ if ( $this->detect_seo_plugins() )
207
+ return;
208
+
209
+ /**
210
+ * Applies filters 'the_seo_framework_description_output' : string
211
+ * @since 2.3.0
212
+ */
213
+ $description = (string) apply_filters( 'the_seo_framework_description_output', '', $this->get_the_real_ID() );
214
+
215
+ if ( empty( $description ) )
216
+ $description = $this->description_from_cache();
217
+
218
+ if ( $description )
219
+ return '<meta name="description" content="' . esc_attr( $description ) . '" />' . "\r\n";
220
+
221
+ return '';
222
+ }
223
+
224
+ /**
225
+ * Render og:description
226
+ *
227
+ * @uses $this->description_from_cache()
228
+ *
229
+ * @since 1.3.0
230
+ */
231
+ public function og_description() {
232
+
233
+ if ( $this->use_og_tags() ) {
234
+
235
+ /**
236
+ * Applies filters 'the_seo_framework_ogdescription_output' : string
237
+ * @since 2.3.0
238
+ */
239
+ $description = (string) apply_filters( 'the_seo_framework_ogdescription_output', '', $this->get_the_real_ID() );
240
+
241
+ if ( empty( $description ) )
242
+ $description = $this->description_from_cache( true );
243
+
244
+ return '<meta property="og:description" content="' . esc_attr( $description ) . '" />' . "\r\n";
245
+ }
246
+
247
+ return '';
248
+ }
249
+
250
+ /**
251
+ * Render the OG locale.
252
+ *
253
+ * @since 1.0.0
254
+ */
255
+ public function og_locale() {
256
+
257
+ if ( $this->use_og_tags() ) {
258
+
259
+ /**
260
+ * Applies filters 'the_seo_framework_oglocale_output' : string
261
+ * @since 2.3.0
262
+ */
263
+ $locale = (string) apply_filters( 'the_seo_framework_oglocale_output', '', $this->get_the_real_ID() );
264
+
265
+ if ( empty( $locale ) )
266
+ $locale = $this->fetch_locale();
267
+
268
+ return '<meta property="og:locale" content="' . esc_attr( $locale ) . '" />' . "\r\n";
269
+ }
270
+
271
+ return '';
272
+ }
273
+
274
+ /**
275
+ * Process the title to WordPress
276
+ *
277
+ * @uses $this->title_from_cache()
278
+ *
279
+ * @since 2.0.3
280
+ */
281
+ public function og_title() {
282
+
283
+ if ( $this->use_og_tags() ) {
284
+
285
+ /**
286
+ * Applies filters 'the_seo_framework_ogtitle_output' : string
287
+ * @since 2.3.0
288
+ */
289
+ $title = (string) apply_filters( 'the_seo_framework_ogtitle_output', '', $this->get_the_real_ID() );
290
+
291
+ if ( empty( $title ) )
292
+ $title = $this->title_from_cache( '', '', '', true );
293
+
294
+ return '<meta property="og:title" content="' . esc_attr( $title ) . '" />' . "\r\n";
295
+ }
296
+
297
+ return '';
298
+ }
299
+
300
+ /**
301
+ * Get the OG type.
302
+ *
303
+ * @since 1.1.0
304
+ */
305
+ public function og_type() {
306
+
307
+ if ( $this->use_og_tags() ) {
308
+
309
+ /**
310
+ * Applies filters 'the_seo_framework_ogtype_output' : string
311
+ * @since 2.3.0
312
+ */
313
+ $type = (string) apply_filters( 'the_seo_framework_ogtype_output', '', $this->get_the_real_ID() );
314
+
315
+ if ( empty( $type ) ) {
316
+ if ( $this->is_wc_product() ) {
317
+ $type = 'product';
318
+ } else if ( $this->is_single() && $this->get_image_from_cache() ) {
319
+ $type = 'article';
320
+ } else if ( $this->is_author() ) {
321
+ $type = 'profile';
322
+ } else if ( $this->is_blog_page() || ( $this->is_front_page() && ! $this->has_page_on_front() ) ) {
323
+ $type = 'blog';
324
+ } else {
325
+ $type = 'website';
326
+ }
327
+ }
328
+
329
+ return '<meta property="og:type" content="' . esc_attr( $type ) . '" />' . "\r\n";
330
+ }
331
+
332
+ return '';
333
+ }
334
+
335
+ /**
336
+ * Adds og:image
337
+ *
338
+ * @param string $image url for image
339
+ *
340
+ * @since 1.3.0
341
+ */
342
+ public function og_image() {
343
+
344
+ if ( $this->use_og_tags() ) {
345
+
346
+ $id = $this->get_the_real_ID();
347
+
348
+ /**
349
+ * Applies filters 'the_seo_framework_ogimage_output' : string|bool
350
+ * @since 2.3.0
351
+ *
352
+ * @NOTE: Use of this might cause incorrect meta since other functions
353
+ * depend on the image from cache.
354
+ *
355
+ * @todo Place in listener cache.
356
+ * @priority medium 2.8.0+
357
+ */
358
+ $image = apply_filters( 'the_seo_framework_ogimage_output', '', $id );
359
+
360
+ /**
361
+ * Now returns empty string on false.
362
+ * @since 2.6.0
363
+ */
364
+ if ( false === $image )
365
+ return '';
366
+
367
+ if ( empty( $image ) ) {
368
+ $image = $this->get_image_from_cache();
369
+ } else {
370
+ $image = (string) $image;
371
+ }
372
+
373
+ /**
374
+ * Always output
375
+ * @since 2.1.1
376
+ */
377
+ $output = '<meta property="og:image" content="' . esc_attr( $image ) . '" />' . "\r\n";
378
+
379
+ //* Fetch Product images.
380
+ $woocommerce_product_images = $this->render_woocommerce_product_og_image();
381
+
382
+ return $output . $woocommerce_product_images;
383
+ }
384
+
385
+ return '';
386
+ }
387
+
388
+ /**
389
+ * Render more OG images to choose from.
390
+ *
391
+ * @since 2.6.0
392
+ *
393
+ * @return string The rendered OG Image.
394
+ */
395
+ public function render_woocommerce_product_og_image() {
396
+
397
+ $output = '';
398
+
399
+ if ( $this->is_wc_product() ) {
400
+
401
+ $images = $this->get_image_from_woocommerce_gallery();
402
+
403
+ if ( $images && is_array( $images ) ) {
404
+ foreach ( $images as $id ) {
405
+ //* Parse 1500px url.
406
+ $img = $this->parse_og_image( $id );
407
+
408
+ if ( $img )
409
+ $output .= '<meta property="og:image" content="' . esc_attr( $img ) . '" />' . "\r\n";
410
+ }
411
+ }
412
+ }
413
+
414
+ return $output;
415
+ }
416
+
417
+ /**
418
+ * Adds og:site_name
419
+ *
420
+ * @param string output the output
421
+ *
422
+ * @since 1.3.0
423
+ */
424
+ public function og_sitename() {
425
+
426
+ if ( $this->use_og_tags() ) {
427
+
428
+ /**
429
+ * Applies filters 'the_seo_framework_ogsitename_output' : string
430
+ * @since 2.3.0
431
+ */
432
+ $sitename = (string) apply_filters( 'the_seo_framework_ogsitename_output', '', $this->get_the_real_ID() );
433
+
434
+ if ( empty( $sitename ) )
435
+ $sitename = get_bloginfo( 'name' );
436
+
437
+ return '<meta property="og:site_name" content="' . esc_attr( $sitename ) . '" />' . "\r\n";
438
+ }
439
+
440
+ return '';
441
+ }
442
+
443
+ /**
444
+ * Adds og:url
445
+ *
446
+ * @return string og:url the url meta
447
+ *
448
+ * @since 1.3.0
449
+ *
450
+ * @uses $this->the_url_from_cache()
451
+ */
452
+ public function og_url() {
453
+
454
+ if ( $this->use_og_tags() )
455
+ return '<meta property="og:url" content="' . $this->the_url_from_cache() . '" />' . "\r\n";
456
+
457
+ return '';
458
+ }
459
+
460
+ /**
461
+ * Render twitter:card
462
+ *
463
+ * @since 2.2.2
464
+ */
465
+ public function twitter_card() {
466
+
467
+ if ( $this->use_twitter_tags() ) {
468
+
469
+ /**
470
+ * Applies filters 'the_seo_framework_twittercard_output' : string
471
+ * @since 2.3.0
472
+ */
473
+ $card = (string) apply_filters( 'the_seo_framework_twittercard_output', '', $this->get_the_real_ID() );
474
+
475
+ if ( empty( $card ) ) {
476
+ /**
477
+ * Return card type if image is found.
478
+ * Return to summary if not.
479
+ */
480
+ $card = $this->get_image_from_cache() ? $this->get_option( 'twitter_card' ) : 'summary';
481
+ }
482
+
483
+ return '<meta name="twitter:card" content="' . esc_attr( $card ) . '" />' . "\r\n";
484
+ }
485
+
486
+ return '';
487
+ }
488
+
489
+ /**
490
+ * Render twitter:site
491
+ *
492
+ * @since 2.2.2
493
+ */
494
+ public function twitter_site() {
495
+
496
+ if ( $this->use_twitter_tags() ) {
497
+
498
+ /**
499
+ * Applies filters 'the_seo_framework_twittersite_output' : string
500
+ * @since 2.3.0
501
+ */
502
+ $site = (string) apply_filters( 'the_seo_framework_twittersite_output', '', $this->get_the_real_ID() );
503
+
504
+ if ( empty( $site ) )
505
+ $site = $this->get_option( 'twitter_site' );
506
+
507
+ if ( $site )
508
+ return '<meta name="twitter:site" content="' . esc_attr( $site ) . '" />' . "\r\n";
509
+ }
510
+
511
+ return '';
512
+ }
513
+
514
+ /**
515
+ * Render twitter:creator or twitter:site:id
516
+ *
517
+ * @since 2.2.2
518
+ */
519
+ public function twitter_creator() {
520
+
521
+ if ( $this->use_twitter_tags() ) {
522
+
523
+ /**
524
+ * Applies filters 'the_seo_framework_twittercreator_output' : string
525
+ * @since 2.3.0
526
+ */
527
+ $creator = (string) apply_filters( 'the_seo_framework_twittercreator_output', '', $this->get_the_real_ID() );
528
+
529
+ if ( empty( $creator ) ) {
530
+ $site = $this->get_option( 'twitter_site' );
531
+ $creator = $this->get_option( 'twitter_creator' );
532
+
533
+ /**
534
+ * Return site:id instead of creator is no twitter:site is found.
535
+ * Per Twitter requirements
536
+ */
537
+ if ( empty( $site ) && $creator )
538
+ return '<meta name="twitter:site:id" content="' . esc_attr( $creator ) . '" />' . "\r\n";
539
+ }
540
+
541
+ if ( $creator )
542
+ return '<meta name="twitter:creator" content="' . esc_attr( $creator ) . '" />' . "\r\n";
543
+ }
544
+
545
+ return '';
546
+ }
547
+
548
+ /**
549
+ * Render twitter:title
550
+ *
551
+ * @uses $this->title_from_cache()
552
+ *
553
+ * @since 2.2.2
554
+ */
555
+ public function twitter_title() {
556
+
557
+ if ( $this->use_twitter_tags() ) {
558
+
559
+ /**
560
+ * Applies filters 'the_seo_framework_twittertitle_output' : string
561
+ * @since 2.3.0
562
+ */
563
+ $title = (string) apply_filters( 'the_seo_framework_twittertitle_output', '', $this->get_the_real_ID() );
564
+
565
+ if ( empty( $title ) )
566
+ $title = $this->title_from_cache( '', '', '', true );
567
+
568
+ return '<meta name="twitter:title" content="' . esc_attr( $title ) . '" />' . "\r\n";
569
+ }
570
+
571
+ return '';
572
+ }
573
+
574
+ /**
575
+ * Render twitter:description
576
+ *
577
+ * @uses $this->description_from_cache()
578
+ *
579
+ * @since 2.2.2
580
+ */
581
+ public function twitter_description() {
582
+
583
+ if ( $this->use_twitter_tags() ) {
584
+
585
+ /**
586
+ * Applies filters 'the_seo_framework_twitterdescription_output' : string
587
+ * @since 2.3.0
588
+ */
589
+ $description = (string) apply_filters( 'the_seo_framework_twitterdescription_output', '', $this->get_the_real_ID() );
590
+
591
+ if ( empty( $description ) )
592
+ $description = $this->description_from_cache( true );
593
+
594
+ return '<meta name="twitter:description" content="' . esc_attr( $description ) . '" />' . "\r\n";
595
+ }
596
+
597
+ return '';
598
+ }
599
+
600
+ /**
601
+ * Render twitter:image:src
602
+ *
603
+ * @param string $image url for image
604
+ *
605
+ * @since 2.2.2
606
+ *
607
+ * @return string|null The twitter image source meta tag
608
+ */
609
+ public function twitter_image() {
610
+
611
+ if ( $this->use_twitter_tags() ) {
612
+
613
+ /**
614
+ * Applies filters 'the_seo_framework_twitterimage_output' : string|bool
615
+ * @since 2.3.0
616
+ */
617
+ $image = apply_filters( 'the_seo_framework_twitterimage_output', '', $this->get_the_real_ID() );
618
+
619
+ /**
620
+ * Now returns empty string on false.
621
+ * @since 2.6.0
622
+ */
623
+ if ( false === $image )
624
+ return '';
625
+
626
+ if ( empty( $image ) ) {
627
+ $image = $this->get_image_from_cache();
628
+ } else {
629
+ $image = (string) $image;
630
+ }
631
+
632
+ if ( $image )
633
+ return '<meta name="twitter:image:src" content="' . esc_attr( $image ) . '" />' . "\r\n";
634
+ }
635
+
636
+ return '';
637
+ }
638
+
639
+ /**
640
+ * Render article:author
641
+ *
642
+ * @since 2.2.2
643
+ *
644
+ * @return string|null The facebook app id
645
+ */
646
+ public function facebook_author() {
647
+
648
+ if ( $this->use_facebook_tags() ) {
649
+
650
+ /**
651
+ * Applies filters 'the_seo_framework_facebookauthor_output' : string
652
+ * @since 2.3.0
653
+ */
654
+ $author = (string) apply_filters( 'the_seo_framework_facebookauthor_output', '', $this->get_the_real_ID() );
655
+
656
+ if ( empty( $author ) )
657
+ $author = $this->get_option( 'facebook_author' );
658
+
659
+ if ( $author )
660
+ return '<meta property="article:author" content="' . esc_attr( esc_url_raw( $author ) ) . '" />' . "\r\n";
661
+ }
662
+
663
+ return '';
664
+ }
665
+
666
+ /**
667
+ * Render article:author
668
+ *
669
+ * @since 2.2.2
670
+ *
671
+ * @return string|null The facebook app id
672
+ */
673
+ public function facebook_publisher() {
674
+
675
+ if ( $this->use_facebook_tags() ) {
676
+
677
+ /**
678
+ * Applies filters 'the_seo_framework_facebookpublisher_output' : string
679
+ * @since 2.3.0
680
+ */
681
+ $publisher = (string) apply_filters( 'the_seo_framework_facebookpublisher_output', '', $this->get_the_real_ID() );
682
+
683
+ if ( empty( $publisher ) )
684
+ $publisher = $this->get_option( 'facebook_publisher' );
685
+
686
+ if ( $publisher )
687
+ return '<meta property="article:publisher" content="' . esc_attr( esc_url_raw( $publisher ) ) . '" />' . "\r\n";
688
+ }
689
+
690
+ return '';
691
+ }
692
+
693
+ /**
694
+ * Render fb:app_id
695
+ *
696
+ * @since 2.2.2
697
+ *
698
+ * @return string|null The facebook app id
699
+ */
700
+ public function facebook_app_id() {
701
+
702
+ if ( $this->use_facebook_tags() ) {
703
+
704
+ /**
705
+ * Applies filters 'the_seo_framework_facebookappid_output' : string
706
+ * @since 2.3.0
707
+ */
708
+ $app_id = (string) apply_filters( 'the_seo_framework_facebookappid_output', '', $this->get_the_real_ID() );
709
+
710
+ if ( empty( $app_id ) )
711
+ $app_id = $this->get_option( 'facebook_appid' );
712
+
713
+ if ( $app_id )
714
+ return '<meta property="fb:app_id" content="' . esc_attr( $app_id ) . '" />' . "\r\n";
715
+ }
716
+
717
+ return '';
718
+ }
719
+
720
+ /**
721
+ * Render article:published_time
722
+ *
723
+ * @since 2.2.2
724
+ *
725
+ * @return string|null The article:published_time
726
+ */
727
+ public function article_published_time() {
728
+
729
+ // Don't do anything if it's not a page or post.
730
+ if ( false === $this->is_singular() )
731
+ return;
732
+
733
+ $front_page = (bool) is_front_page();
734
+
735
+ // If it's a post, but the option is disabled, don't do anyhting.
736
+ if ( ! $front_page && $this->is_single() && ! $this->get_option( 'post_publish_time' ) )
737
+ return;
738
+
739
+ // If it's a page, but the option is disabled, don't do anything.
740
+ if ( ! $front_page && $this->is_page() && ! $this->get_option( 'page_publish_time' ) )
741
+ return;
742
+
743
+ // If it's the home page, but the option is disabled, don't do anything.
744
+ if ( $front_page && ! $this->get_option( 'home_publish_time' ) )
745
+ return;
746
+
747
+ //* @since 2.3.0
748
+ $time = (string) apply_filters( 'the_seo_framework_publishedtime_output', '', $this->get_the_real_ID() );
749
+
750
+ if ( empty( $time ) )
751
+ $time = get_the_date( 'Y-m-d', '' );
752
+
753
+ if ( $time )
754
+ return '<meta property="article:published_time" content="' . esc_attr( $time ) . '" />' . "\r\n";
755
+
756
+ return '';
757
+ }
758
+
759
+ /**
760
+ * Render article:modified_time
761
+ *
762
+ * @since 2.2.2
763
+ *
764
+ * @return string|null The article:modified_time
765
+ */
766
+ public function article_modified_time() {
767
+
768
+ // Don't do anything if it's not a page or post, or if both options are disabled
769
+ if ( false === $this->is_singular() )
770
+ return '';
771
+
772
+ if ( $this->is_front_page() ) {
773
+ // If it's the frontpage, but the option is disabled, don't do anything.
774
+ if ( ! $this->get_option( 'home_modify_time' ) )
775
+ return '';
776
+ } else {
777
+ // If it's a post, but the option is disabled, don't do anyhting.
778
+ if ( $this->is_single() && ! $this->get_option( 'post_modify_time' ) )
779
+ return '';
780
+
781
+ // If it's a page, but the option is disabled, don't do anything.
782
+ if ( $this->is_page() && ! $this->get_option( 'page_modify_time' ) )
783
+ return '';
784
+ }
785
+
786
+ //* @since 2.3.0
787
+ $time = (string) apply_filters( 'the_seo_framework_modifiedtime_output', '', $this->get_the_real_ID() );
788
+
789
+ if ( empty( $time ) )
790
+ $time = the_modified_date( 'Y-m-d', '', '', false );
791
+
792
+ if ( $time ) {
793
+ $output = '<meta property="article:modified_time" content="' . esc_attr( $time ) . '" />' . "\r\n";
794
+
795
+ if ( $this->use_og_tags() )
796
+ $output .= '<meta property="og:updated_time" content="' . esc_attr( $time ) . '" />'. "\r\n";
797
+
798
+ return $output;
799
+ }
800
+
801
+ return '';
802
+ }
803
+
804
+ /**
805
+ * Outputs canonical url
806
+ *
807
+ * @since 2.0.6
808
+ *
809
+ * @uses $this->the_url_from_cache()
810
+ *
811
+ * @return string canonical url meta
812
+ */
813
+ public function canonical() {
814
+
815
+ /**
816
+ * Applies filters the_seo_framework_output_canonical : Don't output canonical if false.
817
+ * @since 2.4.2
818
+ */
819
+ if ( ! apply_filters( 'the_seo_framework_output_canonical', true, $this->get_the_real_ID() ) )
820
+ return;
821
+
822
+ /**
823
+ * Applies filters 'the_seo_framework_rel_canonical_output' : Change canonical URL output.
824
+ * @since 2.6.5
825
+ */
826
+ $url = (string) apply_filters( 'the_seo_framework_rel_canonical_output', $this->the_url_from_cache(), $this->get_the_real_ID() );
827
+
828
+ return '<link rel="canonical" href="' . $url . '" />' . "\r\n";
829
+ }
830
+
831
+ /**
832
+ * LD+JSON helper output
833
+ *
834
+ * @uses $this->render_ld_json_scripts()
835
+ *
836
+ * @since 1.2.0
837
+ * @return string $json LD+json helpers in header on front page.
838
+ */
839
+ public function ld_json() {
840
+
841
+ //* Check for LD+JSON compat
842
+ if ( $this->is_search() || $this->is_404() )
843
+ return;
844
+
845
+ /**
846
+ * Applies filters 'the_seo_framework_ldjson_scripts' : string
847
+ * @since 2.6.0
848
+ */
849
+ $json = (string) apply_filters( 'the_seo_framework_ldjson_scripts', $this->render_ld_json_scripts(), $this->get_the_real_ID() );
850
+
851
+ return $json;
852
+ }
853
+
854
+ /**
855
+ * Outputs Google Site Verification code
856
+ *
857
+ * @since 2.2.4
858
+ *
859
+ * @return string|null google verification code
860
+ */
861
+ public function google_site_output() {
862
+
863
+ /**
864
+ * Applies filters 'the_seo_framework_googlesite_output' : string
865
+ * @since 2.6.0
866
+ */
867
+ $code = (string) apply_filters( 'the_seo_framework_googlesite_output', $this->get_option( 'google_verification' ), $this->get_the_real_ID() );
868
+
869
+ if ( empty( $code ) )
870
+ return '';
871
+
872
+ return '<meta name="google-site-verification" content="' . esc_attr( $code ) . '" />' . "\r\n";
873
+ }
874
+
875
+ /**
876
+ * Outputs Bing Site Verification code
877
+ *
878
+ * @since 2.2.4
879
+ *
880
+ * @return string|null Bing Webmaster code
881
+ */
882
+ public function bing_site_output() {
883
+
884
+ /**
885
+ * Applies filters 'the_seo_framework_bingsite_output' : string
886
+ * @since 2.6.0
887
+ */
888
+ $code = (string) apply_filters( 'the_seo_framework_bingsite_output', $this->get_option( 'bing_verification' ), $this->get_the_real_ID() );
889
+
890
+ if ( empty( $code ) )
891
+ return '';
892
+
893
+ return '<meta name="msvalidate.01" content="' . esc_attr( $code ) . '" />' . "\r\n";
894
+ }
895
+
896
+ /**
897
+ * Outputs Yandex Site Verification code
898
+ *
899
+ * @since 2.6.0
900
+ *
901
+ * @return string|null Yandex Webmaster code
902
+ */
903
+ public function yandex_site_output() {
904
+
905
+ /**
906
+ * Applies filters 'the_seo_framework_yandexsite_output' : string
907
+ * @since 2.6.0
908
+ */
909
+ $code = (string) apply_filters( 'the_seo_framework_yandexsite_output', $this->get_option( 'yandex_verification' ), $this->get_the_real_ID() );
910
+
911
+ if ( empty( $code ) )
912
+ return '';
913
+
914
+ return '<meta name="yandex-verification" content="' . esc_attr( $code ) . '" />' . "\r\n";
915
+ }
916
+
917
+ /**
918
+ * Outputs Bing Site Verification code
919
+ *
920
+ * @since 2.5.2
921
+ *
922
+ * @return string|null Bing Webmaster code
923
+ */
924
+ public function pint_site_output() {
925
+
926
+ /**
927
+ * Applies filters 'the_seo_framework_pintsite_output' : string
928
+ * @since 2.6.0
929
+ */
930
+ $code = (string) apply_filters( 'the_seo_framework_pintsite_output', $this->get_option( 'pint_verification' ), $this->get_the_real_ID() );
931
+
932
+ if ( empty( $code ) )
933
+ return '';
934
+
935
+ return '<meta name="p:domain_verify" content="' . esc_attr( $code ) . '" />' . "\r\n";
936
+ }
937
+
938
+ /**
939
+ * Output robots meta tags
940
+ *
941
+ * @since 2.0.0
942
+ *
943
+ * @return null Return early if blog is not public.
944
+ */
945
+ public function robots() {
946
+
947
+ //* Don't do anything if the blog isn't set to public.
948
+ if ( false === $this->is_blog_public() )
949
+ return '';
950
+
951
+ /**
952
+ * Applies filters 'the_seo_framework_robots_meta' : array
953
+ * @since 2.6.0
954
+ */
955
+ $meta = (array) apply_filters( 'the_seo_framework_robots_meta', $this->robots_meta(), $this->get_the_real_ID() );
956
+
957
+ //* Add meta if any exist
958
+ if ( $meta )
959
+ return sprintf( '<meta name="robots" content="%s" />' . "\r\n", implode( ',', $meta ) );
960
+
961
+ return '';
962
+ }
963
+
964
+ /**
965
+ * Outputs favicon urls
966
+ *
967
+ * @since 2.2.1
968
+ *
969
+ * @uses $this->site_icon()
970
+ *
971
+ * @return string icon links.
972
+ * @TODO Make this work for older wp versions. i.e. add upload area for wp 4.2.99999 and lower
973
+ * @TODO Make this work in the first place
974
+ * @ignore
975
+ * @access private
976
+ */
977
+ public function favicon() {
978
+
979
+ if ( $this->wp_version( '4.2.999', '<=' ) ) {
980
+ $output = '<link rel="icon" type="image/x-icon" href="' . esc_url( $this->site_icon( 16 ) ) . '" sizes="16x16" />' . "\r\n";
981
+ $output .= '<link rel="icon" type="image/x-icon" href="' . esc_url( $this->site_icon( 192 ) ) . '" sizes="192x192" />' . "\r\n";
982
+ $output .= '<link rel="apple-touch-icon-precomposed" href="' . esc_url( $this->site_icon( 180 ) ) . '" />' . "\r\n";
983
+ $output .= '<link rel="msapplication-TileImage" href="' . esc_url( $this->site_icon( 270 ) ) . '" />' . "\r\n";
984
+
985
+ return $output;
986
+ }
987
+
988
+ return '';
989
+ }
990
+
991
+ /**
992
+ * Outputs shortlink meta tag
993
+ *
994
+ * @since 2.2.2
995
+ *
996
+ * @uses $this->get_shortlink()
997
+ *
998
+ * @return string|null shortlink url meta
999
+ */
1000
+ public function shortlink() {
1001
+
1002
+ /**
1003
+ * Applies filters 'the_seo_framework_shortlink_output' : array
1004
+ * @since 2.6.0
1005
+ */
1006
+ $url = (string) apply_filters( 'the_seo_framework_shortlink_output', $this->get_shortlink(), $this->get_the_real_ID() );
1007
+
1008
+ if ( $url )
1009
+ return sprintf( '<link rel="shortlink" href="%s" />' . "\r\n", $url );
1010
+
1011
+ return '';
1012
+ }
1013
+
1014
+ /**
1015
+ * Outputs paged urls meta tag
1016
+ *
1017
+ * @since 2.2.2
1018
+ *
1019
+ * @uses $this->get_paged_url()
1020
+ *
1021
+ * @return string
1022
+ */
1023
+ public function paged_urls() {
1024
+
1025
+ $id = $this->get_the_real_ID();
1026
+
1027
+ /**
1028
+ * Applies filters 'the_seo_framework_paged_url_output' : array
1029
+ * @since 2.6.0
1030
+ */
1031
+ $next = (string) apply_filters( 'the_seo_framework_paged_url_output_next', $this->get_paged_url( 'next' ), $id );
1032
+
1033
+ /**
1034
+ * Applies filters 'the_seo_framework_paged_url_output' : array
1035
+ * @since 2.6.0
1036
+ */
1037
+ $prev = (string) apply_filters( 'the_seo_framework_paged_url_output_prev', $this->get_paged_url( 'prev' ), $id );
1038
+
1039
+ $output = '';
1040
+
1041
+ if ( $prev )
1042
+ $output .= sprintf( '<link rel="prev" href="%s" />' . "\r\n", $prev );
1043
+
1044
+ if ( $next )
1045
+ $output .= sprintf( '<link rel="next" href="%s" />' . "\r\n", $next );
1046
+
1047
+ return $output;
1048
+ }
1049
+
1050
+ /**
1051
+ * Whether we can use Open Graph tags.
1052
+ *
1053
+ * @since 2.6.0
1054
+ * @staticvar bool $cache
1055
+ *
1056
+ * @return bool
1057
+ */
1058
+ public function use_og_tags() {
1059
+
1060
+ static $cache = null;
1061
+
1062
+ if ( isset( $cache ) )
1063
+ return $cache;
1064
+
1065
+ return $cache = $this->is_option_checked( 'og_tags' ) && false === $this->detect_og_plugin();
1066
+ }
1067
+
1068
+ /**
1069
+ * Whether we can use Facebook tags.
1070
+ *
1071
+ * @since 2.6.0
1072
+ * @staticvar bool $cache
1073
+ *
1074
+ * @return bool
1075
+ */
1076
+ public function use_facebook_tags() {
1077
+
1078
+ static $cache = null;
1079
+
1080
+ if ( isset( $cache ) )
1081
+ return $cache;
1082
+
1083
+ return $cache = $this->is_option_checked( 'facebook_tags' );
1084
+ }
1085
+
1086
+ /**
1087
+ * Whether we can use Twitter tags.
1088
+ *
1089
+ * @since 2.6.0
1090
+ * @staticvar bool $cache
1091
+ *
1092
+ * @return bool
1093
+ */
1094
+ public function use_twitter_tags() {
1095
+
1096
+ static $cache = null;
1097
+
1098
+ if ( isset( $cache ) )
1099
+ return $cache;
1100
+
1101
+ return $cache = $this->is_option_checked( 'twitter_tags' ) && false == $this->detect_twitter_card_plugin();
1102
+ }
1103
+
1104
+ /**
1105
+ * Whether we can use Google+ tags.
1106
+ *
1107
+ * @since 2.6.0
1108
+ * @staticvar bool $cache
1109
+ *
1110
+ * @return bool
1111
+ */
1112
+ public function use_googleplus_tags() {
1113
+
1114
+ static $cache = null;
1115
+
1116
+ if ( isset( $cache ) )
1117
+ return $cache;
1118
+
1119
+ return $cache = $this->is_option_checked( 'googleplus_tags' );
1120
+ }
1121
+
1122
+ }
inc/classes/sanitize.class.php ADDED
@@ -0,0 +1,978 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The SEO Framework plugin
4
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License version 3 as published
8
+ * by the Free Software Foundation.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ /**
20
+ * Class AutoDescription_Sanitize
21
+ *
22
+ * Sanitizes strings/arrays within the plugin.
23
+ *
24
+ * @since 2.2.4
25
+ */
26
+ class AutoDescription_Sanitize extends AutoDescription_Adminpages {
27
+
28
+ /**
29
+ * Constructor, load parent constructor
30
+ */
31
+ public function __construct() {
32
+ parent::__construct();
33
+
34
+ /**
35
+ * Sanitizes Site options
36
+ * @see siteoptions.class.php
37
+ */
38
+ add_action( 'admin_init', array( $this, 'sanitizer_filters' ) );
39
+ }
40
+
41
+ /**
42
+ * Register each of the settings with a sanitization filter type.
43
+ *
44
+ * @since 2.2.2
45
+ *
46
+ * @uses autodescription_add_option_filter() Assign filter to array of settings.
47
+ *
48
+ * @see AutoDescription_Sanitize::add_filter() Add sanitization filters to options.
49
+ */
50
+ public function sanitizer_filters() {
51
+
52
+ //* If this page doesn't store settings, no need to sanitize them
53
+ if ( ! $this->settings_field )
54
+ return;
55
+
56
+ /**
57
+ * If this page doesn't parse the site options,
58
+ * There's no need to filter them on each request.
59
+ *
60
+ * @since 2.2.9
61
+ */
62
+ if ( ! isset( $_POST ) || empty( $_POST ) || ! isset( $_POST[THE_SEO_FRAMEWORK_SITE_OPTIONS] ) || ! is_array( $_POST[THE_SEO_FRAMEWORK_SITE_OPTIONS] ) )
63
+ return;
64
+
65
+ //* Update hidden options.
66
+ $this->update_hidden_options_to_default();
67
+
68
+ $this->autodescription_add_option_filter(
69
+ 's_title_separator',
70
+ $this->settings_field,
71
+ array(
72
+ 'title_seperator', // NOTE: Typo
73
+ )
74
+ );
75
+
76
+ $this->autodescription_add_option_filter(
77
+ 's_description_separator',
78
+ $this->settings_field,
79
+ array(
80
+ 'description_separator',
81
+ )
82
+ );
83
+
84
+ $this->autodescription_add_option_filter(
85
+ 's_description',
86
+ $this->settings_field,
87
+ array(
88
+ 'homepage_description',
89
+ 'description_custom',
90
+ )
91
+ );
92
+
93
+ $this->autodescription_add_option_filter(
94
+ 's_title',
95
+ $this->settings_field,
96
+ array(
97
+ 'homepage_title',
98
+ 'homepage_title_tagline',
99
+
100
+ 'knowledge_name',
101
+ )
102
+ );
103
+
104
+ $this->autodescription_add_option_filter(
105
+ 's_knowledge_type',
106
+ $this->settings_field,
107
+ array(
108
+ 'knowledge_type',
109
+ )
110
+ );
111
+
112
+ $this->autodescription_add_option_filter(
113
+ 's_left_right',
114
+ $this->settings_field,
115
+ array(
116
+ 'title_location',
117
+ )
118
+ );
119
+
120
+ $this->autodescription_add_option_filter(
121
+ 's_left_right_home',
122
+ $this->settings_field,
123
+ array(
124
+ 'home_title_location',
125
+ )
126
+ );
127
+
128
+ $this->autodescription_add_option_filter(
129
+ 's_one_zero',
130
+ $this->settings_field,
131
+ array(
132
+ 'title_rem_additions',
133
+ 'title_rem_prefixes',
134
+
135
+ 'description_additions',
136
+ 'description_blogname',
137
+
138
+ 'noodp',
139
+ 'noydir',
140
+
141
+ 'category_noindex',
142
+ 'tag_noindex',
143
+ 'author_noindex',
144
+ 'date_noindex',
145
+ 'search_noindex',
146
+ 'attachment_noindex',
147
+ 'site_noindex',
148
+
149
+ 'category_nofollow',
150
+ 'tag_nofollow',
151
+ 'author_nofollow',
152
+ 'date_nofollow',
153
+ 'search_nofollow',
154
+ 'attachment_nofollow',
155
+ 'site_nofollow',
156
+
157
+ 'category_noarchive',
158
+ 'tag_noarchive',
159
+ 'author_noarchive',
160
+ 'date_noarchive',
161
+ 'search_noarchive',
162
+ 'attachment_noarchive',
163
+ 'site_noarchive',
164
+
165
+ 'paged_noindex',
166
+ 'home_paged_noindex',
167
+
168
+ 'homepage_noindex',
169
+ 'homepage_nofollow',
170
+ 'homepage_noarchive',
171
+
172
+ 'homepage_tagline',
173
+
174
+ 'shortlink_tag',
175
+
176
+ 'prev_next_posts',
177
+ 'prev_next_archives',
178
+ 'prev_next_frontpage',
179
+
180
+ 'og_tags',
181
+ 'facebook_tags',
182
+ 'twitter_tags',
183
+ 'googleplus_tags',
184
+
185
+ 'knowledge_output',
186
+
187
+ 'post_publish_time',
188
+ 'post_modify_time',
189
+
190
+ 'page_publish_time',
191
+ 'page_modify_time',
192
+
193
+ 'home_modify_time',
194
+ 'home_publish_time',
195
+
196
+ 'noodp',
197
+ 'noydir',
198
+
199
+ 'knowledge_logo',
200
+
201
+ 'sitemaps_robots',
202
+ 'ping_google',
203
+ 'ping_bing',
204
+ 'ping_yandex',
205
+
206
+ 'excerpt_the_feed',
207
+ 'source_the_feed',
208
+
209
+ 'ld_json_searchbox',
210
+ 'ld_json_sitename',
211
+ 'ld_json_breadcrumbs',
212
+ )
213
+ );
214
+
215
+ $this->autodescription_add_option_filter(
216
+ 's_absint',
217
+ $this->settings_field,
218
+ array(
219
+ // 'home_author', @TODO
220
+ )
221
+ );
222
+
223
+ $this->autodescription_add_option_filter(
224
+ 's_no_html',
225
+ $this->settings_field,
226
+ array(
227
+ )
228
+ );
229
+
230
+ /**
231
+ * @todo create content="code" stripper
232
+ * @priority low 2.9.0+
233
+ */
234
+ $this->autodescription_add_option_filter(
235
+ 's_no_html_space',
236
+ $this->settings_field,
237
+ array(
238
+ 'facebook_appid',
239
+
240
+ 'google_verification',
241
+ 'bing_verification',
242
+ 'yandex_verification',
243
+ 'pint_verification',
244
+ )
245
+ );
246
+
247
+ $this->autodescription_add_option_filter(
248
+ 's_url',
249
+ $this->settings_field,
250
+ array(
251
+ 'facebook_publisher',
252
+ 'facebook_author',
253
+
254
+ 'knowledge_facebook',
255
+ 'knowledge_twitter',
256
+ 'knowledge_gplus',
257
+ 'knowledge_instagram',
258
+ 'knowledge_youtube',
259
+ // 'knowledge_myspace',
260
+ 'knowledge_pinterest',
261
+ 'knowledge_soundcloud',
262
+ 'knowledge_tumblr',
263
+ )
264
+ );
265
+
266
+ $this->autodescription_add_option_filter(
267
+ 's_url_query',
268
+ $this->settings_field,
269
+ array(
270
+ 'knowledge_linkedin',
271
+ )
272
+ );
273
+
274
+ $this->autodescription_add_option_filter(
275
+ 's_twitter_name',
276
+ $this->settings_field,
277
+ array(
278
+ 'twitter_site',
279
+ 'twitter_creator',
280
+ )
281
+ );
282
+
283
+ $this->autodescription_add_option_filter(
284
+ 's_twitter_card',
285
+ $this->settings_field,
286
+ array(
287
+ 'twitter_card',
288
+ )
289
+ );
290
+
291
+ //* Special action filter.
292
+ $this->autodescription_add_option_filter(
293
+ 's_one_zero_flush_rewrite',
294
+ $this->settings_field,
295
+ array(
296
+ 'sitemaps_output',
297
+ )
298
+ );
299
+
300
+ //* Special action filter.
301
+ $this->autodescription_add_option_filter(
302
+ 's_one_zero_flush_sitemap',
303
+ $this->settings_field,
304
+ array(
305
+ 'sitemaps_modified',
306
+ 'sitemap_timestamps',
307
+ )
308
+ );
309
+
310
+ }
311
+
312
+ /**
313
+ * Registers option sanitation filter
314
+ *
315
+ * @since 2.2.2
316
+ *
317
+ * @param string $filter The filter to call (see AutoDescription_Siteoptions::$available_filters for options)
318
+ * @param string $option The WordPress option name
319
+ * @param string|array $suboption Optional. The suboption or suboptions you want to filter
320
+ *
321
+ * @return true
322
+ */
323
+ public function autodescription_add_option_filter( $filter, $option, $suboption = null ) {
324
+ return $this->add_filter( $filter, $option, $suboption );
325
+ }
326
+
327
+ /**
328
+ * Add sanitization filters to options.
329
+ *
330
+ * Associates a sanitization filter to each option (or sub options if they
331
+ * exist) before adding a reference to run the option through that
332
+ * sanitizer at the right time.
333
+ *
334
+ * @since 2.2.2
335
+ *
336
+ * @thanks StudioPress (http://www.studiopress.com/) for some code.
337
+ *
338
+ * @param string $filter Sanitization filter type
339
+ * @param string $option Option key
340
+ * @param array|string $suboption Optional. Suboption key
341
+ * @return boolean Returns true when complete
342
+ */
343
+ public function add_filter( $filter, $option, $suboption = null ) {
344
+
345
+ if ( is_array( $suboption ) ) {
346
+ foreach ( $suboption as $so ) {
347
+ $this->options[$option][$so] = $filter;
348
+ }
349
+ } else if ( is_null( $suboption ) ) {
350
+ $this->options[$option] = $filter;
351
+ } else {
352
+ $this->options[$option][$suboption] = $filter;
353
+ }
354
+
355
+ add_filter( 'sanitize_option_' . $option, array( $this, 'sanitize' ), 10, 2 );
356
+
357
+ return true;
358
+ }
359
+
360
+ /**
361
+ * Sanitize a value, via the sanitization filter type associated with an option.
362
+ *
363
+ * @since 2.2.2
364
+ *
365
+ * @thanks StudioPress (http://www.studiopress.com/) for some code.
366
+ *
367
+ * @param mixed $new_value New value
368
+ * @param string $option Name of the option
369
+ * @return mixed Filtered, or unfiltered value
370
+ */
371
+ public function sanitize( $new_value, $option ) {
372
+
373
+ if ( ! isset( $this->options[$option] ) ) {
374
+ //* We are not filtering this option at all
375
+ return $new_value;
376
+ } else if ( is_string( $this->options[$option] ) ) {
377
+ //* Single option value
378
+ return $this->do_filter( $this->options[$option], $new_value, get_option( $option ) );
379
+ } else if ( is_array( $this->options[$option] ) ) {
380
+ //* Array of suboption values to loop through
381
+ $old_value = get_option( $option );
382
+ foreach ( $this->options[$option] as $suboption => $filter ) {
383
+ $old_value[$suboption] = isset( $old_value[$suboption] ) ? $old_value[$suboption] : '';
384
+ $new_value[$suboption] = isset( $new_value[$suboption] ) ? $new_value[$suboption] : '';
385
+ $new_value[$suboption] = $this->do_filter( $filter, $new_value[$suboption], $old_value[$suboption] );
386
+ }
387
+ return $new_value;
388
+ }
389
+
390
+ //* Should never hit this, but:
391
+ return $new_value;
392
+ }
393
+
394
+ /**
395
+ * Checks sanitization filter exists, and if so, passes the value through it.
396
+ *
397
+ * @since 2.2.2
398
+ *
399
+ * @thanks StudioPress (http://www.studiopress.com/) for some code.
400
+ *
401
+ * @param string $filter Sanitization filter type
402
+ * @param string $new_value New value
403
+ * @param string $old_value Previous value
404
+ * @return mixed Returns filtered value, or submitted value if value is
405
+ * unfiltered.
406
+ */
407
+ protected function do_filter( $filter, $new_value, $old_value ) {
408
+
409
+ $available_filters = $this->get_available_filters();
410
+
411
+ if ( ! in_array( $filter, array_keys( $available_filters ) ) )
412
+ return $new_value;
413
+
414
+ return call_user_func( $available_filters[$filter], $new_value, $old_value );
415
+ }
416
+
417
+ /**
418
+ * Return array of known sanitization filter types.
419
+ *
420
+ * Array can be filtered via 'the_seo_framework_available_sanitizer_filters'
421
+ * to let themes and other plugins add their own sanitization filters.
422
+ *
423
+ * @since 2.2.2
424
+ *
425
+ * @thanks StudioPress (http://www.studiopress.com/) for some code.
426
+ *
427
+ * @return array Array with keys of sanitization types, and values of the
428
+ * filter function name as a callback
429
+ */
430
+ protected function get_available_filters() {
431
+
432
+ $default_filters = array(
433
+ 's_left_right' => array( $this, 's_left_right' ),
434
+ 's_left_right_home' => array( $this, 's_left_right_home' ),
435
+ 's_title_separator' => array( $this, 's_title_separator' ),
436
+ 's_description_separator' => array( $this, 's_description_separator' ),
437
+ 's_description' => array( $this, 's_description' ),
438
+ 's_title' => array( $this, 's_title' ),
439
+ 's_knowledge_type' => array( $this, 's_knowledge_type' ),
440
+ 's_one_zero' => array( $this, 's_one_zero' ),
441
+ 's_one_zero_flush_rewrite' => array( $this, 's_one_zero_flush_rewrite' ),
442
+ 's_one_zero_flush_sitemap' => array( $this, 's_one_zero_flush_sitemap' ),
443
+ 's_no_html' => array( $this, 's_no_html' ),
444
+ 's_no_html_space' => array( $this, 's_no_html_space' ),
445
+ 's_absint' => array( $this, 's_absint' ),
446
+ 's_safe_html' => array( $this, 's_safe_html' ),
447
+ 's_url' => array( $this, 's_url' ),
448
+ 's_url_query' => array( $this, 's_url_query' ),
449
+ 's_twitter_name' => array( $this, 's_twitter_name' ),
450
+ 's_twitter_card' => array( $this, 's_twitter_card' ),
451
+ );
452
+
453
+ /**
454
+ * Filter the available sanitization filter types.
455
+ *
456
+ * @since 2.2.2
457
+ *
458
+ * Applies filters the_seo_framework_available_sanitizer_filters : array
459
+ * @param array $default_filters Array with keys of sanitization types,
460
+ * and values of the filter function name as a callback
461
+ */
462
+ return (array) apply_filters( 'the_seo_framework_available_sanitizer_filters', $default_filters );
463
+ }
464
+
465
+ /**
466
+ * Returns the title separator value string.
467
+ *
468
+ * @since 2.2.2
469
+ *
470
+ * @param mixed $new_value Should be identical to any of the $this->get_separator_list() values
471
+ * @return string Title separator option
472
+ */
473
+ protected function s_title_separator( $new_value ) {
474
+
475
+ $title_separator = $this->get_separator_list();
476
+
477
+ $key = array_key_exists( $new_value, $title_separator );
478
+
479
+ if ( $key )
480
+ return (string) $new_value;
481
+
482
+ $previous = $this->get_field_value( 'title_seperator' ); // NOTE: Typo
483
+
484
+ //* Fallback to default if empty.
485
+ if ( empty( $previous ) )
486
+ $previous = $this->get_default_option( 'title_seperator' );
487
+
488
+ return (string) $previous;
489
+ }
490
+
491
+ /**
492
+ * Returns the description separator value string.
493
+ *
494
+ * @since 2.2.2
495
+ *
496
+ * @param mixed $new_value Should be identical to any of the $this->description_separator values
497
+ * @return string Description separator option
498
+ */
499
+ protected function s_description_separator( $new_value ) {
500
+
501
+ //* Use the same as title_separator
502
+ $description_separator = $this->get_separator_list();
503
+
504
+ $key = array_key_exists( $new_value, $description_separator );
505
+
506
+ if ( $key )
507
+ return (string) $new_value;
508
+
509
+ $previous = $this->get_field_value( 'description_separator' );
510
+
511
+ //* Fallback to default if empty.
512
+ if ( empty( $previous ) )
513
+ $previous = $this->get_default_option( 'description_separator' );
514
+
515
+ return (string) $previous;
516
+ }
517
+
518
+ /**
519
+ * Returns a one-line sanitized description
520
+ *
521
+ * @since 2.5.0
522
+ * @since 2.6.6 Removes duplicated spaces.
523
+ *
524
+ * @param string $new_value The Description.
525
+ * @return string One line sanitized description.
526
+ */
527
+ protected function s_description( $new_value ) {
528
+
529
+ $description = str_replace( array( "\r\n", "\r", "\n" ), "\n", $new_value );
530
+
531
+ $lines = explode( "\n", $description );
532
+ $new_lines = array();
533
+
534
+ //* Remove line breaks
535
+ foreach ( $lines as $i => $line ) {
536
+ //* Don't add empty lines or paragraphs
537
+ if ( $line && '&nbsp;' !== $line )
538
+ $new_lines[] = trim( $line ) . ' ';
539
+ }
540
+
541
+ $description = trim( implode( $new_lines ) );
542
+
543
+ $i = 0;
544
+ //* Run twice at most, to catch uneven multiple spaces.
545
+ do {
546
+ $description = str_replace( ' ', ' ', $description );
547
+ $i++;
548
+ } while ( strpos( $description, ' ' ) && $i <= 2 );
549
+
550
+ return (string) strip_tags( $description );
551
+ }
552
+
553
+ /**
554
+ * Returns a sanitized and trimmed title.
555
+ *
556
+ * @since 2.5.2
557
+ *
558
+ * @param string $new_value The Title.
559
+ * @return string Sanitized and trimmed title.
560
+ */
561
+ protected function s_title( $new_value ) {
562
+
563
+ $title = esc_html( $new_value );
564
+ $title = trim( $title );
565
+
566
+ return (string) strip_tags( $title );
567
+ }
568
+
569
+ /**
570
+ * Returns the knowledge type value string.
571
+ *
572
+ * @since 2.2.8
573
+ *
574
+ * @param mixed $new_value Should be identical to any of the $person_organization values.
575
+ * @return string title Knowledge type option
576
+ */
577
+ protected function s_knowledge_type( $new_value ) {
578
+
579
+ if ( 'person' === $new_value || 'organization' === $new_value )
580
+ return (string) $new_value;
581
+
582
+ $previous = $this->get_field_value( 'knowledge_type' );
583
+
584
+ return (string) $previous;
585
+ }
586
+
587
+ /**
588
+ * Returns left or right, for the separator location.
589
+ *
590
+ * @since 2.2.2
591
+ *
592
+ * @param mixed $new_value Should ideally be a string 'left' or 'right' passed in.
593
+ * @return string left or right
594
+ */
595
+ protected function s_left_right( $new_value ) {
596
+
597
+ if ( 'left' === $new_value || 'right' === $new_value )
598
+ return (string) $new_value;
599
+
600
+ $previous = $this->get_field_value( 'title_location' );
601
+
602
+ //* Fallback if previous is also empty.
603
+ if ( empty( $previous ) )
604
+ $previous = $this->get_default_option( 'title_location' );
605
+
606
+ return (string) $previous;
607
+ }
608
+
609
+ /**
610
+ * Returns left or right, for the home separator location.
611
+ *
612
+ * @since 2.5.2
613
+ *
614
+ * @param mixed $new_value Should ideally be a string 'left' or 'right' passed in.
615
+ * @return string left or right
616
+ */
617
+ protected function s_left_right_home( $new_value ) {
618
+
619
+ if ( 'left' === $new_value || 'right' === $new_value )
620
+ return (string) $new_value;
621
+
622
+ $previous = $this->get_field_value( 'home_title_location' );
623
+
624
+ //* Fallback if previous is also empty.
625
+ if ( empty( $previous ) )
626
+ $previous = $this->get_default_option( 'home_title_location' );
627
+
628
+ return (string) $previous;
629
+ }
630
+
631
+ /**
632
+ * Returns a 1 or 0, for all truthy / falsy values.
633
+ *
634
+ * Uses double casting. First, we cast to bool, then to integer.
635
+ *
636
+ * @since 2.2.2
637
+ *
638
+ * @param mixed $new_value Should ideally be a 1 or 0 integer passed in.
639
+ * @return integer 1 or 0.
640
+ */
641
+ protected function s_one_zero( $new_value ) {
642
+ return (int) (bool) $new_value;
643
+ }
644
+
645
+ /**
646
+ * Returns a 1 or 0, for all truthy / falsy values.
647
+ *
648
+ * Uses double casting. First, we cast to bool, then to integer.
649
+ *
650
+ * Also flushes rewrite rules.
651
+ *
652
+ * @since 2.2.9
653
+ *
654
+ * @param mixed $new_value Should ideally be a 1 or 0 integer passed in.
655
+ * @return integer 1 or 0.
656
+ */
657
+ protected function s_one_zero_flush_rewrite( $new_value ) {
658
+
659
+ /**
660
+ * Don't call functions anymore. Although it was after admin_init.
661
+ * It was too early for some plugins.
662
+ *
663
+ * @since 2.3.0
664
+ */
665
+ if ( (int) (bool) $new_value ) {
666
+ $this->enqueue_rewrite_activate( true );
667
+ } else {
668
+ $this->enqueue_rewrite_deactivate( true );
669
+ }
670
+
671
+ return (int) (bool) $new_value;
672
+ }
673
+
674
+ /**
675
+ * Returns a 1 or 0, for all truthy / falsy values.
676
+ * Uses double casting. First, we cast to bool, then to integer.
677
+ * Also flushes the sitemap.
678
+ *
679
+ * @since 2.2.9
680
+ * @staticvar bool $flushed
681
+ *
682
+ * @param mixed $new_value Should ideally be a 1 or 0 integer passed in.
683
+ * @return integer 1 or 0.
684
+ */
685
+ protected function s_one_zero_flush_sitemap( $new_value ) {
686
+
687
+ static $flushed = null;
688
+
689
+ if ( ! isset( $flushed ) )
690
+ $this->delete_sitemap_transient();
691
+
692
+ $flushed = true;
693
+
694
+ return (int) (bool) $new_value;
695
+ }
696
+
697
+ /**
698
+ * Returns a positive integer value.
699
+ *
700
+ * @since 2.2.2
701
+ *
702
+ * @param mixed $new_value Should ideally be a positive integer.
703
+ * @return integer Positive integer.
704
+ */
705
+ protected function s_absint( $new_value ) {
706
+ return absint( $new_value );
707
+ }
708
+
709
+ /**
710
+ * Removes HTML tags from string.
711
+ *
712
+ * @since 2.2.2
713
+ *
714
+ * @param string $new_value String, possibly with HTML in it.
715
+ * @return string String without HTML in it.
716
+ */
717
+ protected function s_no_html( $new_value ) {
718
+ return strip_tags( $new_value );
719
+ }
720
+
721
+ /**
722
+ * Removes HTML tags and line breaks from string.
723
+ *
724
+ * @since 2.5.2
725
+ *
726
+ * @param string $new_value String, possibly with HTML and spaces in it.
727
+ * @return string String without HTML and breaks in it.
728
+ */
729
+ protected function s_no_html_space( $new_value ) {
730
+ return str_replace( ' ', '', strip_tags( $new_value ) );
731
+ }
732
+
733
+ /**
734
+ * Makes URLs safe
735
+ *
736
+ * @since 2.2.2
737
+ *
738
+ * @param string $new_value String, a URL, possibly unsafe.
739
+ * @return string String a safe URL without Query Arguments.
740
+ */
741
+ protected function s_url( $new_value ) {
742
+
743
+ $this->delete_front_ld_json_transient();
744
+
745
+ /**
746
+ * If queries have been tokenized, take the value before the query args.
747
+ * Otherwise it's empty, so take the current value.
748
+ */
749
+ $no_query_url = strtok( $new_value, '?' );
750
+ $url = $no_query_url ? $no_query_url : $new_value;
751
+
752
+ return esc_url_raw( $url );
753
+ }
754
+
755
+ /**
756
+ * Makes URLs safe and removes query args.
757
+ *
758
+ * @since 2.2.8
759
+ *
760
+ * @param string $new_value String, a URL, possibly unsafe.
761
+ * @return string String a safe URL with Query Arguments.
762
+ */
763
+ protected function s_url_query( $new_value ) {
764
+
765
+ $this->delete_front_ld_json_transient();
766
+
767
+ return esc_url_raw( $new_value );
768
+ }
769
+
770
+ /**
771
+ * Makes Email Addresses safe, via sanitize_email()
772
+ *
773
+ * @since 2.2.2
774
+ *
775
+ * @param string $new_value String, an email address, possibly unsafe.
776
+ * @return string String a safe email address
777
+ */
778
+ protected function s_email_address( $new_value ) {
779
+ return sanitize_email( $new_value );
780
+ }
781
+
782
+ /**
783
+ * Removes unsafe HTML tags, via wp_kses_post().
784
+ *
785
+ * @since 2.2.2
786
+ *
787
+ * @param string $new_value String with potentially unsafe HTML in it.
788
+ * @return string String with only safe HTML in it
789
+ */
790
+ protected function s_safe_html( $new_value ) {
791
+ return wp_kses_post( $new_value );
792
+ }
793
+
794
+ /**
795
+ * Parses Twitter name and site. Adds @ if it wasn't supplied.
796
+ * Parses URL to path and adds @ if URL is given.
797
+ *
798
+ * @since 2.2.2
799
+ *
800
+ * @param string $new_value String with potentially wrong Twitter username.
801
+ * @return string String with 'correct' Twitter username
802
+ */
803
+ protected function s_twitter_name( $new_value ) {
804
+
805
+ if ( empty( $new_value ) )
806
+ return (string) $new_value;
807
+
808
+ $profile = trim( strip_tags( $new_value ) );
809
+
810
+ if ( 'http' === substr( $profile, 0, 4 ) ) {
811
+ $path = str_replace( '/', '', parse_url( $profile, PHP_URL_PATH ) );
812
+ $profile = $path ? '@' . $path : '';
813
+
814
+ return (string) $profile;
815
+ }
816
+
817
+ if ( '@' !== substr( $profile, 0, 1 ) ) {
818
+ $profile = '@' . $profile;
819
+ }
820
+
821
+ return (string) $profile;
822
+ }
823
+
824
+ /**
825
+ * Parses Twitter Card radio input. Fills in default if incorrect value is supplied.
826
+ * Falls back to previous value if empty. If previous value is empty if will go to default.
827
+ *
828
+ * @since 2.5.2
829
+ *
830
+ * @param string $new_value String with potentially wrong option value.
831
+ * @return string Sanitized twitter card type.
832
+ */
833
+ protected function s_twitter_card( $new_value ) {
834
+
835
+ //* Fetch Twitter card array.
836
+ $card = $this->get_twitter_card_types();
837
+
838
+ $key = array_key_exists( $new_value, $card );
839
+
840
+ if ( $key )
841
+ return (string) $new_value;
842
+
843
+ $previous = $this->get_field_value( 'twitter_card' );
844
+
845
+ if ( empty( $previous ) )
846
+ $previous = $this->get_default_option( 'twitter_card' );
847
+
848
+ return (string) $previous;
849
+ }
850
+
851
+ /**
852
+ * Converts full URL paths to absolute paths.
853
+ *
854
+ * Removes the http or https protocols and the domain. Keeps the path '/' at the
855
+ * beginning, so it isn't a true relative link, but from the web root base.
856
+ *
857
+ * @since 2.6.5
858
+ *
859
+ * @param string $url Full Path URL or relative URL.
860
+ * @return string Abolute path.
861
+ */
862
+ protected function s_relative_url( $url ) {
863
+ return ltrim( preg_replace( '|^(https?:)?//[^/]+(/.*)|i', '$2', $url ), ' \//' );
864
+ }
865
+
866
+ /**
867
+ * Sanitize the Redirect URL
868
+ *
869
+ * @since 2.2.4
870
+ *
871
+ * @param string $new_value String with potentially unwanted redirect URL.
872
+ * @return string The Sanitized Redirect URL
873
+ */
874
+ protected function s_redirect_url( $new_value ) {
875
+
876
+ $url = strip_tags( $new_value );
877
+
878
+ if ( $url ) {
879
+
880
+ $allow_external = $this->allow_external_redirect();
881
+
882
+ /**
883
+ * Sanitize the redirect URL to only a relative link and removes first slash
884
+ * @requires WP 4.1.0 and up to prevent adding upon itself.
885
+ */
886
+ if ( ! $allow_external )
887
+ $url = $this->s_relative_url( $url );
888
+
889
+ //* Find a path.
890
+ if ( _wp_can_use_pcre_u() ) {
891
+ //* URL pattern excluding path.
892
+ $pattern = '/'
893
+ . '((((http)(s)?)?)\:)?' // 1: maybe http: https:
894
+ . '(\/\/)?' // 2: maybe slash slash
895
+ . '((www.)?)' // 3: maybe www.
896
+ . '(.*\.[a-zA-Z0-9]*)' // 4: any legal domain with tld
897
+ . '(?:\/)?' // 5: trailing slash
898
+ . '/'
899
+ ;
900
+
901
+ $is_path = ! preg_match( $pattern, $url );
902
+ } else {
903
+ $parsed_url = parse_url( $url );
904
+
905
+ if ( ! isset( $parsed_url['host'] ) && isset( $parsed_url['path'] ) ) {
906
+ $is_path = true;
907
+ } else {
908
+ $is_path = false;
909
+ }
910
+ }
911
+
912
+ //* If link is relative, make it full again
913
+ if ( $is_path ) {
914
+
915
+ //* The url is a relative path
916
+ $path = $url;
917
+
918
+ //* Try WPMUdev Domain Mapping.
919
+ $wpmu_url = $this->the_url_wpmudev_domainmap( $path, true );
920
+ if ( $wpmu_url && is_array( $wpmu_url ) ) {
921
+ $url = $wpmu_url[0];
922
+ $scheme = $wpmu_url[1];
923
+ }
924
+
925
+ //* Try Donncha Domain Mapping.
926
+ if ( ! isset( $scheme ) ) {
927
+ $dm_url = $this->the_url_donncha_domainmap( $path, true );
928
+ if ( $dm_url && is_array( $dm_url ) ) {
929
+ $url = $dm_url[0];
930
+ $scheme = $dm_url[1];
931
+ }
932
+ }
933
+
934
+ //* Everything else.
935
+ if ( ! isset( $scheme ) ) {
936
+ $url = $this->the_home_url_from_cache( true ) . ltrim( $path, ' /' );
937
+ $scheme = is_ssl() ? 'https' : 'http';
938
+ }
939
+
940
+ //* When nothing is found, fall back on WP defaults (is_ssl).
941
+ $scheme = isset( $scheme ) ? $scheme : '';
942
+
943
+ $url = $this->set_url_scheme( $url, $scheme );
944
+ }
945
+ }
946
+
947
+ /**
948
+ * Applies filters the_seo_framework_301_noqueries : bool remove query args from 301
949
+ * @since 2.5.0
950
+ */
951
+ $noqueries = (bool) apply_filters( 'the_seo_framework_301_noqueries', true );
952
+
953
+ /**
954
+ * Remove queries from the URL
955
+ *
956
+ * Returns plain Home URL if $allow_external is set to false and only a query has been supplied
957
+ * But that's okay. The URL was rogue anyway :)
958
+ */
959
+ if ( $noqueries ) {
960
+ /**
961
+ * Remove query args
962
+ *
963
+ * @see AutoDescription_Sanitize::s_url
964
+ * @since 2.2.4
965
+ */
966
+ $new_value = $this->s_url( $url );
967
+ } else {
968
+ /**
969
+ * Allow query string parameters. XSS safe.
970
+ */
971
+ $new_value = esc_url_raw( $url );
972
+ }
973
+
974
+ //* Save url
975
+ return $new_value;
976
+ }
977
+
978
+ }
inc/classes/search.class.php ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The SEO Framework plugin
4
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License version 3 as published
8
+ * by the Free Software Foundation.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ /**
20
+ * Class AutoDescription_Search
21
+ *
22
+ * Excludes pages from search based on options
23
+ *
24
+ * @since 2.1.6
25
+ */
26
+ class AutoDescription_Search extends AutoDescription_Generate_Ldjson {
27
+
28
+ /**
29
+ * Constructor, load parent constructor
30
+ *
31
+ * Initalizes options
32
+ */
33
+ public function __construct() {
34
+ parent::__construct();
35
+
36
+ add_action( 'pre_get_posts', array( $this, 'search_filter' ), 999, 1 );
37
+ }
38
+
39
+ /**
40
+ * Fetches posts with exclude_local_search option on
41
+ *
42
+ * @param array $query The search query
43
+ *
44
+ * @uses $this->exclude_search_ids()
45
+ *
46
+ * @since 2.1.7
47
+ *
48
+ * @todo run this only when one post triggers this option?
49
+ * @todo priority low 2.7.0+
50
+ */
51
+ public function search_filter( $query ) {
52
+
53
+ // Don't exclude pages in wp-admin
54
+ if ( $query->is_search && false === $this->is_admin() ) {
55
+
56
+ $q = $query->query;
57
+ //* Only interact with an actual Search Query.
58
+ if ( ! isset( $q['s'] ) || ! $q['s'] )
59
+ return;
60
+
61
+ /**
62
+ * @param array $protected_posts : Posts array with excluded key
63
+ */
64
+ $protected_posts = $this->exclude_search_ids();
65
+ if ( $protected_posts ) {
66
+ $get = $query->get( 'post__not_in' );
67
+
68
+ //* Merge user defined query.
69
+ if ( $get )
70
+ $protected_posts = array_merge( $protected_posts, $get );
71
+
72
+ $query->set( 'post__not_in', $protected_posts );
73
+ }
74
+
75
+ // Parse all ID's, even beyond the first page.
76
+ $query->set( 'no_found_rows', false );
77
+
78
+ }
79
+
80
+ }
81
+
82
+ /**
83
+ * Fetches posts with exclude_local_search option on
84
+ *
85
+ * @param array $post_ids The post id's which are excluded
86
+ * @param array $args Posts search arguments
87
+ * @param array $protected_posts Posts array with excluded key
88
+ *
89
+ * @global int $blog_id
90
+ *
91
+ * @since 2.1.7
92
+ *
93
+ * @return array Excluded Post IDs
94
+ */
95
+ public function exclude_search_ids() {
96
+ global $blog_id;
97
+
98
+ $cache_key = 'exclude_search_ids_' . $blog_id;
99
+
100
+ $post_ids = $this->object_cache_get( $cache_key );
101
+ if ( false === $post_ids ) {
102
+ $post_ids = array();
103
+
104
+ $args = array(
105
+ 'post_type' => 'any',
106
+ 'meta_key' => 'exclude_local_search',
107
+ 'meta_value' => 1,
108
+ 'posts_per_page' => 99999, // get them all! Fast enough! :D
109
+ 'meta_compare' => '=',
110
+ /* 'post_status' => array('publish','private'),*/
111
+ );
112
+
113
+ $protected_posts = get_posts( $args );
114
+ if ( $protected_posts )
115
+ $post_ids = wp_list_pluck( $protected_posts, 'ID' );
116
+
117
+ $this->object_cache_set( $cache_key, $post_ids, 86400 );
118
+ }
119
+
120
+ // return an array of exclude post IDs
121
+ return $post_ids;
122
+ }
123
+
124
+ }
inc/classes/sitemaps.class.php ADDED
@@ -0,0 +1,980 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The SEO Framework plugin
4
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License version 3 as published
8
+ * by the Free Software Foundation.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ /**
20
+ * Class AutoDescription_Search
21
+ *
22
+ * Generates sitemap and outputs it.
23
+ *
24
+ * @since 2.2.9
25
+ */
26
+ class AutoDescription_Sitemaps extends AutoDescription_Metaboxes {
27
+
28
+ /**
29
+ * Maximum number of posts that show up in the sitemap.xml page.
30
+ *
31
+ * @since 2.2.9
32
+ *
33
+ * @var int Max Posts in Sitemap
34
+ */
35
+ protected $max_posts;
36
+
37
+ /**
38
+ * Checks for pretty permalinks.
39
+ *
40
+ * @since 2.2.9
41
+ *
42
+ * @var bool true if pretty
43
+ */
44
+ protected $pretty_permalinks;
45
+
46
+ /**
47
+ * Checks if sitemap is being output.
48
+ *
49
+ * @since 2.5.2
50
+ *
51
+ * @var bool true if sitemap is being output.
52
+ */
53
+ protected $doing_sitemap = false;
54
+
55
+ /**
56
+ * Constructor, load parent constructor and set up caches.
57
+ */
58
+ public function __construct() {
59
+ parent::__construct();
60
+
61
+ // I'm not going to initialize my own rewrite engine. Causes too many problems.
62
+ $this->pretty_permalinks = ( '' !== $this->permalink_structure() ) ? true : false;
63
+
64
+ /**
65
+ * Add query strings to rewrite
66
+ */
67
+ add_action( 'init', array( $this, 'rewrite_rule_sitemap' ), 1 );
68
+ add_filter( 'query_vars', array( $this, 'enqueue_sitemap_query_vars' ), 1 );
69
+
70
+ /**
71
+ * Adding a higher priority will cause a trailing slash to be added.
72
+ * We need to be in front of the queue to prevent this from happening.
73
+ */
74
+ add_action( 'template_redirect', array( $this, 'maybe_output_sitemap' ), 1 );
75
+
76
+ //* Edit the robots.txt file
77
+ add_filter( 'robots_txt', array( $this, 'robots_txt' ), 10, 2 );
78
+
79
+ //* Enqueue rewrite flush
80
+ add_action( 'shutdown', array( $this, 'maybe_flush_rewrite' ), 999 );
81
+ }
82
+
83
+ /**
84
+ * Whether we can output sitemap or not based on options.
85
+ *
86
+ * @staticvar bool $cache
87
+ * @since 2.6.0
88
+ *
89
+ * @return bool
90
+ */
91
+ public function can_run_sitemap() {
92
+
93
+ static $cache = null;
94
+
95
+ if ( isset( $cache ) )
96
+ return $cache;
97
+
98
+ /**
99
+ * Don't do anything on a deleted or spam blog.
100
+ * There's nothing to find anyway. Multisite Only.
101
+ */
102
+ return $cache = $this->pretty_permalinks && $this->is_option_checked( 'sitemaps_output' ) && false === $this->current_blog_is_spam_or_deleted() ? true : false;
103
+ }
104
+
105
+ /**
106
+ * Adds rewrite rule to WordPress
107
+ * This rule defines the sitemap.xml output
108
+ *
109
+ * @param bool $override add the rule anyway, regardless of setting.
110
+ *
111
+ * @since 2.2.9
112
+ */
113
+ public function rewrite_rule_sitemap( $run = false ) {
114
+
115
+ //* Adding rewrite rules only has effect when permalink structures are active.
116
+ if ( $this->can_run_sitemap() || $run ) {
117
+
118
+ /**
119
+ * Don't do anything if a sitemap plugin is active.
120
+ * On sitemap plugin activation, the sitemap plugin should flush the
121
+ * rewrite rules. If it doesn't, then this plugin's sitemap will be called.
122
+ */
123
+ if ( $this->detect_sitemap_plugin() )
124
+ return;
125
+
126
+ add_rewrite_rule( 'sitemap\.xml$', 'index.php?the_seo_framework_sitemap=xml', 'top' );
127
+ }
128
+
129
+ }
130
+
131
+ /**
132
+ * Register the_seo_framework_sitemap to wp_query
133
+ *
134
+ * @param array vars The WP_Query vars
135
+ *
136
+ * @since 2.2.9
137
+ */
138
+ public function enqueue_sitemap_query_vars( $vars ) {
139
+
140
+ if ( $this->can_run_sitemap() )
141
+ $vars[] = 'the_seo_framework_sitemap';
142
+
143
+ return $vars;
144
+ }
145
+
146
+ /**
147
+ * Maybe Output sitemap.xml 'file' and header.
148
+ *
149
+ * @since 2.2.9
150
+ *
151
+ * @return void|header+string SiteMap XML file.
152
+ */
153
+ public function maybe_output_sitemap() {
154
+
155
+ if ( $this->can_run_sitemap() ) {
156
+ global $wp_query;
157
+
158
+ if ( isset( $wp_query->query_vars['the_seo_framework_sitemap'] ) && 'xml' === $wp_query->query_vars['the_seo_framework_sitemap'] ) {
159
+ // Don't let WordPress think this is 404.
160
+ $wp_query->is_404 = false;
161
+
162
+ $this->doing_sitemap = true;
163
+
164
+ /**
165
+ * Applies filters 'the_seo_framework_sitemap_post_limit' : int
166
+ * @since 2.2.9
167
+ */
168
+ $this->max_posts = (int) apply_filters( 'the_seo_framework_sitemap_post_limit', 700 );
169
+
170
+ /**
171
+ * Set at least 2000 variables free.
172
+ * Freeing 0.15MB on a clean WordPress installation.
173
+ * @since 2.6.0
174
+ */
175
+ $this->clean_up_globals();
176
+
177
+ $this->output_sitemap();
178
+ }
179
+ }
180
+
181
+ }
182
+
183
+ /**
184
+ * Destroy unused $GLOBALS.
185
+ *
186
+ * @param bool $get_freed_memory Whether to return the freed memory in bytes.
187
+ *
188
+ * @since 2.6.0
189
+ *
190
+ * @return int $freed_memory
191
+ */
192
+ protected function clean_up_globals( $get_freed_memory = false ) {
193
+
194
+ static $freed_memory = null;
195
+
196
+ if ( $get_freed_memory )
197
+ return $freed_memory;
198
+
199
+ if ( $this->the_seo_framework_debug ) $memory = memory_get_usage();
200
+
201
+ $remove = array(
202
+ 'wp_filter' => array(
203
+ 'wp_head',
204
+ 'admin_head',
205
+ 'the_content',
206
+ 'the_content_feed',
207
+ 'the_excerpt_rss',
208
+ 'wp_footer',
209
+ 'admin_footer',
210
+ ),
211
+ 'wp_registered_widgets',
212
+ 'wp_registered_sidebars',
213
+ 'wp_registered_widget_updates',
214
+ 'wp_registered_widget_controls',
215
+ '_wp_deprecated_widgets_callbacks',
216
+ 'posts',
217
+ 'shortcode_tags',
218
+ );
219
+
220
+ foreach ( $remove as $key => $value ) {
221
+ if ( is_array( $value ) ) {
222
+ foreach ( $value as $v )
223
+ unset( $GLOBALS[$key][$v] );
224
+ } else {
225
+ unset( $GLOBALS[$value] );
226
+ }
227
+ }
228
+
229
+ if ( $this->the_seo_framework_debug ) $freed_memory = $memory - memory_get_usage();
230
+
231
+ }
232
+
233
+ /**
234
+ * Output sitemap.xml 'file' and header.
235
+ *
236
+ * @since 2.2.9
237
+ */
238
+ protected function output_sitemap() {
239
+
240
+ if ( ! headers_sent() )
241
+ header( 'Content-type: text/xml; charset=utf-8' );
242
+
243
+ //* Remove output, if any.
244
+ if ( ob_get_level() > 0 ) {
245
+ if ( ob_get_contents() )
246
+ ob_clean();
247
+ }
248
+
249
+ //* Fetch sitemap content.
250
+ $xml_content = $this->get_sitemap_content();
251
+
252
+ //* Echo and add trailing line.
253
+ echo $xml_content . "\r\n";
254
+
255
+ // We're done now.
256
+ die();
257
+ }
258
+
259
+ /**
260
+ * Output sitemap.xml content from transient.
261
+ *
262
+ * @since 2.2.9
263
+ *
264
+ * @return string Sitemap XML contents.
265
+ */
266
+ protected function get_sitemap_content() {
267
+
268
+ if ( $this->the_seo_framework_debug ) $timer_start = microtime( true );
269
+
270
+ /**
271
+ * Re-use the variable, eliminating database requests
272
+ * @since 2.4.0
273
+ */
274
+ $sitemap_content = $this->get_transient( $this->sitemap_transient );
275
+
276
+ if ( false === $sitemap_content ) {
277
+ $cached_content = "\r\n<!-- " . __( 'Sitemap is generated for this view', 'autodescription' ) . " -->";
278
+ } else {
279
+ $cached_content = "\r\n<!-- " . __( 'Sitemap is served from cache', 'autodescription' ) . " -->";
280
+ }
281
+
282
+ $content = '<?xml version="1.0" encoding="UTF-8"?>' . "\r\n";
283
+ $content .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\r\n";
284
+ $content .= $this->setup_sitemap( $sitemap_content );
285
+ $content .= '</urlset>';
286
+
287
+ $content .= $cached_content;
288
+
289
+ /**
290
+ * Output debug info.
291
+ * @since 2.3.7
292
+ */
293
+ if ( $this->the_seo_framework_debug ) {
294
+ $content .= "\r\n<!-- Site estimated peak usage: " . ( memory_get_peak_usage() / 1024 / 1024 ) . " MB -->";
295
+ $content .= "\r\n<!-- System estimated peak usage: " . ( memory_get_peak_usage( true ) / 1024 / 1024 ) . " MB -->";
296
+ $content .= "\r\n<!-- Freed memory prior to generation: " . $this->clean_up_globals( true ) / 1024 . " kB -->";
297
+ $content .= "\r\n<!-- Sitemap generation time: " . ( number_format( microtime( true ) - $timer_start, 6 ) ) . " seconds -->";
298
+ }
299
+
300
+ return $content;
301
+ }
302
+
303
+ /**
304
+ * Create sitemap.xml content transient.
305
+ *
306
+ * @param string|bool $content required The sitemap transient content.
307
+ *
308
+ * @since 2.6.0
309
+ */
310
+ public function setup_sitemap( $sitemap_content ) {
311
+
312
+ if ( false === $sitemap_content ) {
313
+ //* Transient doesn't exist yet.
314
+ $sitemap_content = $this->generate_sitemap();
315
+
316
+ /**
317
+ * Transient expiration: 1 week.
318
+ * Keep the sitemap for at most 1 week.
319
+ */
320
+ $expiration = WEEK_IN_SECONDS;
321
+
322
+ $this->set_transient( $this->sitemap_transient, $sitemap_content, $expiration );
323
+ }
324
+
325
+ return $sitemap_content;
326
+ }
327
+
328
+ /**
329
+ * Generate sitemap.xml content.
330
+ *
331
+ * @param bool $secure Only process when param is given.
332
+ *
333
+ * @since 2.2.9
334
+ */
335
+ protected function generate_sitemap() {
336
+
337
+ $content = '';
338
+
339
+ /**
340
+ * Applies filters the_seo_framework_sitemap_exclude_ids : array of id's
341
+ *
342
+ * @since 2.5.2
343
+ */
344
+ $excluded = (array) apply_filters( 'the_seo_framework_sitemap_exclude_ids', array() );
345
+
346
+ if ( empty( $excluded ) ) {
347
+ $excluded = '';
348
+ } else {
349
+ $excluded = array_flip( $excluded );
350
+ }
351
+
352
+ /**
353
+ * Maximum pages and posts to fetch.
354
+ * A total of 2100, consisting of 3 times $max_posts
355
+ *
356
+ * Applies filters the_seo_framework_sitemap_pages_count : int max pages
357
+ * Applies filters the_seo_framework_sitemap_posts_count : int max posts
358
+ * Applies filters the_seo_framework_sitemap_custom_posts_count : int max posts
359
+ */
360
+ $totalpages = (int) apply_filters( 'the_seo_framework_sitemap_pages_count', $this->max_posts );
361
+ $totalposts = (int) apply_filters( 'the_seo_framework_sitemap_posts_count', $this->max_posts );
362
+ $total_cpt_posts = (int) apply_filters( 'the_seo_framework_sitemap_custom_posts_count', $this->max_posts );
363
+
364
+ $latest_pages = array();
365
+ $latest_posts = array();
366
+ $latest_cpt_posts = array();
367
+ $cpt = array();
368
+
369
+ //* Sets timezone according to WordPress settings.
370
+ $this->set_timezone();
371
+ $timestamp_format = $this->get_option( 'sitemap_timestamps' );
372
+ $timestamp_format = '1' === $timestamp_format ? 'Y-m-d\TH:iP' : 'Y-m-d';
373
+
374
+ /**
375
+ * Fetch the page/post modified options.
376
+ * We can't get specific on the home page, unfortunately.
377
+ */
378
+ $sitemaps_modified = $this->is_option_checked( 'sitemaps_modified' );
379
+ if ( $sitemaps_modified ) {
380
+ $page_lastmod = true;
381
+ $post_lastmod = true;
382
+ $home_lastmod = true;
383
+ } else {
384
+ $page_lastmod = $this->is_option_checked( 'page_modify_time' );
385
+ $post_lastmod = $this->is_option_checked( 'post_modify_time' );
386
+ $home_lastmod = $page_lastmod ? $page_lastmod : $this->is_option_checked( 'home_modify_time' );
387
+ }
388
+
389
+ /**
390
+ * Generation time output
391
+ *
392
+ * Applies filter the_seo_framework_sitemap_timestamp : bool
393
+ */
394
+ $timestamp = (bool) apply_filters( 'the_seo_framework_sitemap_timestamp', true );
395
+
396
+ if ( $timestamp )
397
+ $content .= '<!-- ' . __( 'Sitemap is generated on', 'autodescription' ) . ' ' . current_time( "Y-m-d H:i:s" ) . ' -->' . "\r\n";
398
+
399
+ if ( $totalpages ) {
400
+ //* Ascend by the date for normal pages. Older pages get to the top of the list.
401
+ $args = array(
402
+ 'numberposts' => $totalpages,
403
+ 'posts_per_page' => $totalpages,
404
+ 'post_type' => 'page',
405
+ 'orderby' => 'date',
406
+ 'order' => 'ASC',
407
+ 'post_status' => 'publish',
408
+ 'cache_results' => false,
409
+ );
410
+ $latest_pages = get_posts( $args );
411
+ }
412
+ $latest_pages_amount = (int) count( $latest_pages );
413
+
414
+ if ( $latest_pages_amount > 0 ) {
415
+
416
+ $id_on_front = $this->has_page_on_front() ? (int) get_option( 'page_on_front' ) : (int) get_option( 'page_for_posts' );
417
+
418
+ /**
419
+ * This can be heavy.
420
+ */
421
+ foreach ( $latest_pages as $page ) {
422
+ if ( isset( $page->ID ) ) {
423
+ $page_id = $page->ID;
424
+
425
+ if ( '' === $excluded || ! isset( $excluded[$post_id] ) ) {
426
+ //* Is this the front page?
427
+ $page_is_front = ( $page_id === $id_on_front ) ? true : false;
428
+
429
+ //* Fetch the noindex option, per page.
430
+ $noindex = (bool) $this->get_custom_field( '_genesis_noindex', $page_id );
431
+
432
+ //* Continue if indexed.
433
+ if ( false === $noindex ) {
434
+ $content .= " <url>\r\n";
435
+ if ( $page_is_front ) {
436
+ $content .= ' <loc>' . $this->the_url( '', array( 'get_custom_field' => false, 'external' => true, 'home' => true ) ) . "</loc>\r\n";
437
+ } else {
438
+ $content .= ' <loc>' . $this->the_url( '', array( 'get_custom_field' => false, 'external' => true, 'post' => $page, 'id' => $page_id ) ) . "</loc>\r\n";
439
+ }
440
+
441
+ // Keep it consistent. Only parse if page_lastmod is true.
442
+ if ( $page_lastmod || ( $page_is_front && $home_lastmod ) ) {
443
+ $page_modified_gmt = $page->post_modified_gmt;
444
+
445
+ if ( $page_modified_gmt !== '0000-00-00 00:00:00' )
446
+ $content .= ' <lastmod>' . mysql2date( $timestamp_format, $page_modified_gmt, false ) . "</lastmod>\r\n";
447
+ }
448
+
449
+ // Give higher priority to the home page.
450
+ $priority_page = $page_is_front ? 1 : 0.9;
451
+
452
+ $content .= ' <priority>' . number_format( $priority_page, 1 ) . "</priority>\r\n";
453
+ $content .= " </url>\r\n";
454
+ }
455
+ }
456
+ }
457
+ }
458
+
459
+ //* Free memory.
460
+ unset( $latest_pages );
461
+ }
462
+
463
+ if ( $totalposts ) {
464
+ //* Descend by the date for posts. The latest posts get to the top of the list after pages.
465
+ $args = array(
466
+ 'numberposts' => $totalposts,
467
+ 'posts_per_page' => $totalposts,
468
+ 'post_type' => 'post',
469
+ 'orderby' => 'date',
470
+ 'order' => 'DESC',
471
+ 'post_status' => 'publish',
472
+ 'cache_results' => false,
473
+ );
474
+ $latest_posts = get_posts( $args );
475
+ }
476
+ $latest_posts_amount = (int) count( $latest_posts );
477
+
478
+ if ( $latest_posts_amount > 0 ) {
479
+
480
+ /**
481
+ * Setting up priorities, with pages always being important.
482
+ *
483
+ * From there, older posts get a gradually lower priority. Down to 0.
484
+ * Differentiate with 1 / max posts (0 to $this->max_posts). With a 1 dot decimal.
485
+ */
486
+ $priority = 0.9;
487
+
488
+ /**
489
+ * Infinity is abstract. But what is it when it's both positive and negative?
490
+ * Undefined. Bugfix.
491
+ *
492
+ * @since 2.3.2
493
+ * @thanks Schlock | https://wordpress.org/support/topic/sitemap-xml-parsing-error
494
+ */
495
+ $prioritydiff = 0;
496
+
497
+ if ( $latest_posts_amount > (int) 1 )
498
+ $prioritydiff = 0.9 / $latest_posts_amount;
499
+
500
+ // Keep it consistent. Only remove 0.1 when we only have a few posts.
501
+ if ( $latest_posts_amount <= (int) 9 && $latest_posts_amount > (int) 1 )
502
+ $prioritydiff = 0.1;
503
+
504
+ /**
505
+ * This can be heavy.
506
+ */
507
+ foreach ( $latest_posts as $post ) {
508
+ if ( isset( $post->ID ) ) {
509
+ $post_id = $post->ID;
510
+
511
+ if ( '' === $excluded || ! isset( $excluded[$post_id] ) ) {
512
+
513
+ //* Fetch the noindex option, per page.
514
+ $noindex = (bool) $this->get_custom_field( '_genesis_noindex', $post_id );
515
+
516
+ //* Continue if indexed
517
+ if ( ! $noindex ) {
518
+
519
+ $content .= " <url>\r\n";
520
+ // No need to use static vars
521
+ $content .= ' <loc>' . $this->the_url( '', array( 'get_custom_field' => false, 'external' => true, 'post' => $post, 'id' => $post_id ) ) . "</loc>\r\n";
522
+
523
+ // Keep it consistent. Only parse if page_lastmod is true.
524
+ if ( $post_lastmod ) {
525
+ $post_modified_gmt = $post->post_modified_gmt;
526
+
527
+ if ( $post_modified_gmt !== '0000-00-00 00:00:00' )
528
+ $content .= ' <lastmod>' . mysql2date( $timestamp_format, $post_modified_gmt, false ) . "</lastmod>\r\n";
529
+ }
530
+
531
+ $content .= ' <priority>' . number_format( $priority, 1 ) . "</priority>\r\n";
532
+ $content .= " </url>\r\n";
533
+
534
+ // Lower the priority for the next pass.
535
+ $priority = $priority - $prioritydiff;
536
+
537
+ // Cast away negative numbers.
538
+ $priority = $priority <= (int) 0 ? (int) 0 : (float) $priority;
539
+ }
540
+ }
541
+ }
542
+ }
543
+
544
+ //* Free memory.
545
+ unset( $latest_posts );
546
+ }
547
+
548
+ if ( $total_cpt_posts ) {
549
+ $post_page = (array) get_post_types( array( 'public' => true ) );
550
+
551
+ /**
552
+ * Applies filters Array the_seo_framework_sitemap_exclude_cpt : Excludes these CPT
553
+ * @since 2.5.0
554
+ */
555
+ $excluded_cpt = (array) apply_filters( 'the_seo_framework_sitemap_exclude_cpt', array() );
556
+
557
+ $not_cpt = array( 'post', 'page', 'attachment' );
558
+
559
+ foreach ( $post_page as $post_type ) {
560
+ if ( false === in_array( $post_type, $not_cpt ) ) {
561
+ if ( empty( $excluded_cpt ) || false === in_array( $post_type, $excluded_cpt ) ) {
562
+ if ( $this->post_type_supports_custom_seo( $post_type ) ) {
563
+ $cpt[] = $post_type;
564
+ }
565
+ }
566
+ }
567
+ }
568
+
569
+ if ( $cpt ) {
570
+ //* Descend by the date for CPTs. The latest posts get to the top of the list after pages.
571
+ $args = array(
572
+ 'numberposts' => $total_cpt_posts,
573
+ 'posts_per_page' => $total_cpt_posts,
574
+ 'post_type' => $cpt,
575
+ 'orderby' => 'date',
576
+ 'order' => 'DESC',
577
+ 'post_status' => 'publish',
578
+ 'cache_results' => false,
579
+ );
580
+ $latest_cpt_posts = get_posts( $args );
581
+ }
582
+ }
583
+ $latest_cpt_posts_amount = (int) count( $latest_cpt_posts );
584
+
585
+ if ( $latest_cpt_posts_amount > 0 ) {
586
+
587
+ /**
588
+ * Setting up priorities, with pages always being important.
589
+ *
590
+ * From there, older posts get a gradually lower priority. Down to 0.
591
+ * Differentiate with 1 / max posts (0 to $this->max_posts). With a 1 dot decimal.
592
+ */
593
+ $priority_cpt = 0.9;
594
+
595
+ $prioritydiff_cpt = 0;
596
+
597
+ if ( $latest_cpt_posts_amount > (int) 1 )
598
+ $prioritydiff_cpt = 0.9 / $latest_cpt_posts_amount;
599
+
600
+ // Keep it consistent. Only remove 0.1 when we only have a few posts.
601
+ if ( $latest_cpt_posts_amount <= (int) 9 && $latest_cpt_posts_amount > (int) 1 )
602
+ $prioritydiff_cpt = 0.1;
603
+
604
+ /**
605
+ * This can be heavy.
606
+ */
607
+ foreach ( $latest_cpt_posts as $ctp_post ) {
608
+ if ( isset( $ctp_post->ID ) ) {
609
+ $post_id = $ctp_post->ID;
610
+
611
+ if ( '' === $excluded || ! isset( $excluded[$post_id] ) ) {
612
+
613
+ //* Fetch the noindex option, per page.
614
+ $noindex = (bool) $this->get_custom_field( '_genesis_noindex', $post_id );
615
+
616
+ //* Continue if indexed
617
+ if ( ! $noindex ) {
618
+
619
+ $content .= " <url>\r\n";
620
+ //* No need to use static vars
621
+ $content .= ' <loc>' . $this->the_url( '', array( 'get_custom_field' => false, 'external' => true, 'post' => $ctp_post, 'id' => $post_id ) ) . "</loc>\r\n";
622
+
623
+ //* Keep it consistent. Only parse if page_lastmod is true.
624
+ if ( $post_lastmod ) {
625
+ $post_modified_gmt = $ctp_post->post_modified_gmt;
626
+
627
+ //* Some CPT don't set modified time.
628
+ if ( $post_modified_gmt !== '0000-00-00 00:00:00' )
629
+ $content .= ' <lastmod>' . mysql2date( $timestamp_format, $post_modified_gmt, false ) . "</lastmod>\r\n";
630
+ }
631
+
632
+ $content .= ' <priority>' . number_format( $priority_cpt, 1 ) . "</priority>\r\n";
633
+ $content .= " </url>\r\n";
634
+
635
+ // Lower the priority for the next pass.
636
+ $priority_cpt = $priority_cpt - $prioritydiff_cpt;
637
+
638
+ // Cast away negative numbers.
639
+ $priority_cpt = $priority_cpt <= (int) 0 ? (int) 0 : (float) $priority_cpt;
640
+ }
641
+ }
642
+ }
643
+ }
644
+
645
+ //* Free memory.
646
+ unset( $latest_cpt_posts );
647
+ }
648
+
649
+ /**
650
+ * Applies filters the_seo_framework_sitemap_additional_urls : {
651
+ * @param string url The absolute url to the page. : {
652
+ * @param string lastmod UNIXTIME Last modified date, e.g. "2016-01-26 13:04:55"
653
+ * @param float|int|string priority URL Priority
654
+ * }
655
+ * }
656
+ *
657
+ * @since 2.5.2
658
+ */
659
+ $custom_urls = (array) apply_filters( 'the_seo_framework_sitemap_additional_urls', array() );
660
+
661
+ if ( $custom_urls ) {
662
+ foreach ( $custom_urls as $url => $args ) {
663
+
664
+ if ( ! is_array( $args ) ) {
665
+ //* If there are no args, it's assigned as URL (per example)
666
+ $url = $args;
667
+ }
668
+
669
+ $content .= " <url>\r\n";
670
+ //* No need to use static vars
671
+ $content .= ' <loc>' . esc_url_raw( $url ) . "</loc>\r\n";
672
+
673
+ if ( isset( $args['lastmod'] ) && $args['lastmod'] ) {
674
+ $content .= ' <lastmod>' . mysql2date( $timestamp_format, $args['lastmod'], false ) . "</lastmod>\r\n";
675
+ }
676
+
677
+ if ( isset( $args['priority'] ) && $args['priority'] ) {
678
+ $priority = $args['priority'];
679
+ } else {
680
+ $priority = 0.9;
681
+ }
682
+
683
+ $content .= ' <priority>' . number_format( $priority, 1 ) . "</priority>\r\n";
684
+ $content .= " </url>\r\n";
685
+ }
686
+ }
687
+
688
+ /**
689
+ * Applies filters the_seo_framework_sitemap_extend : string
690
+ * @since 2.5.2
691
+ */
692
+ $extend = (string) apply_filters( 'the_seo_framework_sitemap_extend', '' );
693
+
694
+ if ( $extend )
695
+ $content .= "\t" . $extend . "\r\n";
696
+
697
+ //* Reset timezone to default.
698
+ $this->reset_timezone();
699
+
700
+ return $content;
701
+ }
702
+
703
+ /**
704
+ * Ping search engines on post publish.
705
+ *
706
+ * @since 2.2.9
707
+ */
708
+ public function ping_searchengines() {
709
+
710
+ /**
711
+ * Don't ping if the blog isn't public.
712
+ *
713
+ * @since 2.3.1
714
+ */
715
+ if ( false === $this->is_option_checked( 'site_noindex' ) && $this->is_blog_public() ) {
716
+ global $blog_id;
717
+
718
+ $blog_id = (string) $blog_id;
719
+
720
+ $transient = 'tsf_throttle_ping_' . $blog_id;
721
+
722
+ //* NOTE: Use legacy get_transient to prevent ping spam.
723
+ if ( false === get_transient( $transient ) ) {
724
+ //* Transient doesn't exist yet.
725
+
726
+ if ( $this->is_option_checked( 'ping_google' ) )
727
+ $this->ping_google();
728
+
729
+ if ( $this->is_option_checked( 'ping_bing' ) )
730
+ $this->ping_bing();
731
+
732
+ if ( $this->is_option_checked( 'ping_yandex' ) )
733
+ $this->ping_yandex();
734
+
735
+ // Sorry I couldn't help myself.
736
+ $throttle = 'Bert and Ernie are weird.';
737
+
738
+ /**
739
+ * Limit the pinging to a maximum of 1 per hour.
740
+ * Transient expiration. 1 hour.
741
+ *
742
+ * Applies filters the_seo_framework_sitemap_throttle_s
743
+ * @since 2.5.1
744
+ */
745
+ $expiration = (int) apply_filters( 'the_seo_framework_sitemap_throttle_s', HOUR_IN_SECONDS );
746
+
747
+ //* @NOTE: Using legacy set_transient to prevent ping spam.
748
+ set_transient( $transient, $throttle, $expiration );
749
+ }
750
+ }
751
+
752
+ }
753
+
754
+ /**
755
+ * Ping Google
756
+ *
757
+ * @since 2.2.9
758
+ */
759
+ public function ping_google() {
760
+
761
+ $pingurl = 'http://www.google.com/webmasters/sitemaps/ping?sitemap=' . urlencode( $this->the_home_url_from_cache( true ) . 'sitemap.xml' );
762
+ wp_remote_get( $pingurl, array( 'timeout' => 3 ) );
763
+
764
+ }
765
+
766
+ /**
767
+ * Ping Bing
768
+ *
769
+ * @since 2.2.9
770
+ */
771
+ public function ping_bing() {
772
+
773
+ $pingurl = 'http://www.bing.com/webmaster/ping.aspx?siteMap=' . urlencode( $this->the_home_url_from_cache( true ) . 'sitemap.xml' );
774
+ wp_remote_get( $pingurl, array( 'timeout' => 3 ) );
775
+
776
+ }
777
+
778
+ /**
779
+ * Ping Yandex
780
+ *
781
+ * @since 2.6.0
782
+ */
783
+ public function ping_yandex() {
784
+
785
+ $pingurl = 'http://blogs.yandex.ru/pings/?status=success&url=' . urlencode( $this->the_home_url_from_cache( true ) . 'sitemap.xml' );
786
+ wp_remote_get( $pingurl, array( 'timeout' => 3 ) );
787
+
788
+ }
789
+
790
+ /**
791
+ * Edits the robots.txt output
792
+ *
793
+ * Requires not to have a robots.txt file in the root directory
794
+ *
795
+ * @uses robots_txt filter located at WP core
796
+ *
797
+ * @since 2.2.9
798
+ *
799
+ * @global int $blog_id;
800
+ *
801
+ * @todo maybe combine with noindex/noarchive/(nofollow) -> only when object caching?
802
+ */
803
+ public function robots_txt( $robots_txt = '', $public = '' ) {
804
+ global $blog_id;
805
+
806
+ /**
807
+ * Don't do anything if the blog isn't public
808
+ */
809
+ if ( '0' === $public )
810
+ return $robots_txt;
811
+
812
+ $revision = '1';
813
+
814
+ $cache_key = 'robots_txt_output_' . $revision . $blog_id;
815
+
816
+ $output = $this->object_cache_get( $cache_key );
817
+ if ( false === $output ) {
818
+ $output = '';
819
+
820
+ /**
821
+ * Apply filters the_seo_framework_robots_txt_pre & the_seo_framework_robots_txt_pro
822
+ * : Add custom cacheable lines.
823
+ * : Don't forget to add line breaks ( "\r\n" | PHP_EOL )
824
+ *
825
+ * @since 2.5.0
826
+ */
827
+ $pre = (string) apply_filters( 'the_seo_framework_robots_txt_pre', '' );
828
+ $pro = (string) apply_filters( 'the_seo_framework_robots_txt_pro', '' );
829
+
830
+ $site_url = parse_url( site_url() );
831
+ $path = ( ! empty( $site_url['path'] ) ) ? $site_url['path'] : '';
832
+
833
+ $output .= $pre;
834
+ //* Output defaults
835
+ $output .= "User-agent: *\r\n";
836
+ $output .= "Disallow: $path/wp-admin/\r\n";
837
+ $output .= "Allow: $path/wp-admin/admin-ajax.php\r\n";
838
+
839
+ /**
840
+ * Prevents query indexing
841
+ * @since 2.2.9
842
+ *
843
+ * Applies filters the_seo_framework_robots_disallow_queries : Whether to allow queries for robots.
844
+ * @since 2.5.0
845
+ */
846
+ if ( apply_filters( 'the_seo_framework_robots_disallow_queries', false ) ) {
847
+ $home_url = parse_url( rtrim( $this->the_home_url_from_cache(), ' /\\' ) );
848
+ $home_path = ( ! empty( $home_url['path'] ) ) ? $home_url['path'] : '';
849
+ $output .= "Disallow: $home_path/*?*\r\n";
850
+ }
851
+
852
+ $output .= $pro;
853
+
854
+ if ( $this->get_option( 'sitemaps_robots' ) && $this->can_do_sitemap_robots() ) {
855
+ //* Add whitespace before sitemap.
856
+ $output .= "\r\n";
857
+
858
+ //* Add sitemap full url
859
+ $output .= 'Sitemap: ' . $this->the_home_url_from_cache( true ) . "sitemap.xml\r\n";
860
+ }
861
+
862
+ $this->object_cache_set( $cache_key, $output, 86400 );
863
+ }
864
+
865
+ /**
866
+ * Completely override robots with output.
867
+ * @since 2.5.0
868
+ */
869
+ $robots_txt = $output;
870
+
871
+ return $robots_txt;
872
+ }
873
+
874
+ /**
875
+ * Initialize and flush rewrite rules.
876
+ *
877
+ * @since 2.6.0
878
+ * @access private
879
+ */
880
+ public function flush_rewrite_rules() {
881
+ global $wp_rewrite;
882
+
883
+ $this->rewrite_rule_sitemap();
884
+
885
+ $wp_rewrite->init();
886
+ $wp_rewrite->flush_rules( true );
887
+ }
888
+
889
+ /**
890
+ * Enqueue rewrite flush for activation.
891
+ *
892
+ * @since 2.3.0
893
+ * @access private
894
+ * @staticvar bool $flush Only true
895
+ *
896
+ * @param bool $enqueue Whether to enqueue the flush or return its state.
897
+ *
898
+ * @return bool Whether to flush.
899
+ */
900
+ public function enqueue_rewrite_activate( $enqueue = false ) {
901
+
902
+ static $flush = null;
903
+
904
+ if ( isset( $flush ) )
905
+ return $flush;
906
+
907
+ if ( $enqueue )
908
+ return $flush = true;
909
+
910
+ return false;
911
+ }
912
+
913
+ /**
914
+ * Enqueue rewrite flush for deactivation.
915
+ *
916
+ * @since 2.3.0
917
+ * @access private
918
+ * @staticvar bool $flush Only true
919
+ *
920
+ * @param bool $enqueue Whether to enqueue the flush or return its state.
921
+ *
922
+ * @return bool Whether to flush.
923
+ */
924
+ public function enqueue_rewrite_deactivate( $enqueue = false ) {
925
+
926
+ static $flush = null;
927
+
928
+ if ( isset( $flush ) )
929
+ return $flush;
930
+
931
+ if ( $enqueue )
932
+ return $flush = true;
933
+
934
+ return false;
935
+ }
936
+
937
+ /**
938
+ * Enqueue rewrite flush for deactivation.
939
+ *
940
+ * @since 2.6.0
941
+ * @access private
942
+ * @staticvar bool $flush Only true
943
+ *
944
+ * @param bool $enqueue Whether to enqueue the flush or return its state.
945
+ *
946
+ * @return bool Whether to flush.
947
+ */
948
+ public function enqueue_rewrite_flush_other( $enqueue = false ) {
949
+
950
+ static $flush = null;
951
+
952
+ if ( isset( $flush ) )
953
+ return $flush;
954
+
955
+ if ( $enqueue )
956
+ return $flush = true;
957
+
958
+ return false;
959
+ }
960
+
961
+ /**
962
+ * Flush rewrite rules based on static variables.
963
+ *
964
+ * @since 2.3.0
965
+ * @access private
966
+ */
967
+ public function maybe_flush_rewrite() {
968
+
969
+ if ( $this->enqueue_rewrite_activate() )
970
+ $this->flush_rewrite_rules_activation();
971
+
972
+ if ( $this->enqueue_rewrite_deactivate() )
973
+ $this->flush_rewrite_rules_deactivation();
974
+
975
+ if ( $this->enqueue_rewrite_flush_other() )
976
+ $this->flush_rewrite_rules();
977
+
978
+ }
979
+
980
+ }
inc/classes/siteoptions.class.php ADDED
@@ -0,0 +1,1090 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The SEO Framework plugin
4
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License version 3 as published
8
+ * by the Free Software Foundation.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ /**
20
+ * Class AutoDescription_Siteoptions
21
+ *
22
+ * Holds Site Options for the plugin.
23
+ *
24
+ * @since 2.2.2
25
+ */
26
+ class AutoDescription_Siteoptions extends AutoDescription_Sanitize {
27
+
28
+ /**
29
+ * Site Settings field.
30
+ *
31
+ * @since 2.2.2
32
+ *
33
+ * @var string Settings field.
34
+ */
35
+ protected $settings_field;
36
+
37
+ /**
38
+ * Hold the Page ID for this plugin.
39
+ *
40
+ * @since 2.2.2
41
+ *
42
+ * @var string The page ID
43
+ */
44
+ protected $page_id;
45
+
46
+ /**
47
+ * Holds the update option.
48
+ *
49
+ * @since 2.6.0
50
+ *
51
+ * @var string The Updated option name.
52
+ */
53
+ protected $o_plugin_updated;
54
+
55
+ /**
56
+ * Constructor, load parent constructor and set up cachable variables.
57
+ */
58
+ public function __construct() {
59
+ parent::__construct();
60
+
61
+ $this->settings_field = THE_SEO_FRAMEWORK_SITE_OPTIONS;
62
+ $this->o_plugin_updated = 'updated_' . str_replace( '.', '', THE_SEO_FRAMEWORK_VERSION );
63
+ $this->page_id = 'autodescription-settings';
64
+
65
+ //* Set up site settings and save/reset them
66
+ add_action( 'admin_init', array( $this, 'register_settings' ), 5 );
67
+
68
+ //* Update site options at plugin update.
69
+ add_action( 'admin_init', array( $this, 'site_updated_plugin_option' ), 10 );
70
+
71
+ }
72
+
73
+ /**
74
+ * Holds default site options.
75
+ *
76
+ * @since 2.6.0
77
+ *
78
+ * @return array Default site options.
79
+ */
80
+ public function get_default_site_options() {
81
+
82
+ /**
83
+ * Switch when RTL is active;
84
+ * @since 2.5.0
85
+ */
86
+ if ( is_rtl() ) {
87
+ $titleloc = 'left';
88
+ $h_titleloc = 'right';
89
+ } else {
90
+ $titleloc = 'right';
91
+ $h_titleloc = 'left';
92
+ }
93
+
94
+ /**
95
+ * Default site settings. Separated from Author, page or network settings.
96
+ *
97
+ * These settings can be overwritten per page or post depending on type and setting.
98
+ *
99
+ * @since 2.2.2
100
+ *
101
+ * No longer directly applies filters
102
+ * @since 2.2.7
103
+ */
104
+ return array(
105
+ // Title.
106
+ 'title_seperator' => 'pipe', // Title separator (note: TYPO), dropdown
107
+ 'title_location' => $titleloc, // Title separation location
108
+ 'title_rem_additions' => 0, // Remove title additions
109
+ 'title_rem_prefixes' => 0, // Remove title prefixes
110
+
111
+ // Description.
112
+ 'description_separator' => 'pipe', // Description separator, dropdown
113
+ 'description_additions' => 1, // "Title on Blogname" within Description
114
+ 'description_blogname' => 1, // "on Blogname" within Description
115
+ 'description_custom' => '', // Custom prefix
116
+
117
+ // Robots directory.
118
+ 'noodp' => 1, // Site noopd robots settings
119
+ 'noydir' => 1, // Site noydir robots settings
120
+
121
+ // Robots index.
122
+ 'category_noindex' => 0, // Category Archive robots noindex
123
+ 'tag_noindex' => 0, // Tag Archive robots noindex
124
+ 'author_noindex' => 0, // Author Archive robots noindex
125
+ 'date_noindex' => 1, // Date Archive robots noindex
126
+ 'search_noindex' => 1, // Search Page robots noindex
127
+ 'attachment_noindex' => 1, // Attachment Pages robots noindex
128
+ 'site_noindex' => 0, // Site Page robots noindex
129
+
130
+ // Robots follow.
131
+ 'category_nofollow' => 0, // Category Archive robots nofollow
132
+ 'tag_nofollow' => 0, // Tag Archive robots nofollow
133
+ 'author_nofollow' => 0, // Author Archive robots nofollow
134
+ 'date_nofollow' => 0, // Date Archive robots nofollow
135
+ 'search_nofollow' => 0, // Search Page robots nofollow
136
+ 'attachment_nofollow' => 0, // Attachment Pages robots noindex
137
+ 'site_nofollow' => 0, // Site Page robots nofollow
138
+
139
+ // Robots archive.
140
+ 'category_noarchive' => 0, // Category Archive robots noarchive
141
+ 'tag_noarchive' => 0, // Tag Archive robots noarchive
142
+ 'author_noarchive' => 0, // Author Archive robots noarchive
143
+ 'date_noarchive' => 0, // Date Archive robots noarchive
144
+ 'search_noarchive' => 0, // Search Page robots noarchive
145
+ 'attachment_noarchive' => 0, // Attachment Page robots noarchive
146
+ 'site_noarchive' => 0, // Site Page robots noarchive
147
+
148
+ // Robots pagination index.
149
+ 'paged_noindex' => 1, // Every second or later page noindex
150
+ 'home_paged_noindex' => 0, // Every second or later homepage noindex
151
+
152
+ // Robots home.
153
+ 'homepage_noindex' => 0, // Home Page robots noindex
154
+ 'homepage_nofollow' => 0, // Home Page robots noarchive
155
+ 'homepage_noarchive' => 0, // Home Page robots nofollow
156
+
157
+ // Home meta.
158
+ 'homepage_title' => '', // Home Page Title string
159
+ 'homepage_tagline' => 1, // Home Page add blog Tagline
160
+ 'homepage_description' => '', // Home Page Description string
161
+ 'homepage_title_tagline' => '', // Home Page Tagline string
162
+ 'home_title_location' => $h_titleloc, // Title separation location
163
+
164
+ // Relationships
165
+ 'shortlink_tag' => 0, // Adds shortlink tag
166
+ 'prev_next_posts' => 0, // Adds next/prev tags
167
+ 'prev_next_archives' => 1, // Adds next/prev tags
168
+ 'prev_next_frontpage' => 1, // Adds next/prev tags
169
+
170
+ // Facebook.
171
+ 'facebook_publisher' => '', // Facebook Business Url
172
+ 'facebook_author' => '', // Facebook User URl
173
+ 'facebook_appid' => '', // Facebook App ID
174
+
175
+ // Dates.
176
+ 'post_publish_time' => 1, // Article Published Time
177
+ 'post_modify_time' => 1, // Article Modified Time
178
+
179
+ 'page_publish_time' => 0, // Article Published Time
180
+ 'page_modify_time' => 0, // Article Modified Time
181
+
182
+ 'home_publish_time' => 0, // Article Modified Time
183
+ 'home_modify_time' => 0, // Article Modified Time
184
+
185
+ // Twitter.
186
+ 'twitter_card' => 'summary_large_image', // Twitter Card layout. If no twitter:image image is found, it'll change to 'summary', dropdown
187
+ 'twitter_site' => '', // Twitter business @username
188
+ 'twitter_creator' => '', // Twitter user @username
189
+
190
+ // Social on/off.
191
+ 'og_tags' => 1, // Output of Open Graph meta tags
192
+ 'facebook_tags' => 1, // Output the Facebook meta tags
193
+ 'twitter_tags' => 1, // Output the Twitter meta tags
194
+ 'googleplus_tags' => 1, // Output the Google+ meta tags
195
+
196
+ // Webmasters.
197
+ 'google_verification' => '', // Google Verification Code
198
+ 'bing_verification' => '', // Bing Verification Code
199
+ 'yandex_verification' => '', // Yandex Verification Code
200
+ 'pint_verification' => '', // Pinterest Verification Code
201
+
202
+ // Knowledge general. https://developers.google.com/structured-data/customize/contact-points - This is extremely extended and valuable. Expect a premium version.
203
+ 'knowledge_output' => 1, // Default for outputing the Knowledge SEO.
204
+ 'knowledge_type' => 'organization', // Organization or Person, dropdown
205
+
206
+ // Knowledge business. https://developers.google.com/structured-data/customize/logos
207
+ 'knowledge_logo' => 1, // Fetch logo from WP Favicon
208
+ 'knowledge_name' => '', // Person or Organization name
209
+
210
+ // 'Sameas'
211
+ 'knowledge_facebook' => '', // Facebook Account
212
+ 'knowledge_twitter' => '', // Twitter Account
213
+ 'knowledge_gplus' => '', // Google Plus Account
214
+ 'knowledge_instagram' => '', // Instagram Account
215
+ 'knowledge_youtube' => '', // Youtube Account
216
+ 'knowledge_linkedin' => '', // Linkedin Account
217
+ // 'knowledge_myspace' => '', // MySpace Account // meh.
218
+ 'knowledge_pinterest' => '', // Pinterest Account
219
+ 'knowledge_soundcloud' => '', // SoundCloud Account
220
+ 'knowledge_tumblr' => '', // Tumblr Account
221
+
222
+ // Sitemaps.
223
+ 'sitemaps_output' => 1, // Output of sitemaps
224
+ 'sitemaps_modified' => 1, // Add sitemaps modified time
225
+ 'sitemap_timestamps' => '1', // Sitemaps modified time format, dropdown
226
+ 'sitemaps_robots' => 1, // Add sitemaps location to robots.txt
227
+ 'ping_google' => 1, // Ping Google
228
+ 'ping_bing' => 1, // Ping Bing
229
+ 'ping_yandex' => 1, // Ping Yandex
230
+
231
+ // Feed.
232
+ 'excerpt_the_feed' => 1, // Generate feed Excerpts
233
+ 'source_the_feed' => 1, // Add backlink at the end of the feed
234
+
235
+ // Schema
236
+ 'ld_json_searchbox' => 1, // LD+Json Sitelinks Searchbox
237
+ 'ld_json_sitename' => 1, // LD+Json Sitename
238
+ 'ld_json_breadcrumbs' => 1, // LD+Json Breadcrumbs
239
+
240
+ // Misc.
241
+ 'counter_type' => 3, // JS counter type.
242
+
243
+ // Cache.
244
+ $this->o_plugin_updated => 1, // Plugin update cache.
245
+ );
246
+ }
247
+
248
+ /**
249
+ * Holds warned site options array.
250
+ *
251
+ * @since 2.6.0
252
+ *
253
+ * @return array $options.
254
+ */
255
+ public function get_warned_site_options() {
256
+ /**
257
+ * Warned site settings. Only accepts checkbox options.
258
+ * When listed as 1, it's a feature which can destroy your website's SEO value when checked.
259
+ *
260
+ * Unchecking a box is simply "I'm not active." - Removing features generally do not negatively impact SEO value.
261
+ * Since it's all about the content.
262
+ *
263
+ * Only used within the SEO Settings page.
264
+ */
265
+ return array(
266
+ 'title_rem_additions' => 1, // Title remove additions.
267
+ 'title_rem_prefixes' => 0, // Title remove prefixes.
268
+
269
+ 'noodp' => 0, // Site noopd robots settings
270
+ 'noydir' => 0, // Site noydir robots settings
271
+
272
+ 'description_additions' => 0, // "Title on Blogname" within Description
273
+ 'description_blogname' => 0, // "on Blogname" within Description
274
+
275
+ 'category_noindex' => 0, // Category Archive robots noindex
276
+ 'tag_noindex' => 0, // Tag Archive robots noindex
277
+ 'author_noindex' => 0, // Author Archive robots noindex
278
+ 'date_noindex' => 0, // Date Archive robots noindex
279
+ 'search_noindex' => 0, // Search Page robots noindex
280
+ 'attachment_noindex' => 0, // Attachment Pages robots noindex
281
+ 'site_noindex' => 1, // Site Page robots noindex
282
+
283
+ 'category_nofollow' => 0, // Category Archive robots nofollow
284
+ 'tag_nofollow' => 0, // Tag Archive robots nofollow
285
+ 'author_nofollow' => 0, // Author Archive robots nofollow
286
+ 'date_nofollow' => 0, // Date Archive robots nofollow
287
+ 'search_nofollow' => 0, // Search Page robots nofollow
288
+ 'attachment_nofollow' => 0, // Attachment Pages robots noindex
289
+ 'site_nofollow' => 1, // Site Page robots nofollow
290
+
291
+ 'category_noarchive' => 0, // Category Archive robots noarchive
292
+ 'tag_noarchive' => 0, // Tag Archive robots noarchive
293
+ 'author_noarchive' => 0, // Author Archive robots noarchive
294
+ 'date_noarchive' => 0, // Date Archive robots noarchive
295
+ 'search_noarchive' => 0, // Search Page robots noarchive
296
+ 'attachment_noarchive' => 0, // Attachment Page robots noarchive
297
+ 'site_noarchive' => 0, // Site Page robots noarchive
298
+
299
+ 'paged_noindex' => 0, // Every second or later page noindex
300
+ 'home_paged_noindex' => 0, // Every second or later homepage noindex
301
+
302
+ 'homepage_noindex' => 1, // Home Page robots noindex
303
+ 'homepage_nofollow' => 1, // Home Page robots noarchive
304
+ 'homepage_noarchive' => 0, // Home Page robots nofollow
305
+
306
+ 'homepage_tagline' => 0, // Home Page add blog Tagline
307
+
308
+ 'shortlink_tag' => 0, // Adds shortlink tag
309
+
310
+ 'prev_next_posts' => 0, // Adds next/prev tags
311
+ 'prev_next_archives' => 0, // Adds next/prev tags
312
+ 'prev_next_frontpage' => 0, // Adds next/prev tags
313
+
314
+ 'post_publish_time' => 0, // Article Published Time
315
+ 'post_modify_time' => 0, // Article Modified Time
316
+
317
+ 'page_publish_time' => 0, // Article Published Time
318
+ 'page_modify_time' => 0, // Article Modified Time
319
+
320
+ 'home_publish_time' => 0, // Article Modified Time
321
+ 'home_modify_time' => 0, // Article Modified Time
322
+
323
+ 'og_tags' => 0, // Output of Open Graph meta tags
324
+ 'facebook_tags' => 0, // Output the Facebook meta tags
325
+ 'twitter_tags' => 0, // Output the Twitter meta tags
326
+ 'googleplus_tags' => 0, // Output the Google+ meta tags
327
+
328
+ 'knowledge_output' => 0, // Default for outputing the Knowledge SEO.
329
+ 'knowledge_logo' => 0, // Fetch logo from WP Favicon
330
+
331
+ 'sitemaps_output' => 0, // Output of sitemaps
332
+ 'sitemaps_modified' => 0, // Add sitemaps modified time
333
+ 'sitemaps_robots' => 0, // Add sitemaps location to robots.txt
334
+ 'ping_google' => 0, // Ping Google
335
+ 'ping_bing' => 0, // Ping Bing
336
+ 'ping_yandex' => 0, // Ping Yandex
337
+
338
+ 'excerpt_the_feed' => 0, // Generate feed Excerpts
339
+ 'source_the_feed' => 0, // Add backlink at the end of the feed
340
+
341
+ 'ld_json_searchbox' => 0, // LD+Json Sitelinks Searchbox
342
+ 'ld_json_sitename' => 0, // LD+Json Sitename
343
+ 'ld_json_breadcrumbs' => 0, // LD+Json Breadcrumbs
344
+ );
345
+
346
+ }
347
+
348
+ /**
349
+ * Updates special hidden values to default on settings save.
350
+ *
351
+ * @since 2.6.0
352
+ */
353
+ protected function update_hidden_options_to_default() {
354
+
355
+ //* Disables the New SEO Settings Updated notification.
356
+ $plugin_updated = $this->o_plugin_updated;
357
+ $_POST[THE_SEO_FRAMEWORK_SITE_OPTIONS][$plugin_updated] = 1;
358
+
359
+ }
360
+
361
+ /**
362
+ * Updates option from default options at plugin update.
363
+ *
364
+ * @since 2.6.0
365
+ * @access private
366
+ *
367
+ * @return void early if already has been updated.
368
+ */
369
+ public function site_updated_plugin_option() {
370
+
371
+ if ( false === $this->is_admin() )
372
+ return;
373
+
374
+ $plugin_updated = $this->o_plugin_updated;
375
+
376
+ /**
377
+ * Prevent this function from running more than once after update.
378
+ * Also prevent running if no settings field is found.
379
+ */
380
+ if ( $this->get_option( $plugin_updated ) || empty( $this->settings_field ) )
381
+ return;
382
+
383
+ //* If current user isn't allowed to update options, don't do anything.
384
+ if ( ! current_user_can( $this->settings_capability() ) )
385
+ return;
386
+
387
+ /**
388
+ * Applies filters 'the_seo_framework_update_options_at_update' : bool
389
+ * @since 2.6.0
390
+ */
391
+ if ( ! apply_filters( 'the_seo_framework_update_options_at_update', true ) )
392
+ return;
393
+
394
+ $updated = false;
395
+ $options = $this->get_all_options();
396
+ $new_options = $this->default_site_options();
397
+
398
+ /**
399
+ * Stop this madness from happening again until next update.
400
+ * Also prevent $updated to become true on this call.
401
+ */
402
+ $new_options[$plugin_updated] = 1;
403
+ $options[$plugin_updated] = 1;
404
+
405
+ //* Merge the options. Add to if it's non-existent.
406
+ foreach ( $new_options as $key => $value ) {
407
+ if ( ! isset( $options[$key] ) ) {
408
+ if ( ! empty( $new_options[$key] ) ) {
409
+ $options[$key] = $new_options[$key];
410
+ $updated = true;
411
+ }
412
+ }
413
+ }
414
+
415
+ //* Updated the options. Check for updated flag and see if settings pages are loaded.
416
+ if ( update_option( $this->settings_field, $options ) && $updated && $this->load_options ) {
417
+ $this->pre_output_site_updated_plugin_notice();
418
+ }
419
+ }
420
+
421
+ /**
422
+ * Determine whether to output update notice directly or on refresh.
423
+ * Run before headers are sent.
424
+ *
425
+ * @since 2.6.0
426
+ */
427
+ protected function pre_output_site_updated_plugin_notice() {
428
+
429
+ if ( $this->is_seo_settings_page() ) {
430
+ //* Redirect to current page if on options page to correct option values. Once.
431
+ if ( ! isset( $_REQUEST['seo-updated'] ) || 'true' !== $_REQUEST['seo-updated'] )
432
+ $this->admin_redirect( $this->page_id, array( 'seo-updated' => 'true' ) );
433
+
434
+ //* Notice has already been sent.
435
+ return;
436
+ }
437
+
438
+ //* Make sure this plugin's scripts are being loaded.
439
+ $this->init_admin_scripts();
440
+
441
+ //* Output notice.
442
+ add_action( 'admin_notices', array( $this, 'site_updated_plugin_notice' ) );
443
+
444
+ }
445
+
446
+ /**
447
+ * Echo plugin updated notification.
448
+ *
449
+ * @since 2.6.0
450
+ *
451
+ * @access private
452
+ */
453
+ public function site_updated_plugin_notice() {
454
+
455
+ $notice = $this->page_defaults['plugin_update_text'];
456
+
457
+ $settings_url = $this->seo_settings_page_url();
458
+ $link = sprintf( '<a href="%s" title="%s" target="_self">%s</a>', $settings_url, __( 'SEO Settings', 'autodescription' ), __( 'here', 'autodescription' ) );
459
+
460
+ $go_to_page = sprintf( _x( 'View the new options %s.', '%s = here', 'autodescription' ), $link );
461
+
462
+ $notice = $notice . ' ' . $go_to_page;
463
+
464
+ echo $this->generate_dismissible_notice( $notice, 'updated' );
465
+
466
+ }
467
+
468
+ /**
469
+ * Return SEO options from the SEO options database.
470
+ *
471
+ * @since 2.2.2
472
+ *
473
+ * @uses $this->the_seo_framework_get_option() Return option from the options table and cache result.
474
+ * @uses THE_SEO_FRAMEWORK_SITE_OPTIONS
475
+ *
476
+ * @param string $key Option name.
477
+ * @param boolean $use_cache Optional. Whether to use the cache value or not. Defaults to true.
478
+ *
479
+ * @return mixed The value of this $key in the database.
480
+ */
481
+ public function get_option( $key, $use_cache = true ) {
482
+ return $this->the_seo_framework_get_option( $key, THE_SEO_FRAMEWORK_SITE_OPTIONS, $use_cache );
483
+ }
484
+
485
+ /**
486
+ * Return current option array.
487
+ *
488
+ * Applies filters 'the_seo_framework_get_options' : boolean
489
+ *
490
+ * @since 2.6.0
491
+ * @staticvar array $cache The option cache.
492
+ *
493
+ * @return array Options.
494
+ */
495
+ public function get_all_options( $setting = null ) {
496
+
497
+ static $cache = array();
498
+
499
+ if ( isset( $cache[$setting] ) )
500
+ return $cache[$setting];
501
+
502
+ if ( is_null( $setting ) )
503
+ $setting = THE_SEO_FRAMEWORK_SITE_OPTIONS;
504
+
505
+ return $cache[$setting] = apply_filters( 'the_seo_framework_get_options', get_option( $setting ), $setting );
506
+ }
507
+
508
+ /**
509
+ * Return option from the options table and cache result.
510
+ *
511
+ * Applies `the_seo_framework_get_options` filters.
512
+ * This filter retrieves the (previous) values from Genesis if exists.
513
+ *
514
+ * Values pulled from the database are cached on each request, so a second request for the same value won't cause a
515
+ * second DB interaction.
516
+ * @staticvar array $settings_cache
517
+ * @staticvar array $options_cache
518
+ *
519
+ * @since 2.0.0
520
+ *
521
+ * @param string $key Option name.
522
+ * @param string $setting Optional. Settings field name. Eventually defaults to null if not passed as an argument.
523
+ * @param boolean $use_cache Optional. Whether to use the cache value or not. Default is true.
524
+ *
525
+ * @return mixed The value of this $key in the database.
526
+ *
527
+ * @thanks StudioPress (http://www.studiopress.com/) for some code.
528
+ */
529
+ public function the_seo_framework_get_option( $key, $setting = null, $use_cache = true ) {
530
+
531
+ //* If we need to bypass the cache
532
+ if ( ! $use_cache ) {
533
+ $options = get_option( $setting );
534
+
535
+ if ( ! is_array( $options ) || ! array_key_exists( $key, $options ) )
536
+ return '';
537
+
538
+ return is_array( $options[$key] ) ? stripslashes_deep( $options[$key] ) : stripslashes( wp_kses_decode_entities( $options[$key] ) );
539
+ }
540
+
541
+ //* Setup caches
542
+ static $options_cache = array();
543
+
544
+ //* Check options cache
545
+ if ( isset( $options_cache[$setting][$key] ) )
546
+ //* Option has been cached
547
+ return $options_cache[$setting][$key];
548
+
549
+ $options = $this->get_all_options( $setting );
550
+
551
+ //* Check for non-existent option
552
+ if ( ! is_array( $options ) || ! array_key_exists( $key, (array) $options ) ) {
553
+ //* Cache non-existent option
554
+ $options_cache[$setting][$key] = '';
555
+ } else {
556
+ //* Option has not been previously been cached, so cache now
557
+ $options_cache[$setting][$key] = is_array( $options[$key] ) ? stripslashes_deep( $options[$key] ) : stripslashes( wp_kses_decode_entities( $options[$key] ) );
558
+ }
559
+
560
+ return $options_cache[$setting][$key];
561
+ }
562
+
563
+ /**
564
+ * Return SEO options from the SEO options database.
565
+ *
566
+ * @since 2.2.2
567
+ *
568
+ * @uses $this->the_seo_framework_get_option() Return option from the options table and cache result.
569
+ * @uses THE_SEO_FRAMEWORK_NETWORK_OPTIONS
570
+ *
571
+ * @param string $key Option name.
572
+ * @param boolean $use_cache Optional. Whether to use the cache value or not. Defaults to true.
573
+ *
574
+ * @return mixed The value of this $key in the database.
575
+ */
576
+ public function get_site_option( $key, $use_cache = true ) {
577
+ return $this->the_seo_framework_get_option( $key, THE_SEO_FRAMEWORK_NETWORK_OPTIONS, $use_cache );
578
+ }
579
+
580
+ /**
581
+ * Return Default SEO options from the SEO options array.
582
+ *
583
+ * @since 2.2.5
584
+ *
585
+ * @uses $this->get_default_settings() Return option from the options table and cache result.
586
+ * @uses THE_SEO_FRAMEWORK_SITE_OPTIONS
587
+ *
588
+ * @param string $key Option name.
589
+ * @param boolean $use_cache Optional. Whether to use the cache value or not. Defaults to true.
590
+ *
591
+ * @return mixed The value of this $key in the database.
592
+ */
593
+ public function get_default_option( $key, $use_cache = true ) {
594
+ return $this->get_default_settings( $key, THE_SEO_FRAMEWORK_SITE_OPTIONS, $use_cache );
595
+ }
596
+
597
+ /**
598
+ * Return the parsed default options array.
599
+ *
600
+ * @since 2.2.7
601
+ *
602
+ * Applies filters the_seo_framework_default_site_options : The default site options array.
603
+ *
604
+ * @param array $args Additional default options to filter.
605
+ *
606
+ * @return array The SEO Framework Options
607
+ */
608
+ protected function default_site_options( $args = array() ) {
609
+ return wp_parse_args(
610
+ $args,
611
+ apply_filters(
612
+ 'the_seo_framework_default_site_options',
613
+ wp_parse_args(
614
+ $args,
615
+ $this->get_default_site_options()
616
+ )
617
+ )
618
+ );
619
+ }
620
+
621
+ /**
622
+ * Return the Warned site options. Options which should be 'avoided' return true.
623
+ *
624
+ * @since 2.3.4
625
+ *
626
+ * Applies filters the_seo_framework_warned_site_options The warned site options array.
627
+ *
628
+ * @param array $args Additional warned options to filter.
629
+ *
630
+ * @return array The SEO Framework Warned Options
631
+ */
632
+ protected function warned_site_options( $args = array() ) {
633
+ return wp_parse_args(
634
+ $args,
635
+ apply_filters(
636
+ 'the_seo_framework_warned_site_options',
637
+ wp_parse_args(
638
+ $args,
639
+ $this->get_warned_site_options()
640
+ )
641
+ )
642
+ );
643
+ }
644
+
645
+ /**
646
+ * Register the database settings for storage.
647
+ *
648
+ * @since 2.2.2
649
+ *
650
+ * @return void
651
+ *
652
+ * @thanks StudioPress (http://www.studiopress.com/) for some code.
653
+ */
654
+ public function register_settings() {
655
+
656
+ //* If this page doesn't store settings, no need to register them
657
+ if ( empty( $this->settings_field ) )
658
+ return;
659
+
660
+ register_setting( $this->settings_field, $this->settings_field );
661
+ add_option( $this->settings_field, $this->default_site_options() );
662
+
663
+ //* If this page isn't the SEO Settings page, there's no need to check for a reset.
664
+ if ( false === $this->is_seo_settings_page() )
665
+ return;
666
+
667
+ if ( $this->get_option( 'reset', $this->settings_field ) ) {
668
+ if ( update_option( $this->settings_field, $this->default_site_options() ) )
669
+ $this->admin_redirect( $this->page_id, array( 'reset' => 'true' ) );
670
+ else
671
+ $this->admin_redirect( $this->page_id, array( 'error' => 'true' ) );
672
+ exit;
673
+ }
674
+
675
+ }
676
+
677
+ /**
678
+ * Get the default of any of the The SEO Framework settings.
679
+ *
680
+ * @since 2.2.4
681
+ *
682
+ * @uses $this->settings_field
683
+ * @uses $this->default_site_options()
684
+ *
685
+ * @param string $key required The option name
686
+ * @param string $setting optional The settings field
687
+ * @param bool $use_cache optional Use the options cache or not. For debugging purposes.
688
+ *
689
+ * @staticvar array $defaults_cache
690
+ *
691
+ * @return int|bool|string default option
692
+ * int '-1' if option doesn't exist.
693
+ */
694
+ public function get_default_settings( $key, $setting = '', $use_cache = true ) {
695
+
696
+ if ( ! isset( $key ) || empty( $key ) )
697
+ return false;
698
+
699
+ //* Fetch default settings if it's not set.
700
+ if ( empty( $setting ) )
701
+ $setting = $this->settings_field;
702
+
703
+ //* If we need to bypass the cache
704
+ if ( ! $use_cache ) {
705
+ $defaults = $this->default_site_options();
706
+
707
+ if ( ! is_array( $defaults ) || ! array_key_exists( $key, $defaults ) )
708
+ return -1;
709
+
710
+ return is_array( $defaults[$key] ) ? stripslashes_deep( $defaults[$key] ) : stripslashes( wp_kses_decode_entities( $defaults[$key] ) );
711
+ }
712
+
713
+ static $defaults_cache = array();
714
+
715
+ //* Check options cache
716
+ if ( isset( $defaults_cache[$key] ) )
717
+ //* Option has been cached
718
+ return $defaults_cache[$key];
719
+
720
+ $defaults_cache = $this->default_site_options();
721
+
722
+ if ( ! is_array( $defaults_cache ) || ! array_key_exists( $key, (array) $defaults_cache ) )
723
+ $defaults_cache[$key] = -1;
724
+
725
+ return $defaults_cache[$key];
726
+ }
727
+
728
+ /**
729
+ * Get the warned setting of any of the The SEO Framework settings.
730
+ *
731
+ * @since 2.3.4
732
+ *
733
+ * @uses $this->settings_field
734
+ * @uses $this->warned_site_options()
735
+ *
736
+ * @param string $key required The option name
737
+ * @param string $setting optional The settings field
738
+ * @param bool $use_cache optional Use the options cache or not. For debugging purposes.
739
+ *
740
+ * @staticvar array $warned_cache
741
+ *
742
+ * @return int 0|1 Whether the option is flagged as dangerous for SEO.
743
+ * int '-1' if option doesn't exist.
744
+ */
745
+ public function get_warned_settings( $key, $setting = '', $use_cache = true ) {
746
+
747
+ if ( empty( $key ) )
748
+ return false;
749
+
750
+ //* Fetch default settings if it's not set.
751
+ if ( empty( $setting ) )
752
+ $setting = $this->settings_field;
753
+
754
+ //* If we need to bypass the cache
755
+ if ( ! $use_cache ) {
756
+ $warned = $this->warned_site_options();
757
+
758
+ if ( ! is_array( $warned ) || ! array_key_exists( $key, $warned ) )
759
+ return -1;
760
+
761
+ return $this->s_one_zero( $warned[$key] );
762
+ }
763
+
764
+ static $warned_cache = array();
765
+
766
+ //* Check options cache
767
+ if ( isset( $warned_cache[$key] ) )
768
+ //* Option has been cached
769
+ return $warned_cache[$key];
770
+
771
+ $warned_options = $this->warned_site_options();
772
+
773
+ if ( ! is_array( $warned_options ) || ! array_key_exists( $key, (array) $warned_options ) )
774
+ $warned_cache[$key] = -1;
775
+
776
+ $warned_cache[$key] = $this->s_one_zero( $warned_options[$key] );
777
+
778
+ return $warned_cache[$key];
779
+ }
780
+
781
+ /**
782
+ * Returns Facebook locales array
783
+ *
784
+ * @see https://www.facebook.com/translations/FacebookLocales.xml
785
+ *
786
+ * @since 2.5.2
787
+ * @return array Valid Facebook locales
788
+ */
789
+ public function fb_locales() {
790
+ return array(
791
+ 'af_ZA', // Afrikaans
792
+ 'ak_GH', // Akan
793
+ 'am_ET', // Amharic
794
+ 'ar_AR', // Arabic
795
+ 'as_IN', // Assamese
796
+ 'ay_BO', // Aymara
797
+ 'az_AZ', // Azerbaijani
798
+ 'be_BY', // Belarusian
799
+ 'bg_BG', // Bulgarian
800
+ 'bn_IN', // Bengali
801
+ 'br_FR', // Breton
802
+ 'bs_BA', // Bosnian
803
+ 'ca_ES', // Catalan
804
+ 'cb_IQ', // Sorani Kurdish
805
+ 'ck_US', // Cherokee
806
+ 'co_FR', // Corsican
807
+ 'cs_CZ', // Czech
808
+ 'cx_PH', // Cebuano
809
+ 'cy_GB', // Welsh
810
+ 'da_DK', // Danish
811
+ 'de_DE', // German
812
+ 'el_GR', // Greek
813
+ 'en_GB', // English (UK)
814
+ 'en_IN', // English (India)
815
+ 'en_PI', // English (Pirate)
816
+ 'en_UD', // English (Upside Down)
817
+ 'en_US', // English (US)
818
+ 'eo_EO', // Esperanto
819
+ 'es_CL', // Spanish (Chile)
820
+ 'es_CO', // Spanish (Colombia)
821
+ 'es_ES', // Spanish (Spain)
822
+ 'es_LA', // Spanish
823
+ 'es_MX', // Spanish (Mexico)
824
+ 'es_VE', // Spanish (Venezuela)
825
+ 'et_EE', // Estonian
826
+ 'eu_ES', // Basque
827
+ 'fa_IR', // Persian
828
+ 'fb_LT', // Leet Speak
829
+ 'ff_NG', // Fulah
830
+ 'fi_FI', // Finnish
831
+ 'fo_FO', // Faroese
832
+ 'fr_CA', // French (Canada)
833
+ 'fr_FR', // French (France)
834
+ 'fy_NL', // Frisian
835
+ 'ga_IE', // Irish
836
+ 'gl_ES', // Galician
837
+ 'gn_PY', // Guarani
838
+ 'gu_IN', // Gujarati
839
+ 'gx_GR', // Classical Greek
840
+ 'ha_NG', // Hausa
841
+ 'he_IL', // Hebrew
842
+ 'hi_IN', // Hindi
843
+ 'hr_HR', // Croatian
844
+ 'hu_HU', // Hungarian
845
+ 'hy_AM', // Armenian
846
+ 'id_ID', // Indonesian
847
+ 'ig_NG', // Igbo
848
+ 'is_IS', // Icelandic
849
+ 'it_IT', // Italian
850
+ 'ja_JP', // Japanese
851
+ 'ja_KS', // Japanese (Kansai)
852
+ 'jv_ID', // Javanese
853
+ 'ka_GE', // Georgian
854
+ 'kk_KZ', // Kazakh
855
+ 'km_KH', // Khmer
856
+ 'kn_IN', // Kannada
857
+ 'ko_KR', // Korean
858
+ 'ku_TR', // Kurdish (Kurmanji)
859
+ 'ky_KG', // Kyrgyz
860
+ 'la_VA', // Latin
861
+ 'lg_UG', // Ganda
862
+ 'li_NL', // Limburgish
863
+ 'ln_CD', // Lingala
864
+ 'lo_LA', // Lao
865
+ 'lt_LT', // Lithuanian
866
+ 'lv_LV', // Latvian
867
+ 'mg_MG', // Malagasy
868
+ 'mi_NZ', // Māori
869
+ 'mk_MK', // Macedonian
870
+ 'ml_IN', // Malayalam
871
+ 'mn_MN', // Mongolian
872
+ 'mr_IN', // Marathi
873
+ 'ms_MY', // Malay
874
+ 'mt_MT', // Maltese
875
+ 'my_MM', // Burmese
876
+ 'nb_NO', // Norwegian (bokmal)
877
+ 'nd_ZW', // Ndebele
878
+ 'ne_NP', // Nepali
879
+ 'nl_BE', // Dutch (België)
880
+ 'nl_NL', // Dutch
881
+ 'nn_NO', // Norwegian (nynorsk)
882
+ 'ny_MW', // Chewa
883
+ 'or_IN', // Oriya
884
+ 'pa_IN', // Punjabi
885
+ 'pl_PL', // Polish
886
+ 'ps_AF', // Pashto
887
+ 'pt_BR', // Portuguese (Brazil)
888
+ 'pt_PT', // Portuguese (Portugal)
889
+ 'qu_PE', // Quechua
890
+ 'rm_CH', // Romansh
891
+ 'ro_RO', // Romanian
892
+ 'ru_RU', // Russian
893
+ 'rw_RW', // Kinyarwanda
894
+ 'sa_IN', // Sanskrit
895
+ 'sc_IT', // Sardinian
896
+ 'se_NO', // Northern Sámi
897
+ 'si_LK', // Sinhala
898
+ 'sk_SK', // Slovak
899
+ 'sl_SI', // Slovenian
900
+ 'sn_ZW', // Shona
901
+ 'so_SO', // Somali
902
+ 'sq_AL', // Albanian
903
+ 'sr_RS', // Serbian
904
+ 'sv_SE', // Swedish
905
+ 'sy_SY', // Swahili
906
+ 'sw_KE', // Syriac
907
+ 'sz_PL', // Silesian
908
+ 'ta_IN', // Tamil
909
+ 'te_IN', // Telugu
910
+ 'tg_TJ', // Tajik
911
+ 'th_TH', // Thai
912
+ 'tk_TM', // Turkmen
913
+ 'tl_PH', // Filipino
914
+ 'tl_ST', // Klingon
915
+ 'tr_TR', // Turkish
916
+ 'tt_RU', // Tatar
917
+ 'tz_MA', // Tamazight
918
+ 'uk_UA', // Ukrainian
919
+ 'ur_PK', // Urdu
920
+ 'uz_UZ', // Uzbek
921
+ 'vi_VN', // Vietnamese
922
+ 'wo_SN', // Wolof
923
+ 'xh_ZA', // Xhosa
924
+ 'yi_DE', // Yiddish
925
+ 'yo_NG', // Yoruba
926
+ 'zh_CN', // Simplified Chinese (China)
927
+ 'zh_HK', // Traditional Chinese (Hong Kong)
928
+ 'zh_TW', // Traditional Chinese (Taiwan)
929
+ 'zu_ZA', // Zulu
930
+ 'zz_TR', // Zazaki
931
+ );
932
+ }
933
+
934
+ /**
935
+ * Returns Facebook locales array keys.
936
+ * This is apart from the fb_locales array since there are "duplicated" keys.
937
+ * Use this to compare the numeric key position.
938
+ *
939
+ * @see https://www.facebook.com/translations/FacebookLocales.xml
940
+ *
941
+ * @since 2.5.2
942
+ * @return array Valid Facebook locales
943
+ */
944
+ public function language_keys() {
945
+ return array(
946
+ 'af', // Afrikaans
947
+ 'ak', // Akan
948
+ 'am', // Amharic
949
+ 'ar', // Arabic
950
+ 'as', // Assamese
951
+ 'ay', // Aymara
952
+ 'az', // Azerbaijani
953
+ 'be', // Belarusian
954
+ 'bg', // Bulgarian
955
+ 'bn', // Bengali
956
+ 'br', // Breton
957
+ 'bs', // Bosnian
958
+ 'ca', // Catalan
959
+ 'cb', // Sorani Kurdish
960
+ 'ck', // Cherokee
961
+ 'co', // Corsican
962
+ 'cs', // Czech
963
+ 'cx', // Cebuano
964
+ 'cy', // Welsh
965
+ 'da', // Danish
966
+ 'de', // German
967
+ 'el', // Greek
968
+ 'en', // English (UK)
969
+ 'en', // English (India)
970
+ 'en', // English (Pirate)
971
+ 'en', // English (Upside Down)
972
+ 'en', // English (US)
973
+ 'eo', // Esperanto
974
+ 'es', // Spanish (Chile)
975
+ 'es', // Spanish (Colombia)
976
+ 'es', // Spanish (Spain)
977
+ 'es', // Spanish
978
+ 'es', // Spanish (Mexico)
979
+ 'es', // Spanish (Venezuela)
980
+ 'et', // Estonian
981
+ 'eu', // Basque
982
+ 'fa', // Persian
983
+ 'fb', // Leet Speak
984
+ 'ff', // Fulah
985
+ 'fi', // Finnish
986
+ 'fo', // Faroese
987
+ 'fr', // French (Canada)
988
+ 'fr', // French (France)
989
+ 'fy', // Frisian
990
+ 'ga', // Irish
991
+ 'gl', // Galician
992
+ 'gn', // Guarani
993
+ 'gu', // Gujarati
994
+ 'gx', // Classical Greek
995
+ 'ha', // Hausa
996
+ 'he', // Hebrew
997
+ 'hi', // Hindi
998
+ 'hr', // Croatian
999
+ 'hu', // Hungarian
1000
+ 'hy', // Armenian
1001
+ 'id', // Indonesian
1002
+ 'ig', // Igbo
1003
+ 'is', // Icelandic
1004
+ 'it', // Italian
1005
+ 'ja', // Japanese
1006
+ 'ja', // Japanese (Kansai)
1007
+ 'jv', // Javanese
1008
+ 'ka', // Georgian
1009
+ 'kk', // Kazakh
1010
+ 'km', // Khmer
1011
+ 'kn', // Kannada
1012
+ 'ko', // Korean
1013
+ 'ku', // Kurdish (Kurmanji)
1014
+ 'ky', // Kyrgyz
1015
+ 'la', // Latin
1016
+ 'lg', // Ganda
1017
+ 'li', // Limburgish
1018
+ 'ln', // Lingala
1019
+ 'lo', // Lao
1020
+ 'lt', // Lithuanian
1021
+ 'lv', // Latvian
1022
+ 'mg', // Malagasy
1023
+ 'mi', // Māori
1024
+ 'mk', // Macedonian
1025
+ 'ml', // Malayalam
1026
+ 'mn', // Mongolian
1027
+ 'mr', // Marathi
1028
+ 'ms', // Malay
1029
+ 'mt', // Maltese
1030
+ 'my', // Burmese
1031
+ 'nb', // Norwegian (bokmal)
1032
+ 'nd', // Ndebele
1033
+ 'ne', // Nepali
1034
+ 'nl', // Dutch (België)
1035
+ 'nl', // Dutch
1036
+ 'nn', // Norwegian (nynorsk)
1037
+ 'ny', // Chewa
1038
+ 'or', // Oriya
1039
+ 'pa', // Punjabi
1040
+ 'pl', // Polish
1041
+ 'ps', // Pashto
1042
+ 'pt', // Portuguese (Brazil)
1043
+ 'pt', // Portuguese (Portugal)
1044
+ 'qu', // Quechua
1045
+ 'rm', // Romansh
1046
+ 'ro', // Romanian
1047
+ 'ru', // Russian
1048
+ 'rw', // Kinyarwanda
1049
+ 'sa', // Sanskrit
1050
+ 'sc', // Sardinian
1051
+ 'se', // Northern Sámi
1052
+ 'si', // Sinhala
1053
+ 'sk', // Slovak
1054
+ 'sl', // Slovenian
1055
+ 'sn', // Shona
1056
+ 'so', // Somali
1057
+ 'sq', // Albanian
1058
+ 'sr', // Serbian
1059
+ 'sv', // Swedish
1060
+ 'sy', // Swahili
1061
+ 'sw', // Syriac
1062
+ 'sz', // Silesian
1063
+ 'ta', // Tamil
1064
+ 'te', // Telugu
1065
+ 'tg', // Tajik
1066
+ 'th', // Thai
1067
+ 'tk', // Turkmen
1068
+ 'tl', // Filipino
1069
+ 'tl', // Klingon
1070
+ 'tr', // Turkish
1071
+ 'tt', // Tatar
1072
+ 'tz', // Tamazight
1073
+ 'uk', // Ukrainian
1074
+ 'ur', // Urdu
1075
+ 'uz', // Uzbek
1076
+ 'vi', // Vietnamese
1077
+ 'wo', // Wolof
1078
+ 'xh', // Xhosa
1079
+ 'yi', // Yiddish
1080
+ 'yo', // Yoruba
1081
+ 'zh', // Simplified Chinese (China)
1082
+ 'zh', // Traditional Chinese (Hong Kong)
1083
+ 'zh', // Traditional Chinese (Taiwan)
1084
+ 'zu', // Zulu
1085
+ 'zz', // Zazaki
1086
+ );
1087
+
1088
+ }
1089
+
1090
+ }
inc/classes/termdata.class.php ADDED
@@ -0,0 +1,417 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The SEO Framework plugin
4
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License version 3 as published
8
+ * by the Free Software Foundation.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ /**
20
+ * Class AutoDescription_TermData
21
+ *
22
+ * Holds Term and Taxonomy data.
23
+ *
24
+ * @since 2.6.0
25
+ */
26
+ class AutoDescription_TermData extends AutoDescription_PostData {
27
+
28
+ /**
29
+ * Constructor, load parent constructor.
30
+ */
31
+ public function __construct() {
32
+ parent::__construct();
33
+
34
+ add_action( 'current_screen', array( $this, 'init_term_filters' ), 999 );
35
+ add_action( 'get_header', array( $this, 'init_term_filters' ), 999 );
36
+
37
+ add_action( 'edit_term', array( $this, 'taxonomy_seo_save' ), 10, 2 );
38
+ add_action( 'delete_term', array( $this, 'term_meta_delete' ), 10, 2 );
39
+ }
40
+
41
+ /**
42
+ * Initializes term filters after wp_query or currentscreen has been set.
43
+ *
44
+ * @since 2.6.6
45
+ * @staticvar boolean $run Whether this function has already run.
46
+ * @access private
47
+ *
48
+ * @return void early if already run.
49
+ */
50
+ public function init_term_filters() {
51
+
52
+ static $run = null;
53
+
54
+ if ( isset( $run ) )
55
+ return;
56
+
57
+ add_filter( 'get_term', array( $this, 'get_term_filter' ), 10, 2 );
58
+ add_filter( 'get_terms', array( $this, 'get_terms_filter' ), 10, 2 );
59
+
60
+ $run = true;
61
+
62
+ }
63
+
64
+ /**
65
+ * Add term meta data into options table of the term.
66
+ * Adds separated database options for terms, as the terms table doesn't allow for addition.
67
+ *
68
+ * Applies filters array the_seo_framework_term_meta_defaults : Array of default term SEO options
69
+ * Applies filters mixed the_seo_framework_term_meta_{field} : Override filter for specifics.
70
+ * Applies filters array the_seo_framework_term_meta : Override output for term or taxonomy.
71
+ *
72
+ * @since 2.1.8
73
+ *
74
+ * @todo Use WordPress 4.4.0 get_term_meta() / update_term_meta()
75
+ * @priority OMG WTF BBQ 2.6.x / Genesis 2.3.0
76
+ * @see @link http://www.studiopress.com/important-announcement-for-genesis-plugin-developers/
77
+ * @link https://core.trac.wordpress.org/browser/tags/4.5/src/wp-includes/taxonomy.php#L1814
78
+ * @todo still use arrays in get_term_meta() / update_term_meta() ?
79
+ * @NOTE Keep WP 3.8 compat.
80
+ *
81
+ * @param object $term Database row object.
82
+ * @param string $taxonomy Taxonomy name that $term is part of.
83
+ * @return object $term Database row object.
84
+ */
85
+ public function get_term_filter( $term, $taxonomy ) {
86
+
87
+ //* Do nothing, if $term is not an object.
88
+ if ( ! is_object( $term ) )
89
+ return $term;
90
+
91
+ //* We can't set query vars just yet.
92
+ if ( false === $this->can_cache_query() )
93
+ return $term;
94
+
95
+ /**
96
+ * No need to process this data outside of the Terms' scope.
97
+ * @since 2.6.0
98
+ */
99
+ if ( false === $this->is_admin() && false === $this->is_archive() )
100
+ return $term;
101
+
102
+ /**
103
+ * No need to process this after the data has already been output.
104
+ * @since 2.6.0
105
+ */
106
+ if ( did_action( 'the_seo_framework_do_after_output' ) )
107
+ return $term;
108
+
109
+ /**
110
+ * Do nothing if called in the context of creating a term via an Ajax call to prevent data conflict.
111
+ * @since ???
112
+ *
113
+ * @since 2.6.0 delay did_action call as it's a heavy array call.
114
+ */
115
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX && did_action( 'wp_ajax_add-tag' ) )
116
+ return $term;
117
+
118
+ $db = get_option( 'autodescription-term-meta' );
119
+ $term_meta = isset( $db[$term->term_id] ) ? $db[$term->term_id] : array();
120
+
121
+ $args = (array) apply_filters( 'the_seo_framework_term_meta_defaults', array(
122
+ 'doctitle' => '',
123
+ 'description' => '',
124
+ 'noindex' => 0,
125
+ 'nofollow' => 0,
126
+ 'noarchive' => 0,
127
+ 'saved_flag' => 0, // Don't touch, used to prevent data conflict with Genesis.
128
+ ) );
129
+
130
+ $term->admeta = wp_parse_args( $term_meta, $args );
131
+
132
+ //* Sanitize term meta
133
+ foreach ( $term->admeta as $field => $value ) {
134
+
135
+ /**
136
+ * Trim and sanitize the title beforehand.
137
+ * @since 2.5.0
138
+ */
139
+ if ( 'doctitle' === $field )
140
+ $value = trim( strip_tags( $value ) );
141
+
142
+ /**
143
+ * Trim and sanitize the description beforehand.
144
+ * @since 2.5.0
145
+ */
146
+ if ( 'description' === $field )
147
+ $value = $this->s_description( $value );
148
+
149
+ /**
150
+ * @param object $term The Term object.
151
+ * @param string $taxonomy The Taxonomy name.
152
+ */
153
+ $term->admeta[$field] = (string) apply_filters( "the_seo_framework_term_meta_{$field}", stripslashes( wp_kses_decode_entities( $value ) ), $term, $taxonomy );
154
+ }
155
+
156
+ /**
157
+ * @param object $term The Term object.
158
+ * @param array $taxonomy The Taxonomy name.
159
+ */
160
+ $term->admeta = (array) apply_filters( 'the_seo_framework_term_meta', $term->admeta, $term, $taxonomy );
161
+
162
+ return $term;
163
+ }
164
+
165
+ /**
166
+ * Add AutoDescription term-meta data to functions that return multiple terms.
167
+ *
168
+ * @since 2.0.0
169
+ *
170
+ * @param array $terms Database row objects.
171
+ * @param string $taxonomy Taxonomy name that $terms are part of.
172
+ * @return array $terms Database row objects.
173
+ */
174
+ public function get_terms_filter( array $terms, $taxonomy ) {
175
+
176
+ foreach( $terms as $term )
177
+ $term = $this->get_term_filter( $term, $taxonomy );
178
+
179
+ return $terms;
180
+ }
181
+
182
+ /**
183
+ * Save taxonomy meta data.
184
+ * Fires when a user edits and saves a taxonomy.
185
+ *
186
+ * @since 2.1.8
187
+ *
188
+ * @param integer $term_id Term ID.
189
+ * @param integer $tt_id Term Taxonomy ID.
190
+ * @return void Early on AJAX call.
191
+ */
192
+ public function taxonomy_seo_save( $term_id, $tt_id ) {
193
+
194
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX )
195
+ return;
196
+
197
+ $term_meta = (array) get_option( 'autodescription-term-meta' );
198
+
199
+ $term_meta[$term_id] = isset( $_POST['autodescription-meta'] ) ? (array) $_POST['autodescription-meta'] : array();
200
+
201
+ //* Pass through wp_kses if not super admin.
202
+ if ( ! current_user_can( 'unfiltered_html' ) && isset( $term_meta[$term_id]['archive_description'] ) )
203
+ $term_meta[$term_id]['archive_description'] = wp_kses( $term_meta[$term_id]['archive_description'] );
204
+
205
+ update_option( 'autodescription-term-meta', $term_meta );
206
+
207
+ }
208
+
209
+ /**
210
+ * Delete term meta data.
211
+ * Fires when a user deletes a term.
212
+ *
213
+ * @since 2.1.8
214
+ *
215
+ * @param integer $term_id Term ID.
216
+ * @param integer $tt_id Taxonomy Term ID.
217
+ */
218
+ public function term_meta_delete( $term_id, $tt_id ) {
219
+
220
+ $term_meta = (array) get_option( 'autodescription-term-meta' );
221
+
222
+ unset( $term_meta[$term_id] );
223
+
224
+ update_option( 'autodescription-term-meta', (array) $term_meta );
225
+
226
+ }
227
+
228
+ /**
229
+ * Fetch set Term data.
230
+ *
231
+ * @param object|null $term The TT object, if it isn't set, one is fetched.
232
+ *
233
+ * @since 2.6.0
234
+ *
235
+ * @return array $data The SEO Framework TT data.
236
+ */
237
+ public function get_term_data( $term = null ) {
238
+
239
+ if ( is_null( $term ) ) {
240
+ if ( $this->is_author() ) {
241
+ //* Special handling.
242
+ return null;
243
+ }
244
+
245
+ $term = $this->fetch_the_term();
246
+ }
247
+
248
+ if ( $term ) {
249
+ $data = array();
250
+
251
+ $data['title'] = isset( $term->admeta['doctitle'] ) ? $term->admeta['doctitle'] : '';
252
+ $data['description'] = isset( $term->admeta['description'] ) ? $term->admeta['description'] : '';
253
+ $data['noindex'] = isset( $term->admeta['noindex'] ) ? $term->admeta['noindex'] : '';
254
+ $data['nofollow'] = isset( $term->admeta['nofollow'] ) ? $term->admeta['nofollow'] : '';
255
+ $data['noarchive'] = isset( $term->admeta['noarchive'] ) ? $term->admeta['noarchive'] : '';
256
+ $flag = isset( $term->admeta['saved_flag'] ) ? (bool) $term->admeta['saved_flag'] : false;
257
+
258
+ //* Genesis data fetch. This will override our options with Genesis options on save.
259
+ if ( false === $flag && isset( $term->meta ) ) {
260
+ $data['title'] = empty( $data['title'] ) && isset( $term->meta['doctitle'] ) ? $term->meta['doctitle'] : $data['noindex'];
261
+ $data['description'] = empty( $data['description'] ) && isset( $term->meta['description'] ) ? $term->meta['description'] : $data['description'];
262
+ $data['noindex'] = empty( $data['noindex'] ) && isset( $term->meta['noindex'] ) ? $term->meta['noindex'] : $data['noindex'];
263
+ $data['nofollow'] = empty( $data['nofollow'] ) && isset( $term->meta['nofollow'] ) ? $term->meta['nofollow'] : $data['nofollow'];
264
+ $data['noarchive'] = empty( $data['noarchive'] ) && isset( $term->meta['noarchive'] ) ? $term->meta['noarchive'] : $data['noarchive'];
265
+ }
266
+
267
+ return $data;
268
+ }
269
+
270
+ //* Return null if no term can be set.
271
+ return null;
272
+ }
273
+
274
+ /**
275
+ * Try to fetch a term if none can be found.
276
+ *
277
+ * @since 2.6.0
278
+ * @access private
279
+ *
280
+ * @param int $id The possible taxonomy Term ID.
281
+ * @return null|object The Term object.
282
+ */
283
+ public function fetch_the_term( $id = '' ) {
284
+
285
+ static $term = array();
286
+
287
+ if ( isset( $term[$id] ) )
288
+ return $term[$id];
289
+
290
+ //* Return null if no term can be set.
291
+ if ( false === $this->is_archive() )
292
+ return false;
293
+
294
+ if ( $this->is_admin() ) {
295
+ global $current_screen;
296
+
297
+ if ( isset( $current_screen->taxonomy ) ) {
298
+ $term_id = $id ? $id : $this->get_admin_term_id();
299
+ $term[$id] = get_term_by( 'id', $term_id, $current_screen->taxonomy );
300
+ }
301
+ } else {
302
+ if ( $this->is_category() || $this->is_tag() ) {
303
+ $term[$id] = get_queried_object();
304
+ } else if ( $this->is_tax() ) {
305
+ $term[$id] = get_term_by( 'slug', get_query_var( 'term' ), get_query_var( 'taxonomy' ) );
306
+ }
307
+ }
308
+
309
+ if ( isset( $term[$id] ) )
310
+ return $term[$id];
311
+
312
+ return $term[$id] = false;
313
+ }
314
+
315
+ /**
316
+ * Fetch Tax labels
317
+ *
318
+ * @since 2.3.1
319
+ * @staticvar object $labels
320
+ *
321
+ * @param string $tax_type the Taxonomy type.
322
+ * @return object|null with all the labels as member variables
323
+ */
324
+ public function get_tax_labels( $tax_type ) {
325
+
326
+ static $labels = null;
327
+
328
+ if ( isset( $labels ) )
329
+ return $labels;
330
+
331
+ $tax_object = get_taxonomy( $tax_type );
332
+
333
+ if ( is_object( $tax_object ) )
334
+ return $labels = (object) $tax_object->labels;
335
+
336
+ //* Nothing found.
337
+ return null;
338
+ }
339
+
340
+ /**
341
+ * Get the current screen term labels.
342
+ *
343
+ * @since 2.6.0
344
+ * @staticvar string $term_name : Caution: This function only runs once per screen and doesn't check the term type more than once.
345
+ *
346
+ * @param object $term The Taxonomy Term object.
347
+ * @param bool $singular Whether to fetch a singular or plural name.
348
+ * @param bool $fallback Whether to fallback on a generic name.
349
+ * @param bool $use_cache Whether to read from cache.
350
+ * @return string the Term name.
351
+ */
352
+ protected function get_the_term_name( $term, $singular = true, $fallback = true, $use_cache = true ) {
353
+
354
+ if ( false === $use_cache ) {
355
+ //* No cache. Short circuit.
356
+
357
+ if ( $term && is_object( $term ) ) {
358
+ $tax_type = $term->taxonomy;
359
+ $term_labels = $this->get_tax_labels( $tax_type );
360
+
361
+ if ( $singular ) {
362
+ if ( isset( $term_labels->singular_name ) )
363
+ return $term_labels->singular_name;
364
+ } else {
365
+ if ( isset( $term_labels->name ) )
366
+ return $term_labels->name;
367
+ }
368
+ }
369
+
370
+ if ( $fallback ) {
371
+ //* Fallback to Page as it is generic.
372
+ if ( $singular )
373
+ return __( 'Page', 'autodescription' );
374
+
375
+ return __( 'Pages', 'autodescription' );
376
+ }
377
+ }
378
+
379
+ static $term_name = array();
380
+
381
+ if ( isset( $term_name[$singular] ) )
382
+ return $term_name[$singular];
383
+
384
+ if ( $term && is_object( $term ) ) {
385
+ $tax_type = $term->taxonomy;
386
+
387
+ static $term_labels = null;
388
+
389
+ /**
390
+ * Dynamically fetch the term name.
391
+ *
392
+ * @since 2.3.1
393
+ */
394
+ if ( is_null( $term_labels ) )
395
+ $term_labels = $this->get_tax_labels( $tax_type );
396
+
397
+ if ( $singular ) {
398
+ if ( isset( $term_labels->singular_name ) )
399
+ return $term_name[$singular] = $term_labels->singular_name;
400
+ } else {
401
+ if ( isset( $term_labels->name ) )
402
+ return $term_name[$singular] = $term_labels->name;
403
+ }
404
+ }
405
+
406
+ if ( $fallback ) {
407
+ //* Fallback to Page as it is generic.
408
+ if ( $singular )
409
+ return $term_name[$singular] = __( 'Page', 'autodescription' );
410
+
411
+ return $term_name[$singular] = __( 'Pages', 'autodescription' );
412
+ }
413
+
414
+ return $term_name[$singular] = '';
415
+ }
416
+
417
+ }
inc/classes/transients.class.php ADDED
@@ -0,0 +1,660 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The SEO Framework plugin
4
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License version 3 as published
8
+ * by the Free Software Foundation.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ /**
20
+ * Class AutoDescription_Transients
21
+ *
22
+ * Generates, stores and deletes common transients.
23
+ *
24
+ * @since 2.3.3
25
+ */
26
+ class AutoDescription_Transients extends AutoDescription_Sitemaps {
27
+
28
+ /**
29
+ * The sitemap transient name.
30
+ *
31
+ * @since 2.2.9
32
+ *
33
+ * @var string The Sitemap Transient Name.
34
+ */
35
+ protected $sitemap_transient;
36
+
37
+ /**
38
+ * The Automatic Description transient name.
39
+ *
40
+ * @since 2.3.3
41
+ *
42
+ * @var string The Automatic Description Transient Name.
43
+ */
44
+ protected $auto_description_transient;
45
+
46
+ /**
47
+ * The LD+Json script transient name.
48
+ *
49
+ * @since 2.3.3
50
+ *
51
+ * @var string The LD+Json Script Transient Name.
52
+ */
53
+ protected $ld_json_transient;
54
+
55
+ /**
56
+ * The Theme is doing the Title right transient name
57
+ *
58
+ * @since 2.5.2
59
+ *
60
+ * @var string The Theme Doing It Right Transient Name.
61
+ */
62
+ protected $theme_doing_it_right_transient;
63
+
64
+ /**
65
+ * Constructor, load parent constructor and set up caches.
66
+ */
67
+ public function __construct() {
68
+ parent::__construct();
69
+
70
+ // Setup Transient names
71
+ add_action( 'plugins_loaded', array( $this, 'setup_transient_names' ), 10 );
72
+
73
+ /**
74
+ * Delete Sitemap and Description transients on post publish/delete.
75
+ * @see WP Core wp_transition_post_status()
76
+ */
77
+ add_action( 'publish_post', array( $this, 'delete_transients_post' ) );
78
+ add_action( 'publish_page', array( $this, 'delete_transients_post' ) );
79
+ add_action( 'deleted_post', array( $this, 'delete_transients_post' ) );
80
+ add_action( 'deleted_page', array( $this, 'delete_transients_post' ) );
81
+ add_action( 'post_updated', array( $this, 'delete_transients_post' ) );
82
+ add_action( 'page_updated', array( $this, 'delete_transients_post' ) );
83
+
84
+ add_action( 'profile_update', array( $this, 'delete_transients_author' ) );
85
+
86
+ add_action( 'edit_term', array( $this, 'delete_auto_description_transients_term' ), 10, 3 );
87
+ add_action( 'delete_term', array( $this, 'delete_auto_description_transients_term' ), 10, 4 );
88
+
89
+ //* Delete Sitemap transient on permalink structure change.
90
+ add_action( 'load-options-permalink.php', array( $this, 'delete_sitemap_transient_permalink_updated' ), 20 );
91
+
92
+ add_action( 'update_option_blogdescription', array( $this, 'delete_auto_description_blog_transient' ), 10, 1 );
93
+
94
+ //* Delete doing it wrong transient after theme switch.
95
+ add_action( 'after_switch_theme', array( $this, 'delete_theme_dir_transient' ), 10 );
96
+
97
+ }
98
+
99
+ /**
100
+ * Get the value of the transient.
101
+ *
102
+ * If the transient does not exists, does not have a value or has expired,
103
+ * or transients have been disabled through a constant, then the transient
104
+ * will be false.
105
+ * @see $this->the_seo_framework_use_transients
106
+ *
107
+ * @since 2.6.0
108
+ *
109
+ * @param string $transient Transient name. Expected to not be SQL-escaped.
110
+ *
111
+ * @return mixed|bool Value of the transient. False on failure or non existing transient.
112
+ */
113
+ public function get_transient( $transient ) {
114
+
115
+ if ( $this->the_seo_framework_use_transients )
116
+ return get_transient( $transient );
117
+
118
+ return false;
119
+ }
120
+
121
+ /**
122
+ * Set the value of the transient..
123
+ *
124
+ * Prevents setting of transients when they're disabled.
125
+ * @see $this->the_seo_framework_use_transients
126
+ *
127
+ * @since 2.6.0
128
+ *
129
+ * @param string $transient Transient name. Expected to not be SQL-escaped.
130
+ * @param string $value Transient value. Expected to not be SQL-escaped.
131
+ * @param int $expiration Optional Transient expiration date, optional. Expected to not be SQL-escaped.
132
+ */
133
+ public function set_transient( $transient, $value, $expiration = '' ) {
134
+
135
+ if ( $this->the_seo_framework_use_transients )
136
+ set_transient( $transient, $value, $expiration );
137
+
138
+ }
139
+
140
+ /**
141
+ * Setup vars for general site transients.
142
+ *
143
+ * @global int $blog_id
144
+ *
145
+ * @since 2.3.3
146
+ */
147
+ public function setup_transient_names() {
148
+ global $blog_id;
149
+
150
+ /**
151
+ * When the caching mechanism changes. Change this value.
152
+ * Use hex. e.g. 0, 1, 2, 9, a, b
153
+ */
154
+ $revision = '0';
155
+
156
+ $this->sitemap_transient = 'tsf_sitemap_' . (string) $revision . '_' . (string) $blog_id;
157
+ $this->theme_doing_it_right_transient = 'tsf_tdir_' . (string) $revision . '_' . (string) $blog_id;
158
+ }
159
+
160
+ /**
161
+ * Setup vars for transients which require $page_id.
162
+ *
163
+ * @since 2.3.3
164
+ *
165
+ * @param int|string|bool $page_id the Taxonomy or Post ID. If false it will generate for the blog page.
166
+ * @param string $taxonomy The taxonomy name.
167
+ * @param strgin $type The Post Type
168
+ */
169
+ public function setup_auto_description_transient( $page_id, $taxonomy = '', $type = null ) {
170
+
171
+ $cache_key = $this->generate_cache_key( $page_id, $taxonomy, $type );
172
+
173
+ /**
174
+ * When the caching mechanism changes. Change this value.
175
+ * Use hex. e.g. 0, 1, 2, 9, a, b
176
+ *
177
+ * @since 2.3.4
178
+ */
179
+ $revision = '1';
180
+
181
+ $additions = $this->add_description_additions( $page_id, $taxonomy );
182
+
183
+ if ( $additions ) {
184
+ $option = $this->get_option( 'description_blogname' ) ? '1' : '0';
185
+ $this->auto_description_transient = 'tsf_desc_' . $option . '_' . $revision . '_' . $cache_key;
186
+ } else {
187
+ $this->auto_description_transient = 'tsf_desc_noa_' . $revision . '_' . $cache_key;
188
+ }
189
+
190
+ }
191
+
192
+ /**
193
+ * Setup vars for transients which require $page_id.
194
+ *
195
+ * @since 2.3.3
196
+ *
197
+ * @param int|string|bool $page_id the Taxonomy or Post ID. If false it will generate for the blog page.
198
+ * @param string $taxonomy The taxonomy name.
199
+ * @param string|null $type The post type.
200
+ */
201
+ public function setup_ld_json_transient( $page_id, $taxonomy = '', $type = null ) {
202
+
203
+ $cache_key = $this->generate_cache_key( $page_id, $taxonomy, $type );
204
+
205
+ /**
206
+ * When the caching mechanism changes. Change this value.
207
+ *
208
+ * Use hex. e.g. 0, 1, 2, 9, a, b
209
+ */
210
+ $revision = '7';
211
+
212
+ /**
213
+ * Change key based on options.
214
+ */
215
+ $options = $this->enable_ld_json_breadcrumbs() ? '1' : '0';
216
+ $options .= $this->enable_ld_json_sitename() ? '1' : '0';
217
+ $options .= $this->enable_ld_json_searchbox() ? '1' : '0';
218
+
219
+ $this->ld_json_transient = 'the_seo_f' . $revision . '_' . $options . '_ldjs_' . $cache_key;
220
+ }
221
+
222
+ /**
223
+ * Generate transient key based on query vars.
224
+ *
225
+ * @global int $blog_id;
226
+ *
227
+ * @since 2.3.3
228
+ * @since 2.6.0 Refactored.
229
+ * @staticvar array $cached_id : contains cache strings.
230
+ *
231
+ * @param int|string|bool $page_id the Taxonomy or Post ID.
232
+ * @param string $taxonomy The Taxonomy name.
233
+ * @param string $type The Post Type
234
+ * @return string The generated page id key.
235
+ */
236
+ public function generate_cache_key( $page_id, $taxonomy = '', $type = null ) {
237
+
238
+ $page_id = $page_id ? $page_id : $this->get_the_real_ID();
239
+
240
+ static $cached_id = array();
241
+
242
+ if ( isset( $cached_id[$page_id][$taxonomy][$type] ) )
243
+ return $cached_id[$page_id][$taxonomy][$type];
244
+
245
+ //* Placeholder ID.
246
+ $the_id = '';
247
+ $t = $taxonomy;
248
+
249
+ if ( isset( $type ) ) {
250
+ if ( 'author' === $type ) {
251
+ //* Author page.
252
+ $the_id = 'author_' . $page_id;
253
+ } else if ( 'frontpage' === $type ) {
254
+ //* Front/HomePage.
255
+ $the_id = $this->generate_front_page_cache_key();
256
+ } else {
257
+ $this->_doing_it_wrong( __CLASS__ . '::' . __FUNCTION__, __( 'Third parameter must be a known type.', 'autodescription' ), '2.6.5' );
258
+ $the_id = esc_sql( $type . '_' . $page_id . '_' . $t );
259
+ }
260
+ } else if ( $this->is_404() ) {
261
+ $the_id = '_404_';
262
+ } else if ( ( $this->is_front_page( $page_id ) ) || ( $this->is_admin() && $this->is_menu_page( $this->pagehook ) ) ) {
263
+ //* Front/HomePage.
264
+ $the_id = $this->generate_front_page_cache_key();
265
+ } else if ( $this->is_blog_page( $page_id ) ) {
266
+ $the_id = 'blog_' . $page_id;
267
+ } else if ( $this->is_singular() ) {
268
+ if ( $this->is_page( $page_id ) ) {
269
+ $the_id = 'page_' . $page_id;
270
+ } else if ( $this->is_single( $page_id ) ) {
271
+ $the_id = 'post_' . $page_id;
272
+ } else if ( $this->is_attachment( $page_id ) ) {
273
+ $the_id = 'attach_' . $page_id;
274
+ } else {
275
+ //* Other.
276
+ $the_id = 'singular_' . $page_id;
277
+ }
278
+ } else if ( $this->is_search() ) {
279
+ $query = '';
280
+
281
+ if ( function_exists( 'get_search_query' ) ) {
282
+ $search_query = get_search_query();
283
+
284
+ if ( $search_query )
285
+ $query = str_replace( ' ', '', $search_query );
286
+
287
+ //* Limit to 10 chars.
288
+ if ( mb_strlen( $query ) > 10 )
289
+ $query = mb_substr( $query, 0, 10 );
290
+
291
+ $query = esc_sql( $query );
292
+ }
293
+
294
+ $the_id = $page_id . '_s_' . $query;
295
+ } else if ( $this->is_archive() ) {
296
+ if ( $this->is_category() || $this->is_tag() || $this->is_tax() ) {
297
+
298
+ if ( empty( $t ) ) {
299
+ $o = get_queried_object();
300
+
301
+ if ( isset( $o->taxonomy ) )
302
+ $t = $o->taxonomy;
303
+ }
304
+
305
+ $the_id = $this->generate_taxonomial_cache_key( $page_id, $t );
306
+
307
+ if ( $this->is_tax() )
308
+ $the_id = 'archives_' . $the_id;
309
+
310
+ } else if ( $this->is_author() ) {
311
+ $the_id = 'author_' . $page_id;
312
+ } else if ( $this->is_date() ) {
313
+ $post = get_post();
314
+
315
+ if ( $post && isset( $post->post_date ) ) {
316
+ $date = $post->post_date;
317
+
318
+ if ( $this->is_year() ) {
319
+ $the_id .= 'year_' . mysql2date( 'y', $date, false );
320
+ } else if ( $this->is_month() ) {
321
+ $the_id .= 'month_' . mysql2date( 'm_y', $date, false );
322
+ } else if ( $this->is_day() ) {
323
+ //* Day. The correct notation.
324
+ $the_id .= 'day_' . mysql2date( 'd_m_y', $date, false );
325
+ }
326
+ } else {
327
+ //* Get seconds since UNIX Epoch. This is a failsafe.
328
+
329
+ /**
330
+ * @staticvar string $unix : Used to maintain a static timestamp for this query.
331
+ */
332
+ static $unix = null;
333
+
334
+ if ( ! isset( $unix ) )
335
+ $unix = date( 'U' );
336
+
337
+ //* Temporarily disable caches to prevent database spam.
338
+ $this->the_seo_framework_use_transients = false;
339
+ $this->use_object_cache = false;
340
+
341
+ $the_id = 'unix_' . $unix;
342
+ }
343
+ } else {
344
+ //* Other taxonomial archives.
345
+
346
+ if ( empty( $t ) ) {
347
+ $post_type = get_query_var( 'post_type' );
348
+
349
+ if ( is_array( $post_type ) )
350
+ reset( $post_type );
351
+
352
+ if ( $post_type )
353
+ $post_type_obj = get_post_type_object( $post_type );
354
+
355
+ if ( isset( $post_type_obj->labels->name ) )
356
+ $t = $post_type_obj->labels->name;
357
+ }
358
+
359
+ //* Still empty? Try this.
360
+ if ( empty( $t ) )
361
+ $t = get_query_var( 'taxonomy' );
362
+
363
+ $the_id = $this->generate_taxonomial_cache_key( $page_id, $t );
364
+
365
+ $the_id = 'archives_' . $the_id;
366
+ }
367
+ }
368
+
369
+ /**
370
+ * Blog page isn't set or something else is happening. Causes all kinds of problems :(
371
+ * Noob. :D
372
+ */
373
+ if ( empty( $the_id ) )
374
+ $the_id = 'noob_' . $page_id . '_' . $t;
375
+
376
+ global $blog_id;
377
+
378
+ $locale = strtolower( get_locale() );
379
+
380
+ /**
381
+ * This should be at most 25 chars. Unless the $blog_id is higher than 99,999,999.
382
+ * Then some cache keys will conflict on every 10th blog ID from eachother which post something on the same day..
383
+ * On the day archive. With the same description setting (short).
384
+ */
385
+ return $cached_id[$page_id][$taxonomy][$type] = $the_id . '_' . $blog_id . '_' . $locale;
386
+ }
387
+
388
+ /**
389
+ * Returns the front page partial transient key.
390
+ *
391
+ * @param string $type
392
+ *
393
+ * @return string the front page transient key.
394
+ */
395
+ public function generate_front_page_cache_key( $type = '' ) {
396
+
397
+ if ( empty( $type ) ) {
398
+ if ( $this->has_page_on_front() )
399
+ $type = 'page';
400
+ else
401
+ $type = 'blog';
402
+ } else {
403
+ $type = esc_sql( $type );
404
+ }
405
+
406
+ return $the_id = 'h' . $type . '_' . $this->get_the_front_page_ID();
407
+ }
408
+
409
+ /**
410
+ * Generates Cache key for taxonomial archives.
411
+ *
412
+ * @since 2.6.0
413
+ *
414
+ * @param int $page_id The taxonomy or page ID.
415
+ * @param string $taxonomy The taxonomy name.
416
+ *
417
+ * @return string The Taxonomial Archive cache key.
418
+ */
419
+ protected function generate_taxonomial_cache_key( $page_id = '', $taxonomy = '' ) {
420
+
421
+ $the_id = '';
422
+
423
+ if ( false !== strpos( $taxonomy, '_' ) ) {
424
+ $taxonomy_name = explode( '_', $taxonomy );
425
+ if ( is_array( $taxonomy_name ) ) {
426
+ foreach ( $taxonomy_name as $name )
427
+ if ( mb_strlen( $name ) >= 3 ) {
428
+ $the_id .= mb_substr( $name, 0, 3 ) . '_';
429
+ } else {
430
+ $the_id = strtolower( $name ) . '_';
431
+ }
432
+ }
433
+ }
434
+
435
+ if ( empty( $the_id ) ) {
436
+ if ( mb_strlen( $taxonomy ) >= 5 ) {
437
+ $the_id = mb_substr( $taxonomy, 0, 5 );
438
+ } else {
439
+ $the_id = esc_sql( $taxonomy );
440
+ }
441
+ }
442
+
443
+ $the_id = strtolower( $the_id );
444
+
445
+ //* Put it all together.
446
+ return rtrim( $the_id, '_' ) . '_' . $page_id;
447
+ }
448
+
449
+ /**
450
+ * Delete transient on post save.
451
+ *
452
+ * @since 2.2.9
453
+ *
454
+ * @param int $post_id The Post ID that has been updated.
455
+ * @return bool|null True when sitemap is flushed. False on revision. Null
456
+ * when sitemaps are deactivated.
457
+ */
458
+ public function delete_transients_post( $post_id ) {
459
+
460
+ $this->delete_auto_description_transient( $post_id );
461
+ $this->delete_ld_json_transient( $post_id );
462
+
463
+ if ( $this->is_option_checked( 'sitemaps_output' ) ) {
464
+
465
+ //* Don't flush sitemap on revision.
466
+ if ( wp_is_post_revision( $post_id ) )
467
+ return false;
468
+
469
+ $this->delete_sitemap_transient();
470
+
471
+ return true;
472
+ }
473
+ }
474
+
475
+ /**
476
+ * Delete transient on profile save.
477
+ *
478
+ * @since 2.6.4
479
+ *
480
+ * @param int $user_id The User ID that has been updated.
481
+ */
482
+ public function delete_transients_author( $user_id ) {
483
+ $this->delete_auto_description_transient( $user_id, 'author', 'author' );
484
+ }
485
+
486
+ /**
487
+ * Delete transient on term save/deletion.
488
+ *
489
+ * @param int $term_id The Term ID
490
+ * @param int $tt_id The Term Taxonomy ID.
491
+ * @param string $taxonomy The Taxonomy type.
492
+ * @param mixed $deleted_term Copy of the already-deleted term. Unused.
493
+ *
494
+ * @since 2.3.3
495
+ */
496
+ public function delete_auto_description_transients_term( $term_id, $tt_id, $taxonomy, $deleted_term = '' ) {
497
+
498
+ $term_id = $term_id ? $term_id : $tt_id;
499
+
500
+ $this->delete_auto_description_transient( $term_id, $taxonomy );
501
+ }
502
+
503
+ /**
504
+ * Checks whether the permalink structure is updated.
505
+ *
506
+ * @since 2.3.0
507
+ *
508
+ * @return bool Whether if sitemap transient is deleted.
509
+ */
510
+ public function delete_sitemap_transient_permalink_updated() {
511
+
512
+ if ( isset( $_POST['permalink_structure'] ) || isset( $_POST['category_base'] ) )
513
+ return $this->delete_sitemap_transient();
514
+
515
+ return false;
516
+ }
517
+
518
+ /**
519
+ * Delete transient for sitemap on requests.
520
+ * Also ping search engines.
521
+ *
522
+ * @since 2.2.9
523
+ *
524
+ * @return bool true
525
+ */
526
+ public function delete_sitemap_transient() {
527
+
528
+ delete_transient( $this->sitemap_transient );
529
+
530
+ $this->ping_searchengines();
531
+
532
+ return true;
533
+ }
534
+
535
+ /**
536
+ * Delete transient for the automatic description for blog on save request.
537
+ * Returns old option, since that's passed for sanitation within WP Core.
538
+ *
539
+ * @since 2.3.3
540
+ *
541
+ * @param string $old_option The previous blog description option.
542
+ * @return string Previous option.
543
+ */
544
+ public function delete_auto_description_blog_transient( $old_option ) {
545
+
546
+ $this->setup_auto_description_transient( (int) get_option( 'page_for_posts' ) );
547
+
548
+ delete_transient( $this->auto_description_transient );
549
+
550
+ return $old_option;
551
+ }
552
+
553
+ /**
554
+ * Delete transient for the automatic description on requests.
555
+ *
556
+ * @since 2.3.3
557
+ *
558
+ * @param mixed $page_id The page ID or identifier.
559
+ * @param string $taxonomy The tt name.
560
+ * @param string $type The Post Type
561
+ * @return bool true
562
+ */
563
+ public function delete_auto_description_transient( $page_id, $taxonomy = '', $type = null ) {
564
+
565
+ $this->setup_auto_description_transient( $page_id, $taxonomy, $type );
566
+
567
+ delete_transient( $this->auto_description_transient );
568
+
569
+ return true;
570
+ }
571
+
572
+ /**
573
+ * Delete transient for the LD+Json scripts on requests.
574
+ *
575
+ * @since 2.4.2
576
+ *
577
+ * @param mixed $page_id The page ID or identifier.
578
+ * @param string $taxonomy The tt name.
579
+ * @param string|null $type The post type.
580
+ * @return bool true
581
+ */
582
+ public function delete_ld_json_transient( $page_id, $taxonomy = '', $type = null ) {
583
+
584
+ static $flushed = null;
585
+
586
+ if ( ! isset( $flushed ) ) {
587
+ $this->setup_ld_json_transient( $page_id, $taxonomy, $type );
588
+
589
+ delete_transient( $this->ld_json_transient );
590
+
591
+ $flushed = 'Oh behave!';
592
+
593
+ return true;
594
+ }
595
+
596
+ return false;
597
+ }
598
+
599
+ /**
600
+ * Delete transient for the Theme doing it Right bool on special requests.
601
+ *
602
+ * @since 2.5.2
603
+ *
604
+ * @return bool true
605
+ */
606
+ public function delete_theme_dir_transient() {
607
+
608
+ delete_transient( $this->theme_doing_it_right_transient );
609
+
610
+ return true;
611
+ }
612
+
613
+ /**
614
+ * Sets transient for Theme doing it Right
615
+ *
616
+ * @since 2.5.2
617
+ *
618
+ * @param bool $doing_it_right
619
+ */
620
+ public function set_theme_dir_transient( $dir = '' ) {
621
+
622
+ if ( is_bool( $dir ) && false === get_transient( $this->theme_doing_it_right_transient ) ) {
623
+
624
+ //* Convert $dir to string 1 or 0 as transients can be false on failure.
625
+ $dir = $dir ? '1' : '0';
626
+
627
+ /**
628
+ * Expiration time, 3 days.
629
+ * 60s * 60m * 24d * 3d
630
+ */
631
+ $expiration = DAY_IN_SECONDS * 3;
632
+
633
+ set_transient( $this->theme_doing_it_right_transient, $dir, $expiration );
634
+ }
635
+
636
+ }
637
+
638
+ /**
639
+ * Flushes the home page LD+Json transient.
640
+ *
641
+ * @since 2.6.0
642
+ * @staticvar bool $flushed Prevents second flush.
643
+ *
644
+ * @return bool Whether it's flushed on current call.
645
+ */
646
+ public function delete_front_ld_json_transient() {
647
+
648
+ static $flushed = null;
649
+
650
+ if ( isset( $flushed ) )
651
+ return false;
652
+
653
+ $front_id = $this->get_the_front_page_ID();
654
+
655
+ $this->delete_ld_json_transient( $front_id, '', 'frontpage' );
656
+
657
+ return $flushed = true;
658
+ }
659
+
660
+ }
inc/deprecated/deprecated.class.php ADDED
@@ -0,0 +1,729 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The SEO Framework plugin
4
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License version 3 as published
8
+ * by the Free Software Foundation.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ /**
20
+ * Deprecation class.
21
+ * Contains all deprecated functions. Is autoloaded.
22
+ *
23
+ * @since 2.3.4
24
+ */
25
+ class The_SEO_Framework_Deprecated extends AutoDescription_Feed {
26
+
27
+ /**
28
+ * Constructor. Load parent constructor.
29
+ */
30
+ public function __construct() {
31
+ parent::__construct();
32
+ }
33
+
34
+ /**
35
+ * Return option from the options table and cache result.
36
+ *
37
+ * @since 2.0.0
38
+ *
39
+ * @deprecated
40
+ * @since 2.3.4
41
+ */
42
+ public function autodescription_get_option( $key, $setting = null, $use_cache = true ) {
43
+ $this->_deprecated_function( 'AutoDescription_Adminpages::' . __FUNCTION__, '2.3.4', 'AutoDescription_Adminpages::the_seo_framework_get_option()' );
44
+
45
+ return $this->the_seo_framework_get_option( $key, $setting, $use_cache );
46
+ }
47
+
48
+ /**
49
+ * Enqueues JS in the admin footer
50
+ *
51
+ * @since 2.1.9
52
+ *
53
+ * @deprecated
54
+ * @since 2.3.3
55
+ *
56
+ * @param $hook the current page
57
+ */
58
+ public function enqueue_javascript( $hook ) {
59
+ $this->_deprecated_function( 'AutoDescription_Admin_Init::' . __FUNCTION__, '2.3.3', 'AutoDescription_Admin_Init::enqueue_admin_scripts()' );
60
+
61
+ return $this->enqueue_admin_scripts( $hook );
62
+ }
63
+
64
+ /**
65
+ * Enqueues CSS in the admin header
66
+ *
67
+ * @since 2.1.9
68
+ *
69
+ * @deprecated
70
+ * @since 2.3.3
71
+ *
72
+ * @param $hook the current page
73
+ */
74
+ public function enqueue_css( $hook ) {
75
+ $this->_deprecated_function( 'AutoDescription_Admin_Init::' . __FUNCTION__, '2.3.3', 'AutoDescription_Admin_Init::enqueue_admin_scripts()' );
76
+
77
+ return $this->enqueue_admin_scripts( $hook );
78
+ }
79
+
80
+ /**
81
+ * Setup var for sitemap transient.
82
+ *
83
+ * @since 2.2.9
84
+ *
85
+ * @deprecated
86
+ * @since 2.3.3
87
+ */
88
+ public function fetch_sitemap_transient_name() {
89
+ $this->_deprecated_function( 'AutoDescription_Transients::' . __FUNCTION__, '2.3.3', 'AutoDescription_Transients::$sitemap_transient' );
90
+
91
+ return $this->sitemap_transient;
92
+ }
93
+
94
+ /**
95
+ * Delete Sitemap transient on post save.
96
+ *
97
+ * @since 2.2.9
98
+ *
99
+ * @deprecated
100
+ * @since 2.3.3
101
+ */
102
+ public function delete_sitemap_transient_post( $post_id ) {
103
+ $this->_deprecated_function( 'AutoDescription_Transients::' . __FUNCTION__, '2.3.3', 'AutoDescription_Transients::delete_sitemap_transient_post()' );
104
+
105
+ return $this->delete_transients_post( $post_id );
106
+ }
107
+
108
+ /**
109
+ * Helper function for Doing it Wrong
110
+ *
111
+ * @since 2.2.4
112
+ *
113
+ * @deprecated
114
+ * @since 2.3.0
115
+ */
116
+ public function autodescription_version( $version = '' ) {
117
+ $this->_deprecated_function( 'The_SEO_Framework_Load::' . __FUNCTION__, '2.3.0', 'The_SEO_Framework_Load::the_seo_framework_version()' );
118
+
119
+ return $this->the_seo_framework_version( $version );
120
+ }
121
+
122
+ /**
123
+ * Include the necessary sortable metabox scripts.
124
+ *
125
+ * @since 2.2.2
126
+ *
127
+ * @deprecated
128
+ * @since 2.3.5
129
+ */
130
+ public function scripts() {
131
+ $this->_deprecated_function( 'AutoDescription_Adminpages::' . __FUNCTION__, '2.3.5', 'AutoDescription_Adminpages::metabox_scripts()' );
132
+
133
+ return $this->metabox_scripts();
134
+ }
135
+
136
+ /**
137
+ * Setup var for sitemap transient on init/admin_init.
138
+ *
139
+ * @since 2.3.3
140
+ * @deprecated
141
+ * @since 2.3.3
142
+ * Oops.
143
+ */
144
+ public function setup_transient_names_init() {
145
+ $this->_deprecated_function( 'AutoDescription_Transients::' . __FUNCTION__, '2.3.3', 'AutoDescription_Transients::setup_transient_names()' );
146
+
147
+ $this->setup_transient_names();
148
+ return false;
149
+ }
150
+
151
+ /**
152
+ * Helper function for allowed post/page screens where this plugin is active.
153
+ *
154
+ * @param array $screens The allowed screens
155
+ *
156
+ * @since 2.1.9
157
+ *
158
+ * Applies filters the_seo_framework_supported_screens : The supported administration
159
+ * screens where css and javascript files are loaded.
160
+ *
161
+ * @param array $args the custom supported screens.
162
+ *
163
+ * @deprecated
164
+ * @since 2.5.2
165
+ *
166
+ * @return array $screens
167
+ */
168
+ protected function supported_screens( $args = array() ) {
169
+ $this->_deprecated_function( 'AutoDescription_Admin_Init::' . __FUNCTION__, '2.5.2' );
170
+
171
+ /**
172
+ * Instead of supporting page ID's, we support the Page base now.
173
+ *
174
+ * @since 2.3.3
175
+ */
176
+ $defaults = array(
177
+ 'edit',
178
+ 'post',
179
+ 'edit-tags',
180
+ );
181
+
182
+ $screens = (array) apply_filters( 'the_seo_framework_supported_screens', $defaults, $args );
183
+ $screens = wp_parse_args( $args, $screens );
184
+
185
+ return $screens;
186
+ }
187
+
188
+ /**
189
+ * Add doing it wrong html code in the footer.
190
+ *
191
+ * @since 2.2.5
192
+ *
193
+ * @deprecated
194
+ * @since 2.5.2.1
195
+ */
196
+ public function title_doing_it_wrong() {
197
+ $this->_deprecated_function( 'AutoDescription_Detect::' . __FUNCTION__, '2.5.2.1', 'AutoDescription_Detect::tell_title_doing_it_wrong()' );
198
+
199
+ return;
200
+ }
201
+
202
+ /**
203
+ * Checks a theme's support for a given feature
204
+ *
205
+ * @since 2.2.5
206
+ *
207
+ * @global array $_wp_theme_features
208
+ *
209
+ * @param string $feature the feature being checked
210
+ * @return bool
211
+ *
212
+ * Taken from WP Core, but it now returns true on title-tag support.
213
+ *
214
+ * @deprecated
215
+ * @since 2.6.0
216
+ */
217
+ public function current_theme_supports( $feature ) {
218
+ $this->_deprecated_function( 'AutoDescription_Detect::' . __FUNCTION__, '2.6.0', 'current_theme_supports()' );
219
+
220
+ return current_theme_supports();
221
+ }
222
+
223
+ /**
224
+ * Echo debug values.
225
+ *
226
+ * @param mixed $values What to be output.
227
+ *
228
+ * @since 2.3.4
229
+ *
230
+ * @deprecated
231
+ * @since 2.6.0
232
+ */
233
+ public function echo_debug_information( $values ) {
234
+ $this->_deprecated_function( 'AutoDescription_Debug::' . __FUNCTION__, '2.6.0', 'AutoDescription_Debug::get_debug_information()' );
235
+
236
+ echo $this->get_debug_information( $values );
237
+
238
+ }
239
+
240
+ /**
241
+ * Get the archive Title.
242
+ *
243
+ * WordPress core function @since 4.1.0
244
+ *
245
+ * @since 2.3.6
246
+ *
247
+ * @deprecated
248
+ * @since 2.6.0
249
+ */
250
+ public function get_the_archive_title() {
251
+ $this->_deprecated_function( 'AutoDescription_Generate_Description::' . __FUNCTION__, '2.6.0', 'AutoDescription_Generate_Title::get_the_real_archive_title()' );
252
+
253
+ return $this->get_the_real_archive_title();
254
+ }
255
+
256
+ /**
257
+ * Adds the SEO Bar.
258
+ *
259
+ * @param string $column the current column : If it's a taxonomy, this is empty
260
+ * @param int $post_id the post id : If it's a taxonomy, this is the column name
261
+ * @param string $tax_id this is empty : If it's a taxonomy, this is the taxonomy id
262
+ *
263
+ * @param string $status the status in html
264
+ *
265
+ * @staticvar string $type_cache
266
+ * @staticvar string $column_cache
267
+ *
268
+ * @since 2.1.9
269
+ *
270
+ * @deprecated
271
+ * @since 2.6.0
272
+ */
273
+ public function seo_column( $column, $post_id, $tax_id = '' ) {
274
+ $this->_deprecated_function( 'AutoDescription_DoingItRight::' . __FUNCTION__, '2.6.0', 'AutoDescription_DoingItRight::seo_bar()' );
275
+
276
+ return $this->seo_bar( $column, $post_id, $tax_id );
277
+ }
278
+
279
+ /**
280
+ * Ping Yahoo
281
+ *
282
+ * @since 2.2.9
283
+ * @deprecated
284
+ * @since 2.6.0
285
+ */
286
+ public function ping_yahoo() {
287
+ $this->_deprecated_function( 'AutoDescription_Sitemaps::' . __FUNCTION__, '2.6.0', 'AutoDescription_Sitemaps::ping_bing()' );
288
+
289
+ $this->ping_bing();
290
+ }
291
+
292
+ /**
293
+ * Create sitemap.xml content transient.
294
+ *
295
+ * @param string|bool $content required The sitemap transient content.
296
+ *
297
+ * @since 2.2.9
298
+ * @deprecated
299
+ * @since 2.6.0
300
+ */
301
+ public function setup_sitemap_transient( $sitemap_content ) {
302
+ $this->_deprecated_function( 'AutoDescription_Sitemaps::' . __FUNCTION__, '2.6.0', 'AutoDescription_Sitemaps::setup_sitemap()' );
303
+
304
+ return $this->setup_sitemap( $sitemap_content );
305
+ }
306
+
307
+ /**
308
+ * Detect WordPress language.
309
+ * Considers en_UK, en_US, etc.
310
+ *
311
+ * @param string $str Required, the locale.
312
+ * @param bool $use_cache Set to false to bypass the cache.
313
+ *
314
+ * @staticvar array $locale
315
+ * @staticvar string $get_locale
316
+ *
317
+ * @since 2.3.8
318
+ * @deprecated
319
+ * @since 2.6.0
320
+ *
321
+ * @return bool
322
+ */
323
+ public function is_locale( $str, $use_cache = true ) {
324
+ $this->_deprecated_function( 'AutoDescription_Detect::' . __FUNCTION__, '2.6.0', 'AutoDescription_Detect::check_wp_locale()' );
325
+
326
+ return $this->check_wp_locale( $str, $use_cache );
327
+ }
328
+
329
+ /**
330
+ * Build the title based on input, without tagline.
331
+ * Note: Not escaped.
332
+ *
333
+ * @param string $title The Title to return
334
+ * @param array $args : accepted args : {
335
+ * @param int term_id The Taxonomy Term ID
336
+ * @param bool placeholder Generate placeholder, ignoring options.
337
+ * @param bool page_on_front Page on front condition for example generation
338
+ * }
339
+ *
340
+ * @since 2.4.0
341
+ * @deprecated
342
+ * @since 2.6.0
343
+ *
344
+ * @return string Title without tagline.
345
+ */
346
+ public function get_placeholder_title( $title = '', $args = array() ) {
347
+ $this->_deprecated_function( 'AutoDescription_Generate_Title::' . __FUNCTION__, '2.6.0', 'AutoDescription_Generate_Title::title()` with the argument $args[\'notagline\']' );
348
+
349
+ $args['notagline'] = true;
350
+ return $this->title( $title, '', '', $args );
351
+ }
352
+
353
+ /**
354
+ * Initializes default settings very early at the after_setup_theme hook.
355
+ * Admin only.
356
+ *
357
+ * @since 2.5.0
358
+ * @deprecated
359
+ * @since 2.6.0
360
+ *
361
+ * @return void
362
+ */
363
+ public function initialize_defaults_admin() {
364
+ $this->_deprecated_function( 'AutoDescription_Siteoptions::' . __FUNCTION__, '2.6.0' );
365
+ return;
366
+ }
367
+
368
+ /**
369
+ * Initializes default settings very early at the after_setup_theme hook
370
+ * Therefore supports is_rtl().
371
+ *
372
+ * @since 2.5.0
373
+ * @deprecated
374
+ * @since 2.6.0
375
+ *
376
+ * @return void
377
+ */
378
+ public function initialize_defaults() {
379
+ $this->_deprecated_function( 'AutoDescription_Siteoptions::' . __FUNCTION__, '2.6.0' );
380
+ return;
381
+ }
382
+
383
+ /**
384
+ * Old style method for detecting SEO plugins.
385
+ *
386
+ * @since 2.6.1
387
+ * @access private
388
+ *
389
+ * @return bool
390
+ *
391
+ * @thanks StudioPress (http://www.studiopress.com/) for some code.
392
+ */
393
+ public function detect_seo_plugins_old() {
394
+
395
+ /**
396
+ * Applies filters 'the_seo_framework_detect_seo_plugins' : array
397
+ * @deprecated
398
+ * @since 2.6.1
399
+ *
400
+ * Use this filter to adjust plugin tests.
401
+ */
402
+ $plugins_check = apply_filters(
403
+ 'the_seo_framework_detect_seo_plugins',
404
+ //* Add to this array to add new plugin checks.
405
+ null
406
+ );
407
+
408
+ if ( isset( $plugins_check ) ) {
409
+ $this->_deprecated_function( 'the_seo_framework_detect_seo_plugins', 'the_seo_framework_conflicting_plugins', '2.6.1' );
410
+ return $this->detect_plugin( $plugins_check );
411
+ }
412
+
413
+ return null;
414
+ }
415
+
416
+
417
+ /**
418
+ * Detects if plugins outputting og:type exists
419
+ *
420
+ * @note isn't used in $this->og_image() Because og:image may be output multiple times.
421
+ *
422
+ * @uses $this->detect_plugin()
423
+ *
424
+ * @since 1.3.0
425
+ * @return bool OG plugin detected.
426
+ *
427
+ * @staticvar bool $has_plugin
428
+ * @since 2.2.5
429
+ *
430
+ * @return bool $has_plugin one of the plugins has been found.
431
+ */
432
+ public function has_og_plugin() {
433
+
434
+ /**
435
+ * Applies filters 'the_seo_framework_detect_og_plugins' : array
436
+ * @since 2.6.1
437
+ * @deprecated
438
+ * @since 2.6.1 (same patch)
439
+ *
440
+ * Use this filter to adjust plugin tests.
441
+ */
442
+ $plugins_check = apply_filters(
443
+ 'the_seo_framework_detect_og_plugins',
444
+ //* Add to this array to add new plugin checks.
445
+ null
446
+ );
447
+
448
+ if ( isset( $plugins_check ) ) {
449
+ $this->_deprecated_function( 'the_seo_framework_detect_og_plugins', 'the_seo_framework_conflicting_plugins', '2.6.1' );
450
+ return $this->detect_plugin( $plugins_check );
451
+ }
452
+
453
+ return null;
454
+ }
455
+
456
+ /**
457
+ * Detecs sitemap plugins
458
+ *
459
+ * @since 2.1.0
460
+ * @staticvar bool $detected
461
+ *
462
+ * @deprecated
463
+ * @since 2.6.1
464
+ *
465
+ * @return bool
466
+ */
467
+ public function has_sitemap_plugin() {
468
+ $this->_deprecated_function( 'AutoDescription_Detect::' . __FUNCTION__, 'AutoDescription_Detect::detect_sitemap_plugin', '2.6.1' );
469
+
470
+ return $this->detect_sitemap_plugin();
471
+ }
472
+
473
+ /**
474
+ * Returns Post Type from current screen.
475
+ *
476
+ * @param bool $public Whether to only get Public Post types.
477
+ *
478
+ * @since 2.6.0
479
+ *
480
+ * @deprecated
481
+ * @since 2.6.1
482
+ *
483
+ * @return bool|string The Post Type
484
+ */
485
+ public function get_current_post_type( $public = true ) {
486
+
487
+ $this->_deprecated_function( 'AutoDescription_Detect::' . __FUNCTION__, 'AutoDescription_Detect::get_supported_post_type', '2.6.2' );
488
+
489
+ static $post_type = null;
490
+
491
+ //* Detect post type if empty or not set.
492
+ if ( is_null( $post_type ) || empty( $post_type ) ) {
493
+ global $current_screen;
494
+
495
+ if ( isset( $current_screen->post_type ) ) {
496
+
497
+ static $post_page = array();
498
+
499
+ $args = $public ? array( 'public' => true ) : array();
500
+
501
+ if ( ! isset( $post_page[$public] ) )
502
+ $post_page[$public] = (array) get_post_types( $args );
503
+
504
+ //* Smart var. This elemenates the need for a foreach loop, reducing resource usage.
505
+ $post_type = isset( $post_page[$public][ $current_screen->post_type ] ) ? $current_screen->post_type : '';
506
+ }
507
+ }
508
+
509
+ //* No post type has been found.
510
+ if ( empty( $post_type ) )
511
+ return false;
512
+
513
+ return $post_type;
514
+ }
515
+
516
+ /**
517
+ * Add the WPMUdev Domain Mapping rules again. And flush them on init.
518
+ * Domain Mapping bugfix.
519
+ *
520
+ * @param bool $options_saved : If we're in admin and the sanitiation function runs.
521
+ * @param bool $flush_now : Whether to flush directly on call if not yet flushed. Only when $options_saved is false.
522
+ *
523
+ * Runs a flush and updates the site option to "true".
524
+ * When the site option is set to true, it not flush again on init.
525
+ *
526
+ * If $options_saved is true, it will not check for the init action hook and continue,
527
+ * So it will flush the next time on init.
528
+ *
529
+ * @since 2.3.0
530
+ * @access private
531
+ *
532
+ * @deprecated
533
+ * @since 2.6.3
534
+ */
535
+ public function wpmudev_domainmap_flush_fix( $options_saved = false, $flush_now = true ) {
536
+
537
+ $this->_deprecated_function( 'AutoDescription_Sitemaps::' . __FUNCTION__, '', '2.6.2' );
538
+
539
+ if ( $this->pretty_permalinks && $this->is_domainmapping_active() ) {
540
+ if ( $options_saved || 'init' === current_action() ) {
541
+
542
+ if ( class_exists( 'Domainmap_Module_Cdsso' ) && defined( 'Domainmap_Module_Cdsso::SSO_ENDPOINT' ) ) {
543
+ add_rewrite_endpoint( Domainmap_Module_Cdsso::SSO_ENDPOINT, EP_ALL );
544
+
545
+ $name = 'tsf_wpmudev_dm_fix';
546
+ $option = (array) get_site_option( $name, array() );
547
+ $key = get_current_blog_id();
548
+ $value = $this->o_plugin_updated;
549
+
550
+ if ( $options_saved ) {
551
+ //* Reset the flush on option change.
552
+ if ( isset( $option[$key] ) && $value === $option[$key] ) {
553
+ $option[$key] = false;
554
+ update_site_option( $name, $option );
555
+ }
556
+ } else {
557
+ if ( ! isset( $option[$key] ) || false === $option[$key] ) {
558
+ //* Prevent flushing multiple times.
559
+ $option[$key] = $value;
560
+ update_site_option( $name, $option );
561
+
562
+ //* Now flush
563
+ if ( $flush_now )
564
+ $this->flush_rewrite_rules();
565
+ else
566
+ $this->enqueue_rewrite_flush_other( true );
567
+ }
568
+ }
569
+ }
570
+ }
571
+ }
572
+
573
+ }
574
+
575
+ /**
576
+ * Generates relative URL for current post_ID.
577
+ *
578
+ * @param int|object $post The post object or ID.
579
+ * @param bool $external Whether to fetch the WP Request or get the permalink by Post Object.
580
+ * @param int $depr Deprecated The post ID.
581
+ *
582
+ * @since 2.3.0
583
+ *
584
+ * @deprecated
585
+ * @since 2.6.5
586
+ *
587
+ * @global object $wp
588
+ *
589
+ * @return relative Post or Page url.
590
+ */
591
+ public function get_relative_url( $post = null, $external = false, $depr = null ) {
592
+
593
+ $this->_deprecated_function( 'AutoDescription_Generate_Url::' . __FUNCTION__, 'AutoDescription_Generate_Url::build_singular_relative_url()', '2.6.5' );
594
+
595
+ if ( isset( $depr ) ) {
596
+ $post_id = $depr;
597
+ } else {
598
+ if ( is_object( $post ) ) {
599
+ if ( isset( $post->ID ) )
600
+ $post_id = $post->ID;
601
+ } else if ( is_scalar( $post ) ) {
602
+ $post_id = (int) $post;
603
+ }
604
+ }
605
+
606
+ if ( ! isset( $post_id ) ) {
607
+ if ( ! $external )
608
+ $post_id = $this->get_the_real_ID();
609
+ else
610
+ return '';
611
+ }
612
+
613
+ if ( $external || ! $this->is_home() ) {
614
+ $permalink = get_permalink( $post_id );
615
+ } else if ( ! $external ) {
616
+ global $wp;
617
+
618
+ if ( isset( $wp->request ) )
619
+ $permalink = $wp->request;
620
+ }
621
+
622
+ //* No permalink found.
623
+ if ( ! isset( $permalink ) )
624
+ return '';
625
+
626
+ $path = $this->set_url_scheme( $permalink, 'relative' );
627
+
628
+ return $path;
629
+ }
630
+ /**
631
+ * Creates canonical url for the default permalink structure.
632
+ *
633
+ * @param object|int $post The post object or ID.
634
+ * @param bool $paged Whether to add pagination for all types.
635
+ * @param bool $paged_plural Whether to add pagination for the second or later page.
636
+ *
637
+ * @since 2.3.0
638
+ *
639
+ * @deprecated
640
+ * @since 2.6.5
641
+ *
642
+ * @return string The URL path.
643
+ */
644
+ public function the_url_path_default_permalink_structure( $post = null, $paged = false, $paged_plural = true ) {
645
+
646
+ $this->_deprecated_function( 'AutoDescription_Generate_Url::' . __FUNCTION__, 'AutoDescription_Generate_Url::build_singular_relative_url()', '2.6.5' );
647
+
648
+ //* Don't slash it.
649
+ $this->url_slashit = false;
650
+
651
+ if ( false === $this->is_singular() ) {
652
+ //* We're on a taxonomy
653
+ $object = get_queried_object();
654
+
655
+ if ( is_object( $object ) ) {
656
+ if ( $this->is_category() ) {
657
+ $path = '?cat=' . $object->term_id;
658
+ } else if ( $this->is_tag() ) {
659
+ $path = '?tag=' . $object->name;
660
+ } else if ( $this->is_date() ) {
661
+ global $wp_query;
662
+
663
+ $query = $wp_query->query;
664
+
665
+ $year = $query->year;
666
+ $month = $query->monthnum ? '&monthnum=' . $query->monthnum : '';
667
+ $day = $query->day ? '&day=' . $query->day : '';
668
+
669
+ $path = '?year=' . $year . $month . $day;
670
+ } else if ( $this->is_author() ) {
671
+ $path = '?author=' . $object->author_name;
672
+ } else if ( $this->is_tax() ) {
673
+ $path = '?taxonomy=' . $object->taxonomy . '&term=' . $object->slug;
674
+ } else if ( isset( $object->query_var ) && $object->query_var ) {
675
+ $path = '?' . $object->query_var . '=' . $object->slug;
676
+ } else {
677
+ $path = '?p=' . $object->ID;
678
+ }
679
+
680
+ $paged = $this->maybe_get_paged( $this->paged(), $paged, $paged_plural );
681
+ if ( $paged )
682
+ $path .= '&paged=' . $paged;
683
+ }
684
+
685
+ }
686
+
687
+ if ( ! isset( $path ) ) {
688
+
689
+ if ( isset( $post ) ) {
690
+ if ( is_object( $post ) && isset( $post->ID ) ) {
691
+ $id = $post->ID;
692
+ } else if ( is_scalar( $post ) ) {
693
+ $id = $post;
694
+ }
695
+ }
696
+
697
+ if ( ! isset( $id ) )
698
+ $id = $this->get_the_real_ID();
699
+
700
+ $path = '?p=' . $id;
701
+
702
+ $page = $this->maybe_get_paged( $this->page(), $paged, $paged_plural );
703
+ if ( $page )
704
+ $path .= '&page=' . $page;
705
+ }
706
+
707
+ return $path;
708
+ }
709
+
710
+ /**
711
+ * Doing it Wrong The SEO Framework version wrapper.
712
+ *
713
+ * @since 2.3.0
714
+ *
715
+ * @deprecated
716
+ * @since 2.6.5
717
+ *
718
+ * @return string The SEO Framework version.
719
+ */
720
+ public function the_seo_framework_version( $version = '' ) {
721
+
722
+ $this->_deprecated_function( 'AutoDescription_Load::' . __FUNCTION__, '', '2.6.6' );
723
+
724
+ $output = $version ? sprintf( __( '%s of The SEO Framework', 'autodescription' ), esc_attr( $version ) ) : '';
725
+
726
+ return $output;
727
+ }
728
+
729
+ }
inc/deprecated/deprecated.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The SEO Framework plugin
4
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License version 3 as published
8
+ * by the Free Software Foundation.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ /**
20
+ * This file contains most functions that have been deprecated.
21
+ *
22
+ * @since 2.1.6
23
+ *
24
+ * Emptied.
25
+ * @since 2.3.5 (~2.5 months later )
26
+ *
27
+ * Emptied.
28
+ * @since 2.6.2 (~6 months later)
29
+ */
inc/functions/benchmark.php ADDED
@@ -0,0 +1,369 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ add_action( 'wp_head', 'the_seo_framework_php_benchmark', -1 );
4
+ //* Benchmark PHP.
5
+ function the_seo_framework_php_benchmark() {
6
+
7
+ //* Boolean.
8
+ $b = true;
9
+ $ba = false;
10
+
11
+ //* String.
12
+ $s = '';
13
+
14
+ //* Compare
15
+ $c1 = 'thing1';
16
+ $c4 = 'thing4';
17
+ $c40 = 'thing40';
18
+
19
+ //* Array 5
20
+ $a5 = array( 'thing1', 'thing2', 'thing3', 'thing4', 'thing5' );
21
+
22
+ //* Array 50
23
+ $a50 = array( 'thing1','thing2','thing3','thing4','thing5','thing6','thing7','thing8','thing9','thing10','thing11','thing12','thing13','thing14','thing15','thing16','thing17','thing18','thing19','thing20','thing21','thing22','thing23','thing24','thing25','thing26','thing27','thing28','thing29','thing30','thing31','thing32','thing33','thing34','thing35','thing36','thing37','thing38','thing39','thing40','thing41','thing42','thing43','thing44','thing45','thing46','thing47','thing48','thing49','thing50' );
24
+
25
+ //* Iterations
26
+ $it = 10000000;
27
+
28
+ //* Start the engines.
29
+ $i = 0;
30
+ $t = microtime(true);
31
+ while ( $i < 10 ) {
32
+ if ( $b ) {
33
+ $a = $b;
34
+ }
35
+ if ( empty( $b ) ) {
36
+ $a = $b;
37
+ }
38
+ if ( ! $b ) {
39
+ $a = $b;
40
+ }
41
+ if ( isset( $b ) ) {
42
+ $a = $b;
43
+ }
44
+ if ( the_seo_framework_is_empty_string( $b ) ) {
45
+ $a = $b;
46
+ }
47
+ if ( in_array( $c1, $a5 ) ) {
48
+ $a = $b;
49
+ }
50
+ if ( the_seo_framework_in_array( $c1, $a5 ) ) {
51
+ $a = $b;
52
+ }
53
+ $i++;
54
+ }
55
+ $starttime = microtime(true) - $t;
56
+
57
+ //* Loose
58
+ $i = 0;
59
+ $t = microtime(true);
60
+ while ( $i < $it ) {
61
+ if ( $b ) {
62
+ // valuated
63
+ }
64
+ ++$i;
65
+ }
66
+ $loosetime = microtime(true) - $t;
67
+
68
+ //* Strict
69
+ $i = 0;
70
+ $t = microtime(true);
71
+ while ( $i < $it ) {
72
+ if ( true === $b ) {
73
+ // valuated
74
+ }
75
+ ++$i;
76
+ }
77
+ $stricttime = microtime(true) - $t;
78
+
79
+ //* Strict Neg
80
+ $i = 0;
81
+ $t = microtime(true);
82
+ while ( $i < $it ) {
83
+ if ( true !== $b ) {
84
+ // valuated
85
+ }
86
+ ++$i;
87
+ }
88
+ $strictnegtime = microtime(true) - $t;
89
+
90
+ //* Empty
91
+ $i = 0;
92
+ $t = microtime(true);
93
+ while ( $i < $it ) {
94
+ if ( empty( $b ) ) {
95
+ // valuated
96
+ }
97
+ ++$i;
98
+ }
99
+ $emptytime = microtime(true) - $t;
100
+
101
+ //* Neg Empty
102
+ $i = 0;
103
+ $t = microtime(true);
104
+ while( $i < $it ) {
105
+ if ( ! empty( $b ) ) {
106
+ // valuated
107
+ }
108
+ ++$i;
109
+ }
110
+ $negemptytime = microtime(true) - $t;
111
+
112
+ //* False Empty
113
+ $i = 0;
114
+ $t = microtime(true);
115
+ while( $i < $it ) {
116
+ if ( false === empty( $b ) ) {
117
+ // valuated
118
+ }
119
+ ++$i;
120
+ }
121
+ $strictemptytime = microtime(true) - $t;
122
+
123
+ //* Isset
124
+ $i = 0;
125
+ $t = microtime(true);
126
+ while( $i < $it ) {
127
+ if ( isset( $b ) ) {
128
+ // valuated
129
+ }
130
+ ++$i;
131
+ }
132
+ $issettime = microtime(true) - $t;
133
+
134
+ //* Isset Strict
135
+ $i = 0;
136
+ $t = microtime(true);
137
+ while( $i < $it ) {
138
+ if ( true === isset( $b ) ) {
139
+ // valuated
140
+ }
141
+ ++$i;
142
+ }
143
+ $issetstricttime = microtime(true) - $t;
144
+
145
+ //* Loose Empty string
146
+ $i = 0;
147
+ $t = microtime(true);
148
+ while( $i < $it ) {
149
+ if ( $s ) {
150
+ // valuated
151
+ }
152
+ ++$i;
153
+ }
154
+ $looseemptystring = microtime(true) - $t;
155
+
156
+ //* Loose Neg Empty string
157
+ $i = 0;
158
+ $t = microtime(true);
159
+ while( $i < $it ) {
160
+ if ( ! $s ) {
161
+ // valuated
162
+ }
163
+ ++$i;
164
+ }
165
+ $loosenegemptystring = microtime(true) - $t;
166
+
167
+ //* Empty string
168
+ $i = 0;
169
+ $t = microtime(true);
170
+ while( $i < $it ) {
171
+ if ( empty( $s ) ) {
172
+ // valuated
173
+ }
174
+ ++$i;
175
+ }
176
+ $emptystring = microtime(true) - $t;
177
+
178
+ //* Empty string strict
179
+ $i = 0;
180
+ $t = microtime(true);
181
+ while( $i < $it ) {
182
+ if ( '' === $s ) {
183
+ // valuated
184
+ }
185
+ ++$i;
186
+ }
187
+ $emptystrictstring = microtime(true) - $t;
188
+
189
+ //* Empty string strict
190
+ $i = 0;
191
+ $t = microtime(true);
192
+ while( $i < $it ) {
193
+ if ( the_seo_framework_is_empty_string( $s ) ) {
194
+ // valuated
195
+ }
196
+ ++$i;
197
+ }
198
+ $emptystrictfunctionstring = microtime(true) - $t;
199
+
200
+ //* In array begin 5
201
+ $i = 0;
202
+ $t = microtime(true);
203
+ while( $i < $it ) {
204
+ if ( in_array( $c1, $a5 ) ) {
205
+ // valuated
206
+ }
207
+ ++$i;
208
+ }
209
+ $inarraybegin5 = microtime(true) - $t;
210
+
211
+ //* In array begin 5 function
212
+ $i = 0;
213
+ $t = microtime(true);
214
+ while( $i < $it ) {
215
+ if ( the_seo_framework_in_array( $c1, $a5 ) ) {
216
+ // valuated
217
+ }
218
+ ++$i;
219
+ }
220
+ $inarraybegin5function = microtime(true) - $t;
221
+
222
+
223
+ //* In array end 5
224
+ $i = 0;
225
+ $t = microtime(true);
226
+ while( $i < $it ) {
227
+ if ( in_array( $c4, $a5 ) ) {
228
+ // valuated
229
+ }
230
+ ++$i;
231
+ }
232
+ $inarrayend5 = microtime(true) - $t;
233
+
234
+ //* In array end 5 function
235
+ $i = 0;
236
+ $t = microtime(true);
237
+ while( $i < $it ) {
238
+ if ( the_seo_framework_in_array( $c4, $a5 ) ) {
239
+ // valuated
240
+ }
241
+ ++$i;
242
+ }
243
+ $inarrayend5function = microtime(true) - $t;
244
+
245
+ //* In array begin 50
246
+ $i = 0;
247
+ $t = microtime(true);
248
+ while( $i < $it ) {
249
+ if ( in_array( $c1, $a50 ) ) {
250
+ // valuated
251
+ }
252
+ ++$i;
253
+ }
254
+ $inarraybegin50 = microtime(true) - $t;
255
+
256
+ //* In array begin 50 function
257
+ $i = 0;
258
+ $t = microtime(true);
259
+ while( $i < $it ) {
260
+ if ( the_seo_framework_in_array( $c1, $a50 ) ) {
261
+ // valuated
262
+ }
263
+ ++$i;
264
+ }
265
+ $inarraybegin50function = microtime(true) - $t;
266
+
267
+
268
+ //* In array end 50
269
+ $i = 0;
270
+ $t = microtime(true);
271
+ while( $i < $it ) {
272
+ if ( in_array( $c40, $a50 ) ) {
273
+ // valuated
274
+ }
275
+ ++$i;
276
+ }
277
+ $inarrayend50 = microtime(true) - $t;
278
+
279
+ //* In array end 50 function
280
+ $i = 0;
281
+ $t = microtime(true);
282
+ while( $i < $it ) {
283
+ if ( the_seo_framework_in_array( $c40, $a5 ) ) {
284
+ // valuated
285
+ }
286
+ ++$i;
287
+ }
288
+ $inarrayend50function = microtime(true) - $t;
289
+
290
+ //* Strict false
291
+ $i = 0;
292
+ $t = microtime(true);
293
+ while ( $i < $it ) {
294
+ if ( false === $ba ) {
295
+ // valuated
296
+ }
297
+ ++$i;
298
+ }
299
+ $strictfalsetime = microtime(true) - $t;
300
+
301
+ //* Loose flip false
302
+ $i = 0;
303
+ $t = microtime(true);
304
+ while ( $i < $it ) {
305
+ if ( ! $ba ) {
306
+ // valuated
307
+ }
308
+ ++$i;
309
+ }
310
+ $falsefliptime = microtime(true) - $t;
311
+
312
+ //* PHP 7 FCGI results @ 10,000,000 iterations.
313
+ echo 'Loose time: ' . $loosetime . " seconds\r\n"; // 0.1115360260009765625 seconds
314
+ echo 'Strict time: ' . $stricttime . " seconds\r\n"; // 0.1202042102813720703125 seconds
315
+ echo 'Strict Neg time: ' . $strictnegtime . " seconds\r\n"; // 0.1270349025726318359375 seconds
316
+ echo 'Empty time: ' . $emptytime . " seconds\r\n"; // 0.1297409534454345703125 seconds
317
+ echo 'Neg Empty time: ' . $negemptytime . " seconds\r\n"; // 0.20085906982421875 seconds <- Triple check
318
+ echo 'Strict Neg Empty time: ' . $strictemptytime . " seconds\r\n"; // 0.18640804290771484375 seconds <- Double check
319
+ echo 'Isset time: ' . $issettime . " seconds\r\n"; // 0.115377902984619140625 seconds
320
+ echo 'Strict Isset time: ' . $issetstricttime . " seconds\r\n"; // 0.17035007476806640625 seconds <- Double check
321
+
322
+ echo 'Strict False time: ' . $strictfalsetime . " seconds\r\n"; // 0.1211879253387451171875 seconds
323
+ echo 'Loose Flip time: ' . $falsefliptime . " seconds\r\n"; // 0.1306369304656982421875 seconds
324
+
325
+ echo "\r\n";
326
+
327
+ echo 'Loose Empty String time: ' . $looseemptystring . " seconds\r\n"; // 0.1340930461883544921875 seconds
328
+ echo 'Loose Neg Empty String time: ' . $loosenegemptystring . " seconds\r\n"; // 0.15882110595703125 seconds
329
+ echo 'Empty String time: ' . $emptystring . " seconds\r\n"; // 0.135138034820556640625 seconds
330
+ echo 'Strict Empty String time: ' . $emptystrictstring . " seconds\r\n"; // 0.1573431491851806640625 seconds
331
+ echo 'Strict Empty Function String time: ' . $emptystrictfunctionstring . " seconds\r\n"; // 0.385016918182373046875 seconds <- Triple check.
332
+
333
+ echo "\r\n";
334
+
335
+ echo 'In array begin 5: ' . $inarraybegin5 . " seconds\r\n"; // 0.3640620708465576171875 seconds
336
+ echo 'In array begin function 5: ' . $inarraybegin5function . " seconds\r\n"; // 2.0675928592681884765625 seconds <- VERY bad (1. function call, 2. array sorting. 3. Triple check)
337
+ echo 'In array end 5: ' . $inarrayend5 . " seconds\r\n"; // 0.5424749851226806640625 seconds
338
+ echo 'In array end function 5: ' . $inarrayend5function . " seconds\r\n"; // 2.0651528835296630859375 seconds <- VERY bad
339
+
340
+ echo "\r\n";
341
+
342
+ echo 'In array begin 50: ' . $inarraybegin50 . " seconds\r\n"; // 0.3695099353790283203125 seconds
343
+ echo 'In array begin function 50: ' . $inarraybegin50function . " seconds\r\n"; // 8.4975330829620361328125 seconds <- VERY bad.
344
+ echo 'In array end 50: ' . $inarrayend50 . " seconds\r\n"; // 2.392652988433837890625 seconds
345
+ echo 'In array end function 50: ' . $inarrayend50function . " seconds\r\n"; // 2.004433155059814453125 seconds <- miniscule benefit.
346
+
347
+ }
348
+
349
+ function the_seo_framework_is_empty_string( $string ) {
350
+ if ( '' === $string ) return true;
351
+ return false;
352
+ }
353
+
354
+ function the_seo_framework_in_array( $needle, $array ) {
355
+
356
+ $array = array_flip( $array );
357
+
358
+ if ( is_string( $needle ) ) {
359
+ if ( isset( $array[$needle] ) )
360
+ return true;
361
+ } else if ( is_array( $needle ) ) {
362
+ foreach ( $needle as $str ) {
363
+ if ( isset( $array[$str] ) )
364
+ return true;
365
+ }
366
+ }
367
+
368
+ return false;
369
+ }
inc/functions/compat.php ADDED
@@ -0,0 +1,359 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Returns whether PCRE/u (PCRE_UTF8 modifier) is available for use.
4
+ *
5
+ * @ignore
6
+ * @since WordPress 4.2.2
7
+ * @access private
8
+ *
9
+ * @staticvar string $utf8_pcre
10
+ *
11
+ * @param bool $set - Used for testing only
12
+ * null : default - get PCRE/u capability
13
+ * false : Used for testing - return false for future calls to this function
14
+ * 'reset': Used for testing - restore default behavior of this function
15
+ *
16
+ * WordPress core function.
17
+ * Implemented in this plugin for compatibility with older WordPress versions.
18
+ * @since 2.3.5
19
+ */
20
+ if ( ! function_exists( '_wp_can_use_pcre_u' ) ) :
21
+ function _wp_can_use_pcre_u( $set = null ) {
22
+ static $utf8_pcre = 'reset';
23
+
24
+ if ( null !== $set ) {
25
+ $utf8_pcre = $set;
26
+ }
27
+
28
+ if ( 'reset' === $utf8_pcre ) {
29
+ $utf8_pcre = @preg_match( '/^./u', 'a' );
30
+ }
31
+
32
+ return $utf8_pcre;
33
+ }
34
+ endif;
35
+
36
+ /**
37
+ * Extended charset support
38
+ *
39
+ * @uses strlen
40
+ * @return mb_strlen
41
+ *
42
+ * @since 1.3.0
43
+ *
44
+ * Rewritten
45
+ * @since 2.3.5
46
+ */
47
+ if ( ! function_exists( 'mb_strlen' ) ) :
48
+ function mb_strlen( $str, $encoding = null ) {
49
+ return _mb_strlen( $str, $encoding );
50
+ }
51
+ endif;
52
+
53
+ /*
54
+ * Only understands UTF-8 and 8bit. All other character sets will be treated as 8bit.
55
+ * For $encoding === UTF-8, the $str input is expected to be a valid UTF-8 byte sequence.
56
+ * The behavior of this function for invalid inputs is undefined.
57
+ */
58
+ if ( ! function_exists( '_mb_strlen' ) ) :
59
+ function _mb_strlen( $str, $encoding = null ) {
60
+ if ( null === $encoding ) {
61
+ $encoding = get_option( 'blog_charset' );
62
+ }
63
+
64
+ // The solution below works only for UTF-8,
65
+ // so in case of a different charset just use built-in strlen()
66
+ if ( ! in_array( $encoding, array( 'utf8', 'utf-8', 'UTF8', 'UTF-8' ) ) ) {
67
+ return strlen( $str );
68
+ }
69
+
70
+ if ( _wp_can_use_pcre_u() ) {
71
+ // Use the regex unicode support to separate the UTF-8 characters into an array
72
+ preg_match_all( '/./us', $str, $match );
73
+ return count( $match[0] );
74
+ }
75
+
76
+ $regex = '/(?:
77
+ [\x00-\x7F] # single-byte sequences 0xxxxxxx
78
+ | [\xC2-\xDF][\x80-\xBF] # double-byte sequences 110xxxxx 10xxxxxx
79
+ | \xE0[\xA0-\xBF][\x80-\xBF] # triple-byte sequences 1110xxxx 10xxxxxx * 2
80
+ | [\xE1-\xEC][\x80-\xBF]{2}
81
+ | \xED[\x80-\x9F][\x80-\xBF]
82
+ | [\xEE-\xEF][\x80-\xBF]{2}
83
+ | \xF0[\x90-\xBF][\x80-\xBF]{2} # four-byte sequences 11110xxx 10xxxxxx * 3
84
+ | [\xF1-\xF3][\x80-\xBF]{3}
85
+ | \xF4[\x80-\x8F][\x80-\xBF]{2}
86
+ )/x';
87
+
88
+ $count = 1; // Start at 1 instead of 0 since the first thing we do is decrement
89
+ do {
90
+ // We had some string left over from the last round, but we counted it in that last round.
91
+ $count--;
92
+
93
+ // Split by UTF-8 character, limit to 1000 characters (last array element will contain the rest of the string)
94
+ $pieces = preg_split( $regex, $str, 1000 );
95
+
96
+ // Increment
97
+ $count += count( $pieces );
98
+ } while ( $str = array_pop( $pieces ) ); // If there's anything left over, repeat the loop.
99
+
100
+ // Fencepost: preg_split() always returns one extra item in the array
101
+ return --$count;
102
+ }
103
+ endif;
104
+
105
+ /**
106
+ * Extended charset support
107
+ *
108
+ * @uses substr
109
+ * @return mb_substr
110
+ *
111
+ * @since 1.3.0
112
+ *
113
+ * Rewritten
114
+ * @since 2.3.5
115
+ */
116
+ if ( ! function_exists( 'mb_substr' ) ) :
117
+ function mb_substr( $str, $start, $length = null, $encoding = null ) {
118
+ return _mb_substr( $str, $start, $length, $encoding );
119
+ }
120
+ endif;
121
+
122
+ /*
123
+ * Only understands UTF-8 and 8bit. All other character sets will be treated as 8bit.
124
+ * For $encoding === UTF-8, the $str input is expected to be a valid UTF-8 byte sequence.
125
+ * The behavior of this function for invalid inputs is undefined.
126
+ */
127
+ if ( ! function_exists( '_mb_substr' ) ) :
128
+ function _mb_substr( $str, $start, $length = null, $encoding = null ) {
129
+ if ( null === $encoding ) {
130
+ $encoding = get_option( 'blog_charset' );
131
+ }
132
+
133
+ // The solution below works only for UTF-8,
134
+ // so in case of a different charset just use built-in substr()
135
+ if ( ! in_array( $encoding, array( 'utf8', 'utf-8', 'UTF8', 'UTF-8' ) ) ) {
136
+ return is_null( $length ) ? substr( $str, $start ) : substr( $str, $start, $length );
137
+ }
138
+
139
+ if ( _wp_can_use_pcre_u() ) {
140
+ // Use the regex unicode support to separate the UTF-8 characters into an array
141
+ preg_match_all( '/./us', $str, $match );
142
+ $chars = is_null( $length ) ? array_slice( $match[0], $start ) : array_slice( $match[0], $start, $length );
143
+ return implode( '', $chars );
144
+ }
145
+
146
+ $regex = '/(
147
+ [\x00-\x7F] # single-byte sequences 0xxxxxxx
148
+ | [\xC2-\xDF][\x80-\xBF] # double-byte sequences 110xxxxx 10xxxxxx
149
+ | \xE0[\xA0-\xBF][\x80-\xBF] # triple-byte sequences 1110xxxx 10xxxxxx * 2
150
+ | [\xE1-\xEC][\x80-\xBF]{2}
151
+ | \xED[\x80-\x9F][\x80-\xBF]
152
+ | [\xEE-\xEF][\x80-\xBF]{2}
153
+ | \xF0[\x90-\xBF][\x80-\xBF]{2} # four-byte sequences 11110xxx 10xxxxxx * 3
154
+ | [\xF1-\xF3][\x80-\xBF]{3}
155
+ | \xF4[\x80-\x8F][\x80-\xBF]{2}
156
+ )/x';
157
+
158
+ $chars = array( '' ); // Start with 1 element instead of 0 since the first thing we do is pop
159
+ do {
160
+ // We had some string left over from the last round, but we counted it in that last round.
161
+ array_pop( $chars );
162
+
163
+ // Split by UTF-8 character, limit to 1000 characters (last array element will contain the rest of the string)
164
+ $pieces = preg_split( $regex, $str, 1000, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
165
+
166
+ $chars = array_merge( $chars, $pieces );
167
+ } while ( count( $pieces ) > 1 && $str = array_pop( $pieces ) ); // If there's anything left over, repeat the loop.
168
+
169
+ return join( '', array_slice( $chars, $start, $length ) );
170
+ }
171
+ endif;
172
+
173
+ /**
174
+ * Extended charset support
175
+ *
176
+ * @see _mb_strpos()
177
+ *
178
+ * @param string $haystack The string to search in.
179
+ * @param mixed $needle If needle is not a string, it is converted to an integer and applied as the ordinal value of a character.
180
+ * @param int $offset Optional, search will start this number of characters counted from the beginning of the string. The offset cannot be negative.
181
+ * @param string|null $encoding Optional. Character encoding to use. Default null.
182
+ *
183
+ * @license GLPv2 or later
184
+ * @return int Position of first occurence found of $haystack of `$needle`.
185
+ */
186
+ if ( ! function_exists( 'mb_strpos' ) ) :
187
+ function mb_strpos( $haystack, $needle, $offset = 0, $encoding = null ) {
188
+ return _mb_strpos( $haystack, $needle, $offset, $encoding );
189
+ }
190
+ endif;
191
+
192
+ /**
193
+ * Compat function to mimic mb_strpos().
194
+ *
195
+ * Only understands UTF-8 and 8bit. All other character sets will be treated as 8bit.
196
+ * For $encoding === UTF-8, the $str input is expected to be a valid UTF-8 byte sequence.
197
+ * The behavior of this function for invalid inputs is PHP compliant.
198
+ *
199
+ * @since 4.5.0
200
+ * @license GLPv2 or later
201
+ *
202
+ * @param string $haystack The string to search in.
203
+ * @param mixed $needle If needle is not a string, it is converted to an integer and applied as the ordinal value of a character.
204
+ * @param int $offset Optional, search will start this number of characters counted from the beginning of the string. The offset cannot be negative.
205
+ * @param string|null $encoding Optional. Character encoding to use. Default null.
206
+ *
207
+ * @license GLPv2 or later
208
+ * @return int Position of first occurence found of $haystack of `$needle`.
209
+ */
210
+ if ( ! function_exists( '_mb_strpos' ) ) :
211
+ function _mb_strpos( $haystack, $needle, $offset = 0, $encoding = null ) {
212
+
213
+ if ( null === $encoding ) {
214
+ $encoding = get_option( 'blog_charset' );
215
+ }
216
+
217
+ // The solution below works only for UTF-8,
218
+ // So in case of a different charset just use built-in strpos()
219
+ if ( ! in_array( $encoding, array( 'utf8', 'utf-8', 'UTF8', 'UTF-8' ) ) ) {
220
+ return $offset === 0 ? strpos( $haystack, $needle ) : strpos( $haystack, $needle, $offset );
221
+ }
222
+
223
+ $haystack_len = mb_strlen( $haystack );
224
+
225
+ if ( $offset < (int) 0 || $offset > $haystack_len ) {
226
+ trigger_error( 'mb_strpos(): Offset not contained in string', E_USER_WARNING );
227
+ return false;
228
+ }
229
+
230
+ if ( ! is_string( $needle ) ) {
231
+ $needle = (int) $needle;
232
+
233
+ if ( ! is_int( $needle ) ) {
234
+ trigger_error( 'mb_strpos(): Array to string conversion', E_USER_WARNING );
235
+ return false;
236
+ }
237
+ }
238
+
239
+ if ( empty( $needle ) ) {
240
+ trigger_error( 'mb_strpos(): Empty needle', E_USER_WARNING );
241
+ return false;
242
+ }
243
+
244
+ // Slice off the offset
245
+ $haystack_sub = mb_substr( $haystack, $offset );
246
+
247
+ if ( _wp_can_use_pcre_u() ) {
248
+ // Use the regex unicode support to separate the UTF-8 characters into an array
249
+ preg_match_all( "/./us", $haystack, $match_h );
250
+ preg_match_all( "/$needle/us", $haystack_sub, $match_n );
251
+
252
+ $inter = array_intersect( $match_h[0], $match_n[0] );
253
+
254
+ if ( ! isset( $inter ) )
255
+ return false;
256
+
257
+ //* Prevent bugs, (re)assign var.
258
+ $pos = null;
259
+
260
+ // Find first occurence greater than or equal to offset
261
+ foreach ( $inter as $key => $value ) {
262
+ if ( $key >= $offset ) {
263
+ $pos = $key;
264
+ break;
265
+ }
266
+ }
267
+
268
+ //* No key has been found.
269
+ if ( ! isset( $pos ) )
270
+ return false;
271
+
272
+ return (int) $pos;
273
+ }
274
+
275
+ $regex = '/(
276
+ [\x00-\x7F] # single-byte sequences 0xxxxxxx
277
+ | [\xC2-\xDF][\x80-\xBF] # double-byte sequences 110xxxxx 10xxxxxx
278
+ | \xE0[\xA0-\xBF][\x80-\xBF] # triple-byte sequences 1110xxxx 10xxxxxx * 2
279
+ | [\xE1-\xEC][\x80-\xBF]{2}
280
+ | \xED[\x80-\x9F][\x80-\xBF]
281
+ | [\xEE-\xEF][\x80-\xBF]{2}
282
+ | \xF0[\x90-\xBF][\x80-\xBF]{2} # four-byte sequences 11110xxx 10xxxxxx * 3
283
+ | [\xF1-\xF3][\x80-\xBF]{3}
284
+ | \xF4[\x80-\x8F][\x80-\xBF]{2}
285
+ )/x';
286
+
287
+ /**
288
+ * Place haystack into array
289
+ */
290
+ $match_h = array( '' ); // Start with 1 element instead of 0 since the first thing we do is pop
291
+ do {
292
+ // We had some string left over from the last round, but we counted it in that last round.
293
+ array_pop( $match_h );
294
+
295
+ // Split by UTF-8 character, limit to 1000 characters (last array element will contain the rest of the string)
296
+ $pieces = preg_split( $regex, $haystack, 1000, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
297
+
298
+ $match_h = array_merge( $match_h, $pieces );
299
+ } while ( count( $pieces ) > 1 && $haystack = array_pop( $pieces ) ); // If there's anything left over, repeat the loop.
300
+
301
+ /**
302
+ * Place haystack offset into array
303
+ */
304
+ $match_hs = array( '' ); // Start with 1 element instead of 0 since the first thing we do is pop
305
+ do {
306
+ // We had some string left over from the last round, but we counted it in that last round.
307
+ array_pop( $match_hs );
308
+
309
+ // Split by UTF-8 character, limit to 1000 characters (last array element will contain the rest of the string)
310
+ $pieces = preg_split( $regex, $haystack_sub, 1000, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
311
+
312
+ $match_hs = array_merge( $match_hs, $pieces );
313
+ } while ( count( $pieces ) > 1 && $haystack_sub = array_pop( $pieces ) ); // If there's anything left over, repeat the loop.
314
+
315
+ /**
316
+ * Put needle into array
317
+ */
318
+ $match_n = array( '' ); // Start with 1 element instead of 0 since the first thing we do is pop
319
+ do {
320
+ // We had some string left over from the last round, but we counted it in that last round.
321
+ array_pop( $match_n );
322
+
323
+ // Split by UTF-8 character, limit to 1000 characters (last array element will contain the rest of the string)
324
+ $pieces = preg_split( $regex, $needle, 1000, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
325
+
326
+ $match_n = array_merge( $match_n, $pieces );
327
+ } while ( count( $pieces ) > 1 && $needle = array_pop( $pieces ) ); // If there's anything left over, repeat the loop.
328
+
329
+ /**
330
+ * Compute match of haystack offset with needle
331
+ * If passed, return the array key number within the full haystack.
332
+ */
333
+ if ( false !== in_array( $match_n[0], $match_hs ) ) {
334
+ $inter = array_intersect( $match_h, $match_n );
335
+
336
+ if ( ! isset( $inter ) )
337
+ return false;
338
+
339
+ //* Prevent bugs, (re)assign var.
340
+ $pos = null;
341
+
342
+ // Find first occurence greater than or equal to offset
343
+ foreach ( $inter as $key => $value ) {
344
+ if ( $key >= $offset ) {
345
+ $pos = $key;
346
+ break;
347
+ }
348
+ }
349
+
350
+ //* No key has been found.
351
+ if ( ! isset( $pos ) )
352
+ return false;
353
+
354
+ return (int) $pos;
355
+ } else {
356
+ return false;
357
+ }
358
+ }
359
+ endif;
inc/functions/optionsapi.php ADDED
@@ -0,0 +1,229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The SEO Framework plugin
4
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License version 3 as published
8
+ * by the Free Software Foundation.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ /**
20
+ * This file holds functions for easily extracting or interacting with data
21
+ * from The SEO FrameWork.
22
+ *
23
+ * @since 2.2.5
24
+ *
25
+ * We could bombard it with every public function, but that's very time consuming.
26
+ * I'll add a bunch of functions on 2nd dot (v.X.v) release. e.g. 2.3.0, 2.4.0, etc.
27
+ *
28
+ * This will allow version comparing more easily (as you'll know how many users
29
+ * use v.X version through the WordPress plugin stats.).
30
+ * Therefore reducing work for you.
31
+ */
32
+
33
+ /**
34
+ * Load the class from cache.
35
+ * This is recommended using this above using 'new The_SEO_Framework_Load();'
36
+ * It also checks if the class is callable in the first place.
37
+ *
38
+ * @since 2.2.5
39
+ */
40
+ function the_seo_framework() {
41
+ return the_seo_framework_init();
42
+ }
43
+
44
+ /**
45
+ * The SEO FrameWork version number
46
+ *
47
+ * Useful for version comparing
48
+ *
49
+ * @since 2.2.5
50
+ *
51
+ * @return string|null The SEO Framework three point version number. (e.g. '2.2.5')
52
+ */
53
+ function the_seo_framework_version() {
54
+
55
+ if ( the_seo_framework_active() )
56
+ return THE_SEO_FRAMEWORK_VERSION;
57
+
58
+ return null;
59
+ }
60
+
61
+ /**
62
+ * The SEO Framework dot version compare.
63
+ *
64
+ * @param string version The two dot version: x.v
65
+ *
66
+ * @since 2.4.0
67
+ *
68
+ * @return bool False plugin inactive or version compare fails.
69
+ */
70
+ function the_seo_framework_dot_version( $version = '2.4' ) {
71
+
72
+ $current_version = the_seo_framework_version();
73
+
74
+ if ( $current_version ) {
75
+ $version_len = strlen( $version );
76
+ $current_version_len = strlen( $current_version );
77
+
78
+ //* Only allow 3 length.
79
+ if ( 3 !== $version_len )
80
+ $version = substr( $version, 0, 3 );
81
+
82
+ if ( 3 !== $current_version_len )
83
+ $current_version = substr( $current_version, 0, 3 );
84
+
85
+ if ( $current_version_len === $verion )
86
+ return true;
87
+ }
88
+
89
+ return false;
90
+ }
91
+
92
+ /**
93
+ * Check if The SEO FrameWork is active based on global filter.
94
+ *
95
+ * @since 2.2.5
96
+ *
97
+ * @return bool true if SEO framework is active
98
+ */
99
+ function the_seo_framework_active() {
100
+ return the_seo_framework_load();
101
+ }
102
+
103
+ /**
104
+ * Compare the WordPress version to the input one.
105
+ *
106
+ * @since 2.2.9
107
+ *
108
+ * @param string $version The 3 point version compare
109
+ * @param string $compare The PHP comparison operator.
110
+ *
111
+ * @return bool true if Version passes comparison.
112
+ */
113
+ function tsf_wp_version( $version = '4.3.0', $compare = '>=' ) {
114
+ $theseoframework = the_seo_framework();
115
+
116
+ if ( isset( $theseoframework ) )
117
+ return $theseoframework->wp_version( $version, $compare );
118
+
119
+ return null;
120
+ }
121
+
122
+ /**
123
+ * Fetch the The SEO Framework Options pagehook.
124
+ *
125
+ * @since 2.2.9
126
+ *
127
+ * @return string|null The pagehook.
128
+ */
129
+ function tsf_options_pagehook() {
130
+
131
+ $theseoframework = the_seo_framework();
132
+
133
+ if ( isset( $theseoframework ) )
134
+ return $theseoframework->pagehook;
135
+
136
+ return null;
137
+ }
138
+
139
+ /**
140
+ * Fetch an option from The SEO Framework.
141
+ *
142
+ * @since 2.2.9
143
+ *
144
+ * @param string $key Option name.
145
+ * @param boolean $use_cache Optional. Whether to use the cache value or not. Defaults to true.
146
+ *
147
+ * @return mixed The option value.
148
+ */
149
+ function tsf_get_option( $key, $use_cache = true ) {
150
+
151
+ $theseoframework = the_seo_framework();
152
+
153
+ if ( isset( $theseoframework ) )
154
+ return $theseoframework->get_option( $key, $use_cache );
155
+
156
+ return null;
157
+ }
158
+
159
+ /**
160
+ * Fetch title from cache. Only works within Loop.
161
+ *
162
+ * @param string|null $title the previous title
163
+ *
164
+ * @since 2.4.2
165
+ *
166
+ * @return string|null The current page title.
167
+ */
168
+ function the_seo_framework_title_from_cache( $title = null ) {
169
+
170
+ $theseoframework = the_seo_framework();
171
+
172
+ if ( isset( $theseoframework ) )
173
+ $title = $theseoframework->title_from_cache();
174
+
175
+ return $title;
176
+ }
177
+
178
+ /**
179
+ * Fetch description from cache. Only works within Loop.
180
+ *
181
+ * @param bool $social Fetch social description.
182
+ *
183
+ * @since 2.4.2
184
+ *
185
+ * @return string|null The current page description.
186
+ */
187
+ function the_seo_framework_description_from_cache( $social = false ) {
188
+
189
+ $theseoframework = the_seo_framework();
190
+
191
+ if ( isset( $theseoframework ) )
192
+ return $theseoframework->description_from_cache( $social );
193
+
194
+ return null;
195
+ }
196
+
197
+ /**
198
+ * Fetch url from cache. Only works within Loop.
199
+ *
200
+ * @since 2.4.2
201
+ *
202
+ * @return string|null The current page URL.
203
+ */
204
+ function the_seo_framework_the_url_from_cache() {
205
+
206
+ $theseoframework = the_seo_framework();
207
+
208
+ if ( isset( $theseoframework ) )
209
+ return $theseoframework->the_url_from_cache();
210
+
211
+ return null;
212
+ }
213
+
214
+ /**
215
+ * Whether we're on the SEO settings page.
216
+ *
217
+ * @since 2.6.0
218
+ *
219
+ * @return bool
220
+ */
221
+ function the_seo_framework_is_settings_page() {
222
+
223
+ $theseoframework = the_seo_framework();
224
+
225
+ if ( isset( $theseoframework ) )
226
+ return $theseoframework->is_seo_settings_page();
227
+
228
+ return false;
229
+ }
index.php ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <?php
2
+ //* Our greatest weakness lies in giving up. The most certain way to succeed is always to try just one more time. - Thomas A. Edison
language/autodescription.pot ADDED
@@ -0,0 +1,1718 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2016 The SEO Framework
2
+ # This file is distributed under the same license as the The SEO Framework package.
3
+ msgid ""
4
+ msgstr ""
5
+ "Project-Id-Version: The SEO Framework 2.6.5\n"
6
+ "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/autodescription\n"
7
+ "POT-Creation-Date: 2016-06-08 22:02:53+00:00\n"
8
+ "MIME-Version: 1.0\n"
9
+ "Content-Type: text/plain; charset=UTF-8\n"
10
+ "Content-Transfer-Encoding: 8bit\n"
11
+ "PO-Revision-Date: 2016-MO-DA HO:MI+ZONE\n"
12
+ "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13
+ "Language-Team: LANGUAGE <LL@li.org>\n"
14
+
15
+ #: inc/classes/admininit.class.php:220
16
+ msgid "Good"
17
+ msgstr ""
18
+
19
+ #: inc/classes/admininit.class.php:221
20
+ msgid "Okay"
21
+ msgstr ""
22
+
23
+ #: inc/classes/admininit.class.php:222
24
+ msgid "Bad"
25
+ msgstr ""
26
+
27
+ #: inc/classes/admininit.class.php:223
28
+ msgid "Unknown"
29
+ msgstr ""
30
+
31
+ #: inc/classes/admininit.class.php:310
32
+ msgid "The changes you made will be lost if you navigate away from this page."
33
+ msgstr ""
34
+
35
+ #: inc/classes/admininit.class.php:311
36
+ msgid "Are you sure you want to reset all SEO settings to their defaults?"
37
+ msgstr ""
38
+
39
+ #: inc/classes/adminpages.class.php:114
40
+ msgid "Save Settings"
41
+ msgstr ""
42
+
43
+ #: inc/classes/adminpages.class.php:115
44
+ msgid "Reset Settings"
45
+ msgstr ""
46
+
47
+ #: inc/classes/adminpages.class.php:116
48
+ msgid "Settings are saved."
49
+ msgstr ""
50
+
51
+ #: inc/classes/adminpages.class.php:117
52
+ msgid "Settings are reset."
53
+ msgstr ""
54
+
55
+ #: inc/classes/adminpages.class.php:118
56
+ msgid "Error saving settings."
57
+ msgstr ""
58
+
59
+ #: inc/classes/adminpages.class.php:119
60
+ msgid "New SEO Settings have been updated."
61
+ msgstr ""
62
+
63
+ #: inc/classes/adminpages.class.php:135 inc/classes/core.class.php:108
64
+ #: inc/classes/siteoptions.class.php:461
65
+ msgid "SEO Settings"
66
+ msgstr ""
67
+
68
+ #: inc/classes/adminpages.class.php:136
69
+ msgid "SEO"
70
+ msgstr ""
71
+
72
+ #: inc/classes/adminpages.class.php:177
73
+ msgid "Network SEO Settings"
74
+ msgstr ""
75
+
76
+ #: inc/classes/adminpages.class.php:178
77
+ msgid "Network SEO"
78
+ msgstr ""
79
+
80
+ #: inc/classes/adminpages.class.php:297
81
+ msgid "Title Settings"
82
+ msgstr ""
83
+
84
+ #: inc/classes/adminpages.class.php:307
85
+ msgid "Description Meta Settings"
86
+ msgstr ""
87
+
88
+ #: inc/classes/adminpages.class.php:317
89
+ msgid "Home Page Settings"
90
+ msgstr ""
91
+
92
+ #: inc/classes/adminpages.class.php:327
93
+ msgid "Social Meta Settings"
94
+ msgstr ""
95
+
96
+ #: inc/classes/adminpages.class.php:337 inc/classes/metaboxes.class.php:1802
97
+ msgid "Knowledge Graph Settings"
98
+ msgstr ""
99
+
100
+ #: inc/classes/adminpages.class.php:347
101
+ msgid "Schema Settings"
102
+ msgstr ""
103
+
104
+ #: inc/classes/adminpages.class.php:357 inc/classes/inpost.class.php:377
105
+ #: inc/classes/inpost.class.php:604
106
+ msgid "Robots Meta Settings"
107
+ msgstr ""
108
+
109
+ #: inc/classes/adminpages.class.php:367
110
+ msgid "Webmaster Meta Settings"
111
+ msgstr ""
112
+
113
+ #: inc/classes/adminpages.class.php:377
114
+ msgid "Sitemap Settings"
115
+ msgstr ""
116
+
117
+ #: inc/classes/adminpages.class.php:387
118
+ msgid "Feed Settings"
119
+ msgstr ""
120
+
121
+ #: inc/classes/core.class.php:110
122
+ msgctxt "As in: The Plugin Home Page"
123
+ msgid "Plugin Home"
124
+ msgstr ""
125
+
126
+ #: inc/classes/core.class.php:149
127
+ msgid "Dismiss"
128
+ msgstr ""
129
+
130
+ #: inc/classes/core.class.php:257
131
+ msgctxt "e.g. en for English, nl for Dutch, fi for Finish, de for German"
132
+ msgid "en"
133
+ msgstr ""
134
+
135
+ #: inc/classes/debug.class.php:90
136
+ msgid "%1$s is <strong>deprecated</strong> since version %2$s of The SEO Framework! Use %3$s instead."
137
+ msgstr ""
138
+
139
+ #: inc/classes/debug.class.php:92
140
+ msgid "%1$s is <strong>deprecated</strong> since version %2$s of The SEO Framework with no alternative available."
141
+ msgstr ""
142
+
143
+ #: inc/classes/debug.class.php:142
144
+ msgid "(This message was added in version %s of The SEO Framework.)"
145
+ msgstr ""
146
+
147
+ #. translators: %s: Codex URL
148
+
149
+ #: inc/classes/debug.class.php:144
150
+ msgid "Please see <a href=\"%s\">Debugging in WordPress</a> for more information."
151
+ msgstr ""
152
+
153
+ #: inc/classes/debug.class.php:145
154
+ msgid "https://codex.wordpress.org/Debugging_in_WordPress"
155
+ msgstr ""
156
+
157
+ #. translators: 1: Function name, 2: Message, 3: Plugin Version notification
158
+
159
+ #: inc/classes/debug.class.php:148
160
+ msgid "%1$s was called <strong>incorrectly</strong>. %2$s %3$s"
161
+ msgstr ""
162
+
163
+ #: inc/classes/doingitright.class.php:86
164
+ msgid "No Search"
165
+ msgstr ""
166
+
167
+ #: inc/classes/doingitright.class.php:300
168
+ msgid "Refresh to see the SEO Bar status."
169
+ msgstr ""
170
+
171
+ #: inc/classes/doingitright.class.php:380
172
+ msgid "Post"
173
+ msgstr ""
174
+
175
+ #: inc/classes/doingitright.class.php:384 inc/classes/inpost.class.php:242
176
+ #: inc/classes/metaboxes.class.php:1905 inc/classes/termdata.class.php:187
177
+ #: inc/classes/termdata.class.php:223
178
+ msgid "Page"
179
+ msgstr ""
180
+
181
+ #: inc/classes/doingitright.class.php:428
182
+ msgid "Failed to fetch post ID."
183
+ msgstr ""
184
+
185
+ #. translators: %s = But or And
186
+
187
+ #: inc/classes/doingitright.class.php:826
188
+ msgid "%s the Title contains the Blogname multiple times."
189
+ msgstr ""
190
+
191
+ #: inc/classes/doingitright.class.php:929
192
+ #: inc/classes/doingitright.class.php:1569
193
+ msgid "Length is far too short."
194
+ msgstr ""
195
+
196
+ #: inc/classes/doingitright.class.php:932
197
+ #: inc/classes/doingitright.class.php:1572
198
+ msgid "Length is too short."
199
+ msgstr ""
200
+
201
+ #: inc/classes/doingitright.class.php:937
202
+ #: inc/classes/doingitright.class.php:1575
203
+ msgid "Length is too long."
204
+ msgstr ""
205
+
206
+ #: inc/classes/doingitright.class.php:942
207
+ #: inc/classes/doingitright.class.php:1578
208
+ msgid "Length is far too long."
209
+ msgstr ""
210
+
211
+ #: inc/classes/doingitright.class.php:945
212
+ #: inc/classes/doingitright.class.php:1581
213
+ msgid "Length is good."
214
+ msgstr ""
215
+
216
+ #: inc/classes/doingitright.class.php:1043
217
+ msgid "%s is used %d times."
218
+ msgstr ""
219
+
220
+ #: inc/classes/doingitright.class.php:1087
221
+ msgid "%s is being indexed."
222
+ msgstr ""
223
+
224
+ #: inc/classes/doingitright.class.php:1096
225
+ msgid "But you've disabled indexing for the whole site."
226
+ msgstr ""
227
+
228
+ #. translators: 1: But or And, 2: Current taxonomy term plural label
229
+
230
+ #: inc/classes/doingitright.class.php:1120
231
+ msgid "%1$s indexing for %2$s have been disabled."
232
+ msgstr ""
233
+
234
+ #. translators: %s = But or And
235
+
236
+ #: inc/classes/doingitright.class.php:1130
237
+ msgid "%s the blog isn't set to public. This means WordPress discourages indexing."
238
+ msgstr ""
239
+
240
+ #. translators: %s = But or And
241
+
242
+ #: inc/classes/doingitright.class.php:1144
243
+ msgid "%s there are no posts in this term; therefore, indexing has been disabled."
244
+ msgstr ""
245
+
246
+ #: inc/classes/doingitright.class.php:1229
247
+ msgid "%s links aren't being followed."
248
+ msgstr ""
249
+
250
+ #: inc/classes/doingitright.class.php:1235
251
+ msgid "%s links are being followed."
252
+ msgstr ""
253
+
254
+ #. translators: %s = But or And
255
+
256
+ #: inc/classes/doingitright.class.php:1247
257
+ msgid "%s you've disabled the following of links for the whole site."
258
+ msgstr ""
259
+
260
+ #. translators: 1: But or And, 2: Current taxonomy term plural label
261
+
262
+ #: inc/classes/doingitright.class.php:1273
263
+ msgid "%1$s following for %2$s have been disabled."
264
+ msgstr ""
265
+
266
+ #. translators: %s = But or And
267
+
268
+ #: inc/classes/doingitright.class.php:1285
269
+ msgid "%s the blog isn't set to public. This means WordPress allows the links to be followed regardless."
270
+ msgstr ""
271
+
272
+ #: inc/classes/doingitright.class.php:1337
273
+ msgid "Search Engine aren't allowed to archive this %s."
274
+ msgstr ""
275
+
276
+ #: inc/classes/doingitright.class.php:1342
277
+ msgid "Search Engine are allowed to archive this %s."
278
+ msgstr ""
279
+
280
+ #: inc/classes/doingitright.class.php:1354
281
+ msgid "But you've disabled archiving for the whole site."
282
+ msgstr ""
283
+
284
+ #. translators: 1: But or And, 2: Current taxonomy term plural label
285
+
286
+ #: inc/classes/doingitright.class.php:1380
287
+ msgid "%1$s archiving for %2$s have been disabled."
288
+ msgstr ""
289
+
290
+ #. translators: %s = But or And
291
+
292
+ #: inc/classes/doingitright.class.php:1393
293
+ msgid "%s the blog isn't set to public. This means WordPress allows the blog to be archived regardless."
294
+ msgstr ""
295
+
296
+ #: inc/classes/doingitright.class.php:1439
297
+ msgid "%s isn't being redirected."
298
+ msgstr ""
299
+
300
+ #: inc/classes/doingitright.class.php:1482
301
+ #: inc/classes/doingitright.class.php:1511
302
+ msgid "%s is being redirected. This means no SEO values have to be set."
303
+ msgstr ""
304
+
305
+ #: inc/classes/doingitright.class.php:1485
306
+ #: inc/classes/doingitright.class.php:1527
307
+ msgid "%s is not being indexed. This means no SEO values have to be set."
308
+ msgstr ""
309
+
310
+ #: inc/classes/doingitright.class.php:1640
311
+ msgid "Title:"
312
+ msgstr ""
313
+
314
+ #: inc/classes/doingitright.class.php:1641
315
+ msgid "Description:"
316
+ msgstr ""
317
+
318
+ #: inc/classes/doingitright.class.php:1642
319
+ msgid "Index:"
320
+ msgstr ""
321
+
322
+ #: inc/classes/doingitright.class.php:1643
323
+ msgid "Follow:"
324
+ msgstr ""
325
+
326
+ #: inc/classes/doingitright.class.php:1644
327
+ msgid "Archive:"
328
+ msgstr ""
329
+
330
+ #: inc/classes/doingitright.class.php:1645
331
+ msgid "Redirect:"
332
+ msgstr ""
333
+
334
+ #: inc/classes/doingitright.class.php:1647
335
+ msgid "Generated: Automatically generated."
336
+ msgstr ""
337
+
338
+ #: inc/classes/doingitright.class.php:1649
339
+ msgctxt "Generated"
340
+ msgid "G"
341
+ msgstr ""
342
+
343
+ #: inc/classes/doingitright.class.php:1650
344
+ msgctxt "Title"
345
+ msgid "T"
346
+ msgstr ""
347
+
348
+ #: inc/classes/doingitright.class.php:1651
349
+ msgctxt "Description"
350
+ msgid "D"
351
+ msgstr ""
352
+
353
+ #: inc/classes/doingitright.class.php:1652
354
+ msgctxt "no-Index"
355
+ msgid "I"
356
+ msgstr ""
357
+
358
+ #: inc/classes/doingitright.class.php:1653
359
+ msgctxt "no-Follow"
360
+ msgid "F"
361
+ msgstr ""
362
+
363
+ #: inc/classes/doingitright.class.php:1654
364
+ msgctxt "no-Archive"
365
+ msgid "A"
366
+ msgstr ""
367
+
368
+ #: inc/classes/doingitright.class.php:1655
369
+ msgctxt "Redirect"
370
+ msgid "R"
371
+ msgstr ""
372
+
373
+ #: inc/classes/doingitright.class.php:1657
374
+ msgctxt "But there are..."
375
+ msgid "But"
376
+ msgstr ""
377
+
378
+ #: inc/classes/doingitright.class.php:1658
379
+ msgctxt "And there are..."
380
+ msgid "And"
381
+ msgstr ""
382
+
383
+ #: inc/classes/feed.class.php:133
384
+ msgctxt "The content source"
385
+ msgid "Source"
386
+ msgstr ""
387
+
388
+ #. translators: 1: Title, 2: on, 3: Blogname
389
+ #. translators: 1: Title on Blogname, 2: Separator, 3: Excerpt
390
+
391
+ #: inc/classes/generate-description.class.php:471
392
+ #: inc/classes/generate-description.class.php:475
393
+ msgid "%1$s %2$s %3$s"
394
+ msgstr ""
395
+
396
+ #. translators: Front-end output.
397
+
398
+ #: inc/classes/generate-description.class.php:611
399
+ #: inc/classes/metaboxes.class.php:498
400
+ msgctxt "Placement. e.g. Post Title \"on\" Blog Name"
401
+ msgid "on"
402
+ msgstr ""
403
+
404
+ #. translators: Front-end output.
405
+
406
+ #: inc/classes/generate-description.class.php:675
407
+ msgid "Latest posts:"
408
+ msgstr ""
409
+
410
+ #. translators: Front-end output. 1: Taxonomy singular name, 2: Current
411
+ #. taxonomy term
412
+
413
+ #: inc/classes/generate-title.class.php:857
414
+ #: inc/classes/generate-title.class.php:917
415
+ msgid "%1$s: %2$s"
416
+ msgstr ""
417
+
418
+ #. translators: Front-end output.
419
+
420
+ #: inc/classes/generate-title.class.php:861
421
+ msgid "Author: %s"
422
+ msgstr ""
423
+
424
+ #. translators: Front-end output.
425
+
426
+ #: inc/classes/generate-title.class.php:865
427
+ msgctxt "yearly archives date format"
428
+ msgid "Y"
429
+ msgstr ""
430
+
431
+ #. translators: Front-end output.
432
+
433
+ #: inc/classes/generate-title.class.php:867
434
+ msgid "Year: %s"
435
+ msgstr ""
436
+
437
+ #. translators: Front-end output.
438
+
439
+ #: inc/classes/generate-title.class.php:870
440
+ msgctxt "monthly archives date format"
441
+ msgid "F Y"
442
+ msgstr ""
443
+
444
+ #. translators: Front-end output.
445
+
446
+ #: inc/classes/generate-title.class.php:872
447
+ msgid "Month: %s"
448
+ msgstr ""
449
+
450
+ #. translators: Front-end output.
451
+
452
+ #: inc/classes/generate-title.class.php:875
453
+ msgctxt "daily archives date format"
454
+ msgid "F j, Y"
455
+ msgstr ""
456
+
457
+ #. translators: Front-end output.
458
+
459
+ #: inc/classes/generate-title.class.php:877
460
+ msgid "Day: %s"
461
+ msgstr ""
462
+
463
+ #. translators: Front-end output.
464
+
465
+ #: inc/classes/generate-title.class.php:882
466
+ msgctxt "post format archive title"
467
+ msgid "Asides"
468
+ msgstr ""
469
+
470
+ #. translators: Front-end output.
471
+
472
+ #: inc/classes/generate-title.class.php:885
473
+ msgctxt "post format archive title"
474
+ msgid "Galleries"
475
+ msgstr ""
476
+
477
+ #. translators: Front-end output.
478
+
479
+ #: inc/classes/generate-title.class.php:888
480
+ msgctxt "post format archive title"
481
+ msgid "Images"
482
+ msgstr ""
483
+
484
+ #. translators: Front-end output.
485
+
486
+ #: inc/classes/generate-title.class.php:891
487
+ msgctxt "post format archive title"
488
+ msgid "Videos"
489
+ msgstr ""
490
+
491
+ #. translators: Front-end output.
492
+
493
+ #: inc/classes/generate-title.class.php:894
494
+ msgctxt "post format archive title"
495
+ msgid "Quotes"
496
+ msgstr ""
497
+
498
+ #. translators: Front-end output.
499
+
500
+ #: inc/classes/generate-title.class.php:897
501
+ msgctxt "post format archive title"
502
+ msgid "Links"
503
+ msgstr ""
504
+
505
+ #. translators: Front-end output.
506
+
507
+ #: inc/classes/generate-title.class.php:900
508
+ msgctxt "post format archive title"
509
+ msgid "Statuses"
510
+ msgstr ""
511
+
512
+ #. translators: Front-end output.
513
+
514
+ #: inc/classes/generate-title.class.php:903
515
+ msgctxt "post format archive title"
516
+ msgid "Audio"
517
+ msgstr ""
518
+
519
+ #. translators: Front-end output.
520
+
521
+ #: inc/classes/generate-title.class.php:906
522
+ msgctxt "post format archive title"
523
+ msgid "Chats"
524
+ msgstr ""
525
+
526
+ #. translators: Front-end output.
527
+
528
+ #: inc/classes/generate-title.class.php:911
529
+ msgid "Archives: %s"
530
+ msgstr ""
531
+
532
+ #. translators: Front-end output.
533
+
534
+ #: inc/classes/generate-title.class.php:921
535
+ msgid "Archives"
536
+ msgstr ""
537
+
538
+ #. translators: Front-end output.
539
+
540
+ #: inc/classes/generate-title.class.php:1025
541
+ msgid "Untitled"
542
+ msgstr ""
543
+
544
+ #. translators: Front-end output.
545
+
546
+ #: inc/classes/generate-title.class.php:1062
547
+ msgid "Search results for:"
548
+ msgstr ""
549
+
550
+ #. translators: Front-end output
551
+
552
+ #: inc/classes/generate-title.class.php:1238
553
+ msgid "Protected: %s"
554
+ msgstr ""
555
+
556
+ #. translators: Front-end output
557
+
558
+ #: inc/classes/generate-title.class.php:1242
559
+ msgid "Private: %s"
560
+ msgstr ""
561
+
562
+ #. translators: Front-end output.
563
+
564
+ #: inc/classes/generate-title.class.php:1275
565
+ msgid "Page %s"
566
+ msgstr ""
567
+
568
+ #: inc/classes/init.class.php:314
569
+ msgid "Start The Seo Framework"
570
+ msgstr ""
571
+
572
+ #: inc/classes/init.class.php:315
573
+ msgid "End The Seo Framework"
574
+ msgstr ""
575
+
576
+ #: inc/classes/init.class.php:316
577
+ msgid "by Sybre Waaijer"
578
+ msgstr ""
579
+
580
+ #: inc/classes/inpost.class.php:155 inc/classes/inpost.class.php:333
581
+ msgid "%s SEO Settings"
582
+ msgstr ""
583
+
584
+ #: inc/classes/inpost.class.php:340 inc/classes/inpost.class.php:407
585
+ #: inc/classes/inpost.class.php:565 inc/classes/inpost.class.php:660
586
+ msgid "Doing it Right"
587
+ msgstr ""
588
+
589
+ #: inc/classes/inpost.class.php:350
590
+ msgid "%s Title"
591
+ msgstr ""
592
+
593
+ #: inc/classes/inpost.class.php:351 inc/classes/inpost.class.php:572
594
+ #: inc/classes/metaboxes.class.php:1073
595
+ msgid "Recommended Length: 50 to 55 characters"
596
+ msgstr ""
597
+
598
+ #: inc/classes/inpost.class.php:359 inc/classes/inpost.class.php:372
599
+ #: inc/classes/inpost.class.php:573 inc/classes/inpost.class.php:587
600
+ #: inc/classes/metaboxes.class.php:1074 inc/classes/metaboxes.class.php:1092
601
+ msgid "Characters Used: %s"
602
+ msgstr ""
603
+
604
+ #: inc/classes/inpost.class.php:366
605
+ msgid "%s Meta Description"
606
+ msgstr ""
607
+
608
+ #: inc/classes/inpost.class.php:367 inc/classes/inpost.class.php:586
609
+ #: inc/classes/metaboxes.class.php:1091
610
+ msgid "Recommended Length: 145 to 155 characters"
611
+ msgstr ""
612
+
613
+ #: inc/classes/inpost.class.php:380 inc/classes/inpost.class.php:387
614
+ #: inc/classes/inpost.class.php:394
615
+ msgid "Apply %s to this term"
616
+ msgstr ""
617
+
618
+ #: inc/classes/inpost.class.php:381 inc/classes/metaboxes.class.php:1222
619
+ msgid "Tell Search Engines not to show this page in their search results"
620
+ msgstr ""
621
+
622
+ #: inc/classes/inpost.class.php:388 inc/classes/metaboxes.class.php:1232
623
+ msgid "Tell Search Engines not to follow links on this page"
624
+ msgstr ""
625
+
626
+ #: inc/classes/inpost.class.php:395 inc/classes/metaboxes.class.php:1242
627
+ msgid "Tell Search Engines not to save a cached copy of this page"
628
+ msgstr ""
629
+
630
+ #: inc/classes/inpost.class.php:571 inc/classes/metaboxes.class.php:1072
631
+ msgid "Custom %s Title"
632
+ msgstr ""
633
+
634
+ #: inc/classes/inpost.class.php:585 inc/classes/metaboxes.class.php:1090
635
+ msgid "Custom %s Description"
636
+ msgstr ""
637
+
638
+ #: inc/classes/inpost.class.php:596
639
+ msgid "Custom Canonical URL"
640
+ msgstr ""
641
+
642
+ #: inc/classes/inpost.class.php:597
643
+ msgid "Preferred %s URL location"
644
+ msgstr ""
645
+
646
+ #. translators: 1: Option, 2: Post or Page
647
+
648
+ #: inc/classes/inpost.class.php:609 inc/classes/inpost.class.php:619
649
+ #: inc/classes/inpost.class.php:629
650
+ msgid "Apply %1$s to this %2$s"
651
+ msgstr ""
652
+
653
+ #: inc/classes/inpost.class.php:611
654
+ msgid "Tell Search Engines not to show this %s in their search results"
655
+ msgstr ""
656
+
657
+ #: inc/classes/inpost.class.php:621
658
+ msgid "Tell Search Engines not to follow links on this %s"
659
+ msgstr ""
660
+
661
+ #: inc/classes/inpost.class.php:631
662
+ msgid "Tell Search Engines not to save a cached copy of this %s"
663
+ msgstr ""
664
+
665
+ #: inc/classes/inpost.class.php:640
666
+ msgid "Local Search Settings"
667
+ msgstr ""
668
+
669
+ #: inc/classes/inpost.class.php:643
670
+ msgid "Exclude this %s from local search"
671
+ msgstr ""
672
+
673
+ #: inc/classes/inpost.class.php:644
674
+ msgid "This excludes this %s from local on-site search results"
675
+ msgstr ""
676
+
677
+ #: inc/classes/inpost.class.php:650
678
+ msgid "Custom 301 Redirect URL"
679
+ msgstr ""
680
+
681
+ #: inc/classes/inpost.class.php:651
682
+ msgid "This will force visitors to go to another URL"
683
+ msgstr ""
684
+
685
+ #: inc/classes/metaboxes.class.php:212
686
+ msgid "Example Post Title"
687
+ msgstr ""
688
+
689
+ #: inc/classes/metaboxes.class.php:229
690
+ msgid "Automated Title Settings"
691
+ msgstr ""
692
+
693
+ #: inc/classes/metaboxes.class.php:230
694
+ msgid "The page title is prominently shown within the browser tab as well as within the Search Engine results pages."
695
+ msgstr ""
696
+
697
+ #: inc/classes/metaboxes.class.php:232
698
+ msgid "Example Automated Title Output"
699
+ msgstr ""
700
+
701
+ #: inc/classes/metaboxes.class.php:256 inc/classes/metaboxes.class.php:551
702
+ #: inc/classes/metaboxes.class.php:707 inc/classes/metaboxes.class.php:878
703
+ #: inc/classes/metaboxes.class.php:1328 inc/classes/metaboxes.class.php:1761
704
+ #: inc/classes/metaboxes.class.php:2027
705
+ msgid "General"
706
+ msgstr ""
707
+
708
+ #: inc/classes/metaboxes.class.php:261 inc/classes/metaboxes.class.php:556
709
+ #: inc/classes/metaboxes.class.php:883
710
+ msgid "Additions"
711
+ msgstr ""
712
+
713
+ #: inc/classes/metaboxes.class.php:272
714
+ msgid "Prefixes"
715
+ msgstr ""
716
+
717
+ #: inc/classes/metaboxes.class.php:311 inc/classes/metaboxes.class.php:592
718
+ msgid "Recommended"
719
+ msgstr ""
720
+
721
+ #: inc/classes/metaboxes.class.php:315
722
+ msgid "Document Title Separator"
723
+ msgstr ""
724
+
725
+ #: inc/classes/metaboxes.class.php:322
726
+ msgid "If the title consists of two parts (original title and optional addition), then the separator will go in-between them."
727
+ msgstr ""
728
+
729
+ #: inc/classes/metaboxes.class.php:347
730
+ msgid "The Home Page has a specific option."
731
+ msgstr ""
732
+
733
+ #: inc/classes/metaboxes.class.php:351 inc/classes/metaboxes.class.php:1139
734
+ msgid "Document Title Additions Location"
735
+ msgstr ""
736
+
737
+ #: inc/classes/metaboxes.class.php:354 inc/classes/metaboxes.class.php:1141
738
+ msgid "Determines which side the added title text will go on."
739
+ msgstr ""
740
+
741
+ #: inc/classes/metaboxes.class.php:360 inc/classes/metaboxes.class.php:1148
742
+ msgid "Left:"
743
+ msgstr ""
744
+
745
+ #: inc/classes/metaboxes.class.php:367 inc/classes/metaboxes.class.php:1155
746
+ msgid "Right:"
747
+ msgstr ""
748
+
749
+ #: inc/classes/metaboxes.class.php:385
750
+ msgid "Remove Blogname from Title"
751
+ msgstr ""
752
+
753
+ #: inc/classes/metaboxes.class.php:389
754
+ msgid "Remove Blogname from title?"
755
+ msgstr ""
756
+
757
+ #: inc/classes/metaboxes.class.php:391
758
+ msgid "This might decouple your posts and pages from the rest of the website."
759
+ msgstr ""
760
+
761
+ #: inc/classes/metaboxes.class.php:393
762
+ msgid "Only use this option if you are aware of its SEO effects."
763
+ msgstr ""
764
+
765
+ #: inc/classes/metaboxes.class.php:419 inc/classes/metaboxes.class.php:663
766
+ msgid "Category"
767
+ msgstr ""
768
+
769
+ #: inc/classes/metaboxes.class.php:431
770
+ msgid "Example Category"
771
+ msgstr ""
772
+
773
+ #: inc/classes/metaboxes.class.php:449
774
+ msgid "Title prefix options"
775
+ msgstr ""
776
+
777
+ #: inc/classes/metaboxes.class.php:450
778
+ msgid "On archives a descriptive prefix may be added to the title."
779
+ msgstr ""
780
+
781
+ #: inc/classes/metaboxes.class.php:452
782
+ msgid "Example Automated Archive Title Output"
783
+ msgstr ""
784
+
785
+ #: inc/classes/metaboxes.class.php:460
786
+ msgid "Remove Archive Title Prefixes"
787
+ msgstr ""
788
+
789
+ #: inc/classes/metaboxes.class.php:464
790
+ msgid "Remove Prefixes from title?"
791
+ msgstr ""
792
+
793
+ #: inc/classes/metaboxes.class.php:468
794
+ msgid "The prefix helps visitors and Search Engines determine what kind of page they're visiting"
795
+ msgstr ""
796
+
797
+ #: inc/classes/metaboxes.class.php:497
798
+ msgid "Example Title"
799
+ msgstr ""
800
+
801
+ #: inc/classes/metaboxes.class.php:499
802
+ msgid "This is an example description..."
803
+ msgstr ""
804
+
805
+ #: inc/classes/metaboxes.class.php:526
806
+ msgid "Automated Description Settings"
807
+ msgstr ""
808
+
809
+ #: inc/classes/metaboxes.class.php:527 inc/classes/metaboxes.class.php:1099
810
+ msgid "The meta description can be used to determine the text used under the title on Search Engine results pages."
811
+ msgstr ""
812
+
813
+ #: inc/classes/metaboxes.class.php:529
814
+ msgid "Example Automated Description Output"
815
+ msgstr ""
816
+
817
+ #: inc/classes/metaboxes.class.php:596
818
+ msgid "Description Excerpt Separator"
819
+ msgstr ""
820
+
821
+ #: inc/classes/metaboxes.class.php:603
822
+ msgid "If the Automated Description consists of two parts (title and excerpt), then the separator will go in-between them."
823
+ msgstr ""
824
+
825
+ #: inc/classes/metaboxes.class.php:626
826
+ msgid "Description Additions Settings"
827
+ msgstr ""
828
+
829
+ #: inc/classes/metaboxes.class.php:627
830
+ msgid "To create a more organic description, a small introduction can be added before the description."
831
+ msgstr ""
832
+
833
+ #: inc/classes/metaboxes.class.php:628
834
+ msgid "The introduction consists of the title and optionally the blogname."
835
+ msgstr ""
836
+
837
+ #: inc/classes/metaboxes.class.php:632
838
+ msgid "Add descriptive Additions to Description"
839
+ msgstr ""
840
+
841
+ #: inc/classes/metaboxes.class.php:636
842
+ msgid "Add Additions to automated description?"
843
+ msgstr ""
844
+
845
+ #: inc/classes/metaboxes.class.php:637
846
+ msgid "This creates good meta descriptions"
847
+ msgstr ""
848
+
849
+ #: inc/classes/metaboxes.class.php:641
850
+ msgid "Add Blogname to Additions"
851
+ msgstr ""
852
+
853
+ #: inc/classes/metaboxes.class.php:645
854
+ msgid "Add Blogname to automated description additions?"
855
+ msgstr ""
856
+
857
+ #: inc/classes/metaboxes.class.php:664
858
+ msgid "Tag"
859
+ msgstr ""
860
+
861
+ #: inc/classes/metaboxes.class.php:665
862
+ msgid "Author"
863
+ msgstr ""
864
+
865
+ #: inc/classes/metaboxes.class.php:666
866
+ msgid "Date"
867
+ msgstr ""
868
+
869
+ #: inc/classes/metaboxes.class.php:667
870
+ msgid "Search Pages"
871
+ msgstr ""
872
+
873
+ #: inc/classes/metaboxes.class.php:668
874
+ msgid "Attachment Pages"
875
+ msgstr ""
876
+
877
+ #: inc/classes/metaboxes.class.php:669
878
+ msgctxt "...for the entire site"
879
+ msgid "the entire site"
880
+ msgstr ""
881
+
882
+ #: inc/classes/metaboxes.class.php:676
883
+ msgid "NoIndex"
884
+ msgstr ""
885
+
886
+ #: inc/classes/metaboxes.class.php:677
887
+ msgid "These options prevent indexing of the selected archives and pages. If you enable this, the selected archives or pages will be removed from Search Engine results pages."
888
+ msgstr ""
889
+
890
+ #: inc/classes/metaboxes.class.php:681
891
+ msgid "NoFollow"
892
+ msgstr ""
893
+
894
+ #: inc/classes/metaboxes.class.php:682
895
+ msgid "These options prevent links from being followed on the selected archives and pages. If you enable this, the selected archives or pages in-page links will gain no SEO value, including your own links."
896
+ msgstr ""
897
+
898
+ #: inc/classes/metaboxes.class.php:686
899
+ msgid "NoArchive"
900
+ msgstr ""
901
+
902
+ #: inc/classes/metaboxes.class.php:687
903
+ msgid "These options prevent caching of the selected archives and pages. If you enable this, Search Engines will not create a cached copy of the selected archives or pages."
904
+ msgstr ""
905
+
906
+ #: inc/classes/metaboxes.class.php:713
907
+ msgid "Indexing"
908
+ msgstr ""
909
+
910
+ #: inc/classes/metaboxes.class.php:719
911
+ msgid "Following"
912
+ msgstr ""
913
+
914
+ #: inc/classes/metaboxes.class.php:725
915
+ msgid "Archiving"
916
+ msgstr ""
917
+
918
+ #: inc/classes/metaboxes.class.php:758
919
+ msgid "Open Directory Settings"
920
+ msgstr ""
921
+
922
+ #: inc/classes/metaboxes.class.php:759
923
+ msgid "Sometimes, Search Engines use resources from certain Directories to find titles and descriptions for your content. You generally don't want them to. Turn these options on to prevent them from doing so."
924
+ msgstr ""
925
+
926
+ #: inc/classes/metaboxes.class.php:760
927
+ msgid "The Open Directory Project and the Yahoo! Directory may contain outdated SEO values. Therefore, it's best to leave these options checked."
928
+ msgstr ""
929
+
930
+ #: inc/classes/metaboxes.class.php:767 inc/classes/metaboxes.class.php:772
931
+ msgid "Apply %s to the entire site?"
932
+ msgstr ""
933
+
934
+ #: inc/classes/metaboxes.class.php:782
935
+ msgid "Paginated Archive Settings"
936
+ msgstr ""
937
+
938
+ #: inc/classes/metaboxes.class.php:783
939
+ msgid "Indexing the second or later page of any archive might cause duplication errors. Search Engines look down upon them; therefore, it's recommended to disable indexing of those pages."
940
+ msgstr ""
941
+
942
+ #: inc/classes/metaboxes.class.php:789
943
+ msgid "Apply %s to every second or later archive page?"
944
+ msgstr ""
945
+
946
+ #: inc/classes/metaboxes.class.php:811
947
+ msgid "%s Robots Settings"
948
+ msgstr ""
949
+
950
+ #. translators: 1: Option, 2: Post Type
951
+
952
+ #: inc/classes/metaboxes.class.php:823
953
+ msgid "Apply %1$s to %2$s?"
954
+ msgstr ""
955
+
956
+ #. translators: 1: Option, 2: Post Type
957
+
958
+ #: inc/classes/metaboxes.class.php:827
959
+ msgid "Apply %1$s to %2$s Archives?"
960
+ msgstr ""
961
+
962
+ #: inc/classes/metaboxes.class.php:858
963
+ msgid "These settings will take precedence over the settings set within the Home Page edit screen, if any."
964
+ msgstr ""
965
+
966
+ #: inc/classes/metaboxes.class.php:888
967
+ msgid "Robots"
968
+ msgstr ""
969
+
970
+ #: inc/classes/metaboxes.class.php:938
971
+ msgid "Title"
972
+ msgstr ""
973
+
974
+ #: inc/classes/metaboxes.class.php:939
975
+ msgid "Description"
976
+ msgstr ""
977
+
978
+ #: inc/classes/metaboxes.class.php:940 inc/classes/metaboxes.class.php:1117
979
+ #: inc/classes/metaboxes.class.php:1188 inc/classes/metaboxes.class.php:1583
980
+ #: inc/classes/metaboxes.class.php:1616
981
+ msgid "Home Page"
982
+ msgstr ""
983
+
984
+ #. translators: 1: Option, 2: Page SEO Settings, 3: Home Page
985
+
986
+ #: inc/classes/metaboxes.class.php:983 inc/classes/metaboxes.class.php:1044
987
+ msgid "Note: The %1$s is fetched from the %2$s on the %3$s."
988
+ msgstr ""
989
+
990
+ #: inc/classes/metaboxes.class.php:983 inc/classes/metaboxes.class.php:1044
991
+ msgid "Page SEO Settings"
992
+ msgstr ""
993
+
994
+ #: inc/classes/metaboxes.class.php:1061
995
+ msgid "Custom %s Title Tagline"
996
+ msgstr ""
997
+
998
+ #: inc/classes/metaboxes.class.php:1169
999
+ msgid "%s Tagline"
1000
+ msgstr ""
1001
+
1002
+ #: inc/classes/metaboxes.class.php:1173
1003
+ msgid "Add site description (tagline) to the Title on the %s?"
1004
+ msgstr ""
1005
+
1006
+ #: inc/classes/metaboxes.class.php:1205
1007
+ msgid "View Home Page Settings"
1008
+ msgstr ""
1009
+
1010
+ #: inc/classes/metaboxes.class.php:1205
1011
+ msgid "Checked in Page"
1012
+ msgstr ""
1013
+
1014
+ #: inc/classes/metaboxes.class.php:1211
1015
+ msgid "Home Page Robots Meta Settings"
1016
+ msgstr ""
1017
+
1018
+ #. translators: 1: Option, 2: Location
1019
+
1020
+ #: inc/classes/metaboxes.class.php:1219 inc/classes/metaboxes.class.php:1229
1021
+ #: inc/classes/metaboxes.class.php:1239
1022
+ msgid "Apply %1$s to the %2$s?"
1023
+ msgstr ""
1024
+
1025
+ #: inc/classes/metaboxes.class.php:1272
1026
+ msgid "Note: If any of these options are unchecked, but are checked on the Home Page, they will be outputted regardless."
1027
+ msgstr ""
1028
+
1029
+ #: inc/classes/metaboxes.class.php:1278
1030
+ msgid "Home Page Pagination Robots Settings"
1031
+ msgstr ""
1032
+
1033
+ #: inc/classes/metaboxes.class.php:1280
1034
+ msgid "If your Home Page is paginated and outputs content that's also found elsewhere on the website, enabling this option might prevent duplicate content."
1035
+ msgstr ""
1036
+
1037
+ #. translators: 1: Option, 2: Location
1038
+
1039
+ #: inc/classes/metaboxes.class.php:1288
1040
+ msgid "Apply %1$s to every second or later page on the %2$s?"
1041
+ msgstr ""
1042
+
1043
+ #: inc/classes/metaboxes.class.php:1343
1044
+ msgid "Post Dates"
1045
+ msgstr ""
1046
+
1047
+ #: inc/classes/metaboxes.class.php:1348
1048
+ msgid "Link Relationships"
1049
+ msgstr ""
1050
+
1051
+ #: inc/classes/metaboxes.class.php:1379
1052
+ msgid "Site Shortlink Settings"
1053
+ msgstr ""
1054
+
1055
+ #: inc/classes/metaboxes.class.php:1380
1056
+ msgid "The shortlink tag might have some use for 3rd party service discoverability, but it has little to no SEO value whatsoever."
1057
+ msgstr ""
1058
+
1059
+ #: inc/classes/metaboxes.class.php:1387
1060
+ msgid "Output shortlink tag?"
1061
+ msgstr ""
1062
+
1063
+ #: inc/classes/metaboxes.class.php:1396
1064
+ msgid "Social Meta Tags Settings"
1065
+ msgstr ""
1066
+
1067
+ #: inc/classes/metaboxes.class.php:1397
1068
+ msgid "Output various meta tags for social site integration, among other 3rd party services."
1069
+ msgstr ""
1070
+
1071
+ #: inc/classes/metaboxes.class.php:1406
1072
+ msgid "Output Open Graph meta tags?"
1073
+ msgstr ""
1074
+
1075
+ #: inc/classes/metaboxes.class.php:1407
1076
+ msgid "Facebook, Twitter, Pinterest and many other social sites make use of these tags."
1077
+ msgstr ""
1078
+
1079
+ #: inc/classes/metaboxes.class.php:1413
1080
+ msgid "Note: Another Open Graph plugin has been detected."
1081
+ msgstr ""
1082
+
1083
+ #: inc/classes/metaboxes.class.php:1421
1084
+ msgid "Output Facebook meta tags?"
1085
+ msgstr ""
1086
+
1087
+ #: inc/classes/metaboxes.class.php:1422 inc/classes/metaboxes.class.php:1434
1088
+ msgid "Output various tags targetted at %s."
1089
+ msgstr ""
1090
+
1091
+ #: inc/classes/metaboxes.class.php:1433
1092
+ msgid "Output Twitter meta tags?"
1093
+ msgstr ""
1094
+
1095
+ #: inc/classes/metaboxes.class.php:1440
1096
+ msgid "Note: Another Twitter Card plugin has been detected."
1097
+ msgstr ""
1098
+
1099
+ #: inc/classes/metaboxes.class.php:1454
1100
+ msgctxt "Example Facebook Personal URL"
1101
+ msgid "http://www.facebook.com/YourPersonalProfile"
1102
+ msgstr ""
1103
+
1104
+ #: inc/classes/metaboxes.class.php:1457
1105
+ msgctxt "Example Verified Facebook Business URL"
1106
+ msgid "http://www.facebook.com/YourVerifiedBusinessProfile"
1107
+ msgstr ""
1108
+
1109
+ #: inc/classes/metaboxes.class.php:1463
1110
+ msgid "Default Facebook Integration Settings"
1111
+ msgstr ""
1112
+
1113
+ #: inc/classes/metaboxes.class.php:1464
1114
+ msgid "Facebook post sharing works mostly through Open Graph. However, you can also link your Business and Personal Facebook pages, among various other options."
1115
+ msgstr ""
1116
+
1117
+ #: inc/classes/metaboxes.class.php:1465
1118
+ msgid "When these options are filled in, Facebook might link your Facebook profile to be followed and liked when your post or page is shared."
1119
+ msgstr ""
1120
+
1121
+ #: inc/classes/metaboxes.class.php:1471
1122
+ msgid "Article Author Facebook URL"
1123
+ msgstr ""
1124
+
1125
+ #: inc/classes/metaboxes.class.php:1472
1126
+ msgid "Your Facebook Profile"
1127
+ msgstr ""
1128
+
1129
+ #: inc/classes/metaboxes.class.php:1481
1130
+ msgid "Article Publisher Facebook URL"
1131
+ msgstr ""
1132
+
1133
+ #: inc/classes/metaboxes.class.php:1482
1134
+ msgid "To use this, you need to be a verified business"
1135
+ msgstr ""
1136
+
1137
+ #: inc/classes/metaboxes.class.php:1491
1138
+ msgid "Facebook App ID"
1139
+ msgstr ""
1140
+
1141
+ #: inc/classes/metaboxes.class.php:1492
1142
+ msgid "Get Facebook App ID"
1143
+ msgstr ""
1144
+
1145
+ #: inc/classes/metaboxes.class.php:1512
1146
+ msgctxt "Twitter @username"
1147
+ msgid "@your-site-username"
1148
+ msgstr ""
1149
+
1150
+ #: inc/classes/metaboxes.class.php:1515
1151
+ msgctxt "Twitter @username"
1152
+ msgid "@your-personal-username"
1153
+ msgstr ""
1154
+
1155
+ #: inc/classes/metaboxes.class.php:1520
1156
+ msgid "Default Twitter Integration Settings"
1157
+ msgstr ""
1158
+
1159
+ #: inc/classes/metaboxes.class.php:1521
1160
+ msgid "Twitter post sharing works mostly through Open Graph. However, you can also link your Business and Personal Twitter pages, among various other options."
1161
+ msgstr ""
1162
+
1163
+ #: inc/classes/metaboxes.class.php:1526
1164
+ msgid "Twitter Card Type"
1165
+ msgstr ""
1166
+
1167
+ #: inc/classes/metaboxes.class.php:1527
1168
+ msgid "What kind of Twitter card would you like to use? It will default to %s if no image is found."
1169
+ msgstr ""
1170
+
1171
+ #: inc/classes/metaboxes.class.php:1537
1172
+ msgid "Example"
1173
+ msgstr ""
1174
+
1175
+ #: inc/classes/metaboxes.class.php:1548
1176
+ msgid "When the following options are filled in, Twitter might link your Twitter Site or Personal Profile when your post or page is shared."
1177
+ msgstr ""
1178
+
1179
+ #: inc/classes/metaboxes.class.php:1551
1180
+ msgid "Your Website's Twitter Profile"
1181
+ msgstr ""
1182
+
1183
+ #: inc/classes/metaboxes.class.php:1552 inc/classes/metaboxes.class.php:1562
1184
+ msgid "Find your @username"
1185
+ msgstr ""
1186
+
1187
+ #: inc/classes/metaboxes.class.php:1561
1188
+ msgid "Your Personal Twitter Profile"
1189
+ msgstr ""
1190
+
1191
+ #: inc/classes/metaboxes.class.php:1581 inc/classes/termdata.class.php:189
1192
+ #: inc/classes/termdata.class.php:225
1193
+ msgid "Pages"
1194
+ msgstr ""
1195
+
1196
+ #: inc/classes/metaboxes.class.php:1582
1197
+ msgid "Posts"
1198
+ msgstr ""
1199
+
1200
+ #: inc/classes/metaboxes.class.php:1586
1201
+ msgid "Post Date Settings"
1202
+ msgstr ""
1203
+
1204
+ #: inc/classes/metaboxes.class.php:1587
1205
+ msgid "Some Search Engines output the publishing date and modified date next to the search results. These help Search Engines find new content and could impact the SEO value."
1206
+ msgstr ""
1207
+
1208
+ #: inc/classes/metaboxes.class.php:1588
1209
+ msgid "It's recommended on posts, but it's not recommended on pages unless you modify or create new pages frequently."
1210
+ msgstr ""
1211
+
1212
+ #. translators: 1: Option, 2: Post Type
1213
+
1214
+ #: inc/classes/metaboxes.class.php:1592 inc/classes/metaboxes.class.php:1596
1215
+ #: inc/classes/metaboxes.class.php:1603 inc/classes/metaboxes.class.php:1607
1216
+ #: inc/classes/metaboxes.class.php:1621 inc/classes/metaboxes.class.php:1625
1217
+ msgid "Add %1$s to %2$s?"
1218
+ msgstr ""
1219
+
1220
+ #: inc/classes/metaboxes.class.php:1617
1221
+ msgid "Because you only publish the Home Page once, Search Engines might think your website is outdated. This can be prevented by disabling the following options."
1222
+ msgstr ""
1223
+
1224
+ #: inc/classes/metaboxes.class.php:1643
1225
+ msgid "Link Relationship Settings"
1226
+ msgstr ""
1227
+
1228
+ #: inc/classes/metaboxes.class.php:1644
1229
+ msgid "Some Search Engines look for relations between the content of your pages. If you have multiple pages for a single Post or Page, or have archives indexed, this option will help Search Engines look for the right page to display in the Search Results."
1230
+ msgstr ""
1231
+
1232
+ #: inc/classes/metaboxes.class.php:1645
1233
+ msgid "It's recommended to turn this option on for better SEO consistency and to prevent duplicate content errors."
1234
+ msgstr ""
1235
+
1236
+ #. translators: %s = <code>rel</code>
1237
+
1238
+ #: inc/classes/metaboxes.class.php:1650
1239
+ msgid "Add %s link tags to Posts and Pages?"
1240
+ msgstr ""
1241
+
1242
+ #. translators: %s = <code>rel</code>
1243
+
1244
+ #: inc/classes/metaboxes.class.php:1654
1245
+ msgid "Add %s link tags to Archives?"
1246
+ msgstr ""
1247
+
1248
+ #. translators: %s = <code>rel</code>
1249
+
1250
+ #: inc/classes/metaboxes.class.php:1658
1251
+ msgid "Add %s link tags to the Home Page?"
1252
+ msgstr ""
1253
+
1254
+ #: inc/classes/metaboxes.class.php:1684
1255
+ msgid "Webmaster Integration Settings"
1256
+ msgstr ""
1257
+
1258
+ #: inc/classes/metaboxes.class.php:1685
1259
+ msgid "When adding your website to Google, Bing and other Webmaster Tools, you'll be asked to add a code or file to your website for verification purposes. These options will help you easily integrate those codes."
1260
+ msgstr ""
1261
+
1262
+ #: inc/classes/metaboxes.class.php:1686
1263
+ msgid "Verifying your website has no SEO value whatsoever. But you might gain added benefits such as search ranking insights to help you improve your website's content."
1264
+ msgstr ""
1265
+
1266
+ #: inc/classes/metaboxes.class.php:1692
1267
+ msgid "Google Webmaster Verification Code"
1268
+ msgstr ""
1269
+
1270
+ #: inc/classes/metaboxes.class.php:1693
1271
+ msgid "Get the Google Verification code"
1272
+ msgstr ""
1273
+
1274
+ #: inc/classes/metaboxes.class.php:1702
1275
+ msgid "Bing Webmaster Verification Code"
1276
+ msgstr ""
1277
+
1278
+ #: inc/classes/metaboxes.class.php:1703
1279
+ msgid "Get the Bing Verification Code"
1280
+ msgstr ""
1281
+
1282
+ #: inc/classes/metaboxes.class.php:1712
1283
+ msgid "Yandex Webmaster Verification Code"
1284
+ msgstr ""
1285
+
1286
+ #: inc/classes/metaboxes.class.php:1713
1287
+ msgid "Get the Yandex Verification Code"
1288
+ msgstr ""
1289
+
1290
+ #: inc/classes/metaboxes.class.php:1722
1291
+ msgid "Pinterest Analytics Verification Code"
1292
+ msgstr ""
1293
+
1294
+ #: inc/classes/metaboxes.class.php:1723
1295
+ msgid "Get the Pinterest Verification Code"
1296
+ msgstr ""
1297
+
1298
+ #: inc/classes/metaboxes.class.php:1766
1299
+ msgid "Website"
1300
+ msgstr ""
1301
+
1302
+ #: inc/classes/metaboxes.class.php:1803
1303
+ msgid "The Knowledge Graph lets Google and other Search Engines know where to find you or your organization and its relevant content."
1304
+ msgstr ""
1305
+
1306
+ #: inc/classes/metaboxes.class.php:1804
1307
+ msgid "Google is becoming more of an 'Answer Engine' than a 'Search Engine'. Setting up these options could have a positive impact on the SEO value of your website."
1308
+ msgstr ""
1309
+
1310
+ #: inc/classes/metaboxes.class.php:1807
1311
+ msgid "Output Knowledge tags?"
1312
+ msgstr ""
1313
+
1314
+ #: inc/classes/metaboxes.class.php:1817
1315
+ msgctxt "WordPress Customizer"
1316
+ msgid "Website logo"
1317
+ msgstr ""
1318
+
1319
+ #: inc/classes/metaboxes.class.php:1819
1320
+ msgid "Use the Favicon from Customizer as the Organization Logo?"
1321
+ msgstr ""
1322
+
1323
+ #: inc/classes/metaboxes.class.php:1820
1324
+ msgid "This option only has an effect when this site represents an Organization. If left disabled, Search Engines will look elsewhere for a logo, if it exists and is assigned as a logo."
1325
+ msgstr ""
1326
+
1327
+ #: inc/classes/metaboxes.class.php:1841
1328
+ msgid "About this website"
1329
+ msgstr ""
1330
+
1331
+ #: inc/classes/metaboxes.class.php:1842
1332
+ msgid "Who or what is your website about?"
1333
+ msgstr ""
1334
+
1335
+ #: inc/classes/metaboxes.class.php:1847
1336
+ msgctxt "...Organization or Person."
1337
+ msgid "This website represents:"
1338
+ msgstr ""
1339
+
1340
+ #: inc/classes/metaboxes.class.php:1853
1341
+ msgid "An Organization"
1342
+ msgstr ""
1343
+
1344
+ #: inc/classes/metaboxes.class.php:1854
1345
+ msgid "A Person"
1346
+ msgstr ""
1347
+
1348
+ #: inc/classes/metaboxes.class.php:1867
1349
+ msgid "The organization or personal name"
1350
+ msgstr ""
1351
+
1352
+ #: inc/classes/metaboxes.class.php:1887
1353
+ msgid "Social Pages connected to this website"
1354
+ msgstr ""
1355
+
1356
+ #: inc/classes/metaboxes.class.php:1888
1357
+ msgid "Don't have a page at a site or is the profile only privately accessible? Leave that field empty. Unsure? Fill it in anyway."
1358
+ msgstr ""
1359
+
1360
+ #: inc/classes/metaboxes.class.php:1889
1361
+ msgid "Add the link that leads directly to the social page of this website."
1362
+ msgstr ""
1363
+
1364
+ #: inc/classes/metaboxes.class.php:1894
1365
+ msgctxt "No spaces. E.g. https://facebook.com/RelatedProfile"
1366
+ msgid "RelatedProfile"
1367
+ msgstr ""
1368
+
1369
+ #: inc/classes/metaboxes.class.php:1895
1370
+ msgctxt "Social Profile"
1371
+ msgid "Profile"
1372
+ msgstr ""
1373
+
1374
+ #: inc/classes/metaboxes.class.php:1961
1375
+ msgid "Blog"
1376
+ msgstr ""
1377
+
1378
+ #: inc/classes/metaboxes.class.php:2001
1379
+ msgid "Permalink Settings"
1380
+ msgstr ""
1381
+
1382
+ #: inc/classes/metaboxes.class.php:2001 inc/classes/metaboxes.class.php:2134
1383
+ #: inc/classes/metaboxes.class.php:2153
1384
+ msgctxt "The sitemap can be found %s."
1385
+ msgid "here"
1386
+ msgstr ""
1387
+
1388
+ #: inc/classes/metaboxes.class.php:2004
1389
+ msgid "You're using the plain permalink structure."
1390
+ msgstr ""
1391
+
1392
+ #: inc/classes/metaboxes.class.php:2005
1393
+ msgid "This means we can't output the sitemap through the WordPress rewrite rules."
1394
+ msgstr ""
1395
+
1396
+ #: inc/classes/metaboxes.class.php:2007
1397
+ msgctxt "%s = here"
1398
+ msgid "Change your Permalink Settings %s (Recommended: 'postname')."
1399
+ msgstr ""
1400
+
1401
+ #: inc/classes/metaboxes.class.php:2037
1402
+ msgid "Timestamps"
1403
+ msgstr ""
1404
+
1405
+ #: inc/classes/metaboxes.class.php:2042
1406
+ msgctxt "Ping or notify Search Engine"
1407
+ msgid "Ping"
1408
+ msgstr ""
1409
+
1410
+ #: inc/classes/metaboxes.class.php:2106
1411
+ msgid "Sitemap Integration Settings"
1412
+ msgstr ""
1413
+
1414
+ #: inc/classes/metaboxes.class.php:2111
1415
+ msgid "Another active sitemap plugin has been detected. This means that the sitemap functionality has been replaced."
1416
+ msgstr ""
1417
+
1418
+ #: inc/classes/metaboxes.class.php:2115
1419
+ msgid "A sitemap has been detected in the root folder of your website. This means that the sitemap functionality has no effect."
1420
+ msgstr ""
1421
+
1422
+ #: inc/classes/metaboxes.class.php:2119
1423
+ msgid "The Sitemap is an XML file that lists pages and posts for your website along with optional metadata about each post or page. This helps Search Engines crawl your website more easily."
1424
+ msgstr ""
1425
+
1426
+ #: inc/classes/metaboxes.class.php:2120
1427
+ msgid "The optional metadata include the post and page modified time and a page priority indication, which is automated."
1428
+ msgstr ""
1429
+
1430
+ #: inc/classes/metaboxes.class.php:2124
1431
+ msgid "Sitemap Output"
1432
+ msgstr ""
1433
+
1434
+ #: inc/classes/metaboxes.class.php:2126
1435
+ msgid "Output Sitemap?"
1436
+ msgstr ""
1437
+
1438
+ #: inc/classes/metaboxes.class.php:2134
1439
+ msgid "View sitemap"
1440
+ msgstr ""
1441
+
1442
+ #: inc/classes/metaboxes.class.php:2136
1443
+ msgctxt "%s = here"
1444
+ msgid "The sitemap can be found %s."
1445
+ msgstr ""
1446
+
1447
+ #: inc/classes/metaboxes.class.php:2153
1448
+ msgid "View robots.txt"
1449
+ msgstr ""
1450
+
1451
+ #: inc/classes/metaboxes.class.php:2156
1452
+ msgid "Robots.txt Settings"
1453
+ msgstr ""
1454
+
1455
+ #: inc/classes/metaboxes.class.php:2160
1456
+ msgid "The robots.txt file is the first thing Search Engines look for. If you add the sitemap location in the robots.txt file, then Search Engines will look for and index the sitemap."
1457
+ msgstr ""
1458
+
1459
+ #: inc/classes/metaboxes.class.php:2161
1460
+ msgid "If you do not add the sitemap location to the robots.txt file, you will need to notify Search Engines manually through the Webmaster Console provided by the Search Engines."
1461
+ msgstr ""
1462
+
1463
+ #: inc/classes/metaboxes.class.php:2165
1464
+ msgid "Add sitemap location in robots.txt"
1465
+ msgstr ""
1466
+
1467
+ #: inc/classes/metaboxes.class.php:2171
1468
+ msgid "Add sitemap location in robots?"
1469
+ msgstr ""
1470
+
1471
+ #: inc/classes/metaboxes.class.php:2177
1472
+ msgid "Another robots.txt sitemap Location addition has been detected."
1473
+ msgstr ""
1474
+
1475
+ #: inc/classes/metaboxes.class.php:2182
1476
+ msgctxt "%s = here"
1477
+ msgid "The robots.txt file can be found %s."
1478
+ msgstr ""
1479
+
1480
+ #: inc/classes/metaboxes.class.php:2211
1481
+ msgid "Timestamps Settings"
1482
+ msgstr ""
1483
+
1484
+ #: inc/classes/metaboxes.class.php:2212
1485
+ msgid "The modified time suggests to Search Engines where to look for content changes. It has no impact on the SEO value unless you drastically change pages or posts. It then depends on how well your content is constructed."
1486
+ msgstr ""
1487
+
1488
+ #: inc/classes/metaboxes.class.php:2213
1489
+ msgid "By default, the sitemap only outputs the modified date if you've enabled them within the Social Metabox. This setting overrides those settings for the Sitemap."
1490
+ msgstr ""
1491
+
1492
+ #: inc/classes/metaboxes.class.php:2217
1493
+ msgid "Output Modified Date"
1494
+ msgstr ""
1495
+
1496
+ #: inc/classes/metaboxes.class.php:2219
1497
+ msgid "Add %s to the sitemap?"
1498
+ msgstr ""
1499
+
1500
+ #: inc/classes/metaboxes.class.php:2229
1501
+ msgid "Timestamp Format Settings"
1502
+ msgstr ""
1503
+
1504
+ #: inc/classes/metaboxes.class.php:2231
1505
+ msgid "Determines how specific the modification timestamp is."
1506
+ msgstr ""
1507
+
1508
+ #: inc/classes/metaboxes.class.php:2238
1509
+ msgid "Complete date"
1510
+ msgstr ""
1511
+
1512
+ #: inc/classes/metaboxes.class.php:2244
1513
+ msgid "Complete date plus hours, minutes and timezone"
1514
+ msgstr ""
1515
+
1516
+ #: inc/classes/metaboxes.class.php:2263
1517
+ msgid "Ping Settings"
1518
+ msgstr ""
1519
+
1520
+ #: inc/classes/metaboxes.class.php:2264
1521
+ msgid "Notifying Search Engines of a sitemap change is helpful to get your content indexed as soon as possible."
1522
+ msgstr ""
1523
+
1524
+ #: inc/classes/metaboxes.class.php:2265
1525
+ msgid "By default this will happen at most once an hour."
1526
+ msgstr ""
1527
+
1528
+ #: inc/classes/metaboxes.class.php:2269
1529
+ msgid "Notify Search Engines"
1530
+ msgstr ""
1531
+
1532
+ #: inc/classes/metaboxes.class.php:2280
1533
+ msgid "Notify %s about sitemap changes?"
1534
+ msgstr ""
1535
+
1536
+ #: inc/classes/metaboxes.class.php:2299
1537
+ msgid "Content Feed Settings"
1538
+ msgstr ""
1539
+
1540
+ #: inc/classes/metaboxes.class.php:2300
1541
+ msgid "Sometimes, your content can get stolen by robots through the WordPress feeds. This can cause duplicate content issues. To prevent this from happening, it's recommended to convert the feed's content into an excerpt."
1542
+ msgstr ""
1543
+
1544
+ #: inc/classes/metaboxes.class.php:2301
1545
+ msgid "Adding a backlink below the feed's content will also let the visitors know where the content came from."
1546
+ msgstr ""
1547
+
1548
+ #: inc/classes/metaboxes.class.php:2305
1549
+ msgid "Change Feed Settings"
1550
+ msgstr ""
1551
+
1552
+ #: inc/classes/metaboxes.class.php:2307
1553
+ msgid "Convert feed content into excerpts?"
1554
+ msgstr ""
1555
+
1556
+ #: inc/classes/metaboxes.class.php:2308
1557
+ msgid "By default the excerpt will be at most 400 characters long"
1558
+ msgstr ""
1559
+
1560
+ #: inc/classes/metaboxes.class.php:2310
1561
+ msgid "Add backlinks below the feed content?"
1562
+ msgstr ""
1563
+
1564
+ #: inc/classes/metaboxes.class.php:2311
1565
+ msgid "This link will not be followed by Search Engines"
1566
+ msgstr ""
1567
+
1568
+ #: inc/classes/metaboxes.class.php:2323
1569
+ msgid "Reading Settings"
1570
+ msgstr ""
1571
+
1572
+ #: inc/classes/metaboxes.class.php:2326
1573
+ msgctxt "%s = Reading Settings"
1574
+ msgid "Note: The feed is already converted into an excerpt through the %s."
1575
+ msgstr ""
1576
+
1577
+ #: inc/classes/metaboxes.class.php:2331
1578
+ msgid "View feed"
1579
+ msgstr ""
1580
+
1581
+ #: inc/classes/metaboxes.class.php:2331
1582
+ msgctxt "The feed can be found %s."
1583
+ msgid "here"
1584
+ msgstr ""
1585
+
1586
+ #: inc/classes/metaboxes.class.php:2333
1587
+ msgctxt "%s = here"
1588
+ msgid "The feed can be found %s."
1589
+ msgstr ""
1590
+
1591
+ #: inc/classes/metaboxes.class.php:2349
1592
+ msgid "Schema.org Output Settings"
1593
+ msgstr ""
1594
+
1595
+ #: inc/classes/metaboxes.class.php:2352
1596
+ msgid "Another Schema.org plugin has been detected."
1597
+ msgstr ""
1598
+
1599
+ #: inc/classes/metaboxes.class.php:2354
1600
+ msgid "The Schema.org markup is a standard way of annotating structured data for Search Engines. This markup is represented within hidden scripts throughout the website."
1601
+ msgstr ""
1602
+
1603
+ #: inc/classes/metaboxes.class.php:2355
1604
+ msgid "When your web pages include structured data markup, Search Engines can use that data to index your content better, present it more prominently in Search Results, and use it in several different applications."
1605
+ msgstr ""
1606
+
1607
+ #. translators:
1608
+ #. https:developers.google.com/search/docs/data-types/sitelinks-searchbox
1609
+
1610
+ #: inc/classes/metaboxes.class.php:2360 inc/classes/metaboxes.class.php:2364
1611
+ msgctxt "Product name"
1612
+ msgid "Sitelinks Searchbox"
1613
+ msgstr ""
1614
+
1615
+ #: inc/classes/metaboxes.class.php:2361
1616
+ msgid "When Search users search for your brand name, the following option allows them to search through your website directly from the Search Results."
1617
+ msgstr ""
1618
+
1619
+ #: inc/classes/metaboxes.class.php:2371
1620
+ msgctxt "Product name"
1621
+ msgid "Enable Sitelinks Searchbox?"
1622
+ msgstr ""
1623
+
1624
+ #: inc/classes/metaboxes.class.php:2380
1625
+ msgid "Site Name"
1626
+ msgstr ""
1627
+
1628
+ #: inc/classes/metaboxes.class.php:2381
1629
+ msgid "When using breadcrumbs, the first entry is by default your website's address. Using the following option will convert it to the Site Name."
1630
+ msgstr ""
1631
+
1632
+ #: inc/classes/metaboxes.class.php:2384
1633
+ msgid "Include your Site Name in Search Results"
1634
+ msgstr ""
1635
+
1636
+ #: inc/classes/metaboxes.class.php:2388
1637
+ msgid "The Site Name is: %s"
1638
+ msgstr ""
1639
+
1640
+ #: inc/classes/metaboxes.class.php:2392
1641
+ msgid "Convert URL to Site Name?"
1642
+ msgstr ""
1643
+
1644
+ #: inc/classes/metaboxes.class.php:2401
1645
+ msgid "Breadcrumbs"
1646
+ msgstr ""
1647
+
1648
+ #: inc/classes/metaboxes.class.php:2402
1649
+ msgid "Breadcrumb trails indicate the page's position in the site hierarchy. Using the following option will show the hierarchy within the Search Results when available."
1650
+ msgstr ""
1651
+
1652
+ #: inc/classes/metaboxes.class.php:2405
1653
+ msgid "About Breadcrumbs"
1654
+ msgstr ""
1655
+
1656
+ #: inc/classes/metaboxes.class.php:2409
1657
+ msgid "Multiple trails can be outputted. The longest trail is prioritized."
1658
+ msgstr ""
1659
+
1660
+ #: inc/classes/metaboxes.class.php:2413
1661
+ msgid "Enable Breadcrumbs?"
1662
+ msgstr ""
1663
+
1664
+ #: inc/classes/sitemaps.class.php:277
1665
+ msgid "Sitemap is generated for this view"
1666
+ msgstr ""
1667
+
1668
+ #: inc/classes/sitemaps.class.php:279
1669
+ msgid "Sitemap is served from cache"
1670
+ msgstr ""
1671
+
1672
+ #: inc/classes/sitemaps.class.php:399
1673
+ msgid "Sitemap is generated on"
1674
+ msgstr ""
1675
+
1676
+ #: inc/classes/siteoptions.class.php:461
1677
+ msgid "here"
1678
+ msgstr ""
1679
+
1680
+ #: inc/classes/siteoptions.class.php:463
1681
+ msgctxt "%s = here"
1682
+ msgid "View the new options %s."
1683
+ msgstr ""
1684
+
1685
+ #: inc/classes/transients.class.php:259
1686
+ msgid "Third parameter must be a known type."
1687
+ msgstr ""
1688
+
1689
+ #: load.class.php:224 load.class.php:234 load.class.php:246
1690
+ msgid "Class or Method not found."
1691
+ msgstr ""
1692
+
1693
+ #: load.class.php:258
1694
+ msgid "Function needs to be called as a string."
1695
+ msgstr ""
1696
+
1697
+ #: load.class.php:271
1698
+ msgid "%s of The SEO Framework"
1699
+ msgstr ""
1700
+ #. Plugin Name of the plugin/theme
1701
+ msgid "The SEO Framework"
1702
+ msgstr ""
1703
+
1704
+ #. Plugin URI of the plugin/theme
1705
+ msgid "https://wordpress.org/plugins/autodescription/"
1706
+ msgstr ""
1707
+
1708
+ #. Description of the plugin/theme
1709
+ msgid "An automated, advanced, accessible, unbranded and extremely fast SEO solution for any WordPress website."
1710
+ msgstr ""
1711
+
1712
+ #. Author of the plugin/theme
1713
+ msgid "Sybre Waaijer"
1714
+ msgstr ""
1715
+
1716
+ #. Author URI of the plugin/theme
1717
+ msgid "https://cyberwire.nl/"
1718
+ msgstr ""
lib/css/autodescription-rtl.css ADDED
@@ -0,0 +1,589 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Dismissible Notices.
3
+ */
4
+ .seo-notice {
5
+ position: relative;
6
+ }
7
+
8
+ a.autodescription-dismiss {
9
+ position: absolute;
10
+ top: 0;
11
+ left: 1px;
12
+ border: none;
13
+ margin: 0;
14
+ padding: 9px;
15
+ background: 0 0;
16
+ color: #b4b9be;
17
+ cursor: pointer;
18
+ text-decoration: none;
19
+ }
20
+
21
+ a.autodescription-dismiss:before {
22
+ background: 0 0;
23
+ content: "\f153";
24
+ display: block;
25
+ font: 400 16px/20px dashicons;
26
+ speak: none;
27
+ height: 20px;
28
+ text-align: center;
29
+ width: 20px;
30
+ -webkit-font-smoothing: antialiased;
31
+ -moz-osx-font-smoothing: grayscale;
32
+ }
33
+
34
+ a.autodescription-dismiss:hover {
35
+ color: #c00;
36
+ }
37
+
38
+ /**
39
+ * WooCommerce fix
40
+ */
41
+ table.wp-list-table .column-name {
42
+ width: 15%;
43
+ }
44
+
45
+ table.wp-list-table .column-is_in_stock,
46
+ table.wp-list-table .column-sku,
47
+ table.wp-list-table .column-price {
48
+ width: 8%;
49
+ }
50
+
51
+ .ad-seo a {
52
+ text-decoration: none;
53
+ font-size: 13px;
54
+ }
55
+
56
+ #autodescription-title-wrap {
57
+ position: relative;
58
+ display: block;
59
+ padding: 0;
60
+ height: auto;
61
+ width: auto;
62
+ }
63
+
64
+ #autodescription-title-offset {
65
+ visibility: hidden;
66
+ height: 0;
67
+ display: inline-block;
68
+ position: absolute;
69
+ right: 0;
70
+ color: transparent;
71
+ white-space: pre;
72
+ }
73
+
74
+ #autodescription-title-placeholder {
75
+ position: absolute;
76
+ color: #777;
77
+ -webkit-user-select: none;
78
+ -moz-user-select: none;
79
+ -ms-user-select: none;
80
+ user-select: none;
81
+ box-sizing: content-box;
82
+ top: 0;
83
+ left: 0;
84
+ overflow: hidden;
85
+ white-space: nowrap;
86
+ text-overflow: ellipsis;
87
+ }
88
+
89
+ /* START Character Counters */
90
+
91
+ .theseoframework-counter {
92
+ font-weight: 600;
93
+ cursor: pointer;
94
+ -webkit-user-select: none;
95
+ -moz-user-select: none;
96
+ -ms-user-select: none;
97
+ user-select: none;
98
+ }
99
+
100
+ span.ad-count-bad {
101
+ color: #dd3811;
102
+ }
103
+
104
+ span.ad-count-okay {
105
+ color: #ffa700;
106
+ }
107
+
108
+ span.ad-count-good {
109
+ color: #0cc34b;
110
+ }
111
+
112
+ span.tsf-counter-one {
113
+ color: #fff;
114
+ padding: 3px;
115
+ border-radius: 58px;
116
+ padding: 0 1px;
117
+ min-width: 29px;
118
+ display: inline-block;
119
+ text-align: center;
120
+ vertical-align: baseline;
121
+ }
122
+ span.tsf-counter-one.ad-count-bad {
123
+ background-color: #dd3811;
124
+ }
125
+ span.tsf-counter-one.ad-count-okay {
126
+ background-color: #ffa700;
127
+ }
128
+ span.tsf-counter-one.ad-count-good {
129
+ background-color: #0cc34b;
130
+ }
131
+
132
+ /* END Character Counters */
133
+
134
+ #ad_seo {
135
+ width: 18%;
136
+ }
137
+
138
+ /* WooCommerce fix */
139
+ th:last-of-type#ad_seo {
140
+ width: 160px;
141
+ }
142
+
143
+ span.ad-seo {
144
+ display: block;
145
+ width: 95%;
146
+ max-width: 220px;
147
+ border-radius: 0;
148
+ padding: 3px;
149
+ text-shadow: 0 0 3px rgba(0,0,0,0.3);
150
+ }
151
+
152
+ span.ad-seo.ad-100 {
153
+ width: 100%;
154
+ }
155
+
156
+ span.ad-bar-wrap {
157
+ display: table;
158
+ width: 100%;
159
+ border-radius: 0;
160
+ margin: 0 auto;
161
+ border-collapse: separate;
162
+ border-spacing: 0;
163
+ vertical-align: middle;
164
+ position: relative;
165
+ }
166
+
167
+
168
+ span.ad-sec-wrap {
169
+ display: table-cell;
170
+ border-collapse: collapse;
171
+ }
172
+
173
+ .ad-bar-wrap a {
174
+ display: table;
175
+ width: 100%;
176
+ color: #fff;
177
+ text-align: center;
178
+ cursor: help;
179
+ height: 100%;
180
+ min-width: 12px;
181
+ vertical-align: top;
182
+ line-height: 1.625em;
183
+ box-shadow: 2px 0px 0px -1px rgba(0,0,0,0.1) inset, -2px 0px 0px -1px rgba(0,0,0,0.1) inset;
184
+ }
185
+
186
+ span.ad-seo.pill,
187
+ span.ad-seo.pill span.ad-bar-wrap {
188
+ border-radius: 30px;
189
+ }
190
+
191
+ span.ad-seo.pill span.ad-bar-wrap a {
192
+ box-shadow: 2px 6px 3px -2px rgba(255,255,255,.2) inset,-2px -6px 3px -2px rgba(41,41,41,.2) inset,0 0 1px rgba(41,41,41,.6);
193
+ }
194
+
195
+ .ad-seo.pill .ad-bar-wrap span.ad-sec-wrap:last-of-type,
196
+ .ad-seo.pill .ad-bar-wrap span.ad-sec-wrap:last-of-type a {
197
+ border-top-left-radius: 30px;
198
+ border-bottom-left-radius: 30px;
199
+ }
200
+
201
+ .ad-seo.pill .ad-bar-wrap span.ad-sec-wrap:first-of-type,
202
+ .ad-seo.pill .ad-bar-wrap span.ad-sec-wrap:first-of-type a {
203
+ border-top-right-radius: 30px;
204
+ border-bottom-right-radius: 30px;
205
+ }
206
+
207
+ .ad-100 {
208
+ width: 100%;
209
+ }
210
+
211
+ .ad-60 {
212
+ width: 60%;
213
+ }
214
+
215
+ .ad-50 {
216
+ width: 50%;
217
+ }
218
+
219
+ .ad-40 {
220
+ width: 40%;
221
+ }
222
+
223
+ .ad-33 {
224
+ width: 33.333%;
225
+ }
226
+
227
+ .ad-25 {
228
+ width: 25%;
229
+ }
230
+
231
+ .ad-20 {
232
+ width: 20%;
233
+ }
234
+
235
+ .ad-16 {
236
+ width: 16.666%;
237
+ }
238
+
239
+ .ad-12-5 {
240
+ width: 12.5%;
241
+ }
242
+
243
+ .ad-11 {
244
+ width: 11.333%;
245
+ }
246
+
247
+ .ad-10 {
248
+ width: 10%;
249
+ }
250
+
251
+ .ad-seo-bad {
252
+ background-color: #dd3811;
253
+ }
254
+
255
+ .ad-seo-okay {
256
+ background-color: #ffa700;
257
+ }
258
+
259
+ .ad-seo-good {
260
+ background-color: #0cc34b;
261
+ }
262
+
263
+ .ad-seo-unknown {
264
+ background-color: #007bd2;
265
+ }
266
+
267
+ span.ad-seo .explanation-desc {
268
+ position: absolute;
269
+ width: auto;
270
+ min-width: 90%;
271
+ max-width: 220px;
272
+ font-weight: 600;
273
+ background: #007bd2;
274
+ padding: 8px 12px;
275
+ color: #fdfdfd;
276
+ border-radius: 0px;
277
+ z-index: 900142;
278
+ box-shadow: 0px 0px 2px rgba(0,0,0,0.6);
279
+ left: 0;
280
+ right: 0;
281
+ text-align: left;
282
+ }
283
+
284
+ span.ad-seo .explanation-desc span {
285
+ text-decoration: underline;
286
+ }
287
+
288
+ span.ad-seo .explanation-desc div {
289
+ width: 0;
290
+ height: 0;
291
+ border-left: 12px solid transparent;
292
+ border-right: 12px solid transparent;
293
+ border-top: 12px solid #007bd2;
294
+ position: absolute;
295
+ bottom: -8px;
296
+ z-index: 9999999;
297
+ left: 0;
298
+ }
299
+
300
+ /* Mobile support */
301
+ /* Needs more refining.... e.g. iPhone 6 = good, iPhone 6+ = offscreen */
302
+ .ad_seo.column-ad_seo {
303
+ overflow: initial !important;
304
+ min-width: 160px;
305
+ }
306
+
307
+ /* Taxonomies overflow fix */
308
+ /* @todo Needs to be changed within Javascript to show information below bar on first 2 entries */
309
+ #col-container, #col-right {
310
+ overflow: initial;
311
+ }
312
+
313
+ /* Site Settings */
314
+
315
+ .autodescription-metaboxes {
316
+ box-sizing: border-box;
317
+ max-width: 690px;
318
+ padding-bottom: 20px;
319
+ }
320
+
321
+ .autodescription-metaboxes .top-wrap {
322
+ width: 100%;
323
+ display: inline-block;
324
+ vertical-align: top;
325
+ }
326
+
327
+ .autodescription-metaboxes .top-wrap > h1,
328
+ .autodescription-metaboxes .top-wrap > h2 {
329
+ float: left;
330
+ }
331
+
332
+ .autodescription-metaboxes .metabox-holder {
333
+ clear: both;
334
+ }
335
+
336
+ .autodescription-metaboxes .top-buttons {
337
+ float: left;
338
+ }
339
+
340
+ .autodescription-metaboxes .bottom-buttons {
341
+ text-align: left;
342
+ }
343
+
344
+ .autodescription-metaboxes .top-buttons input,
345
+ .autodescription-metaboxes .bottom-buttons input {
346
+ margin-left: 10px;
347
+ }
348
+
349
+ .seo-notice {
350
+ clear: both;
351
+ }
352
+
353
+ .autodescription-metaboxes #title-separator,
354
+ .autodescription-metaboxes #description-separator {
355
+ display: table;
356
+ width: 100%;
357
+ border-collapse: collapse;
358
+ border-spacing: 0;
359
+ }
360
+
361
+ .autodescription-metaboxes #title-separator input,
362
+ .autodescription-metaboxes #description-separator input {
363
+ display: none;
364
+ float: left;
365
+ width: 0;
366
+ min-width: 0;
367
+ opacity: 0;
368
+ }
369
+
370
+ .autodescription-metaboxes #title-separator label,
371
+ .autodescription-metaboxes #description-separator label {
372
+ display: inline-block;
373
+ width: auto;
374
+ min-width: 28px;
375
+ min-height: 28px;
376
+ margin: 3px;
377
+ -moz-margin-end: 1.5px;
378
+ -moz-margin-start: 1.5px;
379
+ padding: 0 4px;
380
+ border: 1px solid #ccc;
381
+ line-height: 28px;
382
+ text-align: center;
383
+ cursor: pointer;
384
+ box-shadow: -1px -1px 1px #aaa inset;
385
+ font-size: 16px;
386
+ }
387
+
388
+ .autodescription-metaboxes #title-separator label.recommended,
389
+ .autodescription-metaboxes #description-separator label.recommended {
390
+ border: 1px solid #0cc34b;
391
+ box-shadow: -1px -1px 1px #0cc34b inset;
392
+ }
393
+
394
+ .autodescription-metaboxes #title-separator input:hover + label,
395
+ .autodescription-metaboxes #title-separator label:hover,
396
+ .autodescription-metaboxes #description-separator input:hover + label,
397
+ .autodescription-metaboxes #description-separator label:hover {
398
+ box-shadow: 1px 1px 1px #aaa inset;
399
+ background-color: #fff;
400
+ }
401
+
402
+ .autodescription-metaboxes #title-separator input:checked + label,
403
+ .autodescription-metaboxes #description-separator input:checked + label {
404
+ box-shadow: 1px 1px 1px #333 inset;
405
+ background-color: #fff;
406
+ }
407
+
408
+ .autodescription-metaboxes #title-location,
409
+ .autodescription-metaboxes #home-title-location {
410
+ display: block;
411
+ }
412
+
413
+ .autodescription-metaboxes #title-location label span,
414
+ .autodescription-metaboxes #home-title-location label span,
415
+ .autodescription-metaboxes #twitter-cards label span {
416
+ display: inline-block;
417
+ min-width: 60px;
418
+ vertical-align: baseline;
419
+ }
420
+
421
+ .autodescription-metaboxes #twitter-cards label span {
422
+ min-width: 150px;
423
+ }
424
+
425
+ .autodescription-metaboxes #home-title-location label span.custom-title-js,
426
+ .autodescription-metaboxes #home-title-location label span.custom-blogname-js,
427
+ .autodescription-metaboxes #home-title-location label span.custom-tagline-js,
428
+ .autodescription-metaboxes #title-location label .autodescription-sep-js,
429
+ .autodescription-metaboxes #home-title-location label .autodescription-sep-js {
430
+ display: inline;
431
+ min-width: 0;
432
+ white-space: pre;
433
+ }
434
+
435
+ /**
436
+ * Start Tabs.
437
+ */
438
+ .autodescription-metaboxes .seoframework-tab-no-js,
439
+ .autodescription-metaboxes .seoframework-nav-tab-wrapper {
440
+ position: relative;
441
+ clear: both;
442
+ width: 100%;
443
+ display: inline-block;
444
+ border-bottom: 1px solid #ccc;
445
+ line-height: inherit;
446
+ padding: 8px 12px 0;
447
+ margin: -4px -12px;
448
+ }
449
+
450
+ .autodescription-metaboxes .seoframework-nav-tab {
451
+ float: right;
452
+ border: 1px solid #ccc;
453
+ margin-right: .5em;
454
+ margin-bottom: -1px;
455
+ padding: 5px 14px;
456
+ font-size: 12px;
457
+ line-height: 16px;
458
+ background: #f1f1f1;
459
+ color: #555;
460
+ font-weight: 600;
461
+ }
462
+
463
+ .autodescription-metaboxes .seoframework-dashicons-tabs {
464
+ font-size: initial;
465
+ display: inline;
466
+ vertical-align: text-bottom;
467
+ }
468
+
469
+ .autodescription-metaboxes .seoframework-tabs-radio {
470
+ display: none;
471
+ width: 0;
472
+ height: 0;
473
+ position: absolute;
474
+ left: -9001px;
475
+ }
476
+
477
+ .autodescription-metaboxes .seoframework-tabs-radio:checked + label,
478
+ .autodescription-metaboxes .seoframework-active-tab {
479
+ background-color: inherit;
480
+ border-bottom-color: #fff;
481
+ color: #000;
482
+ }
483
+
484
+ .autodescription-metaboxes .seoframework-tabs-content {
485
+ margin: 1.33em auto 0;
486
+ }
487
+
488
+ .autodescription-metaboxes .seoframework-content-no-js {
489
+ margin: 1.33em auto;
490
+ }
491
+
492
+ body.js .autodescription-metaboxes .seoframework-tabs-content {
493
+ display: none;
494
+ }
495
+
496
+ body.js .autodescription-metaboxes .seoframework-tabs-content.seoframework-active-tab-content {
497
+ display: block;
498
+ }
499
+
500
+ /**
501
+ * End tabs.
502
+ */
503
+
504
+ .autodescription-metaboxes .seoframework-default-selected {
505
+ border-color: #1c9d38;
506
+ }
507
+
508
+ .autodescription-metaboxes .seoframework-default-selected:checked:before {
509
+ color: #1c9d38;
510
+ }
511
+
512
+ .autodescription-metaboxes .seoframework-warning-selected {
513
+ border-color: #dd3811;
514
+ }
515
+
516
+ .autodescription-metaboxes .seoframework-warning-selected:checked:before {
517
+ color: #dd3811;
518
+ }
519
+
520
+ .theseoframework-fields {
521
+ font-size: 13px;
522
+ line-height: 1.5;
523
+ margin: 1em 0;
524
+ }
525
+
526
+ .theseoframework-fields .toblock {
527
+ display: block;
528
+ width: 100%;
529
+ margin-bottom: 4px;
530
+ }
531
+
532
+ .theseoframework-fields p.description {
533
+ margin: 7px 0 5px;
534
+ color: #666;
535
+ }
536
+
537
+ .theseoframework-option-spacer {
538
+ margin: 1em 0;
539
+ }
540
+
541
+ @media screen and (max-width: 782px) {
542
+ .autodescription-metaboxes p.theseoframework-fields,
543
+ .theseoframework-inpost-box p.theseoframework-fields {
544
+ line-height: 2.8
545
+ }
546
+
547
+ .autodescription-metaboxes #title-location label span,
548
+ .autodescription-metaboxes #home-title-location label span {
549
+ min-width: 40px;
550
+ }
551
+ }
552
+
553
+ @media screen and (max-width: 642px) {
554
+ .autodescription-metaboxes span.seoframework-nav-desktop {
555
+ display: none;
556
+ }
557
+ }
558
+
559
+ @media screen and (max-width: 600px) {
560
+ .autodescription-metaboxes h3.nav-tab-wrapper {
561
+ border-bottom: 1px solid #ccc;
562
+ }
563
+
564
+ .autodescription-metaboxes h3 .nav-tab {
565
+ margin-right: 7px;
566
+ margin-bottom: -1px;
567
+ }
568
+ }
569
+
570
+ @media screen and (max-width: 510px) {
571
+ span.ad-seo.ad-100 {
572
+ width: inherit;
573
+ }
574
+ }
575
+
576
+ /* Firefix */
577
+ @-moz-document url-prefix() {
578
+ .autodescription-metaboxes .seoframework-default-selected {
579
+ box-shadow: 0 0 2px 0 #1c9d38;
580
+ }
581
+
582
+ .autodescription-metaboxes .seoframework-warning-selected {
583
+ box-shadow: 0 0 2px 0 #dd3811;
584
+ }
585
+
586
+ .autodescription-metaboxes legend h4 {
587
+ margin: 2px 0;
588
+ }
589
+ }
lib/css/autodescription-rtl.min.css ADDED
@@ -0,0 +1 @@
 
1
+ .autodescription-metaboxes .top-buttons,.autodescription-metaboxes .top-wrap>h1,.autodescription-metaboxes .top-wrap>h2{float:left}.seo-notice{position:relative;clear:both}a.autodescription-dismiss{position:absolute;top:0;left:1px;border:none;margin:0;padding:9px;background:0 0;color:#b4b9be;cursor:pointer;text-decoration:none}a.autodescription-dismiss:before{background:0 0;content:"\f153";display:block;font:400 16px/20px dashicons;speak:none;height:20px;text-align:center;width:20px;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}a.autodescription-dismiss:hover{color:#c00}table.wp-list-table .column-name{width:15%}table.wp-list-table .column-is_in_stock,table.wp-list-table .column-price,table.wp-list-table .column-sku{width:8%}.ad-seo a{text-decoration:none;font-size:13px}#autodescription-title-wrap{position:relative;display:block;padding:0;height:auto;width:auto}#autodescription-title-offset{visibility:hidden;height:0;display:inline-block;position:absolute;right:0;color:transparent;white-space:pre}#autodescription-title-placeholder{position:absolute;color:#777;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;box-sizing:content-box;top:0;left:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.theseoframework-counter{font-weight:600;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}span.ad-count-bad{color:#dd3811}span.ad-count-okay{color:#ffa700}span.ad-count-good{color:#0cc34b}span.tsf-counter-one{color:#fff;border-radius:58px;padding:0 1px;min-width:29px;display:inline-block;text-align:center;vertical-align:baseline}span.tsf-counter-one.ad-count-bad{background-color:#dd3811}span.tsf-counter-one.ad-count-okay{background-color:#ffa700}span.tsf-counter-one.ad-count-good{background-color:#0cc34b}#ad_seo{width:18%}th:last-of-type#ad_seo{width:160px}span.ad-seo{display:block;width:95%;max-width:220px;border-radius:0;padding:3px;text-shadow:0 0 3px rgba(0,0,0,.3)}.ad-100,.ad-bar-wrap a,span.ad-seo.ad-100{width:100%}span.ad-bar-wrap{display:table;width:100%;border-radius:0;margin:0 auto;border-collapse:separate;border-spacing:0;vertical-align:middle;position:relative}span.ad-sec-wrap{display:table-cell;border-collapse:collapse}.ad-bar-wrap a{display:table;color:#fff;text-align:center;cursor:help;height:100%;min-width:12px;vertical-align:top;line-height:1.625em;box-shadow:2px 0 0 -1px rgba(0,0,0,.1) inset,-2px 0 0 -1px rgba(0,0,0,.1) inset}span.ad-seo.pill,span.ad-seo.pill span.ad-bar-wrap{border-radius:30px}span.ad-seo.pill span.ad-bar-wrap a{box-shadow:2px 6px 3px -2px rgba(255,255,255,.2) inset,-2px -6px 3px -2px rgba(41,41,41,.2) inset,0 0 1px rgba(41,41,41,.6)}.ad-seo.pill .ad-bar-wrap span.ad-sec-wrap:last-of-type,.ad-seo.pill .ad-bar-wrap span.ad-sec-wrap:last-of-type a{border-top-left-radius:30px;border-bottom-left-radius:30px}.ad-seo.pill .ad-bar-wrap span.ad-sec-wrap:first-of-type,.ad-seo.pill .ad-bar-wrap span.ad-sec-wrap:first-of-type a{border-top-right-radius:30px;border-bottom-right-radius:30px}.ad-60{width:60%}.ad-50{width:50%}.ad-40{width:40%}.ad-33{width:33.333%}.ad-25{width:25%}.ad-20{width:20%}.ad-16{width:16.666%}.ad-12-5{width:12.5%}.ad-11{width:11.333%}.ad-10{width:10%}.ad-seo-bad{background-color:#dd3811}.ad-seo-okay{background-color:#ffa700}.ad-seo-good{background-color:#0cc34b}.ad-seo-unknown{background-color:#007bd2}span.ad-seo .explanation-desc{position:absolute;width:auto;min-width:90%;max-width:220px;font-weight:600;background:#007bd2;padding:8px 12px;color:#fdfdfd;border-radius:0;z-index:900142;box-shadow:0 0 2px rgba(0,0,0,.6);left:0;right:0;text-align:left}span.ad-seo .explanation-desc span{text-decoration:underline}span.ad-seo .explanation-desc div{width:0;height:0;border-left:12px solid transparent;border-right:12px solid transparent;border-top:12px solid #007bd2;position:absolute;bottom:-8px;z-index:9999999;left:0}.ad_seo.column-ad_seo{overflow:initial!important;min-width:160px}#col-container,#col-right{overflow:initial}.autodescription-metaboxes{box-sizing:border-box;max-width:690px;padding-bottom:20px}.autodescription-metaboxes .top-wrap{width:100%;display:inline-block;vertical-align:top}.autodescription-metaboxes .metabox-holder{clear:both}.autodescription-metaboxes .bottom-buttons{text-align:left}.autodescription-metaboxes .bottom-buttons input,.autodescription-metaboxes .top-buttons input{margin-left:10px}.autodescription-metaboxes #description-separator,.autodescription-metaboxes #title-separator{display:table;width:100%;border-collapse:collapse;border-spacing:0}.autodescription-metaboxes #description-separator input,.autodescription-metaboxes #title-separator input{display:none;float:left;width:0;min-width:0;opacity:0}.autodescription-metaboxes #description-separator label,.autodescription-metaboxes #title-separator label{display:inline-block;width:auto;min-width:28px;min-height:28px;margin:3px;-moz-margin-end:1.5px;-moz-margin-start:1.5px;padding:0 4px;border:1px solid #ccc;line-height:28px;text-align:center;cursor:pointer;box-shadow:-1px -1px 1px #aaa inset;font-size:16px}.autodescription-metaboxes #description-separator label.recommended,.autodescription-metaboxes #title-separator label.recommended{border:1px solid #0cc34b;box-shadow:-1px -1px 1px #0cc34b inset}.autodescription-metaboxes #description-separator input:hover+label,.autodescription-metaboxes #description-separator label:hover,.autodescription-metaboxes #title-separator input:hover+label,.autodescription-metaboxes #title-separator label:hover{box-shadow:1px 1px 1px #aaa inset;background-color:#fff}.autodescription-metaboxes #description-separator input:checked+label,.autodescription-metaboxes #title-separator input:checked+label{box-shadow:1px 1px 1px #333 inset;background-color:#fff}.autodescription-metaboxes #home-title-location,.autodescription-metaboxes #title-location{display:block}.autodescription-metaboxes #home-title-location label span,.autodescription-metaboxes #title-location label span,.autodescription-metaboxes #twitter-cards label span{display:inline-block;min-width:60px;vertical-align:baseline}.autodescription-metaboxes #twitter-cards label span{min-width:150px}.autodescription-metaboxes #home-title-location label .autodescription-sep-js,.autodescription-metaboxes #home-title-location label span.custom-blogname-js,.autodescription-metaboxes #home-title-location label span.custom-tagline-js,.autodescription-metaboxes #home-title-location label span.custom-title-js,.autodescription-metaboxes #title-location label .autodescription-sep-js{display:inline;min-width:0;white-space:pre}.autodescription-metaboxes .seoframework-nav-tab-wrapper,.autodescription-metaboxes .seoframework-tab-no-js{position:relative;clear:both;width:100%;display:inline-block;border-bottom:1px solid #ccc;line-height:inherit;padding:8px 12px 0;margin:-4px -12px}.autodescription-metaboxes .seoframework-nav-tab{float:right;border:1px solid #ccc;margin-right:.5em;margin-bottom:-1px;padding:5px 14px;font-size:12px;line-height:16px;background:#f1f1f1;color:#555;font-weight:600}.autodescription-metaboxes .seoframework-dashicons-tabs{font-size:initial;display:inline;vertical-align:text-bottom}.autodescription-metaboxes .seoframework-tabs-radio{display:none;width:0;height:0;position:absolute;left:-9001px}.autodescription-metaboxes .seoframework-active-tab,.autodescription-metaboxes .seoframework-tabs-radio:checked+label{background-color:inherit;border-bottom-color:#fff;color:#000}.autodescription-metaboxes .seoframework-tabs-content{margin:1.33em auto 0}.autodescription-metaboxes .seoframework-content-no-js{margin:1.33em auto}body.js .autodescription-metaboxes .seoframework-tabs-content{display:none}body.js .autodescription-metaboxes .seoframework-tabs-content.seoframework-active-tab-content{display:block}.autodescription-metaboxes .seoframework-default-selected{border-color:#1c9d38}.autodescription-metaboxes .seoframework-default-selected:checked:before{color:#1c9d38}.autodescription-metaboxes .seoframework-warning-selected{border-color:#dd3811}.autodescription-metaboxes .seoframework-warning-selected:checked:before{color:#dd3811}.theseoframework-fields{font-size:13px;line-height:1.5;margin:1em 0}.theseoframework-fields .toblock{display:block;width:100%;margin-bottom:4px}.theseoframework-fields p.description{margin:7px 0 5px;color:#666}.theseoframework-option-spacer{margin:1em 0}@media screen and (max-width:782px){.autodescription-metaboxes p.theseoframework-fields,.theseoframework-inpost-box p.theseoframework-fields{line-height:2.8}.autodescription-metaboxes #home-title-location label span,.autodescription-metaboxes #title-location label span{min-width:40px}}@media screen and (max-width:642px){.autodescription-metaboxes span.seoframework-nav-desktop{display:none}}@media screen and (max-width:600px){.autodescription-metaboxes h3.nav-tab-wrapper{border-bottom:1px solid #ccc}.autodescription-metaboxes h3 .nav-tab{margin-right:7px;margin-bottom:-1px}}@media screen and (max-width:510px){span.ad-seo.ad-100{width:inherit}}@-moz-document url-prefix(){.autodescription-metaboxes .seoframework-default-selected{box-shadow:0 0 2px 0 #1c9d38}.autodescription-metaboxes .seoframework-warning-selected{box-shadow:0 0 2px 0 #dd3811}.autodescription-metaboxes legend h4{margin:2px 0}}
lib/css/autodescription.css ADDED
@@ -0,0 +1,593 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * See, for colors:
3
+ * https://color.adobe.com/create/color-wheel/?base=2&rule=Custom&selected=1&name=Mijn%20Color-thema&mode=rgb&rgbvalues=0,0,0,1,0.6289566724811484,0.10577297701442445,0.04724043389094745,0.7647058823529411,0.2945255209368204,0,0.4806705005377058,0.8235294117647058,0.8666666666666667,0.2196078431372549,0.06666666666666667&swatchOrder=0,1,2,3,4
4
+ */
5
+
6
+ /**
7
+ * Dismissible Notices.
8
+ */
9
+ .seo-notice {
10
+ position: relative;
11
+ }
12
+
13
+ a.autodescription-dismiss {
14
+ position: absolute;
15
+ top: 0;
16
+ right: 1px;
17
+ border: none;
18
+ margin: 0;
19
+ padding: 9px;
20
+ background: 0 0;
21
+ color: #b4b9be;
22
+ cursor: pointer;
23
+ text-decoration: none;
24
+ }
25
+
26
+ a.autodescription-dismiss:before {
27
+ background: 0 0;
28
+ content: "\f153";
29
+ display: block;
30
+ font: 400 16px/20px dashicons;
31
+ speak: none;
32
+ height: 20px;
33
+ text-align: center;
34
+ width: 20px;
35
+ -webkit-font-smoothing: antialiased;
36
+ -moz-osx-font-smoothing: grayscale;
37
+ }
38
+
39
+ a.autodescription-dismiss:hover {
40
+ color: #c00;
41
+ }
42
+
43
+ /**
44
+ * WooCommerce fix
45
+ */
46
+ table.wp-list-table .column-name {
47
+ width: 15%;
48
+ }
49
+
50
+ table.wp-list-table .column-is_in_stock,
51
+ table.wp-list-table .column-sku,
52
+ table.wp-list-table .column-price {
53
+ width: 8%;
54
+ }
55
+
56
+ .ad-seo a {
57
+ text-decoration: none;
58
+ font-size: 13px;
59
+ }
60
+
61
+ #autodescription-title-wrap {
62
+ position: relative;
63
+ display: block;
64
+ padding: 0;
65
+ height: auto;
66
+ width: auto;
67
+ }
68
+
69
+ #autodescription-title-offset {
70
+ visibility: hidden;
71
+ height: 0;
72
+ display: inline-block;
73
+ position: absolute;
74
+ left: 0;
75
+ color: transparent;
76
+ white-space: pre;
77
+ }
78
+
79
+ #autodescription-title-placeholder {
80
+ position: absolute;
81
+ color: #777;
82
+ -webkit-user-select: none;
83
+ -moz-user-select: none;
84
+ -ms-user-select: none;
85
+ user-select: none;
86
+ box-sizing: content-box;
87
+ top: 0;
88
+ left: 0;
89
+ overflow: hidden;
90
+ white-space: nowrap;
91
+ text-overflow: ellipsis;
92
+ }
93
+
94
+ /* START Character Counters */
95
+
96
+ .theseoframework-counter {
97
+ font-weight: 600;
98
+ cursor: pointer;
99
+ -webkit-user-select: none;
100
+ -moz-user-select: none;
101
+ -ms-user-select: none;
102
+ user-select: none;
103
+ }
104
+
105
+ span.ad-count-bad {
106
+ color: #dd3811;
107
+ }
108
+
109
+ span.ad-count-okay {
110
+ color: #ffa700;
111
+ }
112
+
113
+ span.ad-count-good {
114
+ color: #0cc34b;
115
+ }
116
+
117
+ span.tsf-counter-one {
118
+ color: #fff;
119
+ padding: 3px;
120
+ border-radius: 58px;
121
+ padding: 0 1px;
122
+ min-width: 29px;
123
+ display: inline-block;
124
+ text-align: center;
125
+ vertical-align: baseline;
126
+ }
127
+ span.tsf-counter-one.ad-count-bad {
128
+ background-color: #dd3811;
129
+ }
130
+ span.tsf-counter-one.ad-count-okay {
131
+ background-color: #ffa700;
132
+ }
133
+ span.tsf-counter-one.ad-count-good {
134
+ background-color: #0cc34b;
135
+ }
136
+
137
+ /* END Character Counters */
138
+
139
+ #ad_seo {
140
+ width: 18%;
141
+ }
142
+
143
+ /* WooCommerce fix */
144
+ th:last-of-type#ad_seo {
145
+ width: 160px;
146
+ }
147
+
148
+ span.ad-seo {
149
+ display: block;
150
+ width: 95%;
151
+ max-width: 220px;
152
+ border-radius: 0;
153
+ padding: 3px;
154
+ text-shadow: 0 0 3px rgba(0,0,0,0.3);
155
+ }
156
+
157
+ span.ad-seo.ad-100 {
158
+ width: 100%;
159
+ }
160
+
161
+ span.ad-bar-wrap {
162
+ display: table;
163
+ width: 100%;
164
+ border-radius: 0;
165
+ margin: 0 auto;
166
+ border-collapse: separate;
167
+ border-spacing: 0;
168
+ vertical-align: middle;
169
+ position: relative;
170
+ }
171
+
172
+ span.ad-sec-wrap {
173
+ display: table-cell;
174
+ border-collapse: collapse;
175
+ }
176
+
177
+ .ad-bar-wrap a {
178
+ display: table;
179
+ width: 100%;
180
+ color: #fff;
181
+ text-align: center;
182
+ cursor: help;
183
+ height: 100%;
184
+ min-width: 12px;
185
+ vertical-align: top;
186
+ line-height: 1.625em;
187
+ box-shadow: 2px 0px 0px -1px rgba(0,0,0,0.1) inset, -2px 0px 0px -1px rgba(0,0,0,0.1) inset;
188
+ }
189
+
190
+ span.ad-seo.pill,
191
+ span.ad-seo.pill span.ad-bar-wrap {
192
+ border-radius: 30px;
193
+ }
194
+
195
+ span.ad-seo.pill span.ad-bar-wrap a {
196
+ box-shadow: 2px 6px 3px -2px rgba(255,255,255,.2) inset,-2px -6px 3px -2px rgba(41,41,41,.2) inset,0 0 1px rgba(41,41,41,.6);
197
+ }
198
+
199
+ .ad-seo.pill .ad-bar-wrap span.ad-sec-wrap:first-of-type,
200
+ .ad-seo.pill .ad-bar-wrap span.ad-sec-wrap:first-of-type a {
201
+ border-top-left-radius: 30px;
202
+ border-bottom-left-radius: 30px;
203
+ }
204
+
205
+ .ad-seo.pill .ad-bar-wrap span.ad-sec-wrap:last-of-type,
206
+ .ad-seo.pill .ad-bar-wrap span.ad-sec-wrap:last-of-type a {
207
+ border-top-right-radius: 30px;
208
+ border-bottom-right-radius: 30px;
209
+ }
210
+
211
+ .ad-100 {
212
+ width: 100%;
213
+ }
214
+
215
+ .ad-60 {
216
+ width: 60%;
217
+ }
218
+
219
+ .ad-50 {
220
+ width: 50%;
221
+ }
222
+
223
+ .ad-40 {
224
+ width: 40%;
225
+ }
226
+
227
+ .ad-33 {
228
+ width: 33.333%;
229
+ }
230
+
231
+ .ad-25 {
232
+ width: 25%;
233
+ }
234
+
235
+ .ad-20 {
236
+ width: 20%;
237
+ }
238
+
239
+ .ad-16 {
240
+ width: 16.666%;
241
+ }
242
+
243
+ .ad-12-5 {
244
+ width: 12.5%;
245
+ }
246
+
247
+ .ad-11 {
248
+ width: 11.333%;
249
+ }
250
+
251
+ .ad-10 {
252
+ width: 10%;
253
+ }
254
+
255
+ .ad-seo-bad {
256
+ background-color: #dd3811;
257
+ }
258
+
259
+ .ad-seo-okay {
260
+ background-color: #ffa700;
261
+ }
262
+
263
+ .ad-seo-good {
264
+ background-color: #0cc34b;
265
+ }
266
+
267
+ .ad-seo-unknown {
268
+ background-color: #007bd2;
269
+ }
270
+
271
+ span.ad-seo .explanation-desc {
272
+ position: absolute;
273
+ width: auto;
274
+ min-width: 90%;
275
+ max-width: 220px;
276
+ font-weight: 600;
277
+ background: #007bd2;
278
+ padding: 8px 12px;
279
+ color: #fdfdfd;
280
+ border-radius: 0px;
281
+ z-index: 900142;
282
+ box-shadow: 0px 0px 2px rgba(0,0,0,0.6);
283
+ left: 0;
284
+ right: 0;
285
+ text-align: left;
286
+ }
287
+
288
+ span.ad-seo .explanation-desc span {
289
+ text-decoration: underline;
290
+ }
291
+
292
+ span.ad-seo .explanation-desc div {
293
+ width: 0;
294
+ height: 0;
295
+ border-left: 12px solid transparent;
296
+ border-right: 12px solid transparent;
297
+ border-top: 12px solid #007bd2;
298
+ position: absolute;
299
+ bottom: -8px;
300
+ z-index: 9999999;
301
+ left: 0;
302
+ }
303
+
304
+ /* Mobile support */
305
+ /* Needs more refining.... e.g. iPhone 6 = good, iPhone 6+ = offscreen */
306
+ .ad_seo.column-ad_seo {
307
+ overflow: initial !important;
308
+ min-width: 160px;
309
+ }
310
+
311
+ /* Taxonomies overflow fix */
312
+ /* @todo Needs to be changed within Javascript to show information below bar on first 2 entries */
313
+ #col-container, #col-right {
314
+ overflow: initial;
315
+ }
316
+
317
+ /* Site Settings */
318
+
319
+ .autodescription-metaboxes {
320
+ box-sizing: border-box;
321
+ max-width: 690px;
322
+ padding-bottom: 20px;
323
+ }
324
+
325
+ .autodescription-metaboxes .top-wrap {
326
+ width: 100%;
327
+ display: inline-block;
328
+ vertical-align: top;
329
+ }
330
+
331
+ .autodescription-metaboxes .top-wrap > h1,
332
+ .autodescription-metaboxes .top-wrap > h2 {
333
+ float: left;
334
+ }
335
+
336
+ .autodescription-metaboxes .metabox-holder {
337
+ clear: both;
338
+ }
339
+
340
+ .autodescription-metaboxes .top-buttons {
341
+ float: right;
342
+ }
343
+
344
+ .autodescription-metaboxes .bottom-buttons {
345
+ text-align: right;
346
+ }
347
+
348
+ .autodescription-metaboxes .top-buttons input,
349
+ .autodescription-metaboxes .bottom-buttons input {
350
+ margin-left: 10px;
351
+ }
352
+
353
+ .seo-notice {
354
+ clear: both;
355
+ }
356
+
357
+ .autodescription-metaboxes #title-separator,
358
+ .autodescription-metaboxes #description-separator {
359
+ display: table;
360
+ width: 100%;
361
+ border-collapse: collapse;
362
+ border-spacing: 0;
363
+ }
364
+
365
+ .autodescription-metaboxes #title-separator input,
366
+ .autodescription-metaboxes #description-separator input {
367
+ display: none;
368
+ float: left;
369
+ width: 0;
370
+ min-width: 0;
371
+ opacity: 0;
372
+ }
373
+
374
+ .autodescription-metaboxes #title-separator label,
375
+ .autodescription-metaboxes #description-separator label {
376
+ display: inline-block;
377
+ width: auto;
378
+ min-width: 28px;
379
+ min-height: 28px;
380
+ margin: 3px;
381
+ -moz-margin-end: 1.5px;
382
+ -moz-margin-start: 1.5px;
383
+ padding: 0 4px;
384
+ border: 1px solid #ccc;
385
+ line-height: 28px;
386
+ text-align: center;
387
+ cursor: pointer;
388
+ box-shadow: -1px -1px 1px #aaa inset;
389
+ font-size: 16px;
390
+ }
391
+
392
+ .autodescription-metaboxes #title-separator label.recommended,
393
+ .autodescription-metaboxes #description-separator label.recommended {
394
+ border: 1px solid #0cc34b;
395
+ box-shadow: -1px -1px 1px #0cc34b inset;
396
+ }
397
+
398
+ .autodescription-metaboxes #title-separator input:hover + label,
399
+ .autodescription-metaboxes #title-separator label:hover,
400
+ .autodescription-metaboxes #description-separator input:hover + label,
401
+ .autodescription-metaboxes #description-separator label:hover {
402
+ box-shadow: 1px 1px 1px #aaa inset;
403
+ background-color: #fff;
404
+ }
405
+
406
+ .autodescription-metaboxes #title-separator input:checked + label,
407
+ .autodescription-metaboxes #description-separator input:checked + label {
408
+ box-shadow: 1px 1px 1px #333 inset;
409
+ background-color: #fff;
410
+ }
411
+
412
+ .autodescription-metaboxes #title-location,
413
+ .autodescription-metaboxes #home-title-location {
414
+ display: block;
415
+ }
416
+
417
+ .autodescription-metaboxes #title-location label span,
418
+ .autodescription-metaboxes #home-title-location label span,
419
+ .autodescription-metaboxes #twitter-cards label span {
420
+ display: inline-block;
421
+ min-width: 60px;
422
+ vertical-align: baseline;
423
+ }
424
+
425
+ .autodescription-metaboxes #twitter-cards label span {
426
+ min-width: 150px;
427
+ }
428
+
429
+ .autodescription-metaboxes #home-title-location label span.custom-title-js,
430
+ .autodescription-metaboxes #home-title-location label span.custom-blogname-js,
431
+ .autodescription-metaboxes #home-title-location label span.custom-tagline-js,
432
+ .autodescription-metaboxes #title-location label .autodescription-sep-js,
433
+ .autodescription-metaboxes #home-title-location label .autodescription-sep-js {
434
+ display: inline;
435
+ min-width: 0;
436
+ white-space: pre;
437
+ }
438
+
439
+ /**
440
+ * Start Tabs.
441
+ */
442
+ .autodescription-metaboxes .seoframework-tab-no-js,
443
+ .autodescription-metaboxes .seoframework-nav-tab-wrapper {
444
+ position: relative;
445
+ clear: both;
446
+ width: 100%;
447
+ display: inline-block;
448
+ border-bottom: 1px solid #ccc;
449
+ line-height: inherit;
450
+ padding: 8px 12px 0;
451
+ margin: -4px -12px;
452
+ }
453
+
454
+ .autodescription-metaboxes .seoframework-nav-tab {
455
+ float: left;
456
+ border: 1px solid #ccc;
457
+ margin-left: .5em;
458
+ margin-bottom: -1px;
459
+ padding: 5px 14px;
460
+ font-size: 12px;
461
+ line-height: 16px;
462
+ background: #f1f1f1;
463
+ color: #555;
464
+ font-weight: 600;
465
+ }
466
+
467
+ .autodescription-metaboxes .seoframework-dashicons-tabs {
468
+ font-size: initial;
469
+ display: inline;
470
+ vertical-align: text-bottom;
471
+ }
472
+
473
+ .autodescription-metaboxes .seoframework-tabs-radio {
474
+ display: none;
475
+ width: 0;
476
+ height: 0;
477
+ position: absolute;
478
+ left: -9001px;
479
+ }
480
+
481
+ .autodescription-metaboxes .seoframework-tabs-radio:checked + label,
482
+ .autodescription-metaboxes .seoframework-active-tab {
483
+ background-color: inherit;
484
+ border-bottom-color: #fff;
485
+ color: #000;
486
+ }
487
+
488
+ .autodescription-metaboxes .seoframework-tabs-content {
489
+ margin: 1.33em auto 0;
490
+ }
491
+
492
+ .autodescription-metaboxes .seoframework-content-no-js {
493
+ margin: 1.33em auto;
494
+ }
495
+
496
+ body.js .autodescription-metaboxes .seoframework-tabs-content {
497
+ display: none;
498
+ }
499
+
500
+ body.js .autodescription-metaboxes .seoframework-tabs-content.seoframework-active-tab-content {
501
+ display: block;
502
+ }
503
+
504
+ /**
505
+ * End tabs.
506
+ */
507
+
508
+ .autodescription-metaboxes .seoframework-default-selected {
509
+ border-color: #1c9d38;
510
+ }
511
+
512
+ .autodescription-metaboxes .seoframework-default-selected:checked:before {
513
+ color: #1c9d38;
514
+ }
515
+
516
+ .autodescription-metaboxes .seoframework-warning-selected {
517
+ border-color: #dd3811;
518
+ }
519
+
520
+ .autodescription-metaboxes .seoframework-warning-selected:checked:before {
521
+ color: #dd3811;
522
+ }
523
+
524
+ .theseoframework-fields {
525
+ font-size: 13px;
526
+ line-height: 1.5;
527
+ margin: 1em 0;
528
+ }
529
+
530
+ .theseoframework-fields .toblock {
531
+ display: block;
532
+ width: 100%;
533
+ margin-bottom: 4px;
534
+ }
535
+
536
+ .theseoframework-fields p.description {
537
+ margin: 7px 0 5px;
538
+ color: #666;
539
+ }
540
+
541
+ .theseoframework-option-spacer {
542
+ margin: 1em 0;
543
+ }
544
+
545
+ @media screen and (max-width: 782px) {
546
+ .autodescription-metaboxes p.theseoframework-fields,
547
+ .theseoframework-inpost-box p.theseoframework-fields {
548
+ line-height: 2.8;
549
+ }
550
+
551
+ .autodescription-metaboxes #title-location label span,
552
+ .autodescription-metaboxes #home-title-location label span {
553
+ min-width: 40px;
554
+ }
555
+ }
556
+
557
+ @media screen and (max-width: 642px) {
558
+ .autodescription-metaboxes span.seoframework-nav-desktop {
559
+ display: none;
560
+ }
561
+ }
562
+
563
+ @media screen and (max-width: 600px) {
564
+ .autodescription-metaboxes h3.nav-tab-wrapper {
565
+ border-bottom: 1px solid #ccc;
566
+ }
567
+
568
+ .autodescription-metaboxes h3 .nav-tab {
569
+ margin-right: 7px;
570
+ margin-bottom: -1px;
571
+ }
572
+ }
573
+
574
+ @media screen and (max-width: 510px) {
575
+ span.ad-seo.ad-100 {
576
+ width: inherit;
577
+ }
578
+ }
579
+
580
+ /* Firefix */
581
+ @-moz-document url-prefix() {
582
+ .autodescription-metaboxes .seoframework-default-selected {
583
+ box-shadow: 0 0 2px 0 #1c9d38;
584
+ }
585
+
586
+ .autodescription-metaboxes .seoframework-warning-selected {
587
+ box-shadow: 0 0 2px 0 #dd3811;
588
+ }
589
+
590
+ .autodescription-metaboxes legend h4 {
591
+ margin: 2px 0;
592
+ }
593
+ }
lib/css/autodescription.min.css ADDED
@@ -0,0 +1 @@
 
1
+ .seo-notice{position:relative;clear:both}a.autodescription-dismiss{position:absolute;top:0;right:1px;border:none;margin:0;padding:9px;background:0 0;color:#b4b9be;cursor:pointer;text-decoration:none}a.autodescription-dismiss:before{background:0 0;content:"\f153";display:block;font:400 16px/20px dashicons;speak:none;height:20px;text-align:center;width:20px;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}a.autodescription-dismiss:hover{color:#c00}table.wp-list-table .column-name{width:15%}table.wp-list-table .column-is_in_stock,table.wp-list-table .column-price,table.wp-list-table .column-sku{width:8%}.ad-seo a{text-decoration:none;font-size:13px}#autodescription-title-wrap{position:relative;display:block;padding:0;height:auto;width:auto}#autodescription-title-offset{visibility:hidden;height:0;display:inline-block;position:absolute;left:0;color:transparent;white-space:pre}#autodescription-title-placeholder{position:absolute;color:#777;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;box-sizing:content-box;top:0;left:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.theseoframework-counter{font-weight:600;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}span.ad-count-bad{color:#dd3811}span.ad-count-okay{color:#ffa700}span.ad-count-good{color:#0cc34b}span.tsf-counter-one{color:#fff;border-radius:58px;padding:0 1px;min-width:29px;display:inline-block;text-align:center;vertical-align:baseline}span.tsf-counter-one.ad-count-bad{background-color:#dd3811}span.tsf-counter-one.ad-count-okay{background-color:#ffa700}span.tsf-counter-one.ad-count-good{background-color:#0cc34b}#ad_seo{width:18%}th:last-of-type#ad_seo{width:160px}span.ad-seo{display:block;width:95%;max-width:220px;border-radius:0;padding:3px;text-shadow:0 0 3px rgba(0,0,0,.3)}.ad-100,.ad-bar-wrap a,span.ad-seo.ad-100{width:100%}span.ad-bar-wrap{display:table;width:100%;border-radius:0;margin:0 auto;border-collapse:separate;border-spacing:0;vertical-align:middle;position:relative}span.ad-sec-wrap{display:table-cell;border-collapse:collapse}.ad-bar-wrap a{display:table;color:#fff;text-align:center;cursor:help;height:100%;min-width:12px;vertical-align:top;line-height:1.625em;box-shadow:2px 0 0 -1px rgba(0,0,0,.1) inset,-2px 0 0 -1px rgba(0,0,0,.1) inset}span.ad-seo.pill,span.ad-seo.pill span.ad-bar-wrap{border-radius:30px}span.ad-seo.pill span.ad-bar-wrap a{box-shadow:2px 6px 3px -2px rgba(255,255,255,.2) inset,-2px -6px 3px -2px rgba(41,41,41,.2) inset,0 0 1px rgba(41,41,41,.6)}.ad-seo.pill .ad-bar-wrap span.ad-sec-wrap:first-of-type,.ad-seo.pill .ad-bar-wrap span.ad-sec-wrap:first-of-type a{border-top-left-radius:30px;border-bottom-left-radius:30px}.ad-seo.pill .ad-bar-wrap span.ad-sec-wrap:last-of-type,.ad-seo.pill .ad-bar-wrap span.ad-sec-wrap:last-of-type a{border-top-right-radius:30px;border-bottom-right-radius:30px}.ad-60{width:60%}.ad-50{width:50%}.ad-40{width:40%}.ad-33{width:33.333%}.ad-25{width:25%}.ad-20{width:20%}.ad-16{width:16.666%}.ad-12-5{width:12.5%}.ad-11{width:11.333%}.ad-10{width:10%}.ad-seo-bad{background-color:#dd3811}.ad-seo-okay{background-color:#ffa700}.ad-seo-good{background-color:#0cc34b}.ad-seo-unknown{background-color:#007bd2}span.ad-seo .explanation-desc{position:absolute;width:auto;min-width:90%;max-width:220px;font-weight:600;background:#007bd2;padding:8px 12px;color:#fdfdfd;border-radius:0;z-index:900142;box-shadow:0 0 2px rgba(0,0,0,.6);left:0;right:0;text-align:left}span.ad-seo .explanation-desc span{text-decoration:underline}span.ad-seo .explanation-desc div{width:0;height:0;border-left:12px solid transparent;border-right:12px solid transparent;border-top:12px solid #007bd2;position:absolute;bottom:-8px;z-index:9999999;left:0}.ad_seo.column-ad_seo{overflow:initial!important;min-width:160px}#col-container,#col-right{overflow:initial}.autodescription-metaboxes{box-sizing:border-box;max-width:690px;padding-bottom:20px}.autodescription-metaboxes .top-wrap{width:100%;display:inline-block;vertical-align:top}.autodescription-metaboxes .top-wrap>h1,.autodescription-metaboxes .top-wrap>h2{float:left}.autodescription-metaboxes .metabox-holder{clear:both}.autodescription-metaboxes .top-buttons{float:right}.autodescription-metaboxes .bottom-buttons{text-align:right}.autodescription-metaboxes .bottom-buttons input,.autodescription-metaboxes .top-buttons input{margin-left:10px}.autodescription-metaboxes #description-separator,.autodescription-metaboxes #title-separator{display:table;width:100%;border-collapse:collapse;border-spacing:0}.autodescription-metaboxes #description-separator input,.autodescription-metaboxes #title-separator input{display:none;float:left;width:0;min-width:0;opacity:0}.autodescription-metaboxes #description-separator label,.autodescription-metaboxes #title-separator label{display:inline-block;width:auto;min-width:28px;min-height:28px;margin:3px;-moz-margin-end:1.5px;-moz-margin-start:1.5px;padding:0 4px;border:1px solid #ccc;line-height:28px;text-align:center;cursor:pointer;box-shadow:-1px -1px 1px #aaa inset;font-size:16px}.autodescription-metaboxes #description-separator label.recommended,.autodescription-metaboxes #title-separator label.recommended{border:1px solid #0cc34b;box-shadow:-1px -1px 1px #0cc34b inset}.autodescription-metaboxes #description-separator input:hover+label,.autodescription-metaboxes #description-separator label:hover,.autodescription-metaboxes #title-separator input:hover+label,.autodescription-metaboxes #title-separator label:hover{box-shadow:1px 1px 1px #aaa inset;background-color:#fff}.autodescription-metaboxes #description-separator input:checked+label,.autodescription-metaboxes #title-separator input:checked+label{box-shadow:1px 1px 1px #333 inset;background-color:#fff}.autodescription-metaboxes #home-title-location,.autodescription-metaboxes #title-location{display:block}.autodescription-metaboxes #home-title-location label span,.autodescription-metaboxes #title-location label span,.autodescription-metaboxes #twitter-cards label span{display:inline-block;min-width:60px;vertical-align:baseline}.autodescription-metaboxes #twitter-cards label span{min-width:150px}.autodescription-metaboxes #home-title-location label .autodescription-sep-js,.autodescription-metaboxes #home-title-location label span.custom-blogname-js,.autodescription-metaboxes #home-title-location label span.custom-tagline-js,.autodescription-metaboxes #home-title-location label span.custom-title-js,.autodescription-metaboxes #title-location label .autodescription-sep-js{display:inline;min-width:0;white-space:pre}.autodescription-metaboxes .seoframework-nav-tab-wrapper,.autodescription-metaboxes .seoframework-tab-no-js{position:relative;clear:both;width:100%;display:inline-block;border-bottom:1px solid #ccc;line-height:inherit;padding:8px 12px 0;margin:-4px -12px}.autodescription-metaboxes .seoframework-nav-tab{float:left;border:1px solid #ccc;margin-left:.5em;margin-bottom:-1px;padding:5px 14px;font-size:12px;line-height:16px;background:#f1f1f1;color:#555;font-weight:600}.autodescription-metaboxes .seoframework-dashicons-tabs{font-size:initial;display:inline;vertical-align:text-bottom}.autodescription-metaboxes .seoframework-tabs-radio{display:none;width:0;height:0;position:absolute;left:-9001px}.autodescription-metaboxes .seoframework-active-tab,.autodescription-metaboxes .seoframework-tabs-radio:checked+label{background-color:inherit;border-bottom-color:#fff;color:#000}.autodescription-metaboxes .seoframework-tabs-content{margin:1.33em auto 0}.autodescription-metaboxes .seoframework-content-no-js{margin:1.33em auto}body.js .autodescription-metaboxes .seoframework-tabs-content{display:none}body.js .autodescription-metaboxes .seoframework-tabs-content.seoframework-active-tab-content{display:block}.autodescription-metaboxes .seoframework-default-selected{border-color:#1c9d38}.autodescription-metaboxes .seoframework-default-selected:checked:before{color:#1c9d38}.autodescription-metaboxes .seoframework-warning-selected{border-color:#dd3811}.autodescription-metaboxes .seoframework-warning-selected:checked:before{color:#dd3811}.theseoframework-fields{font-size:13px;line-height:1.5;margin:1em 0}.theseoframework-fields .toblock{display:block;width:100%;margin-bottom:4px}.theseoframework-fields p.description{margin:7px 0 5px;color:#666}.theseoframework-option-spacer{margin:1em 0}@media screen and (max-width:782px){.autodescription-metaboxes p.theseoframework-fields,.theseoframework-inpost-box p.theseoframework-fields{line-height:2.8}.autodescription-metaboxes #home-title-location label span,.autodescription-metaboxes #title-location label span{min-width:40px}}@media screen and (max-width:642px){.autodescription-metaboxes span.seoframework-nav-desktop{display:none}}@media screen and (max-width:600px){.autodescription-metaboxes h3.nav-tab-wrapper{border-bottom:1px solid #ccc}.autodescription-metaboxes h3 .nav-tab{margin-right:7px;margin-bottom:-1px}}@media screen and (max-width:510px){span.ad-seo.ad-100{width:inherit}}@-moz-document url-prefix(){.autodescription-metaboxes .seoframework-default-selected{box-shadow:0 0 2px 0 #1c9d38}.autodescription-metaboxes .seoframework-warning-selected{box-shadow:0 0 2px 0 #dd3811}.autodescription-metaboxes legend h4{margin:2px 0}}
lib/js/autodescription.js ADDED
@@ -0,0 +1,1276 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * This file holds The SEO Framework plugin's JS code.\
3
+ * Serve JavaScript as an addition, not as a means.
4
+ *
5
+ * @author Sybre Waaijer https://cyberwire.nl/
6
+ * @pluginURI https://wordpress.org/plugins/autodescription/
7
+ *
8
+ * @credits StudioPress (http://www.studiopress.com/) for some code.
9
+ */
10
+
11
+ /**
12
+ * The SEO Framework plugin
13
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
14
+ *
15
+ * This program is free software: you can redistribute it and/or modify
16
+ * it under the terms of the GNU General Public License version 3 as published
17
+ * by the Free Software Foundation.
18
+ *
19
+ * This program is distributed in the hope that it will be useful,
20
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22
+ * GNU General Public License for more details.
23
+ *
24
+ * You should have received a copy of the GNU General Public License
25
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
26
+ */
27
+
28
+ // ==ClosureCompiler==
29
+ // @compilation_level ADVANCED_OPTIMIZATIONS
30
+ // @output_file_name autodescription.min.js
31
+ // @externs_url https://raw.githubusercontent.com/google/closure-compiler/master/contrib/externs/jquery-1.9.js
32
+ // @js_externs /** @constructor */ function autodescription() {}; /** @function */ autodescription.statusBarHover; /** @type {Array|string} */ var autodescriptionL10n;
33
+ // ==/ClosureCompiler==
34
+ // http://closure-compiler.appspot.com/home
35
+
36
+ /* global autodescription, confirm, autodescriptionL10n */
37
+
38
+ /**
39
+ * Advanced Optimizations caused me to move away from dot annotations, as they
40
+ * get wrongfully minified.
41
+ */
42
+
43
+ /**
44
+ * Holds AutoDescription values in an object to avoid polluting global namespace.
45
+ *
46
+ * @since 2.2.4
47
+ *
48
+ * @constructor
49
+ */
50
+ window[ 'autodescription' ] = {
51
+
52
+ settingsChanged: false,
53
+
54
+ titleTagline : autodescriptionL10n['titleTagline'],
55
+
56
+ /**
57
+ * Mixed string and int (i10n is string, JS is int).
58
+ * @param {String|int} autodescription.counterType
59
+ */
60
+ counterType : autodescriptionL10n['counterType'],
61
+
62
+ additionsClass : '',
63
+
64
+ /**
65
+ * Cached doctitle function.
66
+ *
67
+ * @since 2.3.3
68
+ *
69
+ * @function
70
+ *
71
+ * @returns {Object} The jQuery doctitle ID's
72
+ */
73
+ docTitles: function() {
74
+ 'use strict';
75
+
76
+ var $doctitles = jQuery( '#autodescription_title, #autodescription-meta\\[doctitle\\], #autodescription-site-settings\\[homepage_title\\]' );
77
+
78
+ return $doctitles;
79
+ },
80
+
81
+ /**
82
+ * Cached description function.
83
+ *
84
+ * @since 2.5.0
85
+ *
86
+ * @function
87
+ *
88
+ * @returns {Object} The jQuery description ID's
89
+ */
90
+ docDescriptions: function() {
91
+ 'use strict';
92
+
93
+ var $descriptions = jQuery( "#autodescription_description, #autodescription-meta\\[description\\], #autodescription-site-settings\\[homepage_description\\]" );
94
+
95
+ return $descriptions;
96
+ },
97
+
98
+ /**
99
+ * Helper function for confirming a user action.
100
+ *
101
+ * @since 2.2.4
102
+ *
103
+ * @function
104
+ *
105
+ * @param {String} text The text to display.
106
+ * @return {Boolean|null}
107
+ */
108
+ confirm: function( text ) {
109
+ 'use strict';
110
+
111
+ return confirm( text );
112
+ },
113
+
114
+ /**
115
+ * Description length counter.
116
+ *
117
+ * @since 2.2.4
118
+ *
119
+ * @function
120
+ * @param {jQuery.event} event
121
+ */
122
+ updateCharacterCountDescription: function( event ) {
123
+ 'use strict';
124
+
125
+ var $this = jQuery( event.target ),
126
+ $length = $this.val().length,
127
+ $phLength = $this.attr( 'placeholder' ).length,
128
+ $counter = jQuery( '#' + autodescription.escapeStr( event.target.id ) + '_chars' ),
129
+ $additionsClass = autodescription.additionsClass,
130
+ $counterType = autodescription.counterType,
131
+ $counterClass = '',
132
+ $name = '',
133
+ $output = '';
134
+
135
+ // Emptied input, get Description placeholder.
136
+ if ( 0 === $length ) {
137
+ //* Output length from placeholder.
138
+ $length = $phLength;
139
+ }
140
+
141
+ if ( $length < 100 || $length >= 175 ) {
142
+ $counterClass = 'ad-count-bad';
143
+ $name = autodescription.getCounterName( 'bad' );
144
+ } else if ( $length < 137 || ( $length > 155 && $length < 175 ) ) {
145
+ $counterClass = 'ad-count-okay';
146
+ $name = autodescription.getCounterName( 'okay' );
147
+ } else {
148
+ $counterClass = 'ad-count-good';
149
+ $name = autodescription.getCounterName( 'good' );
150
+ }
151
+
152
+ if ( $additionsClass )
153
+ $counterClass += ' ' + $additionsClass;
154
+
155
+ if ( ! $counterType || 1 == $counterType ) {
156
+ $output = $length.toString();
157
+ } else if ( 2 == $counterType ) {
158
+ $output = $name;
159
+ } else if ( 3 == $counterType ) {
160
+ $output = $length.toString() + ' - ' + $name;
161
+ }
162
+
163
+ $counter.html( $output ).removeClass().addClass( $counterClass );
164
+ },
165
+
166
+ /**
167
+ * Title length counter, with special characters
168
+ *
169
+ * @since 2.2.4
170
+ *
171
+ * @function
172
+ * @param {jQuery.event} event
173
+ */
174
+ updateCharacterCountTitle: function( event ) {
175
+ 'use strict';
176
+
177
+ var $this = jQuery( event.target ),
178
+ $additions = autodescriptionL10n['titleAdditions'].length,
179
+ $description = autodescriptionL10n['blogDescription'].length,
180
+ $siteTitle = autodescriptionL10n['siteTitle'].length,
181
+ $titleLength = $this.val().length,
182
+ $placeholder = $this.attr( 'placeholder' ).length,
183
+ $tagline = jQuery( '#autodescription-site-settings\\[homepage_title_tagline\\]' ).val(),
184
+ $seplen = 3,
185
+ $counter = jQuery( '#' + autodescription.escapeStr( event.target.id ) + '_chars' ),
186
+ $length = 0,
187
+ $additionsClass = autodescription.additionsClass,
188
+ $counterType = autodescription.counterType,
189
+ $counterClass = '',
190
+ $name = '',
191
+ $output = '';
192
+
193
+ // Additions or tagline removed, remove additions and separator.
194
+ if ( ! autodescription.titleTagline ) {
195
+ $additions = 0;
196
+ $seplen = 0;
197
+ }
198
+
199
+ // Emptied input, get Site title.
200
+ if ( 0 === $titleLength ) {
201
+ if ( 0 !== $siteTitle ) {
202
+ $titleLength = $siteTitle;
203
+ } else {
204
+ //* Output length from placeholder.
205
+ $length = $placeholder;
206
+ }
207
+ }
208
+
209
+ // Length should be something now.
210
+ if ( 0 !== $titleLength ) {
211
+
212
+ if ( 0 !== $additions && typeof $tagline !== 'undefined' ) {
213
+ var $tagLength = $tagline.length;
214
+
215
+ // Replace $additions with $tagline is $tagline isn't empty.
216
+ if ( 0 !== $tagLength ) {
217
+ $additions = $tagLength;
218
+ } else {
219
+ $additions = $description;
220
+ }
221
+ }
222
+
223
+ // Put it all together
224
+ if ( 0 === $additions ) {
225
+ $length = $titleLength;
226
+ } else {
227
+ $length = $titleLength + $seplen + $additions;
228
+ }
229
+ }
230
+
231
+ if ( $length < 25 || $length >= 75 ) {
232
+ $counterClass = 'ad-count-bad';
233
+ $name = autodescription.getCounterName( 'bad' );
234
+ } else if ( $length < 42 || ( $length > 55 && $length < 75 ) ) {
235
+ $counterClass = 'ad-count-okay';
236
+ $name = autodescription.getCounterName( 'okay' );
237
+ } else {
238
+ $counterClass = 'ad-count-good';
239
+ $name = autodescription.getCounterName( 'good' );
240
+ }
241
+
242
+ if ( $additionsClass )
243
+ $counterClass += ' ' + $additionsClass;
244
+
245
+ if ( ! $counterType || 1 == $counterType ) {
246
+ $output = $length.toString();
247
+ } else if ( 2 == $counterType ) {
248
+ $output = $name;
249
+ } else if ( 3 == $counterType ) {
250
+ $output = $length.toString() + ' - ' + $name;
251
+ }
252
+
253
+ $counter.html( $output ).removeClass().addClass( $counterClass );
254
+ },
255
+
256
+ /**
257
+ * Escapes HTML strings.
258
+ *
259
+ * @since 2.2.4
260
+ *
261
+ * @function
262
+ *
263
+ * @param {String} str
264
+ * @return {String} HTML to jQuery converted string
265
+ */
266
+ escapeStr: function( str ) {
267
+ 'use strict';
268
+
269
+ if ( str )
270
+ return str.replace(/([\[\]\/])/g,'\\$1');
271
+
272
+ return str;
273
+ },
274
+
275
+ /**
276
+ * Escapes HTML entities.
277
+ *
278
+ * @since 2.5.2.4
279
+ * @function
280
+ *
281
+ * @param {String|null} str
282
+ * @return {String} HTML to jQuery converted string
283
+ */
284
+ escapeTags: function( str ) {
285
+ 'use strict';
286
+
287
+ if ( str )
288
+ str.replace( /&/g, '&amp;' ).replace( /</g, '&lt;' ).replace( />/g, '&gt;' );
289
+
290
+ return str;
291
+ },
292
+
293
+ /**
294
+ * Dynamic Title separator replacement in metabox
295
+ *
296
+ * @since 2.2.2
297
+ *
298
+ * @function
299
+ * @param {jQuery.event} event
300
+ */
301
+ separatorSwitch: function( event ) {
302
+ 'use strict';
303
+
304
+ var $sep = jQuery( ".autodescription-sep-js" ),
305
+ $val = jQuery( event.target ).val();
306
+
307
+ if ( 'pipe' === $val ) {
308
+ $sep.text( " | " );
309
+ } else if ( 'dash' === $val ) {
310
+ $sep.text( " - " );
311
+ } else {
312
+ $sep.html( " &" + $val + "; " );
313
+ }
314
+ },
315
+
316
+ /**
317
+ * Dynamic Description separator replacement in metabox
318
+ *
319
+ * @since 2.3.4
320
+ *
321
+ * @function
322
+ * @param {jQuery.event} event
323
+ */
324
+ separatorSwitchDesc: function( event ) {
325
+ 'use strict';
326
+
327
+ var $sep = jQuery( "#autodescription-descsep-js" ),
328
+ $val = jQuery( event.target ).val();
329
+
330
+ if ( 'pipe' === $val ) {
331
+ $sep.text( " | " );
332
+ } else if ( 'dash' === $val ) {
333
+ $sep.text( " - " );
334
+ } else {
335
+ $sep.html( " &" + $val + "; " );
336
+ }
337
+ },
338
+
339
+ /**
340
+ * Status bar description init on hover actions.
341
+ *
342
+ * @since 2.1.9
343
+ *
344
+ * @function
345
+ */
346
+ statusBarHover: function() {
347
+ 'use strict';
348
+
349
+ var $wrap = jQuery( '.ad-bar-wrap' ).find( 'a' );
350
+
351
+ $wrap.on( "mouseenter", autodescription.statusBarHoverEnter );
352
+ $wrap.on( "mousemove", autodescription.statusBarHoverMove );
353
+ $wrap.on( "mouseleave", autodescription.statusBarHoverLeave );
354
+
355
+ },
356
+
357
+ /**
358
+ * Status bar description output on hover enter.
359
+ *
360
+ * @since 2.6.0
361
+ *
362
+ * @function
363
+ */
364
+ statusBarHoverEnter: function() {
365
+ 'use strict';
366
+
367
+ var $this = jQuery( this ),
368
+ $thisDesc = $this.attr( 'data-desc' );
369
+
370
+ if ( $thisDesc !== undefined && 0 === $this.find( 'div' ).length ) {
371
+ $this.append( '<div class="explanation-desc">' + $thisDesc + '<div></div></div>' );
372
+
373
+ var $thisHeight = $this.find( 'div.explanation-desc' ).height() + 28;
374
+
375
+ $this.find( 'div.explanation-desc' ).css( 'top', ( $this.position().top - $thisHeight ) + 'px' );
376
+ }
377
+ },
378
+
379
+ /**
380
+ * Status bar description output on hover move.
381
+ *
382
+ * @since 2.6.0
383
+ *
384
+ * @function
385
+ * @param {jQuery.event} event
386
+ */
387
+ statusBarHoverMove: function( event ) {
388
+ 'use strict';
389
+
390
+ var $this = jQuery( event.target ),
391
+ $pagex = event.pageX,
392
+ $mousex = $pagex - jQuery( '.ad-bar-wrap' ).offset().left - 11, // 22px width of arrow / 2 = 11 middle
393
+ $balloon = $this.find( '.explanation-desc' ),
394
+ $arrow = $balloon.find( 'div' );
395
+
396
+ if ( $mousex < 1 ) {
397
+ $arrow.css( 'left', 0 + "px" );
398
+ } else if ( $balloon.offset() !== undefined ) {
399
+ var $width = $balloon.width(),
400
+ $maxOffset = $balloon.offset().left + $width + 11;
401
+
402
+ if ( $pagex > $maxOffset ) {
403
+ $arrow.css( 'left', $width + "px" );
404
+ } else {
405
+ $arrow.css( 'left', $mousex + "px" );
406
+ }
407
+ }
408
+ },
409
+
410
+
411
+ /**
412
+ * Status bar description removal on hover leave.
413
+ *
414
+ * @since 2.6.0
415
+ *
416
+ * @function
417
+ */
418
+ statusBarHoverLeave: function() {
419
+ 'use strict';
420
+
421
+ jQuery( this ).find( 'div.explanation-desc' ).remove();
422
+ },
423
+
424
+ /**
425
+ * Remove Status bar desc if clicked outside (touch support)
426
+ *
427
+ * @since 2.1.9
428
+ *
429
+ * @function
430
+ * @param {jQuery.event} event
431
+ */
432
+ removeDesc: function( event ) {
433
+ 'use strict';
434
+
435
+ var $this = jQuery( event.target ),
436
+ $desc = jQuery('.ad-bar-wrap a');
437
+
438
+ if ( ! $this.closest( $desc ).length )
439
+ $desc.find( 'div.explanation-desc' ).remove();
440
+ },
441
+
442
+ /**
443
+ * Refines Styling for the navigation tabs on the settings pages
444
+ *
445
+ * @since 2.2.2
446
+ *
447
+ * Rewritten
448
+ * @since 2.6.0
449
+ *
450
+ * @function
451
+ * @param {jQuery.event} event
452
+ */
453
+ tabToggle: function( event ) {
454
+ 'use strict';
455
+
456
+ var $target = jQuery( event.target ).attr( 'id' ),
457
+ $name = jQuery( event.target ).attr( 'name' );
458
+
459
+ if ( typeof $target !== 'undefined' ) {
460
+ var $content = jQuery( '#' + $target + '-content' ),
461
+ $other = jQuery( '.' + $name + '-content' );
462
+
463
+ if ( typeof $content !== 'undefined' ) {
464
+ $other.removeClass( 'seoframework-active-tab-content' );
465
+ $content.addClass( 'seoframework-active-tab-content' );
466
+ }
467
+ }
468
+
469
+ },
470
+
471
+ /**
472
+ * Toggle tagline within the Left/Right example for the HomePage Title
473
+ *
474
+ * @since 2.2.4
475
+ *
476
+ * @function
477
+ * @param {jQuery.event} event
478
+ */
479
+ taglineToggle: function( event ) {
480
+ 'use strict';
481
+
482
+ var $this = jQuery( event.target ),
483
+ $tag = jQuery( '.custom-blogname-js' );
484
+
485
+ if ( $this.is( ':checked' ) ) {
486
+ $tag.css( 'display', 'inline' );
487
+ autodescription.titleTagline = true;
488
+ } else {
489
+ $tag.css( 'display', 'none' );
490
+ autodescription.titleTagline = false;
491
+ }
492
+
493
+ autodescription.docTitles().trigger( 'keyup', autodescription.updateCharacterCountTitle );
494
+ },
495
+
496
+ /**
497
+ * Toggle tagline within the Description Example.
498
+ *
499
+ * @since 2.3.4
500
+ *
501
+ * @function
502
+ * @param {jQuery.event} event
503
+ */
504
+ taglineToggleDesc: function( event ) {
505
+ 'use strict';
506
+
507
+ var $this = jQuery( event.target ),
508
+ $tagDesc = jQuery( '#on-blogname-js' );
509
+
510
+ if ( $this.is(':checked') ) {
511
+ $tagDesc.css( 'display', 'inline' );
512
+ } else {
513
+ $tagDesc.css( 'display', 'none' );
514
+ }
515
+ },
516
+
517
+ /**
518
+ * Toggle title additions location for the Title examples.
519
+ *
520
+ * @since 2.6.0
521
+ *
522
+ * @function
523
+ * @param {jQuery.event} event
524
+ */
525
+ titleLocationToggle: function( event ) {
526
+ 'use strict';
527
+
528
+ var $this = jQuery( event.target ).val(),
529
+ $titleExampleLeft = jQuery( '.title-additions-example-left' ),
530
+ $titleExampleRight = jQuery( '.title-additions-example-right' );
531
+
532
+ if ( 'right' === $this ) {
533
+ $titleExampleLeft.css( 'display', 'none' );
534
+ $titleExampleRight.css( 'display', 'inline' );
535
+ } else {
536
+ $titleExampleLeft.css( 'display', 'inline' );
537
+ $titleExampleRight.css( 'display', 'none' );
538
+ }
539
+
540
+ },
541
+
542
+ /**
543
+ * Toggle title prefixes for the Prefix Title example.
544
+ *
545
+ * @since 2.6.0
546
+ *
547
+ * @function
548
+ * @param {jQuery.event} event
549
+ */
550
+ titlePrefixToggle: function( event ) {
551
+ 'use strict';
552
+
553
+ var $this = jQuery( event.target ),
554
+ $prefix = jQuery( '.title-prefix-example' );
555
+
556
+ if ( $this.is(':checked') ) {
557
+ $prefix.css( 'display', 'none' );
558
+ } else {
559
+ $prefix.css( 'display', 'inline' );
560
+ }
561
+
562
+ },
563
+
564
+ /**
565
+ * Toggle additions within Description example for the Example Description
566
+ *
567
+ * @since 2.6.0
568
+ *
569
+ * @function
570
+ * @param {jQuery.event} event
571
+ */
572
+ additionsToggleDesc: function( event ) {
573
+ 'use strict';
574
+
575
+ var $this = jQuery( event.target ),
576
+ $tagDesc = jQuery( '#description-additions-js' );
577
+
578
+ if ( $this.is(':checked') ) {
579
+ $tagDesc.css( 'display', 'inline' );
580
+ } else {
581
+ $tagDesc.css( 'display', 'none' );
582
+ }
583
+ },
584
+
585
+ /**
586
+ * Toggle tagline end examples within the Left/Right example for the
587
+ * HomePage Title or Description.
588
+ *
589
+ * @since 2.2.7
590
+ *
591
+ * @function
592
+ * @param {jQuery.event} event
593
+ */
594
+ taglineToggleOnload: function( event ) {
595
+ 'use strict';
596
+
597
+ var $tagTitle = jQuery( '#title-tagline-toggle :input' ),
598
+ $title = jQuery( '.custom-blogname-js' ),
599
+ $tagDescAdditions = jQuery( '#description-additions-toggle :input' ),
600
+ $descAdditions = jQuery( '#description-additions-js' ),
601
+ $tagDescBlogname = jQuery( '#description-onblogname-toggle :input' ),
602
+ $descBlogname = jQuery( '#on-blogname-js' ),
603
+ $tagTitleAdditions = jQuery( '#title-additions-toggle :input' ),
604
+ $titleAdditions = jQuery( '.title-additions-js' );
605
+
606
+ if ( $tagTitle.is( ':checked' ) ) {
607
+ $title.css( 'display', 'inline' );
608
+ } else {
609
+ $title.css( 'display', 'none' );
610
+ }
611
+
612
+ if ( $tagDescAdditions.is( ':checked' ) ) {
613
+ $descAdditions.css( 'display', 'inline' );
614
+ } else {
615
+ $descAdditions.css( 'display', 'none' );
616
+ }
617
+
618
+ if ( $tagDescBlogname.is( ':checked' ) ) {
619
+ $descBlogname.css( 'display', 'inline' );
620
+ } else {
621
+ $descBlogname.css( 'display', 'none' );
622
+ }
623
+
624
+ // Reverse option.
625
+ if ( $tagTitleAdditions.is( ':checked' ) ) {
626
+ $titleAdditions.css( 'display', 'none' );
627
+ } else {
628
+ $titleAdditions.css( 'display', 'inline' );
629
+ }
630
+
631
+ },
632
+
633
+ /**
634
+ * Change Home Page Title based on input of the Custom Title
635
+ *
636
+ * @since 2.2.4
637
+ *
638
+ * @function
639
+ * @param {jQuery.event} event
640
+ */
641
+ titleProp: function( event ) {
642
+ 'use strict';
643
+
644
+ var $val = jQuery( event.target ).val(),
645
+ $title = jQuery( '.custom-title-js' );
646
+
647
+ if ( $val.length === 0 ) {
648
+ $title.text( autodescriptionL10n['siteTitle'] );
649
+ } else {
650
+ $title.text( $val );
651
+ }
652
+
653
+ },
654
+
655
+ /**
656
+ * Change Title based on input of the Custom Title
657
+ *
658
+ * @since 2.3.8
659
+ *
660
+ * @function
661
+ * @param {jQuery.event} event
662
+ */
663
+ taglineProp: function( event ) {
664
+ 'use strict';
665
+
666
+ var $val = jQuery( event.target ).val(),
667
+ $floatTag = jQuery( '.custom-tagline-js' ),
668
+ $target = jQuery( '#autodescription-site-settings\\[homepage_title\\]' ),
669
+ $leftRight = jQuery( '#home-title-location input:checked' ).val(),
670
+ $toggle = jQuery( '#autodescription-site-settings\\[homepage_tagline\\]' ),
671
+ $title = autodescriptionL10n['siteTitle'],
672
+ $placeholder = $title,
673
+ $description = autodescriptionL10n['blogDescription'],
674
+ $sep = jQuery( '#title-separator input:checked' ).val(),
675
+ $sepOutput = autodescriptionL10n['titleSeparator'];
676
+
677
+ if ( $toggle.is( ':checked' ) ) {
678
+
679
+ if ( $val.length !== 0 ) {
680
+ $val = autodescription.escapeTags( $val );
681
+
682
+ // Create a memory div to store the html in, convert to text to append in $placeholder and $floatTag
683
+ $description = jQuery( '<div/>' ).text( $val ).html();
684
+ }
685
+
686
+ if ( $sep.length !== 0 ) {
687
+ if ( 'pipe' === $sep ) {
688
+ $sepOutput = ( "|" );
689
+ } else if ( 'dash' === $sep ) {
690
+ $sepOutput = ( "-" );
691
+ } else {
692
+ // Create a memory div to store the html in, convert to text to append in $placeholder
693
+ $sepOutput = jQuery( '<div/>' ).html( "&" + $sep + ";" ).text();
694
+ }
695
+ }
696
+
697
+ if ( $leftRight.length !== 0 && 'left' === $leftRight ) {
698
+ $placeholder = $title + ' ' + $sepOutput + ' ' + $description;
699
+ } else {
700
+ $placeholder = $description + ' ' + $sepOutput + ' ' + $title;
701
+ }
702
+
703
+ }
704
+
705
+ $floatTag.html( $description );
706
+ $target.attr( "placeholder", $placeholder );
707
+
708
+ // Notify tagline has changed.
709
+ autodescription.docTitles().trigger( 'keyup', autodescription.updateCharacterCountTitle );
710
+ },
711
+
712
+ /**
713
+ * Trigger Change on Left/Right selection of Home Page Title
714
+ *
715
+ * @since 2.5.0
716
+ *
717
+ * @function
718
+ */
719
+ taglinePropTrigger: function() {
720
+ 'use strict';
721
+
722
+ jQuery( "#autodescription-site-settings\\[homepage_title_tagline\\]" ).trigger( 'keyup', autodescription.taglineProp );
723
+ },
724
+
725
+ /**
726
+ * Trigger Change on Left/Right selection of Global Title
727
+ *
728
+ * @since 2.5.2
729
+ *
730
+ * @function
731
+ */
732
+ titleToggle: function() {
733
+ 'use strict';
734
+
735
+ var $this = jQuery( event.target ),
736
+ $tagDesc = jQuery( '.title-additions-js' );
737
+
738
+ if ( $this.is( ':checked' ) ) {
739
+ $tagDesc.css( 'display', 'none' );
740
+ } else {
741
+ $tagDesc.css( 'display', 'inline' );
742
+ }
743
+ },
744
+
745
+ /**
746
+ * Have all form fields in The SEO Framework metaboxes set a dirty flag when changed.
747
+ *
748
+ * @since 2.0.0
749
+ *
750
+ * @function
751
+ */
752
+ attachUnsavedChangesListener: function() {
753
+ 'use strict';
754
+
755
+ jQuery( 'div.autodescription-metaboxes :input, div#theseoframework-inpost-box .inside :input' ).not( '.seoframework-tab :input' ).change( function() {
756
+ autodescription.registerChange();
757
+ });
758
+
759
+ jQuery( 'div.autodescription-metaboxes input[type=text], div.autodescription-metaboxes textarea, div#theseoframework-inpost-box .inside input[type=text], div#theseoframework-inpost-box .inside textarea' ).not('.nav-tab-wrapper :input').on( 'keyup', function() {
760
+ autodescription.registerChange();
761
+ });
762
+
763
+ window.onbeforeunload = function(){
764
+ if ( autodescription.settingsChanged ) {
765
+ return autodescriptionL10n['saveAlert'];
766
+ }
767
+ };
768
+
769
+ jQuery( 'div.autodescription-metaboxes input[type="submit"], div#publishing-action input[type="submit"], div#save-action input[type="submit"], a.submitdelete' ).click( function() {
770
+ window.onbeforeunload = null;
771
+ });
772
+ },
773
+
774
+ /**
775
+ * Set a flag, to indicate form fields have changed.
776
+ *
777
+ * @since 2.2.4
778
+ *
779
+ * @function
780
+ */
781
+ registerChange: function() {
782
+ 'use strict';
783
+
784
+ autodescription.settingsChanged = true;
785
+ },
786
+
787
+ /**
788
+ * Ask user to confirm that settings should now be reset.
789
+ *
790
+ * @since 2.2.4
791
+ *
792
+ * @function
793
+ *
794
+ * @return {Boolean|null} True if reset should occur, false if not.
795
+ */
796
+ confirmedReset: function() {
797
+ 'use strict';
798
+
799
+ return confirm( autodescriptionL10n['confirmReset'] );
800
+ },
801
+
802
+ /**
803
+ * Adds dynamic placeholder to Title input based on site settings.
804
+ *
805
+ * @since 2.5.0
806
+ *
807
+ * @function
808
+ * @param {jQuery.event} event
809
+ *
810
+ * @return {String} the placeholder additions.
811
+ */
812
+ dynamicPlaceholder: function( event ) {
813
+ 'use strict';
814
+
815
+ var $hasAdditions = autodescriptionL10n['titleAdditions'].length,
816
+ $placeholder = jQuery( '#autodescription-title-placeholder' );
817
+
818
+ // If check is defined, we're on SEO settings page.
819
+ if ( 0 === $hasAdditions ) {
820
+ var $this = jQuery( event.target );
821
+
822
+ // Empty the placeholder as we can't execute.
823
+ $this.css( 'text-indent', "initial" );
824
+ return $placeholder.empty();
825
+ }
826
+
827
+ var $after = false,
828
+ $check = jQuery( '#home-title-location input:checked' ).val(),
829
+ $rtl = autodescriptionL10n['isRTL'],
830
+ $additions = '';
831
+
832
+ if ( typeof $check !== 'undefined' && $check.length !== 0 ) {
833
+ //* We're in SEO Settings page.
834
+
835
+ if ( '1' === $rtl ) {
836
+ if ( 'right' === $check ) {
837
+ $after = true;
838
+ }
839
+ } else {
840
+ if ( 'left' === $check ) {
841
+ $after = true;
842
+ }
843
+ }
844
+ } else {
845
+ //* We're in post/page edit screen.
846
+
847
+ var $isHome = autodescriptionL10n['isHome'],
848
+ $titleLocation = autodescriptionL10n['titleLocation'],
849
+ $tagline = autodescription.titleTagline;
850
+
851
+ // We're on post/page screen.
852
+ if ( '1' === $isHome ) {
853
+ // Static Front page, switch check.
854
+ if ( '1' === $tagline ) {
855
+ if ( '1' === $rtl ) {
856
+ if ( 'right' === $titleLocation ) {
857
+ $after = true;
858
+ }
859
+ } else if ( 'left' === $titleLocation ) {
860
+ $after = true;
861
+ }
862
+ }
863
+ } else {
864
+ if ( '1' === $rtl ) {
865
+ if ( 'left' === $titleLocation ) {
866
+ $after = true;
867
+ }
868
+ } else if ( 'right' === $titleLocation ) {
869
+ $after = true;
870
+ }
871
+ }
872
+ }
873
+
874
+ var $tagbox = jQuery( '#title-tagline-toggle :input' );
875
+
876
+ if ( typeof $tagbox !== "undefined" && $tagbox.length > 0 && ! $tagbox.is( ':checked' ) ) {
877
+ //* We're on SEO Settings Page now, and tagline has been disabled.
878
+ var $this = jQuery( event.target );
879
+
880
+ $this.css( 'text-indent', "initial" );
881
+ $placeholder.css( 'display', 'none' );
882
+ } else {
883
+
884
+ var $this = jQuery( event.target ),
885
+ $inputVal = $this.val(),
886
+ $offsetTest = jQuery( "#autodescription-title-offset" ),
887
+ $offsetWidth = 0,
888
+ $heightPad = ( $this.outerHeight( true ) - $this.height() ) / 2,
889
+ $horPad = ( $this.outerWidth() - $this.width() ) / 2,
890
+ $leftOffset = ( $this.outerWidth( true ) - $this.width() ) / 2,
891
+ $taglineVal = jQuery( "#autodescription-site-settings\\[homepage_title_tagline\\]" ).val(),
892
+ $pos = 'left',
893
+ $separator = autodescriptionL10n['titleSeparator'];
894
+
895
+ if ( '1' === $rtl ) {
896
+ $pos = 'right';
897
+ }
898
+
899
+ if ( typeof $taglineVal !== "undefined" && $taglineVal.length === 0) {
900
+ $taglineVal = autodescriptionL10n['blogDescription'];
901
+ }
902
+
903
+ if ( $after ) {
904
+ $additions = $separator + " " + autodescriptionL10n['titleAdditions'];
905
+
906
+ // Exchange the placeholder value of the custom Tagline in the HomePage Metabox
907
+ if ( typeof $taglineVal !== "undefined" && $taglineVal.length > 0 ) {
908
+ $additions = $separator + " " + $taglineVal;
909
+ }
910
+
911
+ $this.css( 'text-indent', "initial" );
912
+ } else {
913
+ $additions = autodescriptionL10n['titleAdditions'] + " " + $separator;
914
+
915
+ // Exchange the placeholder value of the custom Tagline in the HomePage Metabox
916
+ if ( typeof $taglineVal !== "undefined" && $taglineVal.length > 0 ) {
917
+ $additions = $taglineVal + " " + $separator;
918
+ }
919
+ }
920
+
921
+ // Width offset container, copy variables and remain hidden.
922
+ $offsetTest.text( $inputVal );
923
+ $offsetTest.css({
924
+ fontFamily: $this.css( "fontFamily" ),
925
+ fontWeight: $this.css( "fontWeight" ),
926
+ letterSpacing: $this.css( "letterSpacing" ),
927
+ fontSize: $this.css( "fontSize" ),
928
+ });
929
+ $offsetWidth = $offsetTest.width();
930
+
931
+ var $maxWidth = $this.width() - $horPad - $offsetWidth;
932
+
933
+ if ( $maxWidth < 0 )
934
+ $maxWidth = 0;
935
+
936
+ // Moving Placeholder output
937
+ $placeholder.css({
938
+ display: $this.css( "display" ),
939
+ lineHeight: $this.css( "lineHeight" ),
940
+ paddingTop: $heightPad + "px",
941
+ paddingBottom: $heightPad + "px",
942
+ fontFamily: $this.css( "fontFamily" ),
943
+ fontWeight: $this.css( "fontWeight" ),
944
+ fontSize: $this.css( "fontSize" ),
945
+ letterSpacing: $this.css( "letterSpacing" ),
946
+ maxWidth: $maxWidth + "px",
947
+ });
948
+
949
+ //* Empty or fill placeholder and offsets.
950
+ if ( typeof $inputVal === "undefined" || $inputVal.length < 1 ) {
951
+
952
+ if ( ! $after )
953
+ $this.css( 'text-indent', "initial" );
954
+
955
+ $placeholder.empty();
956
+ } else {
957
+ $placeholder.text( $additions );
958
+
959
+ // Don't calculate when empty.
960
+ if ( $this.outerWidth() > $leftOffset ) {
961
+ if ( $after ) {
962
+ $placeholder.css( $pos, $horPad + $leftOffset + $offsetTest.width() + "px" );
963
+ } else {
964
+ var $indent = $horPad + $placeholder.width();
965
+
966
+ if ( $indent < 0 )
967
+ $indent = 0;
968
+
969
+ $placeholder.css( $pos, $leftOffset + "px" );
970
+ $this.css( 'text-indent', $indent + "px" );
971
+ }
972
+ }
973
+ }
974
+ }
975
+ },
976
+
977
+ /**
978
+ * Makes user click act natural by selecting the parent Title text input.
979
+ *
980
+ * @since 2.5.0
981
+ *
982
+ * @function
983
+ */
984
+ selectTitleInput: function() {
985
+ 'use strict';
986
+
987
+ var $input = autodescription.docTitles();
988
+
989
+ $input.focus();
990
+
991
+ if ( $input.setSelectionRange ) {
992
+ // Go to end times 2 if setSelectionRange exists.
993
+ var $length = $input.val().length * 2;
994
+ $input.setSelectionRange( $length, $length );
995
+ } else {
996
+ // Replace value with itself.
997
+ $input.val( $input.val() ).focus();
998
+ }
999
+ },
1000
+
1001
+ /**
1002
+ * Adds dynamic placeholder to Title input based on site settings on Load.
1003
+ *
1004
+ * @since 2.5.0
1005
+ *
1006
+ * @function
1007
+ */
1008
+ dynamicPlaceholderOnLoad: function() {
1009
+ 'use strict';
1010
+
1011
+ var $input = autodescription.docTitles();
1012
+
1013
+ if ( typeof $input.val() !== "undefined" ) {
1014
+ if ( $input.val().length > 0 ) {
1015
+ $input.trigger( 'keyup', autodescription.dynamicPlaceholder );
1016
+ } else {
1017
+ $input.trigger( 'keyup', autodescription.updateCharacterCountTitle );
1018
+ }
1019
+ }
1020
+
1021
+ },
1022
+
1023
+ /**
1024
+ * Triggers keyup on description input so the counter can colorize.
1025
+ *
1026
+ * @since 2.5.0
1027
+ *
1028
+ * @function
1029
+ */
1030
+ triggerDescriptionOnLoad: function() {
1031
+ 'use strict';
1032
+
1033
+ var $input = autodescription.docDescriptions();
1034
+
1035
+ $input.trigger( 'keyup', autodescription.updateCharacterCountDescription );
1036
+ },
1037
+
1038
+
1039
+ /**
1040
+ * Triggers keyup on title input so the counter can colorize.
1041
+ *
1042
+ * @since 2.6.0
1043
+ *
1044
+ * @function
1045
+ */
1046
+ triggerTitleOnLoad: function() {
1047
+ 'use strict';
1048
+
1049
+ var $input = autodescription.docTitles();
1050
+
1051
+ $input.trigger( 'keyup', autodescription.updateCharacterCountTitle );
1052
+ },
1053
+
1054
+ /**
1055
+ * OnLoad changes can affect settings changes. This function reverts those.
1056
+ *
1057
+ * @since 2.5.0
1058
+ *
1059
+ * @function
1060
+ */
1061
+ onLoadUnregisterChange: function() {
1062
+ 'use strict';
1063
+
1064
+ //* Prevent trigger of settings change
1065
+ autodescription.settingsChanged = false;
1066
+ },
1067
+
1068
+ /**
1069
+ * Dismissible notices. Uses class .seo-notice.
1070
+ *
1071
+ * @since 2.6.0
1072
+ *
1073
+ * @function
1074
+ * @param {jQuery.event} event
1075
+ */
1076
+ dismissNotice: function( event ) {
1077
+ 'use strict';
1078
+
1079
+ var $this = jQuery( event.target );
1080
+
1081
+ $this.parents( '.seo-notice' ).slideUp( 200, function() {
1082
+ $this.remove();
1083
+ });
1084
+
1085
+ },
1086
+
1087
+ /**
1088
+ * Updates the counter type.
1089
+ *
1090
+ * @since 2.6.0
1091
+ *
1092
+ * @function
1093
+ */
1094
+ counterUpdate: function() {
1095
+ 'use strict';
1096
+
1097
+ // Count up, reset to 0 if needed. We have 4 options: 0, 1, 2, 3
1098
+ autodescription.counterType = autodescription.counterType + 1;
1099
+ if ( autodescription.counterType > 3 )
1100
+ autodescription.counterType = 0;
1101
+
1102
+ var data = {
1103
+ 'action': 'the_seo_framework_update_counter'
1104
+ };
1105
+
1106
+ //* Call PHP function and update all visible counters.
1107
+ jQuery.post( ajaxurl, data );
1108
+
1109
+ autodescription.additionsClassInit();
1110
+ },
1111
+
1112
+ /**
1113
+ * Sets up additionsClass variable.
1114
+ * Also sets up browser caches correctly.
1115
+ *
1116
+ * @since 2.6.0
1117
+ *
1118
+ * @function
1119
+ */
1120
+ additionsClassInit: function() {
1121
+ 'use strict';
1122
+
1123
+ /**
1124
+ * Mixed string and int (i10n is string, JS is int).
1125
+ * @param {String|int} $counterType
1126
+ */
1127
+ var $counterType = autodescription.counterType;
1128
+
1129
+ if ( 1 == $counterType ) {
1130
+ autodescription.additionsClass = 'tsf-counter-one';
1131
+ autodescription.counterType = 1;
1132
+ } else if ( 2 == $counterType ) {
1133
+ autodescription.additionsClass = 'tsf-counter-two';
1134
+ autodescription.counterType = 2;
1135
+ } else if ( 3 == $counterType ) {
1136
+ autodescription.additionsClass = 'tsf-counter-three';
1137
+ autodescription.counterType = 3;
1138
+ } else {
1139
+ autodescription.additionsClass = 'tsf-counter-zero';
1140
+ autodescription.counterType = 0;
1141
+ }
1142
+
1143
+ autodescription.updateCounters();
1144
+ },
1145
+
1146
+ /**
1147
+ * Update counters.
1148
+ *
1149
+ * @since 2.6.0
1150
+ *
1151
+ * @function
1152
+ */
1153
+ updateCounters: function() {
1154
+ 'use strict';
1155
+
1156
+ autodescription.triggerTitleOnLoad();
1157
+ autodescription.triggerDescriptionOnLoad();
1158
+ },
1159
+
1160
+ /**
1161
+ * Returns counter name.
1162
+ *
1163
+ * @since 2.6.0
1164
+ * @function
1165
+ *
1166
+ * @param {String|null} type
1167
+ * @return {String} Human Readable Counter name.
1168
+ */
1169
+ getCounterName: function( type ) {
1170
+ 'use strict';
1171
+
1172
+ var name = autodescriptionL10n[type];
1173
+
1174
+ return name;
1175
+ },
1176
+
1177
+ /**
1178
+ * Initialises all aspects of the scripts.
1179
+ *
1180
+ * Generally ordered with stuff that inserts new elements into the DOM first,
1181
+ * then stuff that triggers an event on existing DOM elements when ready,
1182
+ * followed by stuff that triggers an event only on user interaction. This
1183
+ * keeps any screen jumping from occuring later on.
1184
+ *
1185
+ * @since 2.2.4
1186
+ *
1187
+ * @function
1188
+ */
1189
+ ready: function() {
1190
+ 'use strict';
1191
+
1192
+ // Move the page updates notices below the top-wrap.
1193
+ jQuery( 'div.updated, div.error, div.notice-warning' ).insertAfter( 'div.top-wrap' );
1194
+
1195
+ // Set up additions classes.
1196
+ jQuery( document.body ).ready( autodescription.additionsClassInit );
1197
+
1198
+ // Toggle Dynamic Title Placeholder onLoad, also toggles doing it right colors.
1199
+ jQuery( document.body ).ready( autodescription.dynamicPlaceholderOnLoad );
1200
+ // Toggle Title doing it right colors.
1201
+ jQuery( document.body ).ready( autodescription.triggerTitleOnLoad );
1202
+ // Toggle Description doing it right colors.
1203
+ jQuery( document.body ).ready( autodescription.triggerDescriptionOnLoad );
1204
+
1205
+ // Check if the Title Tagline or Description Additions should be removed when page is loaded.
1206
+ jQuery( document.body ).ready( autodescription.taglineToggleOnload );
1207
+
1208
+ // Initialize the status bar hover balloon.
1209
+ jQuery( document.body ).ready( autodescription.statusBarHover );
1210
+
1211
+ // Initialize status bar removal hover for touch screens.
1212
+ jQuery( document.body ).on( 'click touchstart MSPointerDown', autodescription.removeDesc );
1213
+
1214
+ // #== Before Change listener
1215
+
1216
+ // Initialise form field changing flag.
1217
+ autodescription.attachUnsavedChangesListener();
1218
+
1219
+ // Deregister changes.
1220
+ jQuery( document.body ).ready( autodescription.onLoadUnregisterChange );
1221
+
1222
+ // #== After Change listener
1223
+
1224
+ // Bind character counters.
1225
+ autodescription.docDescriptions().on( 'keydown keyup paste', autodescription.updateCharacterCountDescription );
1226
+ autodescription.docTitles().on( 'keydown keyup paste', autodescription.updateCharacterCountTitle );
1227
+
1228
+ // Allow the title separator to be changed dynamically.
1229
+ jQuery( '#title-separator input' ).on( 'click', autodescription.separatorSwitch );
1230
+ // Allow description separator to be changed dynamically.
1231
+ jQuery( '#description-separator input' ).on( 'click', autodescription.separatorSwitchDesc );
1232
+
1233
+ // Bind reset confirmation.
1234
+ jQuery( '.autodescription-js-confirm-reset' ).on( 'click.autodescription.autodescription_confirm_reset', autodescription.confirmedReset );
1235
+
1236
+ // Toggle Tabs in the SEO settings page.
1237
+ jQuery( '.seoframework-tab' ).on( 'click', autodescription.tabToggle );
1238
+
1239
+ // Toggle Title tagline aditions removal.
1240
+ jQuery( '#title-tagline-toggle :input' ).on( 'click', autodescription.taglineToggle );
1241
+ // Toggle Title additions location.
1242
+ jQuery( '#title-location input' ).on( 'click', autodescription.titleLocationToggle );
1243
+ // Toggle Title prefixes display.
1244
+ jQuery( '#title-prefixes-toggle :input' ).on( 'click', autodescription.titlePrefixToggle );
1245
+
1246
+ // Toggle Description additions removal.
1247
+ jQuery( '#description-onblogname-toggle :input' ).on( 'click', autodescription.taglineToggleDesc );
1248
+ jQuery( '#description-additions-toggle :input' ).on( 'click', autodescription.additionsToggleDesc );
1249
+
1250
+ // Change Home Page Title Example prop on input changes.
1251
+ jQuery( '#autodescription-site-settings\\[homepage_title\\]' ).on( 'keydown keyup paste', autodescription.titleProp );
1252
+ jQuery( '#home-title-location :input, #title-tagline-toggle :input, #title-separator input' ).on( 'click', autodescription.taglinePropTrigger );
1253
+ jQuery( '#autodescription-site-settings\\[homepage_title_tagline\\]' ).on( 'keydown keyup paste', autodescription.taglineProp );
1254
+
1255
+ // Make sure the titleProp is correctly rendered when revealed after being hidden.
1256
+ jQuery( '#homepage-tab-general' ).on( 'change', autodescription.taglinePropTrigger );
1257
+
1258
+ // Change Global Title Example prop on input changes.
1259
+ jQuery( '#autodescription-site-settings\\[title_rem_additions\\]' ).on( 'click', autodescription.titleToggle );
1260
+
1261
+ // Dynamic Placeholder, acts on keydown for a11y, although more cpu intensive. Acts on keyup for perfect output.
1262
+ autodescription.docTitles().on( 'keydown keyup paste', autodescription.dynamicPlaceholder );
1263
+
1264
+ // Move click on dynamic additions to focus input behind.
1265
+ jQuery( '#autodescription-title-placeholder' ).on( 'click', autodescription.selectTitleInput );
1266
+
1267
+ // Dismiss notices.
1268
+ jQuery( '.autodescription-dismiss' ).on( 'click', autodescription.dismissNotice );
1269
+
1270
+ // AJAX counter
1271
+ jQuery( '.theseoframework-counter' ).on( 'click', autodescription.counterUpdate );
1272
+
1273
+ }
1274
+
1275
+ };
1276
+ jQuery( autodescription.ready );
lib/js/autodescription.min.js ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ window.autodescription={i:!1,h:autodescriptionL10n.titleTagline,a:autodescriptionL10n.counterType,c:"",b:function(){return jQuery("#autodescription_title, #autodescription-meta\\[doctitle\\], #autodescription-site-settings\\[homepage_title\\]")},l:function(){return jQuery("#autodescription_description, #autodescription-meta\\[description\\], #autodescription-site-settings\\[homepage_description\\]")},confirm:function(a){return confirm(a)},B:function(a){var b=jQuery(a.target),c=b.val().length,b=b.attr("placeholder").length;
2
+ a=jQuery("#"+autodescription.o(a.target.id)+"_chars");var e=autodescription.c,g=autodescription.a,d="",k="",h="";0===c&&(c=b);100>c||175<=c?(d="ad-count-bad",k=autodescription.f("bad")):137>c||155<c&&175>c?(d="ad-count-okay",k=autodescription.f("okay")):(d="ad-count-good",k=autodescription.f("good"));e&&(d+=" "+e);g&&1!=g?2==g?h=k:3==g&&(h=c.toString()+" - "+k):h=c.toString();a.html(h).removeClass().addClass(d)},g:function(a){var b=jQuery(a.target),c=autodescriptionL10n.titleAdditions.length,e=autodescriptionL10n.blogDescription.length,
3
+ g=autodescriptionL10n.siteTitle.length,d=b.val().length,k=b.attr("placeholder").length,h=jQuery("#autodescription-site-settings\\[homepage_title_tagline\\]").val(),b=3;a=jQuery("#"+autodescription.o(a.target.id)+"_chars");var f=0,n=autodescription.c,p=autodescription.a,l="",m="",q="";autodescription.h||(b=c=0);0===d&&(0!==g?d=g:f=k);0!==d&&(0!==c&&"undefined"!==typeof h&&(c=h.length,c=0!==c?c:e),f=0===c?d:d+b+c);25>f||75<=f?(l="ad-count-bad",m=autodescription.f("bad")):42>f||55<f&&75>f?(l="ad-count-okay",
4
+ m=autodescription.f("okay")):(l="ad-count-good",m=autodescription.f("good"));n&&(l+=" "+n);p&&1!=p?2==p?q=m:3==p&&(q=f.toString()+" - "+m):q=f.toString();a.html(q).removeClass().addClass(l)},o:function(a){return a?a.replace(/([\[\]\/])/g,"\\$1"):a},J:function(a){a&&a.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");return a},N:function(a){var b=jQuery(".autodescription-sep-js");a=jQuery(a.target).val();"pipe"===a?b.text(" | "):"dash"===a?b.text(" - "):b.html(" &"+a+"; ")},O:function(a){var b=
5
+ jQuery("#autodescription-descsep-js");a=jQuery(a.target).val();"pipe"===a?b.text(" | "):"dash"===a?b.text(" - "):b.html(" &"+a+"; ")},statusBarHover:function(){var a=jQuery(".ad-bar-wrap").find("a");a.on("mouseenter",autodescription.P);a.on("mousemove",autodescription.S);a.on("mouseleave",autodescription.R)},P:function(){var a=jQuery(this),b=a.attr("data-desc");void 0!==b&&0===a.find("div").length&&(a.append('<div class="explanation-desc">'+b+"<div></div></div>"),b=a.find("div.explanation-desc").height()+
6
+ 28,a.find("div.explanation-desc").css("top",a.position().top-b+"px"))},S:function(a){var b=jQuery(a.target);a=a.pageX;var c=a-jQuery(".ad-bar-wrap").offset().left-11,e=b.find(".explanation-desc"),b=e.find("div");if(1>c)b.css("left","0px");else if(void 0!==e.offset()){var g=e.width(),e=e.offset().left+g+11;a>e?b.css("left",g+"px"):b.css("left",c+"px")}},R:function(){jQuery(this).find("div.explanation-desc").remove()},L:function(a){a=jQuery(a.target);var b=jQuery(".ad-bar-wrap a");a.closest(b).length||
7
+ b.find("div.explanation-desc").remove()},T:function(a){var b=jQuery(a.target).attr("id");a=jQuery(a.target).attr("name");"undefined"!==typeof b&&(b=jQuery("#"+b+"-content"),a=jQuery("."+a+"-content"),"undefined"!==typeof b&&(a.removeClass("seoframework-active-tab-content"),b.addClass("seoframework-active-tab-content")))},U:function(a){a=jQuery(a.target);var b=jQuery(".custom-blogname-js");a.is(":checked")?(b.css("display","inline"),autodescription.h=!0):(b.css("display","none"),autodescription.h=
8
+ !1);autodescription.b().trigger("keyup",autodescription.g)},V:function(a){a=jQuery(a.target);var b=jQuery("#on-blogname-js");a.is(":checked")?b.css("display","inline"):b.css("display","none")},X:function(a){a=jQuery(a.target).val();var b=jQuery(".title-additions-example-left"),c=jQuery(".title-additions-example-right");"right"===a?(b.css("display","none"),c.css("display","inline")):(b.css("display","inline"),c.css("display","none"))},Y:function(a){a=jQuery(a.target);var b=jQuery(".title-prefix-example");
9
+ a.is(":checked")?b.css("display","none"):b.css("display","inline")},C:function(a){a=jQuery(a.target);var b=jQuery("#description-additions-js");a.is(":checked")?b.css("display","inline"):b.css("display","none")},W:function(){var a=jQuery("#title-tagline-toggle :input"),b=jQuery(".custom-blogname-js"),c=jQuery("#description-additions-toggle :input"),e=jQuery("#description-additions-js"),g=jQuery("#description-onblogname-toggle :input"),d=jQuery("#on-blogname-js"),k=jQuery("#title-additions-toggle :input"),
10
+ h=jQuery(".title-additions-js");a.is(":checked")?b.css("display","inline"):b.css("display","none");c.is(":checked")?e.css("display","inline"):e.css("display","none");g.is(":checked")?d.css("display","inline"):d.css("display","none");k.is(":checked")?h.css("display","none"):h.css("display","inline")},Z:function(a){a=jQuery(a.target).val();var b=jQuery(".custom-title-js");0===a.length?b.text(autodescriptionL10n.siteTitle):b.text(a)},u:function(a){a=jQuery(a.target).val();var b=jQuery(".custom-tagline-js"),
11
+ c=jQuery("#autodescription-site-settings\\[homepage_title\\]"),e=jQuery("#home-title-location input:checked").val(),g=jQuery("#autodescription-site-settings\\[homepage_tagline\\]"),d=autodescriptionL10n.siteTitle,k=d,h=autodescriptionL10n.blogDescription,f=jQuery("#title-separator input:checked").val(),n=autodescriptionL10n.titleSeparator;g.is(":checked")&&(0!==a.length&&(a=autodescription.J(a),h=jQuery("<div/>").text(a).html()),0!==f.length&&(n="pipe"===f?"|":"dash"===f?"-":jQuery("<div/>").html("&"+
12
+ f+";").text()),k=0!==e.length&&"left"===e?d+" "+n+" "+h:h+" "+n+" "+d);b.html(h);c.attr("placeholder",k);autodescription.b().trigger("keyup",autodescription.g)},v:function(){jQuery("#autodescription-site-settings\\[homepage_title_tagline\\]").trigger("keyup",autodescription.u)},aa:function(){var a=jQuery(event.target),b=jQuery(".title-additions-js");a.is(":checked")?b.css("display","none"):b.css("display","inline")},D:function(){jQuery("div.autodescription-metaboxes :input, div#theseoframework-inpost-box .inside :input").not(".seoframework-tab :input").change(function(){autodescription.s()});
13
+ jQuery("div.autodescription-metaboxes input[type=text], div.autodescription-metaboxes textarea, div#theseoframework-inpost-box .inside input[type=text], div#theseoframework-inpost-box .inside textarea").not(".nav-tab-wrapper :input").on("keyup",function(){autodescription.s()});window.onbeforeunload=function(){if(autodescription.i)return autodescriptionL10n.saveAlert};jQuery('div.autodescription-metaboxes input[type="submit"], div#publishing-action input[type="submit"], div#save-action input[type="submit"], a.submitdelete').click(function(){window.onbeforeunload=
14
+ null})},s:function(){autodescription.i=!0},F:function(){return confirm(autodescriptionL10n.confirmReset)},m:function(a){var b=autodescriptionL10n.titleAdditions.length,c=jQuery("#autodescription-title-placeholder");if(0===b)return a=jQuery(a.target),a.css("text-indent","initial"),c.empty();var b=!1,e=jQuery("#home-title-location input:checked").val(),g=autodescriptionL10n.isRTL,d="";"undefined"!==typeof e&&0!==e.length?"1"===g?"right"===e&&(b=!0):"left"===e&&(b=!0):(d=autodescriptionL10n.titleLocation,
15
+ e=autodescription.h,"1"===autodescriptionL10n.isHome?"1"===e&&("1"===g?"right"===d&&(b=!0):"left"===d&&(b=!0)):"1"===g?"left"===d&&(b=!0):"right"===d&&(b=!0));d=jQuery("#title-tagline-toggle :input");if("undefined"!==typeof d&&0<d.length&&!d.is(":checked"))a=jQuery(a.target),a.css("text-indent","initial"),c.css("display","none");else{a=jQuery(a.target);var k=a.val(),h=jQuery("#autodescription-title-offset"),f=0,n=(a.outerHeight(!0)-a.height())/2,p=(a.outerWidth()-a.width())/2,e=(a.outerWidth(!0)-
16
+ a.width())/2,f=jQuery("#autodescription-site-settings\\[homepage_title_tagline\\]").val(),l="left",m=autodescriptionL10n.titleSeparator;"1"===g&&(l="right");"undefined"!==typeof f&&0===f.length&&(f=autodescriptionL10n.blogDescription);b?(d=m+" "+autodescriptionL10n.titleAdditions,"undefined"!==typeof f&&0<f.length&&(d=m+" "+f),a.css("text-indent","initial")):(d=autodescriptionL10n.titleAdditions+" "+m,"undefined"!==typeof f&&0<f.length&&(d=f+" "+m));h.text(k);h.css({fontFamily:a.css("fontFamily"),
17
+ fontWeight:a.css("fontWeight"),letterSpacing:a.css("letterSpacing"),fontSize:a.css("fontSize")});f=h.width();g=a.width()-p-f;0>g&&(g=0);c.css({display:a.css("display"),lineHeight:a.css("lineHeight"),paddingTop:n+"px",paddingBottom:n+"px",fontFamily:a.css("fontFamily"),fontWeight:a.css("fontWeight"),fontSize:a.css("fontSize"),letterSpacing:a.css("letterSpacing"),maxWidth:g+"px"});"undefined"===typeof k||1>k.length?(b||a.css("text-indent","initial"),c.empty()):(c.text(d),a.outerWidth()>e&&(b?c.css(l,
18
+ p+e+h.width()+"px"):(b=p+c.width(),0>b&&(b=0),c.css(l,e+"px"),a.css("text-indent",b+"px"))))}},M:function(){var a=autodescription.b();a.focus();if(a.setSelectionRange){var b=2*a.val().length;a.setSelectionRange(b,b)}else a.val(a.val()).focus()},I:function(){var a=autodescription.b();"undefined"!==typeof a.val()&&(0<a.val().length?a.trigger("keyup",autodescription.m):a.trigger("keyup",autodescription.g))},w:function(){autodescription.l().trigger("keyup",autodescription.B)},A:function(){autodescription.b().trigger("keyup",
19
+ autodescription.g)},K:function(){autodescription.i=!1},H:function(a){var b=jQuery(a.target);b.parents(".seo-notice").slideUp(200,function(){b.remove()})},G:function(){autodescription.a+=1;3<autodescription.a&&(autodescription.a=0);jQuery.post(ajaxurl,{action:"the_seo_framework_update_counter"});autodescription.j()},j:function(){var a=autodescription.a;1==a?(autodescription.c="tsf-counter-one",autodescription.a=1):2==a?(autodescription.c="tsf-counter-two",autodescription.a=2):3==a?(autodescription.c=
20
+ "tsf-counter-three",autodescription.a=3):(autodescription.c="tsf-counter-zero",autodescription.a=0);autodescription.ba()},ba:function(){autodescription.A();autodescription.w()},f:function(a){return autodescriptionL10n[a]},ready:function(){jQuery("div.updated, div.error, div.notice-warning").insertAfter("div.top-wrap");jQuery(document.body).ready(autodescription.j);jQuery(document.body).ready(autodescription.I);jQuery(document.body).ready(autodescription.A);jQuery(document.body).ready(autodescription.w);
21
+ jQuery(document.body).ready(autodescription.W);jQuery(document.body).ready(autodescription.statusBarHover);jQuery(document.body).on("click touchstart MSPointerDown",autodescription.L);autodescription.D();jQuery(document.body).ready(autodescription.K);autodescription.l().on("keydown keyup paste",autodescription.B);autodescription.b().on("keydown keyup paste",autodescription.g);jQuery("#title-separator input").on("click",autodescription.N);jQuery("#description-separator input").on("click",autodescription.O);
22
+ jQuery(".autodescription-js-confirm-reset").on("click.autodescription.autodescription_confirm_reset",autodescription.F);jQuery(".seoframework-tab").on("click",autodescription.T);jQuery("#title-tagline-toggle :input").on("click",autodescription.U);jQuery("#title-location input").on("click",autodescription.X);jQuery("#title-prefixes-toggle :input").on("click",autodescription.Y);jQuery("#description-onblogname-toggle :input").on("click",autodescription.V);jQuery("#description-additions-toggle :input").on("click",
23
+ autodescription.C);jQuery("#autodescription-site-settings\\[homepage_title\\]").on("keydown keyup paste",autodescription.Z);jQuery("#home-title-location :input, #title-tagline-toggle :input, #title-separator input").on("click",autodescription.v);jQuery("#autodescription-site-settings\\[homepage_title_tagline\\]").on("keydown keyup paste",autodescription.u);jQuery("#homepage-tab-general").on("change",autodescription.v);jQuery("#autodescription-site-settings\\[title_rem_additions\\]").on("click",autodescription.aa);
24
+ autodescription.b().on("keydown keyup paste",autodescription.m);jQuery("#autodescription-title-placeholder").on("click",autodescription.M);jQuery(".autodescription-dismiss").on("click",autodescription.H);jQuery(".theseoframework-counter").on("click",autodescription.G)}};jQuery(autodescription.ready);
license.txt ADDED
@@ -0,0 +1,552 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GNU GENERAL PUBLIC LICENSE
2
+
3
+ Version 3, 29 June 2007
4
+
5
+ Copyright © 2007 Free Software Foundation, Inc. <http://fsf.org/>
6
+
7
+ Everyone is permitted to copy and distribute verbatim copies of this license
8
+ document, but changing it is not allowed.
9
+
10
+ Preamble
11
+
12
+ The GNU General Public License is a free, copyleft license for software and
13
+ other kinds of works.
14
+
15
+ The licenses for most software and other practical works are designed to take
16
+ away your freedom to share and change the works. By contrast, the GNU General
17
+ Public License is intended to guarantee your freedom to share and change all
18
+ versions of a program--to make sure it remains free software for all its users.
19
+ We, the Free Software Foundation, use the GNU General Public License for most of
20
+ our software; it applies also to any other work released this way by its
21
+ authors. You can apply it to your programs, too.
22
+
23
+ When we speak of free software, we are referring to freedom, not price. Our
24
+ General Public Licenses are designed to make sure that you have the freedom
25
+ to distribute copies of free software (and charge for them if you wish), that
26
+ you receive source code or can get it if you want it, that you can change the
27
+ software or use pieces of it in new free programs, and that you know you can do
28
+ these things.
29
+
30
+ To protect your rights, we need to prevent others from denying you these rights
31
+ or asking you to surrender the rights. Therefore, you have certain
32
+ responsibilities if you distribute copies of the software, or if you modify it:
33
+ responsibilities to respect the freedom of others.
34
+
35
+ For example, if you distribute copies of such a program, whether gratis or for a
36
+ fee, you must pass on to the recipients the same freedoms that you received. You
37
+ must make sure that they, too, receive or can get the source code. And you must
38
+ show them these terms so they know their rights.
39
+
40
+ Developers that use the GNU GPL protect your rights with two steps: (1) assert
41
+ copyright on the software, and (2) offer you this License giving you legal
42
+ permission to copy, distribute and/or modify it.
43
+
44
+ For the developers' and authors' protection, the GPL clearly explains that there
45
+ is no warranty for this free software. For both users' and authors' sake, the
46
+ GPL requires that modified versions be marked as changed, so that their problems
47
+ will not be attributed erroneously to authors of previous versions.
48
+
49
+ Some devices are designed to deny users access to install or run modified
50
+ versions of the software inside them, although the manufacturer can do so. This
51
+ is fundamentally incompatible with the aim of protecting users' freedom to
52
+ change the software. The systematic pattern of such abuse occurs in the area of
53
+ products for individuals to use, which is precisely where it is most
54
+ unacceptable. Therefore, we have designed this version of the GPL to prohibit
55
+ the practice for those products. If such problems arise substantially in other
56
+ domains, we stand ready to extend this provision to those domains in future
57
+ versions of the GPL, as needed to protect the freedom of users.
58
+
59
+ Finally, every program is threatened constantly by software patents. States
60
+ should not allow patents to restrict development and use of software on
61
+ general-purpose computers, but in those that do, we wish to avoid the special
62
+ danger that patents applied to a free program could make it effectively
63
+ proprietary. To prevent this, the GPL assures that patents cannot be used to
64
+ render the program non-free.
65
+
66
+ The precise terms and conditions for copying, distribution and modification
67
+ follow.
68
+
69
+ TERMS AND CONDITIONS
70
+
71
+ 0. Definitions.
72
+ “This License” refers to version 3 of the GNU General Public License.
73
+
74
+ “Copyright” also means copyright-like laws that apply to other kinds of works,
75
+ such as semiconductor masks.
76
+
77
+ “The Program” refers to any copyrightable work licensed under this License. Each
78
+ licensee is addressed as “you”. “Licensees” and “recipients” may be individuals
79
+ or organizations.
80
+
81
+ To “modify” a work means to copy from or adapt all or part of the work in a
82
+ fashion requiring copyright permission, other than the making of an exact copy.
83
+ The resulting work is called a “modified version” of the earlier work or a work
84
+ “based on” the earlier work.
85
+
86
+ A “covered work” means either the unmodified Program or a work based on the
87
+ Program.
88
+
89
+ To “propagate” a work means to do anything with it that, without permission,
90
+ would make you directly or secondarily liable for infringement under applicable
91
+ copyright law, except executing it on a computer or modifying a private copy.
92
+ Propagation includes copying, distribution (with or without modification),
93
+ making available to the public, and in some countries other activities as well.
94
+
95
+ To “convey” a work means any kind of propagation that enables other parties to
96
+ make or receive copies. Mere interaction with a user through a computer network,
97
+ with no transfer of a copy, is not conveying.
98
+
99
+ An interactive user interface displays “Appropriate Legal Notices” to the extent
100
+ that it includes a convenient and prominently visible feature that (1) displays
101
+ an appropriate copyright notice, and (2) tells the user that there is no
102
+ warranty for the work (except to the extent that warranties are provided), that
103
+ licensees may convey the work under this License, and how to view a copy of this
104
+ License. If the interface presents a list of user commands or options, such as a
105
+ menu, a prominent item in the list meets this criterion.
106
+
107
+ 1. Source Code.
108
+ The “source code” for a work means the preferred form of the work for making
109
+ modifications to it. “Object code” means any non-source form of a work.
110
+
111
+ A “Standard Interface” means an interface that either is an official standard
112
+ defined by a recognized standards body, or, in the case of interfaces specified
113
+ for a particular programming language, one that is widely used among developers
114
+ working in that language.
115
+
116
+ The “System Libraries” of an executable work include anything, other than the
117
+ work as a whole, that (a) is included in the normal form of packaging a Major
118
+ Component, but which is not part of that Major Component, and (b) serves only to
119
+ enable use of the work with that Major Component, or to implement a Standard
120
+ Interface for which an implementation is available to the public in source code
121
+ form. A “Major Component”, in this context, means a major essential component
122
+ (kernel, window system, and so on) of the specific operating system (if any) on
123
+ which the executable work runs, or a compiler used to produce the work, or an
124
+ object code interpreter used to run it.
125
+
126
+ The “Corresponding Source” for a work in object code form means all the source
127
+ code needed to generate, install, and (for an executable work) run the object
128
+ code and to modify the work, including scripts to control those activities.
129
+ However, it does not include the work's System Libraries, or general-purpose
130
+ tools or generally available free programs which are used unmodified in
131
+ performing those activities but which are not part of the work. For example,
132
+ Corresponding Source includes interface definition files associated with source
133
+ files for the work, and the source code for shared libraries and dynamically
134
+ linked subprograms that the work is specifically designed to require, such as by
135
+ intimate data communication or control flow between those subprograms and other
136
+ parts of the work.
137
+
138
+ The Corresponding Source need not include anything that users can regenerate
139
+ automatically from other parts of the Corresponding Source.
140
+
141
+ The Corresponding Source for a work in source code form is that same work.
142
+
143
+ 2. Basic Permissions.
144
+ All rights granted under this License are granted for the term of copyright on
145
+ the Program, and are irrevocable provided the stated conditions are met. This
146
+ License explicitly affirms your unlimited permission to run the unmodified
147
+ Program. The output from running a covered work is covered by this License only
148
+ if the output, given its content, constitutes a covered work. This License
149
+ acknowledges your rights of fair use or other equivalent, as provided by
150
+ copyright law.
151
+
152
+ You may make, run and propagate covered works that you do not convey, without
153
+ conditions so long as your license otherwise remains in force. You may convey
154
+ covered works to others for the sole purpose of having them make modifications
155
+ exclusively for you, or provide you with facilities for running those works,
156
+ provided that you comply with the terms of this License in conveying all
157
+ material for which you do not control copyright. Those thus making or running
158
+ the covered works for you must do so exclusively on your behalf, under your
159
+ direction and control, on terms that prohibit them from making any copies of
160
+ your copyrighted material outside their relationship with you.
161
+
162
+ Conveying under any other circumstances is permitted solely under the conditions
163
+ stated below. Sublicensing is not allowed; section 10 makes it unnecessary.
164
+
165
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
166
+ No covered work shall be deemed part of an effective technological measure under
167
+ any applicable law fulfilling obligations under article 11 of the WIPO copyright
168
+ treaty adopted on 20 December 1996, or similar laws prohibiting or restricting
169
+ circumvention of such measures.
170
+
171
+ When you convey a covered work, you waive any legal power to forbid
172
+ circumvention of technological measures to the extent such circumvention is
173
+ effected by exercising rights under this License with respect to the covered
174
+ work, and you disclaim any intention to limit operation or modification of the
175
+ work as a means of enforcing, against the work's users, your or third parties'
176
+ legal rights to forbid circumvention of technological measures.
177
+
178
+ 4. Conveying Verbatim Copies.
179
+ You may convey verbatim copies of the Program's source code as you receive it,
180
+ in any medium, provided that you conspicuously and appropriately publish on each
181
+ copy an appropriate copyright notice; keep intact all notices stating that this
182
+ License and any non-permissive terms added in accord with section 7 apply to the
183
+ code; keep intact all notices of the absence of any warranty; and give all
184
+ recipients a copy of this License along with the Program.
185
+
186
+ You may charge any price or no price for each copy that you convey, and you may
187
+ offer support or warranty protection for a fee.
188
+
189
+ 5. Conveying Modified Source Versions.
190
+ You may convey a work based on the Program, or the modifications to produce it
191
+ from the Program, in the form of source code under the terms of section 4,
192
+ provided that you also meet all of these conditions:
193
+
194
+ a) The work must carry prominent notices stating that you modified it, and
195
+ giving a relevant date.
196
+
197
+ b) The work must carry prominent notices stating that it is released under this
198
+ License and any conditions added under section 7. This requirement modifies the
199
+ requirement in section 4 to “keep intact all notices”.
200
+
201
+ c) You must license the entire work, as a whole, under this License to anyone
202
+ who comes into possession of a copy. This License will therefore apply, along
203
+ with any applicable section 7 additional terms, to the whole of the work, and
204
+ all its parts, regardless of how they are packaged. This License gives no
205
+ permission to license the work in any other way, but it does not invalidate
206
+ such permission if you have separately received it.
207
+
208
+ d) If the work has interactive user interfaces, each must display Appropriate
209
+ Legal Notices; however, if the Program has interactive interfaces that do not
210
+ display Appropriate Legal Notices, your work need not make them do so.
211
+
212
+ A compilation of a covered work with other separate and independent works, which
213
+ are not by their nature extensions of the covered work, and which are not
214
+ combined with it such as to form a larger program, in or on a volume of a
215
+ storage or distribution medium, is called an “aggregate” if the compilation and
216
+ its resulting copyright are not used to limit the access or legal rights of the
217
+ compilation's users beyond what the individual works permit. Inclusion of a
218
+ covered work in an aggregate does not cause this License to apply to the other
219
+ parts of the aggregate.
220
+
221
+ 6. Conveying Non-Source Forms.
222
+ You may convey a covered work in object code form under the terms of sections 4
223
+ and 5, provided that you also convey the machine-readable Corresponding Source
224
+ under the terms of this License, in one of these ways:
225
+
226
+ a) Convey the object code in, or embodied in, a physical product (including a
227
+ physical distribution medium), accompanied by the Corresponding Source fixed on
228
+ a durable physical medium customarily used for software interchange.
229
+
230
+ b) Convey the object code in, or embodied in, a physical product (including a
231
+ physical distribution medium), accompanied by a written offer, valid for at
232
+ least three years and valid for as long as you offer spare parts or customer
233
+ support for that product model, to give anyone who possesses the object code
234
+ either (1) a copy of the Corresponding Source for all the software in the
235
+ product that is covered by this License, on a durable physical medium
236
+ customarily used for software interchange, for a price no more than your
237
+ reasonable cost of physically performing this conveying of source, or (2) access
238
+ to copy the Corresponding Source from a network server at no charge.
239
+
240
+ c) Convey individual copies of the object code with a copy of the written offer
241
+ to provide the Corresponding Source. This alternative is allowed only
242
+ occasionally and noncommercially, and only if you received the object code with
243
+ such an offer, in accord with subsection 6b.
244
+
245
+ d) Convey the object code by offering access from a designated place (gratis or
246
+ for a charge), and offer equivalent access to the Corresponding Source in the
247
+ same way through the same place at no further charge. You need not require
248
+ recipients to copy the Corresponding Source along with the object code. If the
249
+ place to copy the object code is a network server, the Corresponding Source may
250
+ be on a different server (operated by you or a third party) that supports
251
+ equivalent copying facilities, provided you maintain clear directions next to
252
+ the object code saying where to find the Corresponding Source. Regardless of
253
+ what server hosts the Corresponding Source, you remain obligated to ensure that
254
+ it is available for as long as needed to satisfy these requirements.
255
+
256
+ e) Convey the object code using peer-to-peer transmission, provided you inform
257
+ other peers where the object code and Corresponding Source of the work are being
258
+ offered to the general public at no charge under subsection 6d.
259
+
260
+ A separable portion of the object code, whose source code is excluded from the
261
+ Corresponding Source as a System Library, need not be included in conveying the
262
+ object code work.
263
+
264
+ A “User Product” is either (1) a “consumer product”, which means any tangible
265
+ personal property which is normally used for personal, family, or household
266
+ purposes, or (2) anything designed or sold for incorporation into a dwelling. In
267
+ determining whether a product is a consumer product, doubtful cases shall be
268
+ resolved in favor of coverage. For a particular product received by a particular
269
+ user, “normally used” refers to a typical or common use of that class of
270
+ product, regardless of the status of the particular user or of the way in which
271
+ the particular user actually uses, or expects or is expected to use, the
272
+ product. A product is a consumer product regardless of whether the product
273
+ has substantial commercial, industrial or non-consumer uses, unless such uses
274
+ represent the only significant mode of use of the product.
275
+
276
+ “Installation Information” for a User Product means any methods, procedures,
277
+ authorization keys, or other information required to install and execute
278
+ modified versions of a covered work in that User Product from a modified version
279
+ of its Corresponding Source. The information must suffice to ensure that the
280
+ continued functioning of the modified object code is in no case prevented or
281
+ interfered with solely because modification has been made.
282
+
283
+ If you convey an object code work under this section in, or with, or
284
+ specifically for use in, a User Product, and the conveying occurs as part of a
285
+ transaction in which the right of possession and use of the User Product is
286
+ transferred to the recipient in perpetuity or for a fixed term (regardless of
287
+ how the transaction is characterized), the Corresponding Source conveyed under
288
+ this section must be accompanied by the Installation Information. But this
289
+ requirement does not apply if neither you nor any third party retains the
290
+ ability to install modified object code on the User Product (for example, the
291
+ work has been installed in ROM).
292
+
293
+ The requirement to provide Installation Information does not include a
294
+ requirement to continue to provide support service, warranty, or updates for a
295
+ work that has been modified or installed by the recipient, or for the User
296
+ Product in which it has been modified or installed. Access to a network may be
297
+ denied when the modification itself materially and adversely affects the
298
+ operation of the network or violates the rules and protocols for communication
299
+ across the network.
300
+
301
+ Corresponding Source conveyed, and Installation Information provided, in accord
302
+ with this section must be in a format that is publicly documented (and with an
303
+ implementation available to the public in source code form), and must require no
304
+ special password or key for unpacking, reading or copying.
305
+
306
+ 7. Additional Terms.
307
+ “Additional permissions” are terms that supplement the terms of this License by
308
+ making exceptions from one or more of its conditions. Additional permissions
309
+ that are applicable to the entire Program shall be treated as though they were
310
+ included in this License, to the extent that they are valid under applicable
311
+ law. If additional permissions apply only to part of the Program, that part may
312
+ be used separately under those permissions, but the entire Program remains
313
+ governed by this License without regard to the additional permissions.
314
+
315
+ When you convey a copy of a covered work, you may at your option remove any
316
+ additional permissions from that copy, or from any part of it. (Additional
317
+ permissions may be written to require their own removal in certain cases when
318
+ you modify the work.) You may place additional permissions on material, added
319
+ by you to a covered work, for which you have or can give appropriate copyright
320
+ permission.
321
+
322
+ Notwithstanding any other provision of this License, for material you add to a
323
+ covered work, you may (if authorized by the copyright holders of that material)
324
+ supplement the terms of this License with terms:
325
+
326
+ a) Disclaiming warranty or limiting liability differently from the terms of
327
+ sections 15 and 16 of this License; or
328
+
329
+ b) Requiring preservation of specified reasonable legal notices or author
330
+ attributions in that material or in the Appropriate Legal Notices displayed by
331
+ works containing it; or
332
+
333
+ c) Prohibiting misrepresentation of the origin of that material, or requiring
334
+ that modified versions of such material be marked in reasonable ways as
335
+ different from the original version; or
336
+
337
+ d) Limiting the use for publicity purposes of names of licensors or authors of
338
+ the material; or
339
+
340
+ e) Declining to grant rights under trademark law for use of some trade names,
341
+ trademarks, or service marks; or
342
+
343
+ f) Requiring indemnification of licensors and authors of that material by anyone
344
+ who conveys the material (or modified versions of it) with contractual
345
+ assumptions of liability to the recipient, for any liability that these
346
+ contractual assumptions directly impose on those licensors and authors.
347
+
348
+ All other non-permissive additional terms are considered “further restrictions”
349
+ within the meaning of section 10. If the Program as you received it, or any part
350
+ of it, contains a notice stating that it is governed by this License along with
351
+ a term that is a further restriction, you may remove that term. If a license
352
+ document contains a further restriction but permits relicensing or conveying
353
+ under this License, you may add to a covered work material governed by the terms
354
+ of that license document, provided that the further restriction does not survive
355
+ such relicensing or conveying.
356
+
357
+ If you add terms to a covered work in accord with this section, you must place,
358
+ in the relevant source files, a statement of the additional terms that apply to
359
+ those files, or a notice indicating where to find the applicable terms.
360
+
361
+ Additional terms, permissive or non-permissive, may be stated in the form of a
362
+ separately written license, or stated as exceptions; the above requirements
363
+ apply either way.
364
+
365
+ 8. Termination.
366
+ You may not propagate or modify a covered work except as expressly provided
367
+ under this License. Any attempt otherwise to propagate or modify it is void, and
368
+ will automatically terminate your rights under this License (including any
369
+ patent licenses granted under the third paragraph of section 11).
370
+
371
+ However, if you cease all violation of this License, then your license from a
372
+ particular copyright holder is reinstated (a) provisionally, unless and until
373
+ the copyright holder explicitly and finally terminates your license, and (b)
374
+ permanently, if the copyright holder fails to notify you of the violation by
375
+ some reasonable means prior to 60 days after the cessation.
376
+
377
+ Moreover, your license from a particular copyright holder is reinstated
378
+ permanently if the copyright holder notifies you of the violation by some
379
+ reasonable means, this is the first time you have received notice of violation
380
+ of this License (for any work) from that copyright holder, and you cure the
381
+ violation prior to 30 days after your receipt of the notice.
382
+
383
+ Termination of your rights under this section does not terminate the licenses of
384
+ parties who have received copies or rights from you under this License. If your
385
+ rights have been terminated and not permanently reinstated, you do not qualify
386
+ to receive new licenses for the same material under section 10.
387
+
388
+ 9. Acceptance Not Required for Having Copies.
389
+ You are not required to accept this License in order to receive or run a copy of
390
+ the Program. Ancillary propagation of a covered work occurring solely as a
391
+ consequence of using peer-to-peer transmission to receive a copy likewise does
392
+ not require acceptance. However, nothing other than this License grants you
393
+ permission to propagate or modify any covered work. These actions infringe
394
+ copyright if you do not accept this License. Therefore, by modifying or
395
+ propagating a covered work, you indicate your acceptance of this License to do
396
+ so.
397
+
398
+ 10. Automatic Licensing of Downstream Recipients.
399
+ Each time you convey a covered work, the recipient automatically receives a
400
+ license from the original licensors, to run, modify and propagate that work,
401
+ subject to this License. You are not responsible for enforcing compliance by
402
+ third parties with this License.
403
+
404
+ An “entity transaction” is a transaction transferring control of an
405
+ organization, or substantially all assets of one, or subdividing an
406
+ organization, or merging organizations. If propagation of a covered work results
407
+ from an entity transaction, each party to that transaction who receives a copy
408
+ of the work also receives whatever licenses to the work the party's predecessor
409
+ in interest had or could give under the previous paragraph, plus a right to
410
+ possession of the Corresponding Source of the work from the predecessor in
411
+ interest, if the predecessor has it or can get it with reasonable efforts.
412
+
413
+ You may not impose any further restrictions on the exercise of the rights
414
+ granted or affirmed under this License. For example, you may not impose a
415
+ license fee, royalty, or other charge for exercise of rights granted under this
416
+ License, and you may not initiate litigation (including a cross-claim or
417
+ counterclaim in a lawsuit) alleging that any patent claim is infringed by
418
+ making, using, selling, offering for sale, or importing the Program or any
419
+ portion of it.
420
+
421
+ 11. Patents.
422
+ A “contributor” is a copyright holder who authorizes use under this License of
423
+ the Program or a work on which the Program is based. The work thus licensed is
424
+ called the contributor's “contributor version”.
425
+
426
+ A contributor's “essential patent claims” are all patent claims owned or
427
+ controlled by the contributor, whether already acquired or hereafter acquired,
428
+ that would be infringed by some manner, permitted by this License, of making,
429
+ using, or selling its contributor version, but do not include claims that would
430
+ be infringed only as a consequence of further modification of the contributor
431
+ version. For purposes of this definition, “control” includes the right to grant
432
+ patent sublicenses in a manner consistent with the requirements of this License.
433
+
434
+ Each contributor grants you a non-exclusive, worldwide, royalty-free patent
435
+ license under the contributor's essential patent claims, to make, use, sell,
436
+ offer for sale, import and otherwise run, modify and propagate the contents of
437
+ its contributor version.
438
+
439
+ In the following three paragraphs, a “patent license” is any express agreement
440
+ or commitment, however denominated, not to enforce a patent (such as an express
441
+ permission to practice a patent or covenant not to sue for patent infringement).
442
+ To “grant” such a patent license to a party means to make such an agreement or
443
+ commitment not to enforce a patent against the party.
444
+
445
+ If you convey a covered work, knowingly relying on a patent license, and the
446
+ Corresponding Source of the work is not available for anyone to copy, free of
447
+ charge and under the terms of this License, through a publicly available network
448
+ server or other readily accessible means, then you must either (1) cause the
449
+ Corresponding Source to be so available, or (2) arrange to deprive yourself of
450
+ the benefit of the patent license for this particular work, or (3) arrange, in a
451
+ manner consistent with the requirements of this License, to extend the patent
452
+ license to downstream recipients. “Knowingly relying” means you have actual
453
+ knowledge that, but for the patent license, your conveying the covered work in a
454
+ country, or your recipient's use of the covered work in a country, would
455
+ infringe one or more identifiable patents in that country that you have reason
456
+ to believe are valid.
457
+
458
+ If, pursuant to or in connection with a single transaction or arrangement, you
459
+ convey, or propagate by procuring conveyance of, a covered work, and grant a
460
+ patent license to some of the parties receiving the covered work authorizing
461
+ them to use, propagate, modify or convey a specific copy of the covered work,
462
+ then the patent license you grant is automatically extended to all recipients of
463
+ the covered work and works based on it.
464
+
465
+ A patent license is “discriminatory” if it does not include within the scope of
466
+ its coverage, prohibits the exercise of, or is conditioned on the non-exercise
467
+ of one or more of the rights that are specifically granted under this License.
468
+ You may not convey a covered work if you are a party to an arrangement with a
469
+ third party that is in the business of distributing software, under which you
470
+ make payment to the third party based on the extent of your activity of
471
+ conveying the work, and under which the third party grants, to any of the
472
+ parties who would receive the covered work from you, a discriminatory patent
473
+ license (a) in connection with copies of the covered work conveyed by you (or
474
+ copies made from those copies), or (b) primarily for and in connection with
475
+ specific products or compilations that contain the covered work, unless you
476
+ entered into that arrangement, or that patent license was granted, prior to 28
477
+ March 2007.
478
+
479
+ Nothing in this License shall be construed as excluding or limiting any implied
480
+ license or other defenses to infringement that may otherwise be available to you
481
+ under applicable patent law.
482
+
483
+ 12. No Surrender of Others' Freedom.
484
+ If conditions are imposed on you (whether by court order, agreement or
485
+ otherwise) that contradict the conditions of this License, they do not excuse
486
+ you from the conditions of this License. If you cannot convey a covered work so
487
+ as to satisfy simultaneously your obligations under this License and any other
488
+ pertinent obligations, then as a consequence you may not convey it at all. For
489
+ example, if you agree to terms that obligate you to collect a royalty for
490
+ further conveying from those to whom you convey the Program, the only way you
491
+ could satisfy both those terms and this License would be to refrain entirely
492
+ from conveying the Program.
493
+
494
+ 13. Use with the GNU Affero General Public License.
495
+ Notwithstanding any other provision of this License, you have permission to link
496
+ or combine any covered work with a work licensed under version 3 of the GNU
497
+ Affero General Public License into a single combined work, and to convey the
498
+ resulting work. The terms of this License will continue to apply to the part
499
+ which is the covered work, but the special requirements of the GNU Affero
500
+ General Public License, section 13, concerning interaction through a network
501
+ will apply to the combination as such.
502
+
503
+ 14. Revised Versions of this License.
504
+ The Free Software Foundation may publish revised and/or new versions of the GNU
505
+ General Public License from time to time. Such new versions will be similar in
506
+ spirit to the present version, but may differ in detail to address new problems
507
+ or concerns.
508
+
509
+ Each version is given a distinguishing version number. If the Program specifies
510
+ that a certain numbered version of the GNU General Public License “or any later
511
+ version” applies to it, you have the option of following the terms and
512
+ conditions either of that numbered version or of any later version published by
513
+ the Free Software Foundation. If the Program does not specify a version number
514
+ of the GNU General Public License, you may choose any version ever published by
515
+ the Free Software Foundation.
516
+
517
+ If the Program specifies that a proxy can decide which future versions of the
518
+ GNU General Public License can be used, that proxy's public statement of
519
+ acceptance of a version permanently authorizes you to choose that version for
520
+ the Program.
521
+
522
+ Later license versions may give you additional or different permissions.
523
+ However, no additional obligations are imposed on any author or copyright holder
524
+ as a result of your choosing to follow a later version.
525
+
526
+ 15. Disclaimer of Warranty.
527
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
528
+ EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER
529
+ PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER
530
+ EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
531
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE
532
+ QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE
533
+ DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
534
+
535
+ 16. Limitation of Liability.
536
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY
537
+ COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS
538
+ PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
539
+ INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
540
+ THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
541
+ INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE
542
+ PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY
543
+ HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
544
+
545
+ 17. Interpretation of Sections 15 and 16.
546
+ If the disclaimer of warranty and limitation of liability provided above cannot
547
+ be given local legal effect according to their terms, reviewing courts shall
548
+ apply local law that most closely approximates an absolute waiver of all civil
549
+ liability in connection with the Program, unless a warranty or assumption of
550
+ liability accompanies a copy of the Program in return for a fee.
551
+
552
+ END OF TERMS AND CONDITIONS
load.class.php ADDED
@@ -0,0 +1,256 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The SEO Framework plugin
4
+ * Copyright (C) 2015 - 2016 Sybre Waaijer, CyberWire (https://cyberwire.nl/)
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License version 3 as published
8
+ * by the Free Software Foundation.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ add_action( 'plugins_loaded', 'the_seo_framework_init', 5 );
20
+ /**
21
+ * Load The_SEO_Framework_Load class
22
+ *
23
+ * @action plugins_loaded
24
+ * @priority 5 Use anything above 5, or any action later than plugins_loaded and
25
+ * you can access the class and functions.
26
+ *
27
+ * @staticvar object $the_seo_framework
28
+ *
29
+ * @since 2.2.5
30
+ */
31
+ function the_seo_framework_init() {
32
+ //* Cache the class. Do not run everything more than once.
33
+ static $the_seo_framework = null;
34
+
35
+ if ( the_seo_framework_active() )
36
+ if ( ! isset( $the_seo_framework ) )
37
+ $the_seo_framework = new The_SEO_Framework_Load();
38
+
39
+ return $the_seo_framework;
40
+ }
41
+
42
+ /**
43
+ * Allow this plugin to load through filter
44
+ *
45
+ * Applies the_seo_framework_load filters.
46
+ *
47
+ * @return bool allow loading of plugin
48
+ *
49
+ * @since 2.1.0
50
+ * @staticvar bool $loaded
51
+ *
52
+ * New function name.
53
+ * @since 2.3.7
54
+ *
55
+ * @action plugins_loaded
56
+ */
57
+ function the_seo_framework_load() {
58
+
59
+ static $loaded = null;
60
+
61
+ if ( isset( $loaded ) )
62
+ return $loaded;
63
+
64
+ return $loaded = (bool) apply_filters( 'the_seo_framework_load', true );
65
+ }
66
+
67
+ /**
68
+ * Load plugin files
69
+ * @uses THE_SEO_FRAMEWORK_DIR_PATH_FUNCT
70
+ * @uses THE_SEO_FRAMEWORK_DIR_PATH_CLASS
71
+ *
72
+ * @benchmarked require_once (file inclusion) takes less than 0.0001s.
73
+ *
74
+ * @since 2.1.6
75
+ */
76
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_FUNCT . 'compat.php' );
77
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_FUNCT . 'optionsapi.php' );
78
+ //require_once( THE_SEO_FRAMEWORK_DIR_PATH_FUNCT . 'benchmark.php' );
79
+
80
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'core.class.php' );
81
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'debug.class.php' );
82
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'compat.class.php' );
83
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'query.class.php' );
84
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'init.class.php' );
85
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'admininit.class.php' );
86
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'render.class.php' );
87
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'detect.class.php' );
88
+
89
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'postdata.class.php' );
90
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'termdata.class.php' );
91
+
92
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'generate.class.php' );
93
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'generate-description.class.php' );
94
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'generate-title.class.php' );
95
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'generate-url.class.php' );
96
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'generate-image.class.php' );
97
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'generate-ldjson.class.php' );
98
+
99
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'search.class.php' );
100
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'doingitright.class.php' );
101
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'inpost.class.php' );
102
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'adminpages.class.php' );
103
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'sanitize.class.php' );
104
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'siteoptions.class.php' );
105
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'metaboxes.class.php' );
106
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'sitemaps.class.php' );
107
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'transients.class.php' );
108
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'feed.class.php' );
109
+
110
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH . 'inc/deprecated/deprecated.class.php' );
111
+
112
+ /**
113
+ * Facade class.
114
+ *
115
+ * Extending upon parent classes.
116
+ *
117
+ * @since 2.1.6
118
+ */
119
+ class The_SEO_Framework_Load extends The_SEO_Framework_Deprecated {
120
+
121
+ /**
122
+ * Cached debug/profile constants. Initialized on plugins_loaded priority 5.
123
+ *
124
+ * @since 2.2.9
125
+ *
126
+ * @var bool The SEO Framework Debug/Profile states.
127
+ */
128
+ public $the_seo_framework_debug = false;
129
+ public $the_seo_framework_debug_hidden = false;
130
+ public $the_seo_framework_use_transients = true;
131
+ public $script_debug = false;
132
+
133
+ /**
134
+ * Constructor, setup debug vars and then load parent constructor.
135
+ */
136
+ public function __construct() {
137
+ //* Setup debug vars before initializing parents.
138
+ $this->init_debug_vars();
139
+
140
+ parent::__construct();
141
+ }
142
+
143
+ /**
144
+ * Initializes public debug variables for the class to use.
145
+ *
146
+ * @since 2.6.0
147
+ */
148
+ public function init_debug_vars() {
149
+
150
+ $this->the_seo_framework_debug = defined( 'THE_SEO_FRAMEWORK_DEBUG' ) && THE_SEO_FRAMEWORK_DEBUG ? true : $this->the_seo_framework_debug;
151
+ if ( $this->the_seo_framework_debug ) {
152
+ //* No need to set these to true if no debugging is enabled.
153
+ $this->the_seo_framework_debug_hidden = defined( 'THE_SEO_FRAMEWORK_DEBUG_HIDDEN' ) && THE_SEO_FRAMEWORK_DEBUG_HIDDEN ? true : $this->the_seo_framework_debug_hidden;
154
+ }
155
+
156
+ $this->the_seo_framework_use_transients = defined( 'THE_SEO_FRAMEWORK_DISABLE_TRANSIENTS' ) && THE_SEO_FRAMEWORK_DISABLE_TRANSIENTS ? false : $this->the_seo_framework_use_transients;
157
+
158
+ //* WP Core definition.
159
+ $this->script_debug = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? true : $this->script_debug;
160
+
161
+ }
162
+
163
+ /**
164
+ * Wrapper for function calling through parameters. The golden nugget.
165
+ *
166
+ * @since 2.2.2
167
+ * @NOTE _doing_it_wrong notices go towards the callback. Unless this
168
+ * function is used wrongfully. Then the notice is about this function.
169
+ *
170
+ * @param array|string $callback the method array or function string.
171
+ * @param string $version the version of AutoDescription the function is used.
172
+ * @param array|string $args The arguments passed to the function.
173
+ * @return mixed $output The function called.
174
+ */
175
+ public function call_function( $callback, $version = '', $args = array() ) {
176
+
177
+ $output = '';
178
+
179
+ /**
180
+ * Convert string/object to array
181
+ */
182
+ if ( is_object( $callback ) ) {
183
+ $function = array( $callback, '' );
184
+ } else {
185
+ $function = (array) $callback;
186
+ }
187
+
188
+ /**
189
+ * Convert string/object to array
190
+ */
191
+ if ( is_object( $args ) ) {
192
+ $args = array( $args, '' );
193
+ } else {
194
+ $args = (array) $args;
195
+ }
196
+
197
+ $class = reset( $function );
198
+ $method = next( $function );
199
+
200
+ /**
201
+ * Fetch method/function
202
+ */
203
+ if ( is_object( $class ) && is_string( $method ) ) {
204
+ $class = get_class( $class );
205
+
206
+ if ( $class === get_class( $this ) ) {
207
+ if ( method_exists( $this, $method ) ) {
208
+ if ( empty( $args ) ) {
209
+ // In-Object calling.
210
+ $output = call_user_func( array( $this, $method ) );
211
+ } else {
212
+ // In-Object calling.
213
+ $output = call_user_func_array( array( $this, $method ), $args );
214
+ }
215
+ } else {
216
+ $this->_doing_it_wrong( (string) $class . '::' . (string) $method, __( "Class or Method not found.", 'autodescription' ), $version );
217
+ }
218
+ } else {
219
+ if ( method_exists( $class, $method ) ) {
220
+ if ( empty( $args ) ) {
221
+ $output = call_user_func( array( $class, $method ) );
222
+ } else {
223
+ $output = call_user_func_array( array( $class, $method ), $args );
224
+ }
225
+ } else {
226
+ $this->_doing_it_wrong( (string) $class . '::' . (string) $method, __( "Class or Method not found.", 'autodescription' ), $version );
227
+ }
228
+ }
229
+ } else if ( is_string( $class ) && is_string( $method ) ) {
230
+ //* This could be combined with the one above.
231
+ if ( method_exists( $class, $method ) ) {
232
+ if ( empty( $args ) ) {
233
+ $output = call_user_func( array( $class, $method ) );
234
+ } else {
235
+ $output = call_user_func_array( array( $class, $method ), $args );
236
+ }
237
+ } else {
238
+ $this->_doing_it_wrong( (string) $class . '::' . (string) $method, __( "Class or Method not found.", 'autodescription' ), $version );
239
+ }
240
+ } else if ( is_string( $class ) ) {
241
+ //* Class is function.
242
+ $func = $class;
243
+
244
+ if ( empty( $args ) ) {
245
+ $output = call_user_func( $func );
246
+ } else {
247
+ $output = call_user_func_array( $func, $args );
248
+ }
249
+ } else {
250
+ $this->_doing_it_wrong( __CLASS__ . '::' . __FUNCTION__, __( "Function needs to be called as a string.", 'autodescription' ), $version );
251
+ }
252
+
253
+ return $output;
254
+ }
255
+
256
+ }
patch/2.6.6.patch ADDED
@@ -0,0 +1,3633 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Index: autodescription.php
2
+ ===================================================================
3
+ --- autodescription.php (revision 1433521)
4
+ +++ autodescription.php (working copy)
5
+ @@ -3,7 +3,7 @@
6
+ * Plugin Name: The SEO Framework
7
+ * Plugin URI: https://wordpress.org/plugins/autodescription/
8
+ * Description: An automated, advanced, accessible, unbranded and extremely fast SEO solution for any WordPress website.
9
+ - * Version: 2.6.5.1
10
+ + * Version: 2.6.5.1dev6
11
+ * Author: Sybre Waaijer
12
+ * Author URI: https://cyberwire.nl/
13
+ * License: GPLv3
14
+ @@ -119,5 +119,41 @@
15
+ * Calls functions statically.
16
+ * @since 2.2.9
17
+ */
18
+ -register_activation_hook( THE_SEO_FRAMEWORK_PLUGIN_BASE_FILE, array( 'The_SEO_Framework_Load', 'flush_rewrite_rules_activation' ) );
19
+ -register_deactivation_hook( THE_SEO_FRAMEWORK_PLUGIN_BASE_FILE, array( 'The_SEO_Framework_Load', 'flush_rewrite_rules_deactivation' ) );
20
+ +register_activation_hook( THE_SEO_FRAMEWORK_PLUGIN_BASE_FILE, 'the_seo_framework_flush_rewrite_rules_activation' );
21
+ +register_deactivation_hook( THE_SEO_FRAMEWORK_PLUGIN_BASE_FILE, 'the_seo_framework_flush_rewrite_rules_deactivation' );
22
+ +
23
+ +/**
24
+ + * Add and Flush rewrite rules on plugin activation.
25
+ + *
26
+ + * @global object $wp_rewrite
27
+ + *
28
+ + * @since 2.6.6
29
+ + * @access private
30
+ + */
31
+ +function the_seo_framework_flush_rewrite_rules_activation() {
32
+ + global $wp_rewrite;
33
+ +
34
+ + $the_seo_framework = the_seo_framework();
35
+ + $the_seo_framework->rewrite_rule_sitemap( true );
36
+ +
37
+ + $wp_rewrite->init();
38
+ + $wp_rewrite->flush_rules( true );
39
+ +}
40
+ +
41
+ +/**
42
+ + * Flush rewrite rules on plugin deactivation.
43
+ + *
44
+ + * @global object $wp_rewrite
45
+ + *
46
+ + * @since 2.6.6
47
+ + * @access private
48
+ + */
49
+ +function the_seo_framework_flush_rewrite_rules_deactivation() {
50
+ + global $wp_rewrite;
51
+ +
52
+ + $wp_rewrite->init();
53
+ +
54
+ + unset( $wp_rewrite->extra_rules_top['sitemap\.xml$'] );
55
+ +
56
+ + $wp_rewrite->flush_rules( true );
57
+ +}
58
+ Index: inc/classes/adminpages.class.php
59
+ ===================================================================
60
+ --- inc/classes/adminpages.class.php (revision 1418924)
61
+ +++ inc/classes/adminpages.class.php (working copy)
62
+ @@ -506,11 +506,11 @@
63
+
64
+ if ( isset( $_REQUEST['settings-updated'] ) && 'true' === $_REQUEST['settings-updated'] )
65
+ echo $this->generate_dismissible_notice( $this->page_defaults['saved_notice_text'], 'updated' );
66
+ - else if ( isset( $_REQUEST['reset'] ) && 'true' === $_REQUEST['reset'] )
67
+ + elseif ( isset( $_REQUEST['reset'] ) && 'true' === $_REQUEST['reset'] )
68
+ echo $this->generate_dismissible_notice( $this->page_defaults['reset_notice_text'], 'warning' );
69
+ - else if ( isset( $_REQUEST['error'] ) && 'true' === $_REQUEST['error'] )
70
+ + elseif ( isset( $_REQUEST['error'] ) && 'true' === $_REQUEST['error'] )
71
+ echo $this->generate_dismissible_notice( $this->page_defaults['error_notice_text'], 'error' );
72
+ - else if ( isset( $_REQUEST['seo-updated'] ) && 'true' === $_REQUEST['seo-updated'] )
73
+ + elseif ( isset( $_REQUEST['seo-updated'] ) && 'true' === $_REQUEST['seo-updated'] )
74
+ echo $this->generate_dismissible_notice( $this->page_defaults['plugin_update_text'], 'updated' );
75
+
76
+ }
77
+ Index: inc/classes/compat.class.php
78
+ ===================================================================
79
+ --- inc/classes/compat.class.php (revision 1418924)
80
+ +++ inc/classes/compat.class.php (working copy)
81
+ @@ -64,9 +64,9 @@
82
+ * Removes the Genesis SEO meta boxes on the SEO Settings page
83
+ *
84
+ * @since 2.2.4
85
+ + *
86
+ * @param array $plugins, overwritten as this filter will fire the
87
+ * detection, regardless of other SEO plugins.
88
+ - *
89
+ * @return array Plugins to detect.
90
+ */
91
+ public function no_more_genesis_seo( $plugins ) {
92
+ Index: inc/classes/core.class.php
93
+ ===================================================================
94
+ --- inc/classes/core.class.php (revision 1418924)
95
+ +++ inc/classes/core.class.php (working copy)
96
+ @@ -26,7 +26,7 @@
97
+ class AutoDescription_Core {
98
+
99
+ /**
100
+ - * Constructor, just be there for me when I need you.
101
+ + * Constructor. Loads actions and filters.
102
+ * Latest Class. Doesn't have parent.
103
+ */
104
+ public function __construct() {
105
+ @@ -281,16 +281,13 @@
106
+
107
+ /**
108
+ * Object cache set wrapper.
109
+ - * Applies filters 'the_seo_framework_use_object_cache' : Disable object
110
+ - * caching for this plugin, when applicable.
111
+ *
112
+ + * @since 2.4.3
113
+ + *
114
+ * @param string $key The Object cache key.
115
+ * @param mixed $data The Object cache data.
116
+ * @param int $expire The Object cache expire time.
117
+ * @param string $group The Object cache group.
118
+ - *
119
+ - * @since 2.4.3
120
+ - *
121
+ * @return bool true on set, false when disabled.
122
+ */
123
+ public function object_cache_set( $key, $data, $expire = 0, $group = 'the_seo_framework' ) {
124
+ @@ -303,8 +300,6 @@
125
+
126
+ /**
127
+ * Object cache get wrapper.
128
+ - * Applies filters 'the_seo_framework_use_object_cache' : Disable object
129
+ - * caching for this plugin, when applicable.
130
+ *
131
+ * @param string $key The Object cache key.
132
+ * @param string $group The Object cache group.
133
+ @@ -328,14 +323,10 @@
134
+ * @NOTE only to show improvement with large arrays. Might slow down with small arrays.
135
+ * @NOTE can't do type checks. Always assume the comparing value is a string.
136
+ *
137
+ - * @uses array_flip()
138
+ - * @uses isset()
139
+ - *
140
+ * @since 2.5.2
141
+ *
142
+ * @param string|array $needle The needle(s) to search for
143
+ * @param array $array The single dimensional array to search in.
144
+ - *
145
+ * @return bool true if value is in array.
146
+ */
147
+ public function in_array( $needle, $array ) {
148
+ @@ -358,9 +349,9 @@
149
+ /**
150
+ * Checks if the string input is exactly '1'.
151
+ *
152
+ + * @since 2.6.0
153
+ + *
154
+ * @param string $value The value to check.
155
+ - *
156
+ - * @since 2.6.0
157
+ * @return bool true if value is '1'
158
+ */
159
+ public function is_checked( $value ) {
160
+ @@ -374,9 +365,9 @@
161
+ /**
162
+ * Checks if the option is used and checked.
163
+ *
164
+ + * @since 2.6.0
165
+ + *
166
+ * @param string $option The option name.
167
+ - *
168
+ - * @since 2.6.0
169
+ * @return bool Option is checked.
170
+ */
171
+ public function is_option_checked( $option ) {
172
+ @@ -392,8 +383,8 @@
173
+ /**
174
+ * Checks if blog is public through WordPress core settings.
175
+ *
176
+ + * @since 2.6.0
177
+ * @staticvar bool $cache
178
+ - * @since 2.6.0
179
+ *
180
+ * @return bool True is blog is public.
181
+ */
182
+ @@ -415,7 +406,6 @@
183
+ * Multisite Only.
184
+ *
185
+ * @since 2.6.0
186
+ - *
187
+ * @global object $current_blog. NULL on single site.
188
+ *
189
+ * @return bool Current blog is spam.
190
+ @@ -433,8 +423,8 @@
191
+ * Whether to lowercase the noun or keep it UCfirst.
192
+ * Depending if language is German.
193
+ *
194
+ + * @since 2.6.0
195
+ * @staticvar array $lowercase Contains nouns.
196
+ - * @since 2.6.0
197
+ *
198
+ * @return string The maybe lowercase noun.
199
+ */
200
+ @@ -449,7 +439,7 @@
201
+ }
202
+
203
+ /**
204
+ - * The minimum role required to
205
+ + * Returns the minimum role required to adjust settings.
206
+ *
207
+ * Applies filter 'the_seo_framework_settings_capability' : string
208
+ * This filter changes the minimum role for viewing and editing the plugin's settings.
209
+ @@ -520,7 +510,7 @@
210
+ */
211
+ protected function get_tzstring_from_offset( $offset = 0 ) {
212
+
213
+ - $seconds = round( $offset * 60 * 60 );
214
+ + $seconds = round( $offset * HOUR_IN_SECONDS );
215
+
216
+ //* Try Daylight savings.
217
+ $tzstring = timezone_name_from_abbr( '', $seconds, 1 );
218
+ Index: inc/classes/debug.class.php
219
+ ===================================================================
220
+ --- inc/classes/debug.class.php (revision 1433389)
221
+ +++ inc/classes/debug.class.php (working copy)
222
+ @@ -35,6 +35,15 @@
223
+ protected $debug_output = '';
224
+
225
+ /**
226
+ + * Whether to accumulate data.
227
+ + *
228
+ + * @since 2.6.5
229
+ + *
230
+ + * @var bool Whether to add to AutoDescription_Debug::debug_output.
231
+ + */
232
+ + protected $add_debug_output = true;
233
+ +
234
+ + /**
235
+ * Constructor, load parent constructor and add actions.
236
+ */
237
+ public function __construct() {
238
+ @@ -266,12 +275,33 @@
239
+ if ( $this->the_seo_framework_debug_hidden ) {
240
+ echo "\r\n<!--\r\n:: THE SEO FRAMEWORK DEBUG :: \r\n" . $this->debug_output . "\r\n:: / THE SEO FRAMEWORK DEBUG ::\r\n-->\r\n";
241
+ } else {
242
+ +
243
+ + $id = $this->get_the_real_ID();
244
+ + $mdash = ' &mdash; ';
245
+ + $term = $this->is_archive() ? $this->fetch_the_term( $id ) : '';
246
+ + $taxonomy = isset( $term->taxonomy ) ? $term->taxonomy : '';
247
+ +
248
+ + //* This will return 'Page' on all non-archive types (except the home page)
249
+ + $type = ! $this->is_archive() && $this->is_front_page( $id ) ? 'Front Page' : $this->get_the_term_name( $term );
250
+ + $cache_key = $this->generate_cache_key( $this->get_the_real_ID(), $taxonomy );
251
+ +
252
+ if ( $this->is_admin() ) {
253
+ ?>
254
+ - <div style="clear:both;float:left;position:relative;width:calc( 100% - 200px );min-height:700px;padding:0;margin:20px 20px 40px 180px;overflow:hidden;border:1px solid #ccc;border-radius:3px;">
255
+ - <h3 style="font-size:14px;padding:0 12px;margin:0;line-height:39px;border-bottom: 2px solid #aaa;position:absolute;z-index:1;width:100%;right:0;left:0;top:0;background:#fff;border-radius:3px 3px 0 0;height:39px;">SEO Debug Information</h3>
256
+ + <div style="color:#444;font-family:Georgio,sans-serif;font-size:14px;clear:both;float:left;position:relative;width:calc( 100% - 200px );min-height:700px;padding:0;margin:20px 20px 40px 180px;overflow:hidden;border:1px solid #ccc;border-radius:3px;font:'Open Sans',sans-serif">
257
+ + <h3 style="font-size:14px;padding:0 12px;margin:0;line-height:39px;border-bottom:2px solid #aaa;position:absolute;z-index:1;width:100%;right:0;left:0;top:0;background:#fff;border-radius:3px 3px 0 0;height:39px;">
258
+ + SEO Debug Information
259
+ + <?php
260
+ + if ( $this->is_post_edit() || $this->is_term_edit() ) :
261
+ + echo ' :: ';
262
+ + echo 'Type: ' . $type;
263
+ + echo $mdash . 'ID: ' . $id;
264
+ + echo $mdash . 'Cache key: ' . $cache_key;
265
+ + endif;
266
+ + ?>
267
+ + </h3>
268
+ <div style="position:absolute;bottom:0;right:0;left:0;top:41px;margin:0;padding:0;background:#fff;border-radius:3px;overflow-x:hidden;">
269
+ - <?php echo $this->debug_init_output(); ?>
270
+ + <?php echo $this->debug_header_output(); ?>
271
+ + <?php echo $this->debug_query_output(); ?>
272
+ <?php echo $this->debug_output; ?>
273
+ </div>
274
+ </div>
275
+ @@ -279,12 +309,19 @@
276
+ } else {
277
+ ?>
278
+ <style type="text/css">.wp-ui-notification{color:#fff;background-color:#d54e21}.code.highlight{font-family:Consolas,Monaco,monospace;font-size:14px;}</style>
279
+ - <div style="clear:both;float:left;position:relative;width:calc( 100% - 80px );min-height:700px;padding:0;margin:40px;overflow:hidden;border:1px solid #ccc;border-radius:3px;">
280
+ - <h3 style="font-size:14px;padding:0 12px;margin:0;line-height:39px;border-bottom: 2px solid #aaa;position:absolute;z-index:1;width:100%;right:0;left:0;top:0;background:#fff;border-radius:3px 3px 0 0;height:39px;">
281
+ - SEO Debug Information :: Type: <?php echo $this->get_the_term_name( $this->fetch_the_term( $this->get_the_real_ID() ) ); ?> &mdash; ID: <?php echo $this->get_the_real_ID(); ?> &mdash; Is front: <?php echo $this->is_front_page() ? 'Yes' : 'No'; ?>
282
+ + <div style="color:#444;font-family:Georgio,sans-serif;font-size:14px;clear:both;float:left;position:relative;width:calc( 100% - 80px );min-height:700px;padding:0;margin:40px;overflow:hidden;border:1px solid #ccc;border-radius:3px;font:'Open Sans',sans-serif">
283
+ + <h3 style="font-size:14px;padding:0 12px;margin:0;line-height:39px;border-bottom:2px solid #aaa;position:absolute;z-index:1;width:100%;right:0;left:0;top:0;background:#fff;border-radius:3px 3px 0 0;height:39px;">
284
+ + SEO Debug Information
285
+ + <?php
286
+ + echo ' :: ';
287
+ + echo 'Type: ' . $type;
288
+ + echo $mdash . 'ID: ' . $id;
289
+ + echo $mdash . 'Cache key: ' . $cache_key;
290
+ + ?>
291
+ </h3>
292
+ <div style="position:absolute;bottom:0;right:0;left:0;top:41px;margin:0;padding:0;background:#fff;border-radius:3px;overflow-x:hidden;">
293
+ - <?php echo $this->debug_init_output(); ?>
294
+ + <?php echo $this->debug_header_output(); ?>
295
+ + <?php echo $this->debug_query_output(); ?>
296
+ <?php echo $this->debug_output; ?>
297
+ </div>
298
+ </div>
299
+ @@ -296,12 +333,13 @@
300
+ }
301
+
302
+ /**
303
+ - * Return debug values.
304
+ + * Parses input values and wraps them in human-readable elements.
305
+ *
306
+ - * @param mixed $values What to be output.
307
+ - *
308
+ * @access private
309
+ * @since 2.6.0
310
+ + *
311
+ + * @param mixed $values Values to be parsed.
312
+ + * @return string $output The parsed value.
313
+ */
314
+ public function get_debug_information( $values = null ) {
315
+
316
+ @@ -334,6 +372,9 @@
317
+ }
318
+ }
319
+
320
+ + /**
321
+ + * @TODO Use var_export()?
322
+ + */
323
+ if ( is_array( $values ) ) {
324
+ $output .= $this->the_seo_framework_debug_hidden ? '' : '<div style="margin:0;padding-left:12px">';
325
+ foreach ( $values as $key => $value ) {
326
+ @@ -456,20 +497,18 @@
327
+ *
328
+ * @since 2.6.0
329
+ *
330
+ + * @access private
331
+ + *
332
+ * @param string $class The class name.
333
+ * @param string $method The function name.
334
+ * @param bool $store Whether to store the output in cache for next run to pick up on.
335
+ * @param double $debug_key Use $debug_key as variable, it's reserved.
336
+ - *
337
+ * @param mixed function args.
338
+ - *
339
+ - * @access private
340
+ - *
341
+ - * @return void early if debugging is disabled.
342
+ + * @return void early if debugging is disabled or when storing cache values.
343
+ */
344
+ protected function debug_init( $class, $method, $store, $debug_key ) {
345
+
346
+ - if ( false === $this->the_seo_framework_debug )
347
+ + if ( false === $this->the_seo_framework_debug || false === $this->add_debug_output )
348
+ return;
349
+
350
+ $output = '';
351
+ @@ -673,14 +712,15 @@
352
+ *
353
+ * @since 2.6.0
354
+ *
355
+ + * @param bool $set Whether to reset the timer.
356
+ * @return float PHP Microtime for code execution.
357
+ */
358
+ - protected function timer() {
359
+ + protected function timer( $reset = false ) {
360
+
361
+ static $previous = null;
362
+
363
+ - if ( isset( $previous ) ) {
364
+ - $output = $previous - microtime( true );
365
+ + if ( isset( $previous ) && false === $reset ) {
366
+ + $output = microtime( true ) - $previous;
367
+ $previous = null;
368
+ } else {
369
+ $output = $previous = microtime( true );
370
+ @@ -691,12 +731,13 @@
371
+
372
+ /**
373
+ * Wraps header output in front-end code.
374
+ + * This won't consider hiding the output.
375
+ *
376
+ * @since 2.6.5
377
+ *
378
+ - * @return string Wrapped HTML debug output.
379
+ + * @return string Wrapped SEO meta tags output.
380
+ */
381
+ - protected function debug_init_output() {
382
+ + protected function debug_header_output() {
383
+
384
+ if ( $this->is_admin() && ! $this->is_term_edit() && ! $this->is_post_edit() && ! $this->is_seo_settings_page() )
385
+ return;
386
+ @@ -704,8 +745,12 @@
387
+ if ( $this->is_seo_settings_page() )
388
+ add_filter( 'the_seo_framework_current_object_id', array( $this, 'get_the_front_page_ID' ) );
389
+
390
+ - $init_start = microtime( true );
391
+ + //* Start timer.
392
+ + $this->timer( true );
393
+
394
+ + //* Don't register this output.
395
+ + $this->add_debug_output = false;
396
+ +
397
+ $output = $this->the_description()
398
+ . $this->og_image()
399
+ . $this->og_locale()
400
+ @@ -735,13 +780,13 @@
401
+ . $this->pint_site_output()
402
+ ;
403
+
404
+ - $timer = '<div style="display:inline-block;width:100%;padding:20px;border-bottom:1px solid #ccc;">Generated in: ' . number_format( microtime( true ) - $init_start, 5 ) . ' seconds</div>' ;
405
+ + $timer = '<div style="display:inline-block;width:100%;padding:20px;border-bottom:1px solid #ccc;">Generated in: ' . number_format( $this->timer(), 5 ) . ' seconds</div>' ;
406
+
407
+ $title = $this->is_admin() ? 'Expected SEO Output' : 'Current SEO Output';
408
+ $title = '<div style="display:inline-block;width:100%;padding:20px;margin:0 auto;border-bottom:1px solid #ccc;"><h2 style="color:#ddd;font-size:22px;padding:0;margin:0">' . $title . '</h2></div>';
409
+
410
+ //* Escape it, replace EOL with breaks, and style everything between quotes (which are ending with space).
411
+ - $output = str_replace( PHP_EOL, '<br>', esc_html( $output ) );
412
+ + $output = str_replace( PHP_EOL, '<br>' . PHP_EOL, esc_html( $output ) );
413
+ $output = preg_replace( "/(&quot;.*?&quot;)(\s)/", '<font color="arnoldschwarzenegger">$1</font> ', $output );
414
+
415
+ $output = '<div style="display:inline-block;width:100%;padding:20px;font-family:Consolas,Monaco,monospace;font-size:14px;">' . $output . '</div>';
416
+ @@ -750,4 +795,96 @@
417
+ return $output;
418
+ }
419
+
420
+ + /**
421
+ + * Wraps query status booleans in human-readable code.
422
+ + *
423
+ + * @since 2.6.6
424
+ + *
425
+ + * @return string Wrapped Query State debug output.
426
+ + */
427
+ + protected function debug_query_output() {
428
+ +
429
+ + //* Start timer.
430
+ + $this->timer( true );
431
+ +
432
+ + //* Don't register this output.
433
+ + $this->add_debug_output = false;
434
+ +
435
+ + //* Only get true/false values.
436
+ + $is_404 = $this->is_404();
437
+ + $is_admin = $this->is_admin();
438
+ + $is_attachment = $this->is_attachment();
439
+ + $is_archive = $this->is_archive();
440
+ + $is_term_edit = $this->is_term_edit();
441
+ + $is_post_edit = $this->is_post_edit();
442
+ + $is_wp_lists_edit = $this->is_wp_lists_edit();
443
+ + $is_wp_lists_edit = $this->is_wp_lists_edit();
444
+ + $is_author = $this->is_author();
445
+ + $is_blog_page = $this->is_blog_page();
446
+ + $is_category = $this->is_category();
447
+ + $is_date = $this->is_date();
448
+ + $is_day = $this->is_day();
449
+ + $is_feed = $this->is_feed();
450
+ + $is_front_page = $this->is_front_page();
451
+ + $is_home = $this->is_home();
452
+ + $is_month = $this->is_month();
453
+ + $is_page = $this->is_page();
454
+ + $is_preview = $this->is_preview();
455
+ + $is_search = $this->is_search();
456
+ + $is_single = $this->is_single();
457
+ + $is_singular = $this->is_singular();
458
+ + $is_static_frontpage = $this->is_static_frontpage();
459
+ + $is_tag = $this->is_tag();
460
+ + $is_tax = $this->is_tax();
461
+ + $is_ultimate_member_user_page = $this->is_ultimate_member_user_page();
462
+ + $is_wc_shop = $this->is_wc_shop();
463
+ + $is_wc_product = $this->is_wc_product();
464
+ + $is_year = $this->is_year();
465
+ + $is_seo_settings_page = $this->is_seo_settings_page();
466
+ +
467
+ + //* Get all above vars, split them in two (true and false) and sort them by key names.
468
+ + $vars = get_defined_vars();
469
+ + $current = array_filter( $vars );
470
+ + $not_current = array_diff_key( $vars, $current );
471
+ + ksort( $current );
472
+ + ksort( $not_current );
473
+ +
474
+ + $timer = '<div style="display:inline-block;width:100%;padding:20px;border-bottom:1px solid #666;">Generated in: ' . number_format( $this->timer(), 5 ) . ' seconds</div>' ;
475
+ +
476
+ + $output = '';
477
+ + foreach ( $current as $name => $value ) {
478
+ + $type = '(' . gettype( $value ) . ')';
479
+ + if ( is_bool( $value ) )
480
+ + $value = $value ? 'true' : 'false';
481
+ + else
482
+ + $value = esc_attr( var_export( $value, true ) );
483
+ +
484
+ + $value = $this->the_seo_framework_debug_hidden ? $type . ' ' . $value : '<font color="harrisonford">' . $type . ' ' . $value . '</font>';
485
+ + $out = $name . ' => ' . $value;
486
+ + $output .= $this->the_seo_framework_debug_hidden ? $out . PHP_EOL : '<span style="background:#dadada">' . $out . '</span>' . PHP_EOL;
487
+ + }
488
+ +
489
+ + foreach ( $not_current as $name => $value ) {
490
+ + $type = '(' . gettype( $value ) . ')';
491
+ + if ( is_bool( $value ) )
492
+ + $value = $value ? 'true' : 'false';
493
+ + else
494
+ + $value = esc_attr( var_export( $value, true ) );
495
+ +
496
+ + $value = $this->the_seo_framework_debug_hidden ? $type . ' ' . $value : '<font color="harrisonford">' . $type . ' ' . $value . '</font>';
497
+ + $out = $name . ' => ' . $value;
498
+ + $output .= $out . PHP_EOL;
499
+ + }
500
+ +
501
+ + $title = $this->is_admin() ? 'Current WordPress Screen + Expected WordPress Query' : 'Current WordPress Query';
502
+ + $title = '<div style="display:inline-block;width:100%;padding:20px;margin:0 auto;border-bottom:1px solid #666;"><h2 style="color:#222;font-size:22px;padding:0;margin:0">' . $title . '</h2></div>';
503
+ +
504
+ + $output = $this->the_seo_framework_debug_hidden ? $output : str_replace( PHP_EOL, '<br>' . PHP_EOL, $output );
505
+ +
506
+ + $output = '<div style="display:inline-block;width:100%;padding:20px;font-family:Consolas,Monaco,monospace;font-size:14px;">' . $output . '</div>';
507
+ + $output = '<div style="display:block;width:100%;background:#fafafa;color:#333;border-bottom:1px solid #666">' . $title . $timer . $output . '</div>';
508
+ +
509
+ + return $output;
510
+ + }
511
+ +
512
+ }
513
+ Index: inc/classes/detect.class.php
514
+ ===================================================================
515
+ --- inc/classes/detect.class.php (revision 1433389)
516
+ +++ inc/classes/detect.class.php (working copy)
517
+ @@ -1076,6 +1076,7 @@
518
+ * Detect theme title fix extension plugin.
519
+ *
520
+ * @since 2.6.0
521
+ + * @staticvar bool $fixed
522
+ *
523
+ * @return bool True theme will do it right.
524
+ */
525
+ @@ -1108,10 +1109,10 @@
526
+ }
527
+
528
+ /**
529
+ - * Whether a page or blog is on front.
530
+ + * Determines whether a page or blog is on front.
531
+ *
532
+ + * @since 2.6.0
533
+ * @staticvar bool $pof
534
+ - * @since 2.6.0
535
+ *
536
+ * @return bool
537
+ */
538
+ Index: inc/classes/doingitright.class.php
539
+ ===================================================================
540
+ --- inc/classes/doingitright.class.php (revision 1424405)
541
+ +++ inc/classes/doingitright.class.php (working copy)
542
+ @@ -27,8 +27,7 @@
543
+ class AutoDescription_DoingItRight extends AutoDescription_Search {
544
+
545
+ /**
546
+ - * Constructor, load parent constructor
547
+ - *
548
+ + * Constructor, load parent constructor.
549
+ * Initalizes columns and load post states.
550
+ */
551
+ public function __construct() {
552
+ @@ -47,7 +46,7 @@
553
+ /**
554
+ * Add post state on edit.php to the page or post that has been altered
555
+ *
556
+ - * Applies filters `the_seo_framework_allow_states` : boolean
557
+ + * Applies filters `the_seo_framework_allow_states` : boolean Whether to allow post states output.
558
+ *
559
+ * @uses $this->add_post_state
560
+ *
561
+ @@ -70,10 +69,10 @@
562
+ /**
563
+ * Adds post states in post/page edit.php query
564
+ *
565
+ + * @since 2.1.0
566
+ + *
567
+ * @param array $states The current post states array
568
+ * @param object $post The Post Object.
569
+ - *
570
+ - * @since 2.1.0
571
+ */
572
+ public function add_post_state( $states = array(), $post ) {
573
+
574
+ @@ -110,12 +109,12 @@
575
+ /**
576
+ * Initializes columns
577
+ *
578
+ - * Applies filter the_seo_framework_show_seo_column : Show the SEO column in edit.php
579
+ + * Applies filter the_seo_framework_show_seo_column : Boolean Show the SEO column in edit.php
580
+ *
581
+ + * @since 2.1.9
582
+ + *
583
+ * @param object|empty $screen WP_Screen
584
+ * @param bool $doing_ajax Whether we're doing an AJAX response.
585
+ - *
586
+ - * @since 2.1.9
587
+ */
588
+ public function init_columns( $screen = '', $doing_ajax = false ) {
589
+
590
+ @@ -171,12 +170,12 @@
591
+ /**
592
+ * Adds SEO column on edit(-tags).php
593
+ *
594
+ - * @param array $columns The existing columns
595
+ + * Also determines where the column should be placed. Prefered before comments, then data, then tags.
596
+ + * If neither found, it will add the column to the end.
597
+ *
598
+ - * @param $offset Determines where the column should be placed. Prefered before comments, then data, then tags.
599
+ - * If neither found, it will add the column to the end.
600
+ + * @since 2.1.9
601
+ *
602
+ - * @since 2.1.9
603
+ + * @param array $columns The existing columns
604
+ * @return array $columns the column data
605
+ */
606
+ public function add_column( $columns ) {
607
+ @@ -223,25 +222,20 @@
608
+ }
609
+
610
+ /**
611
+ - * Adds the SEO Bar.
612
+ + * Echo's the SEO Bar.
613
+ *
614
+ + * @since 2.6.0
615
+ + * @staticvar string $type
616
+ + *
617
+ * @param string $column the current column : If it's a taxonomy, this is empty
618
+ * @param int $post_id the post id : If it's a taxonomy, this is the column name
619
+ * @param string $tax_id this is empty : If it's a taxonomy, this is the taxonomy id
620
+ - *
621
+ - * @param string $status the status in html
622
+ - *
623
+ - * @staticvar string $type_cache
624
+ - * @staticvar string $column_cache
625
+ - *
626
+ - * @since 2.6.0
627
+ */
628
+ public function seo_bar( $column, $post_id, $tax_id = '' ) {
629
+
630
+ - static $type_cache = null;
631
+ - static $column_cache = null;
632
+ + static $type = null;
633
+
634
+ - if ( ! isset( $type_cache ) || ! isset( $column_cache ) ) {
635
+ + if ( ! isset( $type ) ) {
636
+ $type = get_post_type( $post_id );
637
+
638
+ if ( false === $type || '' !== $tax_id ) {
639
+ @@ -250,9 +244,6 @@
640
+ if ( isset( $screen->taxonomy ) )
641
+ $type = $screen->taxonomy;
642
+ }
643
+ -
644
+ - $type_cache = $type;
645
+ - $column_cache = $column;
646
+ }
647
+
648
+ /**
649
+ @@ -265,23 +256,18 @@
650
+ }
651
+
652
+ if ( 'ad_seo' === $column )
653
+ - echo $this->post_status( $post_id, $type_cache, true );
654
+ + echo $this->post_status( $post_id, $type, true );
655
+
656
+ }
657
+
658
+ /**
659
+ - * Adds SEO column to edit screens.
660
+ + * Echo's the SEO column in edit screens on Ajax call.
661
+ *
662
+ + * @since 2.1.9
663
+ + *
664
+ * @param string $column the current column : If it's a taxonomy, this is empty
665
+ * @param int $post_id the post id : If it's a taxonomy, this is the column name
666
+ * @param string $tax_id this is empty : If it's a taxonomy, this is the taxonomy id
667
+ - *
668
+ - * @param string $status the status in html
669
+ - *
670
+ - * @staticvar string $type_cache
671
+ - * @staticvar string $column_cache
672
+ - *
673
+ - * @since 2.1.9
674
+ */
675
+ public function seo_bar_ajax( $column, $post_id, $tax_id = '' ) {
676
+
677
+ @@ -294,12 +280,13 @@
678
+ if ( '' !== $tax_id ) {
679
+ $is_term = true;
680
+ $column = $post_id;
681
+ + $post_id = $tax_id;
682
+ }
683
+
684
+ if ( 'ad_seo' === $column ) {
685
+ $context = __( 'Refresh to see the SEO Bar status.', 'autodescription' );
686
+
687
+ - $ajax_id = $column . $tax_id;
688
+ + $ajax_id = $column . $post_id;
689
+
690
+ echo $this->post_status_special( $context, '?', 'unknown', $is_term, $ajax_id );
691
+ }
692
+ @@ -309,13 +296,12 @@
693
+ /**
694
+ * Wrap a single-line block for the SEO bar, showing special statuses.
695
+ *
696
+ + * @since 2.6.0
697
+ + *
698
+ * @param string $context The hover/screenreader context.
699
+ * @param string $symbol The single-character symbol.
700
+ * @param string $class The SEO block color code. : 'bad', 'okay', 'good', 'unknown'.
701
+ * @param int|null $ajax_id The unique Ajax ID to generate a small on-hover script for this ID. May be Arbitrary.
702
+ - *
703
+ - * @since 2.6.0
704
+ - *
705
+ * @return string The special block with wrap.
706
+ */
707
+ protected function post_status_special( $context, $symbol = '?', $color = 'unknown', $is_term = '', $ajax_id = null ) {
708
+ @@ -339,14 +325,13 @@
709
+ /**
710
+ * Renders post status. Caches the output.
711
+ *
712
+ - * @param int $post_id The Post ID or taxonomy ID
713
+ - * @param string $type Is fetched on edit.php, inpost, taxonomies, etc.
714
+ - * @param bool $html return the status in html or string
715
+ - *
716
+ + * @since 2.1.9
717
+ * @staticvar string $post_i18n The post type slug.
718
+ * @staticvar bool $is_term If we're dealing with TT pages.
719
+ *
720
+ - * @since 2.1.9
721
+ + * @param int $post_id The Post ID or taxonomy ID.
722
+ + * @param string $type Is fetched on edit.php, inpost, taxonomies, etc.
723
+ + * @param bool $html return the status in html or string.
724
+ * @return string $content the post SEO status
725
+ */
726
+ public function post_status( $post_id = '', $type = 'inpost', $html = true ) {
727
+ @@ -442,7 +427,6 @@
728
+ * string $width
729
+ * string $class
730
+ * }
731
+ - *
732
+ * @return string The SEO Bar block part.
733
+ */
734
+ protected function wrap_the_seo_bar_block( $args ) {
735
+ @@ -459,16 +443,15 @@
736
+ /**
737
+ * Wrap the SEO bar.
738
+ *
739
+ + * If Ajax ID is set, a small jQuery script will also be output to reset the
740
+ + * DOM element for the status bar hover.
741
+ + *
742
+ + * @since 2.6.0
743
+ * @staticvar string $class
744
+ - * @since 2.6.0
745
+ *
746
+ * @param string $content The SEO Bar content.
747
+ * @param bool $is_term Whether the bar is for a term.
748
+ * @param int|null $ajax_id The unique Ajax ID to generate a small on-hover script for.
749
+ - *
750
+ - * If Ajax ID is set, a small jQuery script will also be output to reset the
751
+ - * DOM element for the status bar hover.
752
+ - *
753
+ * @return string The SEO Bar wrapped.
754
+ */
755
+ protected function get_the_seo_bar_wrap( $content, $is_term, $ajax_id = null ) {
756
+ @@ -507,7 +490,6 @@
757
+ * 'post_low' => string $post_low,
758
+ * 'type' => string $type,
759
+ * }
760
+ - *
761
+ * @return string $content The SEO bar.
762
+ */
763
+ protected function the_seo_bar_term( $args ) {
764
+ @@ -555,7 +537,6 @@
765
+ * 'post_low' => $post_low,
766
+ * 'type' => $type,
767
+ * }
768
+ - *
769
+ * @return string $content The SEO bar.
770
+ */
771
+ protected function the_seo_bar_page( $args ) {
772
+ @@ -592,11 +573,10 @@
773
+ /**
774
+ * Fetch the post or term data for The SEO Bar, structured and cached.
775
+ *
776
+ + * @since 2.6.0
777
+ * @staticvar array $data
778
+ - * @since 2.6.0
779
+ *
780
+ * @param array $args The term/post args.
781
+ - *
782
+ * @return array $data {
783
+ * 'title' => $title,
784
+ * 'title_is_from_custom_field' => $title_is_from_custom_field,
785
+ @@ -625,11 +605,10 @@
786
+ /**
787
+ * Fetch the term data for The SEO Bar.
788
+ *
789
+ + * @since 2.6.0
790
+ * @staticvar array $data
791
+ - * @since 2.6.0
792
+ *
793
+ * @param array $args The term args.
794
+ - *
795
+ * @return array $data {
796
+ * 'title' => $title,
797
+ * 'title_is_from_custom_field' => $title_is_from_custom_field,
798
+ @@ -676,12 +655,12 @@
799
+
800
+ $description_is_from_custom_field = (bool) $description_custom_field;
801
+ if ( $description_is_from_custom_field ) {
802
+ - $taxonomy = isset( $term->taxonomy ) && $term->taxonomy ? $term->taxonomy : false;
803
+ + $taxonomy = isset( $term->taxonomy ) && $term->taxonomy ? $term->taxonomy : '';
804
+ $description_args = $taxonomy ? array( 'id' => $post_id, 'taxonomy' => $term->taxonomy, 'get_custom_field' => true ) : array( 'get_custom_field' => true );
805
+
806
+ $description = $this->generate_description( '', $description_args );
807
+ } else {
808
+ - $taxonomy = isset( $term->taxonomy ) && $term->taxonomy ? $term->taxonomy : false;
809
+ + $taxonomy = isset( $term->taxonomy ) && $term->taxonomy ? $term->taxonomy : '';
810
+ $description_args = $taxonomy ? array( 'id' => $post_id, 'taxonomy' => $term->taxonomy, 'get_custom_field' => false ) : array( 'get_custom_field' => false );
811
+
812
+ $description = $this->generate_description( '', $description_args );
813
+ @@ -703,11 +682,10 @@
814
+ /**
815
+ * Fetch the post data for The SEO Bar.
816
+ *
817
+ + * @since 2.6.0
818
+ * @staticvar array $data
819
+ - * @since 2.6.0
820
+ *
821
+ * @param array $args The post args.
822
+ - *
823
+ * @return array $data {
824
+ * 'title' => $title,
825
+ * 'title_is_from_custom_field' => $title_is_from_custom_field,
826
+ @@ -735,18 +713,16 @@
827
+ }
828
+
829
+ $title_is_from_custom_field = (bool) $title_custom_field;
830
+ - if ( $title_is_from_custom_field ) {
831
+ + if ( $title_is_from_custom_field )
832
+ $title = $this->title( '', '', '', array( 'term_id' => $post_id, 'page_on_front' => $page_on_front, 'get_custom_field' => true ) );
833
+ - } else {
834
+ + else
835
+ $title = $this->title( '', '', '', array( 'term_id' => $post_id, 'page_on_front' => $page_on_front, 'get_custom_field' => false ) );
836
+ - }
837
+
838
+ $description_is_from_custom_field = (bool) $description_custom_field;
839
+ - if ( $description_is_from_custom_field ) {
840
+ + if ( $description_is_from_custom_field )
841
+ $description = $this->generate_description( '', array( 'id' => $post_id, 'get_custom_field' => true ) );
842
+ - } else {
843
+ + else
844
+ $description = $this->generate_description( '', array( 'id' => $post_id, 'get_custom_field' => false ) );
845
+ - }
846
+
847
+ $nofollow = $this->is_checked( $nofollow );
848
+ $noarchive = $this->is_checked( $noarchive );
849
+ @@ -764,9 +740,9 @@
850
+ /**
851
+ * Render the SEO bar title block and notice.
852
+ *
853
+ - * @param array $args
854
+ * @since 2.6.0
855
+ *
856
+ + * @param array $args
857
+ * @return string The SEO Bar Title Block
858
+ */
859
+ protected function the_seo_bar_title_notice( $args ) {
860
+ @@ -846,9 +822,9 @@
861
+ /**
862
+ * Render the SEO bar description block and notice.
863
+ *
864
+ - * @param array $args
865
+ * @since 2.6.0
866
+ *
867
+ + * @param array $args
868
+ * @return string The SEO Bar Description Block
869
+ */
870
+ protected function the_seo_bar_description_notice( $args ) {
871
+ @@ -908,11 +884,10 @@
872
+ /**
873
+ * Description Length notices.
874
+ *
875
+ + * @since 2.6.0
876
+ + *
877
+ * @param int $desc_len The Title length
878
+ * @param string $class The current color class.
879
+ - *
880
+ - * @since 2.6.0
881
+ - *
882
+ * @return array {
883
+ * notice => The notice,
884
+ * class => The class,
885
+ @@ -958,11 +933,10 @@
886
+ * Calculates the word count and returns a warning with the words used.
887
+ * Only when count is over 3.
888
+ *
889
+ + * @since 2.6.0
890
+ + *
891
+ * @param string $description The Description with maybe words too many.
892
+ * @param string $class The current color class.
893
+ - *
894
+ - * @since 2.6.0
895
+ - *
896
+ * @return string The warning notice.
897
+ */
898
+ protected function get_the_seo_bar_description_words_warning( $description, $class ) {
899
+ @@ -1058,9 +1032,9 @@
900
+ /**
901
+ * Render the SEO bar index block and notice.
902
+ *
903
+ - * @param array $args
904
+ * @since 2.6.0
905
+ *
906
+ + * @param array $args
907
+ * @return string The SEO Bar Index Block
908
+ */
909
+ protected function the_seo_bar_index_notice( $args ) {
910
+ @@ -1165,7 +1139,6 @@
911
+ * @staticvar bool $cache
912
+ *
913
+ * @param string $type : 'noindex', 'nofollow', 'noarchive'
914
+ - *
915
+ * @return bool
916
+ */
917
+ protected function the_seo_bar_archive_robots_options( $type ) {
918
+ @@ -1196,9 +1169,9 @@
919
+ /**
920
+ * Render the SEO bar follow block and notice.
921
+ *
922
+ - * @param array $args
923
+ * @since 2.6.0
924
+ *
925
+ + * @param array $args
926
+ * @return string The SEO Bar Follow Block
927
+ */
928
+ protected function the_seo_bar_follow_notice( $args ) {
929
+ @@ -1304,9 +1277,9 @@
930
+ /**
931
+ * Render the SEO bar archive block and notice.
932
+ *
933
+ - * @param array $args
934
+ * @since 2.6.0
935
+ *
936
+ + * @param array $args
937
+ * @return string The SEO Bar Follow Block
938
+ */
939
+ protected function the_seo_bar_archive_notice( $args ) {
940
+ @@ -1412,9 +1385,9 @@
941
+ /**
942
+ * Render the SEO bar redirect block and notice.
943
+ *
944
+ - * @param array $args
945
+ * @since 2.6.0
946
+ *
947
+ + * @param array $args
948
+ * @return string The SEO Bar Redirect Block
949
+ */
950
+ protected function the_seo_bar_redirect_notice( $args ) {
951
+ @@ -1455,6 +1428,8 @@
952
+ /**
953
+ * Render the SEO bar when the page/term is blocked.
954
+ *
955
+ + * @since 2.6.0
956
+ + *
957
+ * @param array $args {
958
+ * $is_term => bool,
959
+ * $redirect => bool,
960
+ @@ -1461,9 +1436,6 @@
961
+ * $noindex => bool,
962
+ * $post_i18n => string
963
+ * }
964
+ - *
965
+ - * @since 2.6.0
966
+ - *
967
+ * @return string The SEO Bar
968
+ */
969
+ protected function the_seo_bar_blocked( $args ) {
970
+ @@ -1549,7 +1521,6 @@
971
+ *
972
+ * @param int $tit_len The Title length
973
+ * @param string $class The Current Title notification class.
974
+ - *
975
+ * @return array {
976
+ * string $notice => The notice,
977
+ * string $class => The class,
978
+ Index: inc/classes/generate-description.class.php
979
+ ===================================================================
980
+ --- inc/classes/generate-description.class.php (revision 1427876)
981
+ +++ inc/classes/generate-description.class.php (working copy)
982
+ @@ -26,7 +26,7 @@
983
+ class AutoDescription_Generate_Description extends AutoDescription_Generate {
984
+
985
+ /**
986
+ - * Whether we're parsing the manual Excerpt for the automated description.
987
+ + * Determines whether we're parsing the manual content Excerpt for the automated description.
988
+ *
989
+ * @since 2.6.0
990
+ *
991
+ @@ -35,7 +35,7 @@
992
+ protected $using_manual_excerpt = false;
993
+
994
+ /**
995
+ - * Constructor, load parent constructor
996
+ + * Constructor, loads parent constructor.
997
+ */
998
+ public function __construct() {
999
+ parent::__construct();
1000
+ @@ -42,9 +42,11 @@
1001
+ }
1002
+
1003
+ /**
1004
+ - * Create description
1005
+ + * Creates description. Base function.
1006
+ *
1007
+ - * @param string $description the description.
1008
+ + * @since 1.0.0
1009
+ + *
1010
+ + * @param string $description The optional description to simply parse.
1011
+ * @param array $args description args : {
1012
+ * @param int $id the term or page id.
1013
+ * @param string $taxonomy taxonomy name.
1014
+ @@ -52,9 +54,6 @@
1015
+ * @param bool $get_custom_field Do not fetch custom title when false.
1016
+ * @param bool $social Generate Social Description when true.
1017
+ * }
1018
+ - *
1019
+ - * @since 1.0.0
1020
+ - *
1021
+ * @return string The description
1022
+ */
1023
+ public function generate_description( $description = '', $args = array() ) {
1024
+ @@ -78,9 +77,16 @@
1025
+ $description = $this->generate_description_from_id( $args, false );
1026
+
1027
+ /**
1028
+ - * Beautify.
1029
+ - * @since 2.3.4
1030
+ + * Applies filters 'the_seo_framework_do_shortcodes_in_description' : Boolean
1031
+ + * @since 2.6.6
1032
+ */
1033
+ + if ( apply_filters( 'the_seo_framework_do_shortcodes_in_description', false ) )
1034
+ + $description = do_shortcode( $description );
1035
+ +
1036
+ + /**
1037
+ + * Sanitize.
1038
+ + * @since 2.3.4 Beautifies too.
1039
+ + */
1040
+ $description = $this->escape_description( $description );
1041
+
1042
+ return $description;
1043
+ @@ -89,10 +95,9 @@
1044
+ /**
1045
+ * Escapes and beautifies description.
1046
+ *
1047
+ - * @param string $description The description to escape and beautify.
1048
+ - *
1049
+ * @since 2.5.2
1050
+ *
1051
+ + * @param string $description The description to escape and beautify.
1052
+ * @return string Escaped and beautified description.
1053
+ */
1054
+ public function escape_description( $description = '' ) {
1055
+ @@ -107,11 +112,9 @@
1056
+ }
1057
+
1058
+ /**
1059
+ - * Parse and sanitize description args.
1060
+ + * Parses and sanitizes description arguments.
1061
+ *
1062
+ - * @param array $args required The passed arguments.
1063
+ - * @param array $defaults The default arguments.
1064
+ - * @param bool $get_defaults Return the default arguments. Ignoring $args.
1065
+ + * @since 2.5.0
1066
+ *
1067
+ * @applies filters the_seo_framework_description_args : {
1068
+ * @param int $id the term or page id.
1069
+ @@ -121,7 +124,9 @@
1070
+ * @param bool $social Generate Social Description when true.
1071
+ * }
1072
+ *
1073
+ - * @since 2.5.0
1074
+ + * @param array $args required The passed arguments.
1075
+ + * @param array $defaults The default arguments.
1076
+ + * @param bool $get_defaults Return the default arguments. Ignoring $args.
1077
+ * @return array $args parsed args.
1078
+ */
1079
+ public function parse_description_args( $args = array(), $defaults = array(), $get_defaults = false ) {
1080
+ @@ -154,7 +159,7 @@
1081
+ }
1082
+
1083
+ /**
1084
+ - * Reparse description args.
1085
+ + * Reparses description args.
1086
+ *
1087
+ * @param array $args required The passed arguments.
1088
+ *
1089
+ @@ -181,8 +186,10 @@
1090
+ }
1091
+
1092
+ /**
1093
+ - * Create description
1094
+ + * Creates description from custom fields.
1095
+ *
1096
+ + * @since 2.4.1
1097
+ + *
1098
+ * @param array $args description args : {
1099
+ * @param int $id the term or page id.
1100
+ * @param string $taxonomy taxonomy name.
1101
+ @@ -189,9 +196,6 @@
1102
+ * @param bool $is_home We're generating for the home page.
1103
+ * }
1104
+ * @param bool $escape Escape the output if true.
1105
+ - *
1106
+ - * @since 2.4.1
1107
+ - *
1108
+ * @return string|mixed The description, might be unsafe for html output.
1109
+ */
1110
+ public function description_from_custom_field( $args = array(), $escape = true ) {
1111
+ @@ -205,14 +209,13 @@
1112
+ //* HomePage Description.
1113
+ $description = $this->get_custom_homepage_description( $args );
1114
+
1115
+ - //* Singular Description.
1116
+ - if ( empty( $description ) && false === $this->is_archive() )
1117
+ - $description = $this->get_custom_singular_description( $args['id'] );
1118
+ + if ( empty( $description ) ) {
1119
+ + if ( $this->is_archive() )
1120
+ + $description = $this->get_custom_archive_description( $args );
1121
+ + else
1122
+ + $description = $this->get_custom_singular_description( $args['id'] );
1123
+ + }
1124
+
1125
+ - //* Archive Description.
1126
+ - if ( empty( $description ) && $this->is_archive() )
1127
+ - $description = $this->get_custom_archive_description( $args );
1128
+ -
1129
+ if ( $escape && $description )
1130
+ $description = $this->escape_description( $description );
1131
+
1132
+ @@ -220,15 +223,13 @@
1133
+ }
1134
+
1135
+ /**
1136
+ - * Fetch HomePage Description from custom field.
1137
+ + * Fetches HomePage Description from custom field.
1138
+ *
1139
+ + * @since 2.6.0
1140
+ * @access protected
1141
+ * Use $this->description_from_custom_field() instead.
1142
+ *
1143
+ * @param array $args Description args.
1144
+ - *
1145
+ - * @since 2.6.0
1146
+ - *
1147
+ * @return string The Description
1148
+ */
1149
+ protected function get_custom_homepage_description( $args ) {
1150
+ @@ -244,15 +245,13 @@
1151
+ }
1152
+
1153
+ /**
1154
+ - * Fetch Singular Description from custom field.
1155
+ + * Fetches Singular Description from custom field.
1156
+ *
1157
+ + * @since 2.6.0
1158
+ * @access protected
1159
+ * Use $this->description_from_custom_field() instead.
1160
+ *
1161
+ * @param int $id The page ID.
1162
+ - *
1163
+ - * @since 2.6.0
1164
+ - *
1165
+ * @return string The Description
1166
+ */
1167
+ protected function get_custom_singular_description( $id ) {
1168
+ @@ -270,13 +269,11 @@
1169
+ /**
1170
+ * Fetch Archive Description from custom field.
1171
+ *
1172
+ + * @since 2.6.0
1173
+ * @access protected
1174
+ * Use $this->description_from_custom_field() instead.
1175
+ *
1176
+ * @param array $args
1177
+ - *
1178
+ - * @since 2.6.0
1179
+ - *
1180
+ * @return string The Description
1181
+ */
1182
+ protected function get_custom_archive_description( $args ) {
1183
+ @@ -313,7 +310,7 @@
1184
+ }
1185
+
1186
+ /**
1187
+ - * Generate description from content.
1188
+ + * Generates description from content while parsing filters.
1189
+ *
1190
+ * @since 2.3.3
1191
+ *
1192
+ @@ -325,7 +322,6 @@
1193
+ * @param bool $social Generate Social Description when true.
1194
+ * }
1195
+ * @param bool $escape Escape output when true.
1196
+ - *
1197
+ * @return string $output The description.
1198
+ */
1199
+ public function generate_description_from_id( $args = array(), $escape = true ) {
1200
+ @@ -352,9 +348,10 @@
1201
+ }
1202
+
1203
+ /**
1204
+ - * Generate description from content
1205
+ + * Generates description from content.
1206
+ *
1207
+ * @since 2.6.0
1208
+ + * @staticvar string $title
1209
+ *
1210
+ * @param array $args description args : {
1211
+ * @param int $id the term or page id.
1212
+ @@ -364,9 +361,6 @@
1213
+ * @param bool $social Generate Social Description when true.
1214
+ * }
1215
+ * @param bool $escape Whether to escape the description.
1216
+ - *
1217
+ - * @staticvar string $title
1218
+ - *
1219
+ * @return string The description.
1220
+ */
1221
+ protected function generate_the_description( $args, $escape = true ) {
1222
+ @@ -400,11 +394,8 @@
1223
+
1224
+ /**
1225
+ * Cache the generated description within a transient.
1226
+ - *
1227
+ * @since 2.3.3
1228
+ - *
1229
+ - * Put inside a different function.
1230
+ - * @since 2.3.4
1231
+ + * @since 2.3.4 Put inside a different function.
1232
+ */
1233
+ $excerpt = $this->get_transient( $this->auto_description_transient );
1234
+ if ( false === $excerpt ) {
1235
+ @@ -434,10 +425,8 @@
1236
+ /**
1237
+ * Transient expiration: 1 week.
1238
+ * Keep the description for at most 1 week.
1239
+ - *
1240
+ - * 60s * 60m * 24h * 7d
1241
+ */
1242
+ - $expiration = 60 * 60 * 24 * 7;
1243
+ + $expiration = WEEK_IN_SECONDS;
1244
+
1245
+ $this->set_transient( $this->auto_description_transient, $excerpt, $expiration );
1246
+ }
1247
+ @@ -487,12 +476,11 @@
1248
+ }
1249
+
1250
+ /**
1251
+ - * Generate the home page description.
1252
+ + * Generates the home page description.
1253
+ *
1254
+ - * @param bool $custom_field whether to check the Custom Field.
1255
+ - *
1256
+ * @since 2.6.0
1257
+ *
1258
+ + * @param bool $custom_field whether to check the Custom Field.
1259
+ * @return string The description.
1260
+ */
1261
+ public function generate_home_page_description( $custom_field = true ) {
1262
+ @@ -520,15 +508,14 @@
1263
+ }
1264
+
1265
+ /**
1266
+ - * Whether to add description additions. (╯°□°)╯︵ ┻━┻
1267
+ + * Determines whether to add description additions. (╯°□°)╯︵ ┻━┻
1268
+ *
1269
+ + * @since 2.6.0
1270
+ * @staticvar bool $cache
1271
+ - * @since 2.6.0
1272
+ *
1273
+ * @param int $id The current page or post ID.
1274
+ * @param object|emptystring $term The current Term.
1275
+ - *
1276
+ - * @return bool
1277
+ + * @return bool Whether to add description additions.
1278
+ */
1279
+ public function add_description_additions( $id = '', $term = '' ) {
1280
+
1281
+ @@ -554,14 +541,12 @@
1282
+ }
1283
+
1284
+ /**
1285
+ - * Get Description Separator.
1286
+ + * Gets Description Separator.
1287
+ *
1288
+ - * Applies filters the_seo_framework_description_separator
1289
+ + * Applies filters 'the_seo_framework_description_separator' : string
1290
+ * @since 2.3.9
1291
+ + * @staticvar string $sep
1292
+ *
1293
+ - * @staticvar $sep
1294
+ - * @since 2.6.0
1295
+ - *
1296
+ * @return string The Separator
1297
+ */
1298
+ public function get_description_separator() {
1299
+ @@ -575,18 +560,16 @@
1300
+ }
1301
+
1302
+ /**
1303
+ - * Generate description additions.
1304
+ + * Generates description additions.
1305
+ *
1306
+ * @since 2.6.0
1307
+ + * @staticvar array $title string of titles.
1308
+ + * @staticvar string $on
1309
+ * @access private
1310
+ *
1311
+ * @param int $id The post or term ID
1312
+ * @param object|empty $term The term object
1313
+ * @param bool $ignore Whether to ignore options and filters.
1314
+ - *
1315
+ - * @staticvar array $title string of titles.
1316
+ - * @staticvar string $on
1317
+ - *
1318
+ * @return array : {
1319
+ * $title => The title
1320
+ * $on => The word separator
1321
+ @@ -638,12 +621,11 @@
1322
+ /**
1323
+ * Generates the Title for description.
1324
+ *
1325
+ + * @since 2.5.2
1326
+ + *
1327
+ * @param int $id The page ID.
1328
+ * @param void|object $term The term object.
1329
+ * @param bool $page_on_front If front page.
1330
+ - *
1331
+ - * @since 2.5.2
1332
+ - *
1333
+ * @return string The description title.
1334
+ */
1335
+ public function generate_description_title( $id = '', $term = '', $page_on_front = false ) {
1336
+ @@ -700,18 +682,16 @@
1337
+ }
1338
+
1339
+ /**
1340
+ - * Generate the excerpt.
1341
+ + * Generates the excerpt.
1342
+ + * @NOTE Supply calculated $max_char_length to reflect actual output.
1343
+ *
1344
+ - * @param int|string $page_id required : The Page ID
1345
+ - * @param object|null $term The Taxonomy Term.
1346
+ - * @param int $max_char_length The maximum excerpt char length.
1347
+ - *
1348
+ * @since 2.3.4
1349
+ - *
1350
+ * @staticvar array $excerpt_cache Holds the excerpt
1351
+ * @staticvar array $excerptlength_cache Holds the excerpt length
1352
+ *
1353
+ - * Please note that this does not reflect the actual output becaue the $max_char_length isn't calculated on direct call.
1354
+ + * @param int|string $page_id required : The Page ID
1355
+ + * @param object|null $term The Taxonomy Term.
1356
+ + * @param int $max_char_length The maximum excerpt char length.
1357
+ */
1358
+ public function generate_excerpt( $page_id, $term = '', $max_char_length = 154 ) {
1359
+
1360
+ @@ -727,9 +707,9 @@
1361
+ $excerpt = $this->get_excerpt_by_id( '', $page_id );
1362
+ } else if ( $term && is_object( $term ) ) {
1363
+ //* We're on a taxonomy now.
1364
+ - $excerpt = empty( $term->description ) ? $this->get_excerpt_by_id( '', '', $page_id ) : $term->description;
1365
+ + $excerpt = empty( $term->description ) ? $this->get_excerpt_by_id( '', '', $page_id ) : $this->s_description( $term->description );
1366
+ } else if ( $this->is_author() ) {
1367
+ - $excerpt = get_the_author_meta( 'description', (int) get_query_var( 'author' ) );
1368
+ + $excerpt = $this->s_description( get_the_author_meta( 'description', (int) get_query_var( 'author' ) ) );
1369
+ } else {
1370
+ $excerpt = '';
1371
+ }
1372
+ @@ -758,17 +738,16 @@
1373
+ }
1374
+
1375
+ /**
1376
+ - * Trim the excerpt.
1377
+ + * Trims the excerpt by word and determines sentence stops.
1378
+ *
1379
+ + * @since 2.6.0
1380
+ + *
1381
+ * @param string $excerpt The untrimmed excerpt.
1382
+ * @param int $excerpt_length The current excerpt length.
1383
+ * @param int $max_char_length At what point to shave off the excerpt.
1384
+ - *
1385
+ - * @since 2.6.0
1386
+ - *
1387
+ * @return string The trimmed excerpt.
1388
+ */
1389
+ - protected function trim_excerpt( $excerpt, $excerpt_length, $max_char_length ) {
1390
+ + public function trim_excerpt( $excerpt, $excerpt_length, $max_char_length ) {
1391
+
1392
+ if ( $excerpt_length > $max_char_length ) {
1393
+
1394
+ @@ -814,7 +793,7 @@
1395
+ }
1396
+ }
1397
+
1398
+ - //* Remove comma's and spaces.
1399
+ + //* Remove trailing/leading comma's and spaces.
1400
+ $excerpt = trim( $excerpt, ' ,' );
1401
+
1402
+ //* Fetch last character.
1403
+ @@ -827,7 +806,7 @@
1404
+
1405
+ }
1406
+
1407
+ - return $excerpt;
1408
+ + return trim( $excerpt );
1409
+ }
1410
+
1411
+ }
1412
+ Index: inc/classes/generate-image.class.php
1413
+ ===================================================================
1414
+ --- inc/classes/generate-image.class.php (revision 1418924)
1415
+ +++ inc/classes/generate-image.class.php (working copy)
1416
+ @@ -26,7 +26,7 @@
1417
+ class AutoDescription_Generate_Image extends AutoDescription_Generate_Url {
1418
+
1419
+ /**
1420
+ - * Constructor, load parent constructor
1421
+ + * Constructor, loads parent constructor.
1422
+ */
1423
+ public function __construct() {
1424
+ parent::__construct();
1425
+ @@ -33,24 +33,18 @@
1426
+ }
1427
+
1428
+ /**
1429
+ - * Fetches og:image
1430
+ + * Fetches og:image URL.
1431
+ *
1432
+ - * @uses get_header_image
1433
+ + * @since 2.5.2 Applies filters string the_seo_framework_og_image_after_featured
1434
+ + * @since 2.5.2 Applies filters string the_seo_framework_og_image_after_header
1435
+ *
1436
+ - * @param string $post_id the post ID
1437
+ - * @param string $image output url for image
1438
+ - * @param bool $escape Whether to escape the image url
1439
+ - *
1440
+ - * @since 2.2.1
1441
+ - *
1442
+ - * Applies filters string the_seo_framework_og_image_after_featured
1443
+ - * Applies filters string the_seo_framework_og_image_after_header
1444
+ - * @since 2.5.2
1445
+ - *
1446
+ - * @todo create options and upload area.
1447
+ - * @priority medium 2.8.0+
1448
+ * @todo listen to attached images within post.
1449
+ * @priority medium 2.7.0+
1450
+ + *
1451
+ + * @param string $post_id The post ID.
1452
+ + * @param array $args The image arguments.
1453
+ + * @param bool $escape Whether to escape the image URL.
1454
+ + * @return string the Open Graph Image URL.
1455
+ */
1456
+ public function get_image( $post_id = '', $args = array(), $escape = true ) {
1457
+
1458
+ @@ -60,29 +54,15 @@
1459
+ if ( empty( $post_id ) )
1460
+ return '';
1461
+
1462
+ - $default_args = $this->parse_image_args( '', '', true );
1463
+ -
1464
+ /**
1465
+ - * Parse args.
1466
+ + * Backwards compat with parse args.
1467
+ * @since 2.5.0
1468
+ */
1469
+ - if ( ! is_array( $args ) ) {
1470
+ - //* Old style parameters are used. Doing it wrong.
1471
+ - $this->_doing_it_wrong( __CLASS__ . '::' . __FUNCTION__, 'Use $args = array() for parameters.', '2.5.0' );
1472
+ - $args = $default_args;
1473
+ - } else if ( $args ) {
1474
+ - $args = $this->parse_image_args( $args, $default_args );
1475
+ - } else {
1476
+ - $args = $default_args;
1477
+ - }
1478
+ -
1479
+ - /**
1480
+ - * Backwards compat with parse args
1481
+ - * @since 2.5.0
1482
+ - */
1483
+ if ( ! isset( $args['post_id'] ) )
1484
+ $args['post_id'] = $post_id;
1485
+
1486
+ + $args = $this->reparse_image_args( $args );
1487
+ +
1488
+ //* 0. Image from argument.
1489
+ $image = $args['image'];
1490
+
1491
+ @@ -122,11 +102,9 @@
1492
+ /**
1493
+ * Parse and sanitize image args.
1494
+ *
1495
+ - * @param array $args required The passed arguments.
1496
+ - * @param array $defaults The default arguments.
1497
+ - * @param bool $get_defaults Return the default arguments. Ignoring $args.
1498
+ + * @since 2.5.0
1499
+ *
1500
+ - * Applies filters the_seo_framework_og_image_args : {
1501
+ + * @since 2.0.1 Applies filters the_seo_framework_og_image_args : {
1502
+ * @param string image The image url
1503
+ * @param mixed size The image size
1504
+ * @param bool icon Fetch Image icon
1505
+ @@ -141,7 +119,9 @@
1506
+ * }
1507
+ * The image set in the filter will always be used as fallback
1508
+ *
1509
+ - * @since 2.5.0
1510
+ + * @param array $args required The passed arguments.
1511
+ + * @param array $defaults The default arguments.
1512
+ + * @param bool $get_defaults Return the default arguments. Ignoring $args.
1513
+ * @return array $args parsed args.
1514
+ */
1515
+ public function parse_image_args( $args = array(), $defaults = array(), $get_defaults = false ) {
1516
+ @@ -157,7 +137,6 @@
1517
+ 'disallowed' => array(),
1518
+ );
1519
+
1520
+ - //* @since 2.0.1
1521
+ $defaults = (array) apply_filters( 'the_seo_framework_og_image_args', $defaults, $args );
1522
+ }
1523
+
1524
+ @@ -177,18 +156,47 @@
1525
+ }
1526
+
1527
+ /**
1528
+ + * Reparses image args.
1529
+ + *
1530
+ + * @since 2.6.6
1531
+ + *
1532
+ + * @param array $args required The passed arguments.
1533
+ + * @return array $args parsed args.
1534
+ + */
1535
+ + public function reparse_image_args( $args = array() ) {
1536
+ +
1537
+ + $default_args = $this->parse_image_args( '', '', true );
1538
+ +
1539
+ + if ( is_array( $args ) ) {
1540
+ + if ( empty( $args ) ) {
1541
+ + $args = $default_args;
1542
+ + } else {
1543
+ + $args = $this->parse_image_args( $args, $default_args );
1544
+ + }
1545
+ + } else {
1546
+ + //* Old style parameters are used. Doing it wrong.
1547
+ + $this->_doing_it_wrong( __CLASS__ . '::' . __FUNCTION__, 'Use $args = array() for parameters.', '2.5.0' );
1548
+ + $args = $default_args;
1549
+ + }
1550
+ +
1551
+ + return $args;
1552
+ + }
1553
+ +
1554
+ + /**
1555
+ * Fetches image from post thumbnail.
1556
+ * Resizes the image between 1500px if bigger. Then it saves the image and
1557
+ * Keeps dimensions relative.
1558
+ *
1559
+ - * @param array $args Image arguments.
1560
+ - *
1561
+ * @since 2.3.0
1562
+ *
1563
+ + * @param array $args Image arguments.
1564
+ * @return string|null the image url.
1565
+ */
1566
+ - public function get_image_from_post_thumbnail( $args ) {
1567
+ + public function get_image_from_post_thumbnail( $args = array() ) {
1568
+
1569
+ + if ( empty( $args ) )
1570
+ + $args = $this->reparse_image_args( $args );
1571
+ +
1572
+ if ( ! isset( $args['post_id'] ) )
1573
+ $args['post_id'] = $this->get_the_real_ID();
1574
+
1575
+ @@ -202,12 +210,10 @@
1576
+ /**
1577
+ * Fetches images id's from WooCommerce gallery
1578
+ *
1579
+ + * @since 2.5.0
1580
+ * @staticvar array $ids The image ids
1581
+ *
1582
+ * @param array $args Image arguments.
1583
+ - *
1584
+ - * @since 2.5.0
1585
+ - *
1586
+ * @return array The image URL's.
1587
+ */
1588
+ public function get_image_from_woocommerce_gallery() {
1589
+ @@ -231,18 +237,16 @@
1590
+ }
1591
+
1592
+ /**
1593
+ - * Parses OG image to correct size
1594
+ + * Parses OG image to correct size.
1595
+ *
1596
+ + * @since 2.5.0
1597
+ * @staticvar string $called Checks if image ID has already been fetched (to prevent duplicate output on WooCommerce).
1598
+ *
1599
+ - * @param int $id The attachment ID.
1600
+ - * @param array $args The image args
1601
+ - *
1602
+ - * @since 2.5.0
1603
+ - *
1604
+ * @todo create formula to fetch transient.
1605
+ * @priority high 2.7.0
1606
+ *
1607
+ + * @param int $id The attachment ID.
1608
+ + * @param array $args The image args
1609
+ * @return string|empty Parsed image url or empty if already called
1610
+ */
1611
+ public function parse_og_image( $id, $args = array() ) {
1612
+ @@ -257,7 +261,7 @@
1613
+ return '';
1614
+
1615
+ if ( empty( $args ) )
1616
+ - $args = $this->parse_image_args( '', '', true );
1617
+ + $args = $this->reparse_image_args( $args );
1618
+
1619
+ $src = wp_get_attachment_image_src( $id, $args['size'], $args['icon'], $args['attr'] );
1620
+
1621
+ @@ -293,21 +297,18 @@
1622
+
1623
+ $i_file_file_name = pathinfo( $i_file_path, PATHINFO_FILENAME );
1624
+
1625
+ - //* Yes I know, I should use generate_filename, but it's slower.
1626
+ + //* Yes I know, I should use generate_filename(), but it's slower.
1627
+ //* Will look at that later. This is already 100 lines of correctly working code.
1628
+ $new_image_dirfile = $i_file_dir_name . $i_file_file_name . '-' . $w . 'x' . $h . '.' . $i_file_ext;
1629
+
1630
+ - /**
1631
+ - * Generate image URL.
1632
+ - */
1633
+ + //* Generate image URL.
1634
+ $upload_dir = wp_upload_dir();
1635
+ $upload_url = $upload_dir['baseurl'];
1636
+ $upload_basedir = $upload_dir['basedir'];
1637
+ - $new_image_url = str_ireplace( $upload_basedir, '', $new_image_dirfile );
1638
+ - $new_image_url = $upload_url . $new_image_url;
1639
+
1640
+ //* We've got our image path.
1641
+ - $i = $new_image_url;
1642
+ + $i = str_ireplace( $upload_basedir, '', $new_image_dirfile );
1643
+ + $i = $upload_url . $i;
1644
+
1645
+ // Generate file if it doesn't exists yet.
1646
+ if ( ! file_exists( $new_image_dirfile ) ) {
1647
+ @@ -332,9 +333,9 @@
1648
+ /**
1649
+ * Fetches site icon brought in WordPress 4.3.0
1650
+ *
1651
+ - * @param string $size The icon size, accepts 'full' and pixel values
1652
+ * @since 2.2.1
1653
+ *
1654
+ + * @param string $size The icon size, accepts 'full' and pixel values
1655
+ * @return string url site icon, not escaped.
1656
+ */
1657
+ public function site_icon( $size = 'full' ) {
1658
+ Index: inc/classes/generate-ldjson.class.php
1659
+ ===================================================================
1660
+ --- inc/classes/generate-ldjson.class.php (revision 1433521)
1661
+ +++ inc/classes/generate-ldjson.class.php (working copy)
1662
+ @@ -47,7 +47,7 @@
1663
+
1664
+ $this->setup_ld_json_transient( $this->get_the_real_ID() );
1665
+
1666
+ - if ( $this->the_seo_framework_debug ) $this->debug_init( __CLASS__, __FUNCTION__, true, $debug_key = microtime(true), array( 'LD Json transient' => $this->ld_json_transient, 'Is output' => (bool) $this->get_transient( $this->ld_json_transient ) ) );
1667
+ + if ( $this->the_seo_framework_debug ) $this->debug_init( __CLASS__, __FUNCTION__, false, $debug_key = microtime(true), array( 'LD Json transient' => $this->ld_json_transient, 'Output from transient' => false !== $this->get_transient( $this->ld_json_transient ) ) );
1668
+
1669
+ $output = $this->get_transient( $this->ld_json_transient );
1670
+ if ( false === $output ) {
1671
+ @@ -80,11 +80,9 @@
1672
+
1673
+ /**
1674
+ * Transient expiration: 1 week.
1675
+ - * Keep the description for at most 1 week.
1676
+ - *
1677
+ - * 60s * 60m * 24h * 7d
1678
+ + * Keep the script for at most 1 week.
1679
+ */
1680
+ - $expiration = 60 * 60 * 24 * 7;
1681
+ + $expiration = WEEK_IN_SECONDS;
1682
+
1683
+ $this->set_transient( $this->ld_json_transient, $output, $expiration );
1684
+ }
1685
+ Index: inc/classes/generate-title.class.php
1686
+ ===================================================================
1687
+ --- inc/classes/generate-title.class.php (revision 1433389)
1688
+ +++ inc/classes/generate-title.class.php (working copy)
1689
+ @@ -527,6 +527,13 @@
1690
+
1691
+ $title = $this->do_title_pro_filter( $title, $args, false );
1692
+
1693
+ + /**
1694
+ + * Applies filters 'the_seo_framework_do_shortcodes_in_title' : Boolean
1695
+ + * @since 2.6.6
1696
+ + */
1697
+ + if ( apply_filters( 'the_seo_framework_do_shortcodes_in_title', false ) )
1698
+ + $title = do_shortcode( $title );
1699
+ +
1700
+ if ( $args['escape'] )
1701
+ $title = $this->escape_title( $title );
1702
+
1703
+ @@ -765,7 +772,7 @@
1704
+ if ( $args['get_custom_field'] && isset( $term ) ) {
1705
+ $title = empty( $term->admeta['doctitle'] ) ? $title : wp_kses_stripslashes( wp_kses_decode_entities( $term->admeta['doctitle'] ) );
1706
+
1707
+ - $flag = $this->is_checked( $term->admeta['saved_flag'] );
1708
+ + $flag = isset( $term->admeta['saved_flag'] ) && $this->is_checked( $term->admeta['saved_flag'] );
1709
+ if ( false === $flag && empty( $title ) && isset( $term->meta['doctitle'] ) )
1710
+ $title = empty( $term->meta['doctitle'] ) ? $title : wp_kses_stripslashes( wp_kses_decode_entities( $term->meta['doctitle'] ) );
1711
+ }
1712
+ Index: inc/classes/generate-url.class.php
1713
+ ===================================================================
1714
+ --- inc/classes/generate-url.class.php (revision 1433389)
1715
+ +++ inc/classes/generate-url.class.php (working copy)
1716
+ @@ -1123,7 +1123,7 @@
1717
+ if ( $output_singular_paged ) {
1718
+
1719
+ $page = $this->page();
1720
+ - $numpages = substr_count( $this->get_post_content( $this->get_the_real_ID() ), '<!--nextpage-->' ) + 1;
1721
+ + $numpages = substr_count( $this->get_post_content( $post_id ), '<!--nextpage-->' ) + 1;
1722
+
1723
+ if ( ! $page )
1724
+ $page = 1;
1725
+ Index: inc/classes/generate.class.php
1726
+ ===================================================================
1727
+ --- inc/classes/generate.class.php (revision 1418924)
1728
+ +++ inc/classes/generate.class.php (working copy)
1729
+ @@ -101,7 +101,7 @@
1730
+ $meta['noarchive'] = empty( $meta['noarchive'] ) && $this->is_option_checked( 'tag_noindex' ) ? 'noarchive' : $meta['noarchive'];
1731
+ }
1732
+
1733
+ - $flag = '0' !== $term->admeta['saved_flag'] ? true : false;
1734
+ + $flag = isset( $term->admeta['saved_flag'] ) && $this->is_checked( $term->admeta['saved_flag'] );
1735
+
1736
+ if ( false === $flag && isset( $term->meta ) ) {
1737
+ //* Genesis support.
1738
+ Index: inc/classes/init.class.php
1739
+ ===================================================================
1740
+ --- inc/classes/init.class.php (revision 1433389)
1741
+ +++ inc/classes/init.class.php (working copy)
1742
+ @@ -35,9 +35,7 @@
1743
+ protected $use_object_cache = true;
1744
+
1745
+ /**
1746
+ - * Constructor. Init actions.
1747
+ - *
1748
+ - * @since 2.1.6
1749
+ + * Constructor. Initializes actions and loads parent constructor.
1750
+ */
1751
+ public function __construct() {
1752
+ parent::__construct();
1753
+ @@ -55,7 +53,7 @@
1754
+ }
1755
+
1756
+ /**
1757
+ - * Run the plugin
1758
+ + * Runs the plugin on the front-end.
1759
+ *
1760
+ * @since 1.0.0
1761
+ */
1762
+ @@ -75,7 +73,7 @@
1763
+ }
1764
+
1765
+ /**
1766
+ - * Initialize front-end actions.
1767
+ + * Initializes front-end actions.
1768
+ *
1769
+ * @since 2.5.2
1770
+ */
1771
+ @@ -99,7 +97,7 @@
1772
+ }
1773
+
1774
+ /**
1775
+ - * Initialize front-end filters.
1776
+ + * Runs front-end filters.
1777
+ *
1778
+ * @since 2.5.2
1779
+ */
1780
+ @@ -118,9 +116,8 @@
1781
+ add_filter( 'woo_title', array( $this, 'title_from_cache'), 99 );
1782
+
1783
+ /**
1784
+ + * Applies filters 'the_seo_framework_manipulate_title' : boolean
1785
+ * Disables the title tag manipulation on old themes.
1786
+ - * Applies filters the_seo_framework_manipulate_title
1787
+ - *
1788
+ * @since 2.4.1
1789
+ */
1790
+ if ( (bool) apply_filters( 'the_seo_framework_manipulate_title', true ) ) {
1791
+ @@ -131,16 +128,14 @@
1792
+ }
1793
+
1794
+ /**
1795
+ - * Header actions.
1796
+ + * Runs header actions.
1797
+ *
1798
+ + * @since 2.2.6
1799
+ * @uses The_SEO_Framework_Load::call_function()
1800
+ *
1801
+ * @param string|array $args the arguments that will be passed
1802
+ * @param bool $before if the header actions should be before or after the SEO Frameworks output
1803
+ - *
1804
+ - * @since 2.2.6
1805
+ - *
1806
+ - * @return string|null
1807
+ + * @return string|empty The filter output.
1808
+ */
1809
+ public function header_actions( $args = '', $before = true ) {
1810
+
1811
+ @@ -172,33 +167,9 @@
1812
+ }
1813
+
1814
+ /**
1815
+ - * Output the header meta and script
1816
+ + * Echos the header meta and scripts.
1817
+ *
1818
+ * @since 1.0.0
1819
+ - *
1820
+ - * @param blog_id : the blog id
1821
+ - *
1822
+ - * Applies filters the_seo_framework_pre : Adds content before
1823
+ - * : @param before
1824
+ - * : cached
1825
+ - * Applies filters the_seo_framework_pro : Adds content after
1826
+ - * : @param after
1827
+ - * : cached
1828
+ - * Applies filters the_seo_framework_generator_tag : String generator tag content
1829
+ - * Applies filters the_seo_framework_indicator : True to show indicator in html
1830
+ - *
1831
+ - * @uses hmpl_ad_description()
1832
+ - * @uses $this->og_image()
1833
+ - * @uses $this->og_locale()
1834
+ - * @uses $this->og_type()
1835
+ - * @uses $this->og_title()
1836
+ - * @uses $this->og_description()
1837
+ - * @uses $this->og_url()
1838
+ - * @uses $this->og_sitename()
1839
+ - * @uses $this->ld_json()
1840
+ - * @uses $this->canonical()
1841
+ - *
1842
+ - * Echos output.
1843
+ */
1844
+ public function html_output() {
1845
+
1846
+ @@ -237,6 +208,11 @@
1847
+
1848
+ $robots = $this->robots();
1849
+
1850
+ + /**
1851
+ + * Applies filters 'the_seo_framework_pre' : string
1852
+ + * Adds content before the output.
1853
+ + * @since 2.6.0
1854
+ + */
1855
+ $before = (string) apply_filters( 'the_seo_framework_pre', '' );
1856
+
1857
+ $before_actions = $this->header_actions( '', true );
1858
+ @@ -287,10 +263,17 @@
1859
+
1860
+ $after_actions = $this->header_actions( '', false );
1861
+
1862
+ + /**
1863
+ + * Applies filters 'the_seo_framework_pro' : string
1864
+ + * Adds content before the output.
1865
+ + * @since 2.6.0
1866
+ + */
1867
+ $after = (string) apply_filters( 'the_seo_framework_pro', '' );
1868
+
1869
+ /**
1870
+ - * @see https://wordpress.org/plugins/generator-the-seo-framework/
1871
+ + * Applies filters 'the_seo_framework_generator_tag' : String generator tag content.
1872
+ + * @since 2.0.1
1873
+ + * @see https://wordpress.org/plugins/generator-the-seo-framework/ For an alternative.
1874
+ */
1875
+ $generator = (string) apply_filters( 'the_seo_framework_generator_tag', '' );
1876
+
1877
+ @@ -302,6 +285,11 @@
1878
+ $this->object_cache_set( $cache_key, $output, 86400 );
1879
+ }
1880
+
1881
+ + /**
1882
+ + * Applies filters 'the_seo_framework_indicator' : Boolean
1883
+ + * Whether to show the indicator in HTML.
1884
+ + * @since 2.0.0
1885
+ + */
1886
+ $indicator = (bool) apply_filters( 'the_seo_framework_indicator', true );
1887
+
1888
+ $indicatorbefore = '';
1889
+ @@ -308,7 +296,19 @@
1890
+ $indicatorafter = '';
1891
+
1892
+ if ( $indicator ) {
1893
+ +
1894
+ + /**
1895
+ + * Applies filters 'the_seo_framework_indicator_timing' : Boolean
1896
+ + * Whether to show the hidden generation time in HTML.
1897
+ + * @since 2.4.0
1898
+ + */
1899
+ $timer = (bool) apply_filters( 'the_seo_framework_indicator_timing', true );
1900
+ +
1901
+ + /**
1902
+ + * Applies filters 'sybre_waaijer_<3' : Boolean
1903
+ + * Whether to show the hidden author name in HTML.
1904
+ + * @since 2.4.0
1905
+ + */
1906
+ $sybre = (bool) apply_filters( 'sybre_waaijer_<3', true );
1907
+
1908
+ $start = __( 'Start The Seo Framework', 'autodescription' );
1909
+ @@ -337,15 +337,16 @@
1910
+ }
1911
+
1912
+ /**
1913
+ - * Redirect singular page to an alternate URL.
1914
+ - * Called outside html_output
1915
+ + * Redirects singular page to an alternate URL.
1916
+ *
1917
+ * @since 2.0.9
1918
+ + *
1919
+ + * @return void early on non-singular pages.
1920
+ */
1921
+ public function custom_field_redirect() {
1922
+
1923
+ //* Prevent redirect from options on uneditable pages.
1924
+ - if ( ! $this->is_singular() )
1925
+ + if ( ! $this->is_singular() || $this->is_admin() )
1926
+ return;
1927
+
1928
+ $url = $this->get_custom_field( 'redirect' );
1929
+ @@ -353,25 +354,15 @@
1930
+ if ( $url ) {
1931
+
1932
+ $allow_external = $this->allow_external_redirect();
1933
+ + $scheme = null;
1934
+
1935
+ - /**
1936
+ - * If the URL is made relative, prevent scheme issues
1937
+ - *
1938
+ - * Removes http:// and https://
1939
+ - *
1940
+ - * esc_url_raw uses is_ssl() to make the url valid again :)
1941
+ - */
1942
+ - if ( true !== $allow_external ) {
1943
+ - $pattern = '/'
1944
+ - . '(((http)(s)?)\:)' // 1: http: https:
1945
+ - . '(\/\/)' // 2: slash slash
1946
+ - . '/s'
1947
+ - ;
1948
+ -
1949
+ - $url = preg_replace( $pattern, '', $url );
1950
+ + if ( false === $allow_external ) {
1951
+ + $url = $this->set_url_scheme( $url, 'relative' );
1952
+ + $url = $this->add_url_host( $url );
1953
+ + $scheme = is_ssl() ? 'https' : 'http';
1954
+ }
1955
+
1956
+ - wp_redirect( esc_url_raw( $url ), 301 );
1957
+ + wp_redirect( esc_url_raw( $url, $scheme ), 301 );
1958
+ exit;
1959
+ }
1960
+
1961
+ Index: inc/classes/inpost.class.php
1962
+ ===================================================================
1963
+ --- inc/classes/inpost.class.php (revision 1433389)
1964
+ +++ inc/classes/inpost.class.php (working copy)
1965
+ @@ -23,7 +23,7 @@
1966
+ *
1967
+ * @since 2.2.2
1968
+ */
1969
+ -class AutoDescription_Inpost extends AutoDescription_PageOptions {
1970
+ +class AutoDescription_Inpost extends AutoDescription_DoingItRight {
1971
+
1972
+ /**
1973
+ * Add inpost SEO Bar through a filter.
1974
+ @@ -630,11 +630,6 @@
1975
+ ?>
1976
+ <a href="https://support.google.com/webmasters/answer/79812?hl=<?php echo $language; ?>" target="_blank" title="<?php printf( __( 'Tell Search Engines not to save a cached copy of this %s', 'autodescription' ), $type ); ?>">[?]</a>
1977
+ </label>
1978
+ -
1979
+ - <?php // Saved flag, if set then it won't fetch for genesis meta anymore ?>
1980
+ - <label class="hidden" for="autodescription_saved_flag">
1981
+ - <input name="autodescription[saved_flag]" id="autodescription[saved_flag]" type="checkbox" value="1" checked='checked' />
1982
+ - </label>
1983
+ </p>
1984
+
1985
+ <p><strong><?php _e( 'Local Search Settings', 'autodescription' ); ?></strong></p>
1986
+ Index: inc/classes/postdata.class.php
1987
+ ===================================================================
1988
+ --- inc/classes/postdata.class.php (revision 1418924)
1989
+ +++ inc/classes/postdata.class.php (working copy)
1990
+ @@ -30,11 +30,118 @@
1991
+ */
1992
+ public function __construct() {
1993
+ parent::__construct();
1994
+ +
1995
+ + add_action( 'save_post', array( $this, 'inpost_seo_save' ), 1, 2 );
1996
+ }
1997
+
1998
+ /**
1999
+ - * Get or parse the excerpt of the post
2000
+ + * Save the SEO settings when we save a post or page.
2001
+ + * Some values get sanitized, the rest are pulled from identically named subkeys in the $_POST['autodescription'] array.
2002
+ *
2003
+ + * @uses $this->save_custom_fields() Perform checks and saves post meta / custom field data to a post or page.
2004
+ + *
2005
+ + * @since 2.0.0
2006
+ + *
2007
+ + * @param integer $post_id Post ID.
2008
+ + * @param stdClass $post Post object.
2009
+ + * @return mixed Returns post id if permissions incorrect, null if doing autosave, ajax or future post, false if update
2010
+ + * or delete failed, and true on success.
2011
+ + */
2012
+ + public function inpost_seo_save( $post_id, $post ) {
2013
+ +
2014
+ + if ( ! isset( $_POST['autodescription'] ) )
2015
+ + return;
2016
+ +
2017
+ + //* Merge user submitted options with fallback defaults
2018
+ + $data = wp_parse_args( $_POST['autodescription'], array(
2019
+ + '_genesis_title' => '',
2020
+ + '_genesis_description' => '',
2021
+ + '_genesis_canonical_uri' => '',
2022
+ + 'redirect' => '',
2023
+ + '_genesis_noindex' => 0,
2024
+ + '_genesis_nofollow' => 0,
2025
+ + '_genesis_noarchive' => 0,
2026
+ + 'exclude_local_search' => 0,
2027
+ + ) );
2028
+ +
2029
+ + foreach ( (array) $data as $key => $value ) {
2030
+ + //* Sanitize the title
2031
+ + if ( '_genesis_title' === $key )
2032
+ + $data[$key] = trim( strip_tags( $value ) );
2033
+ +
2034
+ + //* Sanitize the description
2035
+ + if ( '_genesis_description' === $key )
2036
+ + $data[$key] = $this->s_description( $value );
2037
+ +
2038
+ + //* Sanitize the URL. Make sure it's an absolute URL
2039
+ + if ( 'redirect' === $key )
2040
+ + $data[$key] = $this->s_redirect_url( $value );
2041
+ +
2042
+ + }
2043
+ +
2044
+ + $this->save_custom_fields( $data, 'inpost_seo_save', 'hmpl_ad_inpost_seo_nonce', $post );
2045
+ + }
2046
+ +
2047
+ + /**
2048
+ + * Save post meta / custom field data for a post or page.
2049
+ + *
2050
+ + * It verifies the nonce, then checks we're not doing autosave, ajax or a future post request. It then checks the
2051
+ + * current user's permissions, before finally* either updating the post meta, or deleting the field if the value was not
2052
+ + * truthy.
2053
+ + *
2054
+ + * By passing an array of fields => values from the same metabox (and therefore same nonce) into the $data argument,
2055
+ + * repeated checks against the nonce, request and permissions are avoided.
2056
+ + *
2057
+ + * @since 2.0.0
2058
+ + *
2059
+ + * @thanks StudioPress (http://www.studiopress.com/) for some code.
2060
+ + *
2061
+ + * @param array $data Key/Value pairs of data to save in '_field_name' => 'value' format.
2062
+ + * @param string $nonce_action Nonce action for use with wp_verify_nonce().
2063
+ + * @param string $nonce_name Name of the nonce to check for permissions.
2064
+ + * @param WP_Post|integer $post Post object or ID.
2065
+ + * @return mixed Return null if permissions incorrect, doing autosave, ajax or future post, false if update or delete
2066
+ + * failed, and true on success.
2067
+ + */
2068
+ + public function save_custom_fields( array $data, $nonce_action, $nonce_name, $post ) {
2069
+ +
2070
+ + //* Verify the nonce
2071
+ + if ( ! isset( $_POST[ $nonce_name ] ) || ! wp_verify_nonce( $_POST[ $nonce_name ], $nonce_action ) )
2072
+ + return;
2073
+ +
2074
+ + //* Don't try to save the data under autosave, ajax, or future post.
2075
+ + if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
2076
+ + return;
2077
+ + if ( defined( 'DOING_AJAX' ) && DOING_AJAX )
2078
+ + return;
2079
+ + if ( defined( 'DOING_CRON' ) && DOING_CRON )
2080
+ + return;
2081
+ +
2082
+ + //* Grab the post object
2083
+ + $post = get_post( $post );
2084
+ +
2085
+ + //* Don't save if WP is creating a revision (same as DOING_AUTOSAVE?)
2086
+ + if ( 'revision' === get_post_type( $post ) )
2087
+ + return;
2088
+ +
2089
+ + //* Check that the user is allowed to edit the post
2090
+ + if ( ! current_user_can( 'edit_post', $post->ID ) )
2091
+ + return;
2092
+ +
2093
+ + //* Cycle through $data, insert value or delete field
2094
+ + foreach ( (array) $data as $field => $value ) {
2095
+ + //* Save $value, or delete if the $value is empty
2096
+ + if ( $value )
2097
+ + update_post_meta( $post->ID, $field, $value );
2098
+ + else
2099
+ + delete_post_meta( $post->ID, $field );
2100
+ + }
2101
+ +
2102
+ + }
2103
+ +
2104
+ + /**
2105
+ + * Fetches or parses the excerpt of the post
2106
+ + *
2107
+ * @since 1.0.0
2108
+ *
2109
+ * @param string $excerpt the Excerpt
2110
+ @@ -58,36 +165,27 @@
2111
+ return '';
2112
+
2113
+ $excerpt = wp_strip_all_tags( strip_shortcodes( $excerpt ) );
2114
+ - $excerpt = str_replace( array( "\r\n", "\r", "\n" ), "\n", $excerpt );
2115
+
2116
+ - $lines = explode( "\n", $excerpt );
2117
+ - $new_lines = array();
2118
+ + $output = $this->s_description( $excerpt );
2119
+
2120
+ - //* Remove line breaks
2121
+ - foreach ( $lines as $i => $line ) {
2122
+ - //* Don't add empty lines or paragraphs
2123
+ - if ( $line && '&nbsp;' !== $line )
2124
+ - $new_lines[] = trim( $line ) . ' ';
2125
+ - }
2126
+ -
2127
+ - $output = implode( $new_lines );
2128
+ -
2129
+ - return (string) $output;
2130
+ + return $output;
2131
+ }
2132
+
2133
+ /**
2134
+ - * Generate excerpt.
2135
+ + * Fetches excerpt from post excerpt or fetches the full post content.
2136
+ + * Determines if a page builder is used to return an empty string.
2137
+ + * Does not sanitize output.
2138
+ *
2139
+ * @since 2.5.2
2140
+ + * @since 2.6.6 Detects Page builders.
2141
+ *
2142
+ * @param int $the_id The Post ID.
2143
+ - * @param int $tt_id The Taxonomy Term ID
2144
+ - *
2145
+ + * @param int $tt_id The Taxonomy Term ID.
2146
+ * @return string|empty excerpt.
2147
+ */
2148
+ public function fetch_excerpt( $the_id = '', $tt_id = '' ) {
2149
+
2150
+ - $post = $this->fetch_post_by_id( $the_id, $tt_id );
2151
+ + $post = $this->fetch_post_by_id( $the_id, $tt_id, OBJECT );
2152
+
2153
+ if ( empty( $post ) )
2154
+ return '';
2155
+ @@ -96,10 +194,11 @@
2156
+ * Fetch custom excerpt, if not empty, from the post_excerpt field.
2157
+ * @since 2.5.2
2158
+ */
2159
+ - if ( isset( $post['post_excerpt'] ) && $post['post_excerpt'] ) {
2160
+ - $excerpt = $post['post_excerpt'];
2161
+ - } else if ( isset( $post['post_content'] ) ) {
2162
+ - $excerpt = $post['post_content'];
2163
+ + if ( isset( $post->post_excerpt ) && $post->post_excerpt ) {
2164
+ + $excerpt = $post->post_excerpt;
2165
+ + } else if ( isset( $post->post_content ) ) {
2166
+ + $uses_builder = $this->uses_page_builder( $post->ID );
2167
+ + $excerpt = $uses_builder ? '' : $post->post_content;
2168
+ } else {
2169
+ $excerpt = '';
2170
+ }
2171
+ @@ -112,13 +211,14 @@
2172
+ * Also returns latest post from blog or archive if applicable.
2173
+ *
2174
+ * @since 2.6.0
2175
+ + * @since 2.6.6 Added $output parameter.
2176
+ *
2177
+ * @param int $the_id The Post ID.
2178
+ * @param int $tt_id The Taxonomy Term ID
2179
+ - *
2180
+ + * @param mixed $output The value type to return. Accepts OBJECT, ARRAY_A, or ARRAY_N
2181
+ * @return empty|array The Post Array.
2182
+ */
2183
+ - protected function fetch_post_by_id( $the_id = '', $tt_id = '' ) {
2184
+ + protected function fetch_post_by_id( $the_id = '', $tt_id = '', $output = ARRAY_A ) {
2185
+
2186
+ if ( '' === $the_id && '' === $tt_id ) {
2187
+ $the_id = $this->get_the_real_ID();
2188
+ @@ -128,11 +228,8 @@
2189
+ }
2190
+
2191
+ /**
2192
+ - * Use the 2nd parameter.
2193
+ - * @since 2.2.8
2194
+ - *
2195
+ - * Now casts to array
2196
+ - * @since 2.3.3
2197
+ + * @since 2.2.8 Use the 2nd parameter.
2198
+ + * @since 2.3.3 Now casts to array
2199
+ */
2200
+ if ( '' !== $the_id ) {
2201
+ if ( $this->is_blog_page( $the_id ) ) {
2202
+ @@ -150,12 +247,11 @@
2203
+
2204
+ $post = get_posts( $args );
2205
+ } else {
2206
+ - $post = get_post( $the_id, ARRAY_A );
2207
+ + $post = get_post( $the_id );
2208
+ }
2209
+ } else if ( '' !== $tt_id ) {
2210
+ /**
2211
+ - * Match the descriptions in admin as on the front end.
2212
+ - * @since 2.3.3
2213
+ + * @since 2.3.3 Match the descriptions in admin as on the front end.
2214
+ */
2215
+ $args = array(
2216
+ 'posts_per_page' => 1,
2217
+ @@ -169,26 +265,34 @@
2218
+
2219
+ $post = get_posts( $args );
2220
+ } else {
2221
+ - $post = get_post( $the_id, ARRAY_A );
2222
+ + $post = get_post( $the_id );
2223
+ }
2224
+
2225
+ /**
2226
+ - * Cast last found post object to array and put it in $post.
2227
+ - * @since 2.3.3
2228
+ + * @since 2.6.5 Transform post array to object (on Archives).
2229
+ */
2230
+ - if ( isset( $post[0] ) && is_object( $post[0] ) ) {
2231
+ - $object = $post[0];
2232
+ - $post = (array) $object;
2233
+ - }
2234
+ + if ( is_array( $post ) && isset( $post[0] ) && is_object( $post[0] ) )
2235
+ + $post = $post[0];
2236
+
2237
+ - // Something went wrong, nothing to be found. Return empty.
2238
+ - if ( empty( $post ) || ! is_array( $post ) )
2239
+ + //* Something went wrong, nothing to be found. Return empty.
2240
+ + if ( empty( $post ) )
2241
+ return '';
2242
+
2243
+ //* Stop getting something that doesn't exists. E.g. 404
2244
+ - if ( isset( $post['ID'] ) && 0 === $post['ID'] )
2245
+ + if ( isset( $post->ID ) && 0 === $post->ID )
2246
+ return '';
2247
+
2248
+ + /**
2249
+ + * @since 2.6.6
2250
+ + */
2251
+ + if ( ARRAY_A === $output || ARRAY_N === $output ) {
2252
+ + $_post = WP_Post::get_instance( $post );
2253
+ + $post = $_post->to_array();
2254
+ +
2255
+ + if ( ARRAY_N === $output )
2256
+ + $post = array_values( $post );
2257
+ + }
2258
+ +
2259
+ return $post;
2260
+ }
2261
+
2262
+ @@ -195,11 +299,12 @@
2263
+ /**
2264
+ * Fetch latest public post ID.
2265
+ *
2266
+ + * @since 2.4.3
2267
+ * @staticvar int $page_id
2268
+ * @global object $wpdb
2269
+ * @global int $blog_id
2270
+ *
2271
+ - * @since 2.4.3
2272
+ + * @return int Latest Post ID.
2273
+ */
2274
+ public function get_latest_post_id() {
2275
+ global $wpdb, $blog_id;
2276
+ @@ -235,7 +340,7 @@
2277
+ '', 1 );
2278
+
2279
+ $page_id = (int) $wpdb->get_var( $sql );
2280
+ - $this->object_cache_set( $latest_posts_key, $page_id, 86400 );
2281
+ + $this->object_cache_set( $latest_posts_key, $page_id, DAY_IN_SECONDS );
2282
+ }
2283
+
2284
+ return $page_id;
2285
+ @@ -246,25 +351,71 @@
2286
+ *
2287
+ * @since 2.6.0
2288
+ *
2289
+ - * @param int $id.
2290
+ - *
2291
+ + * @param int $id The post ID.
2292
+ * @return string The post content.
2293
+ */
2294
+ public function get_post_content( $id = 0 ) {
2295
+
2296
+ - if ( empty( $id ) ) {
2297
+ - global $wp_query;
2298
+ + $id = $id ? $id : $this->get_the_real_ID();
2299
+
2300
+ - if ( isset( $wp_query->post->post_content ) )
2301
+ - return $wp_query->post->post_content;
2302
+ - } else {
2303
+ - $content = get_post_field( 'post_content', $id );
2304
+ + $content = get_post_field( 'post_content', $id );
2305
+
2306
+ - if ( is_string( $content ) )
2307
+ - return $content;
2308
+ - }
2309
+ + if ( is_string( $content ) )
2310
+ + return $content;
2311
+
2312
+ return '';
2313
+ }
2314
+
2315
+ + /**
2316
+ + * Determines whether the post has a page builder attached to it.
2317
+ + * Doesn't use plugin detection features as some builders might be incorporated within themes.
2318
+ + *
2319
+ + * Detects the following builders:
2320
+ + * - Divi Builder by Elegant Themes
2321
+ + * - Visual Composer by WPBakery
2322
+ + * - Page Builder by SiteOrigin
2323
+ + * - Beaver Builder by Fastline Media
2324
+ + *
2325
+ + * @since 2.6.6
2326
+ + *
2327
+ + * @param int $post_id
2328
+ + * @return boolean
2329
+ + */
2330
+ + public function uses_page_builder( $post_id ) {
2331
+ +
2332
+ + $meta = get_post_meta( $post_id );
2333
+ +
2334
+ + /**
2335
+ + * Applies filters 'the_seo_framework_detect_page_builder' : boolean
2336
+ + * Determines whether a page builder has been detected.
2337
+ + * @since 2.6.6
2338
+ + *
2339
+ + * @param boolean The current state.
2340
+ + * @param int $post_id The current Post ID.
2341
+ + * @param array $meta The current post meta.
2342
+ + */
2343
+ + $detected = (bool) apply_filters( 'the_seo_framework_detect_page_builder', false, $post_id, $meta );
2344
+ +
2345
+ + if ( $detected )
2346
+ + return true;
2347
+ +
2348
+ + if ( empty( $meta ) )
2349
+ + return false;
2350
+ +
2351
+ + if ( isset( $meta['_et_pb_use_builder'][0] ) && 'on' === $meta['_et_pb_use_builder'][0] && defined( 'ET_BUILDER_VERSION' ) )
2352
+ + //* Divi Builder by Elegant Themes
2353
+ + return true;
2354
+ + elseif ( isset( $meta['_wpb_vc_js_status'][0] ) && 'true' === $meta['_wpb_vc_js_status'][0] && defined( 'WPB_VC_VERSION' ) )
2355
+ + //* Visual Composer by WPBakery
2356
+ + return true;
2357
+ + elseif ( isset( $meta['panels_data'][0] ) && '' !== $meta['panels_data'][0] && defined( 'SITEORIGIN_PANELS_VERSION' ) )
2358
+ + //* Page Builder by SiteOrigin
2359
+ + return true;
2360
+ + elseif ( isset( $meta['_fl_builder_enabled'][0] ) && '1' === $meta['_fl_builder_enabled'][0] && defined( 'FL_BUILDER_VERSION' ) )
2361
+ + //* Beaver Builder by Fastline Media
2362
+ + return true;
2363
+ +
2364
+ + return false;
2365
+ + }
2366
+ +
2367
+ }
2368
+ Index: inc/classes/query.class.php
2369
+ ===================================================================
2370
+ --- inc/classes/query.class.php (revision 1433389)
2371
+ +++ inc/classes/query.class.php (working copy)
2372
+ @@ -96,6 +96,10 @@
2373
+ //* Never get this when this is an archive. It will always return the wrong value.
2374
+ if ( empty( $id ) && false === is_archive() && false === is_home() )
2375
+ $id = get_the_ID();
2376
+ +
2377
+ + //* Determine the Archive ID on term edit.
2378
+ + if ( empty( $id ) && $is_admin && $this->is_archive_admin() )
2379
+ + $id = $this->get_admin_term_id();
2380
+ }
2381
+
2382
+ /**
2383
+ @@ -151,10 +155,37 @@
2384
+ }
2385
+
2386
+ /**
2387
+ + * Fetches the Term ID on admin pages.
2388
+ + *
2389
+ + * @since 2.6.0
2390
+ + * @since 2.6.6 Moved from class AutoDescription_TermData.
2391
+ + * @staticvar int $term_id The Term ID.
2392
+ + *
2393
+ + * @return int Term ID.
2394
+ + */
2395
+ + public function get_admin_term_id() {
2396
+ +
2397
+ + static $term_id = null;
2398
+ +
2399
+ + if ( isset( $term_id ) )
2400
+ + return $term_id;
2401
+ +
2402
+ + if ( isset( $_REQUEST['tag_ID'] ) && $_REQUEST['tag_ID'] ) {
2403
+ + //* WordPress 4.5+
2404
+ + $term_id = $_REQUEST['tag_ID'];
2405
+ + } else if ( isset( $_REQUEST['term_id'] ) && $_REQUEST['term_id'] ) {
2406
+ + //* Older WordPress versions.
2407
+ + $term_id = $_REQUEST['term_id'];
2408
+ + }
2409
+ +
2410
+ + return $term_id = $term_id ? absint( $term_id ) : 0;
2411
+ + }
2412
+ +
2413
+ + /**
2414
+ * Detects 404.
2415
+ *
2416
+ + * @since 2.6.0
2417
+ * @staticvar bool $cache
2418
+ - * @since 2.6.0
2419
+ *
2420
+ * @return bool
2421
+ */
2422
+ @@ -172,10 +203,10 @@
2423
+ }
2424
+
2425
+ /**
2426
+ - * Detects admin.
2427
+ + * Detects admin screen.
2428
+ *
2429
+ + * @since 2.6.0
2430
+ * @staticvar bool $cache
2431
+ - * @since 2.6.0
2432
+ *
2433
+ * @return bool
2434
+ */
2435
+ @@ -195,8 +226,8 @@
2436
+ /**
2437
+ * Detects attachment page.
2438
+ *
2439
+ + * @since 2.6.0
2440
+ * @staticvar bool $cache
2441
+ - * @since 2.6.0
2442
+ * @uses $this->is_singular()
2443
+ *
2444
+ * @param int|string|array|object $attachment Attachment ID, title, slug, or array of such.
2445
+ @@ -219,9 +250,11 @@
2446
+ /**
2447
+ * Detects archive pages. Also in admin.
2448
+ *
2449
+ + * @since 2.6.0
2450
+ * @staticvar bool $cache
2451
+ - * @since 2.6.0
2452
+ *
2453
+ + * @global object $wp_query
2454
+ + *
2455
+ * @return bool
2456
+ */
2457
+ public function is_archive() {
2458
+ @@ -234,25 +267,24 @@
2459
+ if ( isset( $cache ) && $this->can_cache_query() )
2460
+ return $cache;
2461
+
2462
+ - global $wp_query;
2463
+ -
2464
+ if ( is_archive() && false === $this->is_singular() )
2465
+ return $cache = true;
2466
+
2467
+ - global $wp_query;
2468
+ + if ( $this->can_cache_query() && false === $this->is_singular() ) {
2469
+ + global $wp_query;
2470
+
2471
+ - if ( $this->can_cache_query() && false === $this->is_singular() )
2472
+ if ( $wp_query->is_post_type_archive || $wp_query->is_date || $wp_query->is_author || $wp_query->is_category || $wp_query->is_tag || $wp_query->is_tax )
2473
+ return $cache = true;
2474
+ + }
2475
+
2476
+ return $cache = false;
2477
+ }
2478
+
2479
+ /**
2480
+ - * Extends default WordPress is_archive and made available in admin.
2481
+ + * Extends default WordPress is_archive() and determines screen in admin.
2482
+ *
2483
+ + * @since 2.6.0
2484
+ * @staticvar bool $cache
2485
+ - * @since 2.6.0
2486
+ *
2487
+ * @global object $current_screen
2488
+ *
2489
+ @@ -276,9 +308,8 @@
2490
+ /**
2491
+ * Detects Term edit screen in WP Admin.
2492
+ *
2493
+ + * @since 2.6.0
2494
+ * @staticvar bool $cache
2495
+ - * @since 2.6.0
2496
+ - *
2497
+ * @global object $current_screen
2498
+ *
2499
+ * @return bool We're on Term Edit screen.
2500
+ @@ -306,9 +337,8 @@
2501
+ /**
2502
+ * Detects Post edit screen in WP Admin.
2503
+ *
2504
+ + * @since 2.6.0
2505
+ * @staticvar bool $cache
2506
+ - * @since 2.6.0
2507
+ - *
2508
+ * @global object $current_screen
2509
+ *
2510
+ * @return bool We're on Post Edit screen.
2511
+ @@ -331,9 +361,8 @@
2512
+ /**
2513
+ * Detects Post or Archive Lists in Admin.
2514
+ *
2515
+ + * @since 2.6.0
2516
+ * @staticvar bool $cache
2517
+ - * @since 2.6.0
2518
+ - *
2519
+ * @global object $current_screen
2520
+ *
2521
+ * @return bool We're on the edit screen.
2522
+ @@ -357,8 +386,8 @@
2523
+ /**
2524
+ * Detects author archives.
2525
+ *
2526
+ + * @since 2.6.0
2527
+ * @staticvar bool $cache
2528
+ - * @since 2.6.0
2529
+ * @uses $this->is_archive()
2530
+ *
2531
+ * @param mixed $author Optional. User ID, nickname, nicename, or array of User IDs, nicknames, and nicenames
2532
+ @@ -381,12 +410,10 @@
2533
+ /**
2534
+ * Detect the separated blog page.
2535
+ *
2536
+ - * @param int $id the Page ID.
2537
+ - *
2538
+ * @since 2.3.4
2539
+ - *
2540
+ * @staticvar bool $is_blog_page
2541
+ *
2542
+ + * @param int $id the Page ID.
2543
+ * @return bool true if is blog page. Always false if blog page is homepage.
2544
+ */
2545
+ public function is_blog_page( $id = '' ) {
2546
+ @@ -414,12 +441,11 @@
2547
+ /**
2548
+ * Detects category archives.
2549
+ *
2550
+ + * @since 2.6.0
2551
+ * @staticvar bool $cache
2552
+ - * @since 2.6.0
2553
+ * @uses $this->is_archive()
2554
+ *
2555
+ * @param mixed $category Optional. Category ID, name, slug, or array of Category IDs, names, and slugs.
2556
+ - *
2557
+ * @return bool
2558
+ */
2559
+ public function is_category( $category = '' ) {
2560
+ @@ -439,11 +465,10 @@
2561
+ }
2562
+
2563
+ /**
2564
+ - * Extends default WordPress is_category and made available in admin.
2565
+ + * Extends default WordPress is_category() and determines screen in admin.
2566
+ *
2567
+ + * @since 2.6.0
2568
+ * @staticvar bool $cache
2569
+ - * @since 2.6.0
2570
+ - *
2571
+ * @global object $current_screen
2572
+ *
2573
+ * @return bool Post Type is category
2574
+ @@ -458,6 +483,7 @@
2575
+ global $current_screen;
2576
+
2577
+ if ( $this->is_archive_admin() && isset( $current_screen->taxonomy ) ) {
2578
+ +
2579
+ $tax = $current_screen->taxonomy;
2580
+ $len = strlen( $tax );
2581
+
2582
+ @@ -474,8 +500,8 @@
2583
+ /**
2584
+ * Detects date archives.
2585
+ *
2586
+ + * @since 2.6.0
2587
+ * @staticvar bool $cache
2588
+ - * @since 2.6.0
2589
+ * @uses $this->is_archive()
2590
+ *
2591
+ * @return bool
2592
+ @@ -518,11 +544,10 @@
2593
+ /**
2594
+ * Detects feed.
2595
+ *
2596
+ + * @since 2.6.0
2597
+ * @staticvar bool $cache
2598
+ - * @since 2.6.0
2599
+ *
2600
+ * @param string|array $feeds Optional feed types to check.
2601
+ - *
2602
+ * @return bool
2603
+ */
2604
+ public function is_feed( $feeds = '' ) {
2605
+ @@ -529,7 +554,7 @@
2606
+
2607
+ static $cache = array();
2608
+
2609
+ - if ( isset( $cache[$feeds] ) && $this->can_cache_query() )
2610
+ + if ( is_string( $feeds ) && isset( $cache[$feeds] ) && $this->can_cache_query() )
2611
+ return $cache[$feeds];
2612
+
2613
+ if ( is_feed( $feeds ) )
2614
+ @@ -541,11 +566,10 @@
2615
+ /**
2616
+ * Detects front page.
2617
+ *
2618
+ + * @since 2.6.0
2619
+ * @staticvar bool $cache
2620
+ - * @since 2.6.0
2621
+ *
2622
+ * @param int $id The page or Post ID.
2623
+ - *
2624
+ * @return bool
2625
+ */
2626
+ public function is_front_page( $id = 0 ) {
2627
+ @@ -582,8 +606,8 @@
2628
+ /**
2629
+ * Detects home page.
2630
+ *
2631
+ + * @since 2.6.0
2632
+ * @staticvar bool $cache
2633
+ - * @since 2.6.0
2634
+ *
2635
+ * @return bool
2636
+ */
2637
+ @@ -603,8 +627,8 @@
2638
+ /**
2639
+ * Detects month archives.
2640
+ *
2641
+ + * @since 2.6.0
2642
+ * @staticvar bool $cache
2643
+ - * @since 2.6.0
2644
+ * @uses $this->is_date()
2645
+ *
2646
+ * @return bool
2647
+ @@ -625,12 +649,11 @@
2648
+ /**
2649
+ * Detects pages.
2650
+ *
2651
+ + * @since 2.6.0
2652
+ * @staticvar bool $cache
2653
+ - * @since 2.6.0
2654
+ * @uses $this->is_singular()
2655
+ *
2656
+ * @param int|string|array $page Optional. Page ID, title, slug, or array of such. Default empty.
2657
+ - *
2658
+ * @return bool
2659
+ */
2660
+ public function is_page( $page = '' ) {
2661
+ @@ -656,11 +679,9 @@
2662
+ *
2663
+ * @since 2.6.0
2664
+ * @see $this->is_page()
2665
+ - *
2666
+ * @global object $current_screen;
2667
+ *
2668
+ * @param int|string|array $page Optional. Page ID, title, slug, or array of such. Default empty.
2669
+ - *
2670
+ * @return bool
2671
+ */
2672
+ public function is_page_admin( $page = '' ) {
2673
+ @@ -675,8 +696,8 @@
2674
+ /**
2675
+ * Detects preview.
2676
+ *
2677
+ + * @since 2.6.0
2678
+ * @staticvar bool $cache
2679
+ - * @since 2.6.0
2680
+ *
2681
+ * @return bool
2682
+ */
2683
+ @@ -696,8 +717,8 @@
2684
+ /**
2685
+ * Detects preview.
2686
+ *
2687
+ + * @since 2.6.0
2688
+ * @staticvar bool $cache
2689
+ - * @since 2.6.0
2690
+ *
2691
+ * @return bool
2692
+ */
2693
+ @@ -717,12 +738,11 @@
2694
+ /**
2695
+ * Detects posts.
2696
+ *
2697
+ + * @since 2.6.0
2698
+ * @staticvar bool $cache
2699
+ - * @since 2.6.0
2700
+ * @uses $this->is_singular()
2701
+ *
2702
+ * @param int|string|array $post Optional. Post ID, title, slug, or array of such. Default empty.
2703
+ - *
2704
+ * @return bool
2705
+ */
2706
+ public function is_single( $post = '' ) {
2707
+ @@ -747,12 +767,10 @@
2708
+ * Detects posts within the admin area.
2709
+ *
2710
+ * @since 2.6.0
2711
+ + * @global object $current_screen
2712
+ * @see $this->is_single()
2713
+ *
2714
+ - * @global object $current_screen;
2715
+ - *
2716
+ * @param int|string|array $post Optional. Page ID, title, slug, or array of such. Default empty.
2717
+ - *
2718
+ * @return bool
2719
+ */
2720
+ public function is_single_admin( $post = '' ) {
2721
+ @@ -765,20 +783,15 @@
2722
+ }
2723
+
2724
+ /**
2725
+ - * Replaces and expands default WordPress is_singular.
2726
+ + * Determines if the current page is singular is holds singular items within the admin screen.
2727
+ + * Replaces and expands default WordPress is_singular().
2728
+ *
2729
+ + * @since 2.5.2
2730
+ + * @staticvar bool $cache
2731
+ * @uses $this->is_blog_page()
2732
+ * @uses $this->is_wc_shop()
2733
+ - * @uses $this->is_single()
2734
+ - * @uses $this->is_page()
2735
+ - * @uses $this->is_attachment()
2736
+ *
2737
+ * @param string|array $post_types Optional. Post type or array of post types. Default empty.
2738
+ - *
2739
+ - * @staticvar bool $cache
2740
+ - *
2741
+ - * @since 2.5.2
2742
+ - *
2743
+ * @return bool Post Type is singular
2744
+ */
2745
+ public function is_singular( $post_types = '' ) {
2746
+ @@ -812,12 +825,10 @@
2747
+ }
2748
+
2749
+ /**
2750
+ - * Extends default WordPress is_singular and made available in admin.
2751
+ + * Determines if the page is singular within the admin screen.
2752
+ *
2753
+ + * @since 2.5.2
2754
+ * @staticvar bool $cache
2755
+ - *
2756
+ - * @since 2.5.2
2757
+ - *
2758
+ * @global object $current_screen
2759
+ *
2760
+ * @return bool Post Type is singular
2761
+ @@ -838,15 +849,13 @@
2762
+ }
2763
+
2764
+ /**
2765
+ - * Detect the static front page.
2766
+ + * Detects the static front page.
2767
+ *
2768
+ - * @param int $id the Page ID.
2769
+ - *
2770
+ + * @since 2.3.8
2771
+ * @staticvar array $cache
2772
+ - * @since 2.3.8
2773
+ *
2774
+ - * @return bool true if is blog page. Always false if blog page is homepage.
2775
+ - * False early when false as ID is entered.
2776
+ + * @param int $id the Page ID to check. If empty, the current ID will be fetched.
2777
+ + * @return bool true if is blog page. Always false if the homepage is a blog.
2778
+ */
2779
+ public function is_static_frontpage( $id = '' ) {
2780
+
2781
+ @@ -878,7 +887,6 @@
2782
+ * @uses $this->is_archive()
2783
+ *
2784
+ * @param mixed $tag Optional. Tag ID, name, slug, or array of Tag IDs, names, and slugs.
2785
+ - *
2786
+ * @return bool
2787
+ */
2788
+ public function is_tag( $tag = '' ) {
2789
+ @@ -899,11 +907,10 @@
2790
+ }
2791
+
2792
+ /**
2793
+ - * Extends default WordPress is_tag and made available in admin.
2794
+ + * Determines if the page is a tag within the admin screen.
2795
+ *
2796
+ + * @since 2.6.0
2797
+ * @staticvar bool $cache
2798
+ - * @since 2.6.0
2799
+ - *
2800
+ * @global object $current_screen
2801
+ *
2802
+ * @return bool Post Type is category
2803
+ @@ -928,13 +935,12 @@
2804
+ /**
2805
+ * Detects taxonomy archives.
2806
+ *
2807
+ + * @since 2.6.0
2808
+ * @staticvar bool $cache
2809
+ - * @since 2.6.0
2810
+ * @uses $this->is_archive()
2811
+ *
2812
+ * @param string|array $taxonomy Optional. Taxonomy slug or slugs.
2813
+ * @param int|string|array $term Optional. Term ID, name, slug or array of Term IDs, names, and slugs.
2814
+ - *
2815
+ * @return bool
2816
+ */
2817
+ public function is_tax( $taxonomy = '', $term = '' ) {
2818
+ @@ -951,13 +957,14 @@
2819
+ }
2820
+
2821
+ /**
2822
+ - * Is Ulimate Member user page.
2823
+ - * Check for function accessibility: um_user, um_is_core_page, um_get_requested_user
2824
+ + * Determines if the page is a Ulimate Member's plugin User page.
2825
+ + * Checks for function availability: um_user, um_is_core_page, um_get_requested_user
2826
+ *
2827
+ + * @since 2.5.2
2828
+ * @staticvar bool $cache
2829
+ * @uses $this->can_i_use()
2830
+ *
2831
+ - * @since 2.5.2
2832
+ + * @return bool Whether we're on a Ultimate Member page.
2833
+ */
2834
+ public function is_ultimate_member_user_page() {
2835
+
2836
+ @@ -972,11 +979,12 @@
2837
+ }
2838
+
2839
+ /**
2840
+ - * Check for WooCommerce shop page.
2841
+ + * Determines if the page is the WooCommerce plugin Shop page.
2842
+ *
2843
+ + * @since 2.5.2
2844
+ * @staticvar bool $cache
2845
+ *
2846
+ - * @since 2.5.2
2847
+ + * @return bool True if on the WooCommerce shop page.
2848
+ */
2849
+ public function is_wc_shop() {
2850
+
2851
+ @@ -993,11 +1001,12 @@
2852
+ }
2853
+
2854
+ /**
2855
+ - * Check for WooCommerce product page.
2856
+ + * Determines if the page is the WooCommerce plugin Product page.
2857
+ *
2858
+ + * @since 2.5.2
2859
+ * @staticvar bool $cache
2860
+ *
2861
+ - * @since 2.5.2
2862
+ + * @return bool True if on a WooCommerce Product page.
2863
+ */
2864
+ public function is_wc_product() {
2865
+
2866
+ @@ -1016,8 +1025,8 @@
2867
+ /**
2868
+ * Detects year archives.
2869
+ *
2870
+ + * @since 2.6.0
2871
+ * @staticvar bool $cache
2872
+ - * @since 2.6.0
2873
+ * @uses $this->is_date()
2874
+ *
2875
+ * @return bool
2876
+ @@ -1036,10 +1045,10 @@
2877
+ }
2878
+
2879
+ /**
2880
+ - * Whether we're on the SEO settings page.
2881
+ + * Determines whether we're on the SEO settings page.
2882
+ *
2883
+ + * @since 2.6.0
2884
+ * @staticvar bool $cache
2885
+ - * @since 2.6.0
2886
+ *
2887
+ * @return bool
2888
+ */
2889
+ @@ -1054,13 +1063,13 @@
2890
+ }
2891
+
2892
+ /**
2893
+ - * The amount of pages.
2894
+ - * Fetches global $page through Query Var.
2895
+ + * Fetches the amount of pages on the screen.
2896
+ + * Fetches global $page through Query Var to prevent conflicts.
2897
+ *
2898
+ + * @since 2.6.0
2899
+ * @staticvar int $page
2900
+ - * @since 2.6.0
2901
+ *
2902
+ - * @return int $page
2903
+ + * @return int $page Always a positive number.
2904
+ */
2905
+ public function page() {
2906
+
2907
+ @@ -1075,11 +1084,11 @@
2908
+ }
2909
+
2910
+ /**
2911
+ - * The number of the current page.
2912
+ + * Fetches the number of the current page.
2913
+ * Fetches global $paged through Query Var. Determines
2914
+ *
2915
+ + * @since 2.6.0
2916
+ * @staticvar int $paged
2917
+ - * @since 2.6.0
2918
+ *
2919
+ * @return int $paged
2920
+ */
2921
+ Index: inc/classes/sanitize.class.php
2922
+ ===================================================================
2923
+ --- inc/classes/sanitize.class.php (revision 1433389)
2924
+ +++ inc/classes/sanitize.class.php (working copy)
2925
+ @@ -519,6 +519,7 @@
2926
+ * Returns a one-line sanitized description
2927
+ *
2928
+ * @since 2.5.0
2929
+ + * @since 2.6.6 Removes duplicated spaces.
2930
+ *
2931
+ * @param string $new_value The Description.
2932
+ * @return string One line sanitized description.
2933
+ @@ -539,6 +540,13 @@
2934
+
2935
+ $description = trim( implode( $new_lines ) );
2936
+
2937
+ + $i = 0;
2938
+ + //* Run twice at most, to catch uneven multiple spaces.
2939
+ + do {
2940
+ + $description = str_replace( ' ', ' ', $description );
2941
+ + $i++;
2942
+ + } while ( strpos( $description, ' ' ) && $i <= 2 );
2943
+ +
2944
+ return (string) strip_tags( $description );
2945
+ }
2946
+
2947
+ @@ -665,9 +673,7 @@
2948
+
2949
+ /**
2950
+ * Returns a 1 or 0, for all truthy / falsy values.
2951
+ - *
2952
+ * Uses double casting. First, we cast to bool, then to integer.
2953
+ - *
2954
+ * Also flushes the sitemap.
2955
+ *
2956
+ * @since 2.2.9
2957
+ Index: inc/classes/sitemaps.class.php
2958
+ ===================================================================
2959
+ --- inc/classes/sitemaps.class.php (revision 1433389)
2960
+ +++ inc/classes/sitemaps.class.php (working copy)
2961
+ @@ -316,10 +316,8 @@
2962
+ /**
2963
+ * Transient expiration: 1 week.
2964
+ * Keep the sitemap for at most 1 week.
2965
+ - *
2966
+ - * 60s * 60m * 24h * 7d
2967
+ */
2968
+ - $expiration = 60 * 60 * 24 * 7;
2969
+ + $expiration = WEEK_IN_SECONDS;
2970
+
2971
+ $this->set_transient( $this->sitemap_transient, $sitemap_content, $expiration );
2972
+ }
2973
+ @@ -741,12 +739,10 @@
2974
+ * Limit the pinging to a maximum of 1 per hour.
2975
+ * Transient expiration. 1 hour.
2976
+ *
2977
+ - * 60s * 60m
2978
+ - *
2979
+ * Applies filters the_seo_framework_sitemap_throttle_s
2980
+ * @since 2.5.1
2981
+ */
2982
+ - $expiration = (int) apply_filters( 'the_seo_framework_sitemap_throttle_s', 60 * 60 );
2983
+ + $expiration = (int) apply_filters( 'the_seo_framework_sitemap_throttle_s', HOUR_IN_SECONDS );
2984
+
2985
+ //* @NOTE: Using legacy set_transient to prevent ping spam.
2986
+ set_transient( $transient, $throttle, $expiration );
2987
+ @@ -876,43 +872,6 @@
2988
+ }
2989
+
2990
+ /**
2991
+ - * Add and Flush rewrite rules on plugin activation.
2992
+ - *
2993
+ - * @since 2.2.9
2994
+ - * @access private
2995
+ - *
2996
+ - * Do not return anything. Just be here. Thanks.
2997
+ - */
2998
+ - public static function flush_rewrite_rules_activation() {
2999
+ - global $wp_rewrite;
3000
+ -
3001
+ - //* This function is called statically.
3002
+ - $the_seo_framework = the_seo_framework();
3003
+ - $the_seo_framework->rewrite_rule_sitemap( true );
3004
+ -
3005
+ - $wp_rewrite->init();
3006
+ - $wp_rewrite->flush_rules( true );
3007
+ - }
3008
+ -
3009
+ - /**
3010
+ - * Flush rewrite rules on plugin deactivation.
3011
+ - *
3012
+ - * @since 2.2.9
3013
+ - * @access private
3014
+ - *
3015
+ - * Do not return anything. Just be here. Thanks.
3016
+ - */
3017
+ - public static function flush_rewrite_rules_deactivation() {
3018
+ - global $wp_rewrite;
3019
+ -
3020
+ - $wp_rewrite->init();
3021
+ -
3022
+ - unset( $wp_rewrite->extra_rules_top['sitemap\.xml$'] );
3023
+ -
3024
+ - $wp_rewrite->flush_rules( true );
3025
+ - }
3026
+ -
3027
+ - /**
3028
+ * Initialize and flush rewrite rules.
3029
+ *
3030
+ * @since 2.6.0
3031
+ Index: inc/classes/siteoptions.class.php
3032
+ ===================================================================
3033
+ --- inc/classes/siteoptions.class.php (revision 1433389)
3034
+ +++ inc/classes/siteoptions.class.php (working copy)
3035
+ @@ -416,9 +416,6 @@
3036
+ if ( update_option( $this->settings_field, $options ) && $updated && $this->load_options ) {
3037
+ $this->pre_output_site_updated_plugin_notice();
3038
+ }
3039
+ -
3040
+ - //* Flush rewrite rules at shutdown.
3041
+ - $this->enqueue_rewrite_flush_other( true );
3042
+ }
3043
+
3044
+ /**
3045
+ Index: inc/classes/termdata.class.php
3046
+ ===================================================================
3047
+ --- inc/classes/termdata.class.php (revision 1418924)
3048
+ +++ inc/classes/termdata.class.php (working copy)
3049
+ @@ -26,13 +26,206 @@
3050
+ class AutoDescription_TermData extends AutoDescription_PostData {
3051
+
3052
+ /**
3053
+ - * Constructor, load parent constructor
3054
+ + * Constructor, load parent constructor.
3055
+ */
3056
+ public function __construct() {
3057
+ parent::__construct();
3058
+ +
3059
+ + add_action( 'current_screen', array( $this, 'init_term_filters' ), 999 );
3060
+ + add_action( 'get_header', array( $this, 'init_term_filters' ), 999 );
3061
+ +
3062
+ + add_action( 'edit_term', array( $this, 'taxonomy_seo_save' ), 10, 2 );
3063
+ + add_action( 'delete_term', array( $this, 'term_meta_delete' ), 10, 2 );
3064
+ }
3065
+
3066
+ /**
3067
+ + * Initializes term filters after wp_query or currentscreen has been set.
3068
+ + *
3069
+ + * @since 2.6.6
3070
+ + * @staticvar boolean $run Whether this function has already run.
3071
+ + * @access private
3072
+ + *
3073
+ + * @return void early if already run.
3074
+ + */
3075
+ + public function init_term_filters() {
3076
+ +
3077
+ + static $run = null;
3078
+ +
3079
+ + if ( isset( $run ) )
3080
+ + return;
3081
+ +
3082
+ + add_filter( 'get_term', array( $this, 'get_term_filter' ), 10, 2 );
3083
+ + add_filter( 'get_terms', array( $this, 'get_terms_filter' ), 10, 2 );
3084
+ +
3085
+ + $run = true;
3086
+ +
3087
+ + }
3088
+ +
3089
+ + /**
3090
+ + * Add term meta data into options table of the term.
3091
+ + * Adds separated database options for terms, as the terms table doesn't allow for addition.
3092
+ + *
3093
+ + * Applies filters array the_seo_framework_term_meta_defaults : Array of default term SEO options
3094
+ + * Applies filters mixed the_seo_framework_term_meta_{field} : Override filter for specifics.
3095
+ + * Applies filters array the_seo_framework_term_meta : Override output for term or taxonomy.
3096
+ + *
3097
+ + * @since 2.1.8
3098
+ + *
3099
+ + * @todo Use WordPress 4.4.0 get_term_meta() / update_term_meta()
3100
+ + * @priority OMG WTF BBQ 2.6.x / Genesis 2.3.0
3101
+ + * @see @link http://www.studiopress.com/important-announcement-for-genesis-plugin-developers/
3102
+ + * @link https://core.trac.wordpress.org/browser/tags/4.5/src/wp-includes/taxonomy.php#L1814
3103
+ + * @todo still use arrays in get_term_meta() / update_term_meta() ?
3104
+ + * @NOTE Keep WP 3.8 compat.
3105
+ + *
3106
+ + * @param object $term Database row object.
3107
+ + * @param string $taxonomy Taxonomy name that $term is part of.
3108
+ + * @return object $term Database row object.
3109
+ + */
3110
+ + public function get_term_filter( $term, $taxonomy ) {
3111
+ +
3112
+ + //* Do nothing, if $term is not an object.
3113
+ + if ( ! is_object( $term ) )
3114
+ + return $term;
3115
+ +
3116
+ + //* We can't set query vars just yet.
3117
+ + if ( false === $this->can_cache_query() )
3118
+ + return $term;
3119
+ +
3120
+ + /**
3121
+ + * No need to process this data outside of the Terms' scope.
3122
+ + * @since 2.6.0
3123
+ + */
3124
+ + if ( false === $this->is_admin() && false === $this->is_archive() )
3125
+ + return $term;
3126
+ +
3127
+ + /**
3128
+ + * No need to process this after the data has already been output.
3129
+ + * @since 2.6.0
3130
+ + */
3131
+ + if ( did_action( 'the_seo_framework_do_after_output' ) )
3132
+ + return $term;
3133
+ +
3134
+ + /**
3135
+ + * Do nothing if called in the context of creating a term via an Ajax call to prevent data conflict.
3136
+ + * @since ???
3137
+ + *
3138
+ + * @since 2.6.0 delay did_action call as it's a heavy array call.
3139
+ + */
3140
+ + if ( defined( 'DOING_AJAX' ) && DOING_AJAX && did_action( 'wp_ajax_add-tag' ) )
3141
+ + return $term;
3142
+ +
3143
+ + $db = get_option( 'autodescription-term-meta' );
3144
+ + $term_meta = isset( $db[$term->term_id] ) ? $db[$term->term_id] : array();
3145
+ +
3146
+ + $args = (array) apply_filters( 'the_seo_framework_term_meta_defaults', array(
3147
+ + 'doctitle' => '',
3148
+ + 'description' => '',
3149
+ + 'noindex' => 0,
3150
+ + 'nofollow' => 0,
3151
+ + 'noarchive' => 0,
3152
+ + 'saved_flag' => 0, // Don't touch, used to prevent data conflict with Genesis.
3153
+ + ) );
3154
+ +
3155
+ + $term->admeta = wp_parse_args( $term_meta, $args );
3156
+ +
3157
+ + //* Sanitize term meta
3158
+ + foreach ( $term->admeta as $field => $value ) {
3159
+ +
3160
+ + /**
3161
+ + * Trim and sanitize the title beforehand.
3162
+ + * @since 2.5.0
3163
+ + */
3164
+ + if ( 'doctitle' === $field )
3165
+ + $value = trim( strip_tags( $value ) );
3166
+ +
3167
+ + /**
3168
+ + * Trim and sanitize the description beforehand.
3169
+ + * @since 2.5.0
3170
+ + */
3171
+ + if ( 'description' === $field )
3172
+ + $value = $this->s_description( $value );
3173
+ +
3174
+ + /**
3175
+ + * @param object $term The Term object.
3176
+ + * @param string $taxonomy The Taxonomy name.
3177
+ + */
3178
+ + $term->admeta[$field] = (string) apply_filters( "the_seo_framework_term_meta_{$field}", stripslashes( wp_kses_decode_entities( $value ) ), $term, $taxonomy );
3179
+ + }
3180
+ +
3181
+ + /**
3182
+ + * @param object $term The Term object.
3183
+ + * @param array $taxonomy The Taxonomy name.
3184
+ + */
3185
+ + $term->admeta = (array) apply_filters( 'the_seo_framework_term_meta', $term->admeta, $term, $taxonomy );
3186
+ +
3187
+ + return $term;
3188
+ + }
3189
+ +
3190
+ + /**
3191
+ + * Add AutoDescription term-meta data to functions that return multiple terms.
3192
+ + *
3193
+ + * @since 2.0.0
3194
+ + *
3195
+ + * @param array $terms Database row objects.
3196
+ + * @param string $taxonomy Taxonomy name that $terms are part of.
3197
+ + * @return array $terms Database row objects.
3198
+ + */
3199
+ + public function get_terms_filter( array $terms, $taxonomy ) {
3200
+ +
3201
+ + foreach( $terms as $term )
3202
+ + $term = $this->get_term_filter( $term, $taxonomy );
3203
+ +
3204
+ + return $terms;
3205
+ + }
3206
+ +
3207
+ + /**
3208
+ + * Save taxonomy meta data.
3209
+ + * Fires when a user edits and saves a taxonomy.
3210
+ + *
3211
+ + * @since 2.1.8
3212
+ + *
3213
+ + * @param integer $term_id Term ID.
3214
+ + * @param integer $tt_id Term Taxonomy ID.
3215
+ + * @return void Early on AJAX call.
3216
+ + */
3217
+ + public function taxonomy_seo_save( $term_id, $tt_id ) {
3218
+ +
3219
+ + if ( defined( 'DOING_AJAX' ) && DOING_AJAX )
3220
+ + return;
3221
+ +
3222
+ + $term_meta = (array) get_option( 'autodescription-term-meta' );
3223
+ +
3224
+ + $term_meta[$term_id] = isset( $_POST['autodescription-meta'] ) ? (array) $_POST['autodescription-meta'] : array();
3225
+ +
3226
+ + //* Pass through wp_kses if not super admin.
3227
+ + if ( ! current_user_can( 'unfiltered_html' ) && isset( $term_meta[$term_id]['archive_description'] ) )
3228
+ + $term_meta[$term_id]['archive_description'] = wp_kses( $term_meta[$term_id]['archive_description'] );
3229
+ +
3230
+ + update_option( 'autodescription-term-meta', $term_meta );
3231
+ +
3232
+ + }
3233
+ +
3234
+ + /**
3235
+ + * Delete term meta data.
3236
+ + * Fires when a user deletes a term.
3237
+ + *
3238
+ + * @since 2.1.8
3239
+ + *
3240
+ + * @param integer $term_id Term ID.
3241
+ + * @param integer $tt_id Taxonomy Term ID.
3242
+ + */
3243
+ + public function term_meta_delete( $term_id, $tt_id ) {
3244
+ +
3245
+ + $term_meta = (array) get_option( 'autodescription-term-meta' );
3246
+ +
3247
+ + unset( $term_meta[$term_id] );
3248
+ +
3249
+ + update_option( 'autodescription-term-meta', (array) $term_meta );
3250
+ +
3251
+ + }
3252
+ +
3253
+ + /**
3254
+ * Fetch set Term data.
3255
+ *
3256
+ * @param object|null $term The TT object, if it isn't set, one is fetched.
3257
+ @@ -85,7 +278,6 @@
3258
+ * @access private
3259
+ *
3260
+ * @param int $id The possible taxonomy Term ID.
3261
+ - *
3262
+ * @return null|object The Term object.
3263
+ */
3264
+ public function fetch_the_term( $id = '' ) {
3265
+ @@ -100,13 +292,11 @@
3266
+ return false;
3267
+
3268
+ if ( $this->is_admin() ) {
3269
+ - if ( 'term.php' === $this->page_hook ) {
3270
+ - global $current_screen;
3271
+ + global $current_screen;
3272
+
3273
+ - if ( isset( $current_screen->taxonomy ) ) {
3274
+ - $term_id = $id ? $id : $this->get_admin_term_id();
3275
+ - $term[$id] = get_term_by( 'id', $term_id, $current_screen->taxonomy );
3276
+ - }
3277
+ + if ( isset( $current_screen->taxonomy ) ) {
3278
+ + $term_id = $id ? $id : $this->get_admin_term_id();
3279
+ + $term[$id] = get_term_by( 'id', $term_id, $current_screen->taxonomy );
3280
+ }
3281
+ } else {
3282
+ if ( $this->is_category() || $this->is_tag() ) {
3283
+ @@ -125,12 +315,10 @@
3284
+ /**
3285
+ * Fetch Tax labels
3286
+ *
3287
+ - * @param string $tax_type the Taxonomy type.
3288
+ - *
3289
+ * @since 2.3.1
3290
+ - *
3291
+ * @staticvar object $labels
3292
+ *
3293
+ + * @param string $tax_type the Taxonomy type.
3294
+ * @return object|null with all the labels as member variables
3295
+ */
3296
+ public function get_tax_labels( $tax_type ) {
3297
+ @@ -153,7 +341,6 @@
3298
+ * Get the current screen term labels.
3299
+ *
3300
+ * @since 2.6.0
3301
+ - *
3302
+ * @staticvar string $term_name : Caution: This function only runs once per screen and doesn't check the term type more than once.
3303
+ *
3304
+ * @param object $term The Taxonomy Term object.
3305
+ @@ -160,7 +347,6 @@
3306
+ * @param bool $singular Whether to fetch a singular or plural name.
3307
+ * @param bool $fallback Whether to fallback on a generic name.
3308
+ * @param bool $use_cache Whether to read from cache.
3309
+ - *
3310
+ * @return string the Term name.
3311
+ */
3312
+ protected function get_the_term_name( $term, $singular = true, $fallback = true, $use_cache = true ) {
3313
+ @@ -228,28 +414,4 @@
3314
+ return $term_name[$singular] = '';
3315
+ }
3316
+
3317
+ - /**
3318
+ - * Fetch the Admin Term ID. For WordPress 4.5 up and below.
3319
+ - *
3320
+ - * @since 2.6.0
3321
+ - * @staticvar int $term_id The Term ID.
3322
+ - *
3323
+ - * @return int Term ID.
3324
+ - */
3325
+ - public function get_admin_term_id() {
3326
+ -
3327
+ - static $term_id = null;
3328
+ -
3329
+ - if ( isset( $term_id ) )
3330
+ - return $term_id;
3331
+ -
3332
+ - if ( isset( $_REQUEST['tag_ID'] ) && $_REQUEST['tag_ID'] ) {
3333
+ - $term_id = $_REQUEST['tag_ID'];
3334
+ - } else if ( isset( $_REQUEST['term_id'] ) && $_REQUEST['term_id'] ) {
3335
+ - $term_id = $_REQUEST['term_id'];
3336
+ - }
3337
+ -
3338
+ - return $term_id = $term_id ? abs( (int) $term_id ) : 0;
3339
+ - }
3340
+ -
3341
+ }
3342
+ Index: inc/classes/transients.class.php
3343
+ ===================================================================
3344
+ --- inc/classes/transients.class.php (revision 1433521)
3345
+ +++ inc/classes/transients.class.php (working copy)
3346
+ @@ -138,8 +138,10 @@
3347
+ }
3348
+
3349
+ /**
3350
+ - * Setup vars for transients.
3351
+ + * Setup vars for general site transients.
3352
+ *
3353
+ + * @global int $blog_id
3354
+ + *
3355
+ * @since 2.3.3
3356
+ */
3357
+ public function setup_transient_names() {
3358
+ @@ -158,11 +160,11 @@
3359
+ /**
3360
+ * Setup vars for transients which require $page_id.
3361
+ *
3362
+ + * @since 2.3.3
3363
+ + *
3364
+ * @param int|string|bool $page_id the Taxonomy or Post ID. If false it will generate for the blog page.
3365
+ * @param string $taxonomy The taxonomy name.
3366
+ * @param strgin $type The Post Type
3367
+ - *
3368
+ - * @since 2.3.3
3369
+ */
3370
+ public function setup_auto_description_transient( $page_id, $taxonomy = '', $type = null ) {
3371
+
3372
+ @@ -174,7 +176,7 @@
3373
+ *
3374
+ * @since 2.3.4
3375
+ */
3376
+ - $revision = '0';
3377
+ + $revision = '1';
3378
+
3379
+ $additions = $this->add_description_additions( $page_id, $taxonomy );
3380
+
3381
+ @@ -190,11 +192,11 @@
3382
+ /**
3383
+ * Setup vars for transients which require $page_id.
3384
+ *
3385
+ + * @since 2.3.3
3386
+ + *
3387
+ * @param int|string|bool $page_id the Taxonomy or Post ID. If false it will generate for the blog page.
3388
+ * @param string $taxonomy The taxonomy name.
3389
+ * @param string|null $type The post type.
3390
+ - *
3391
+ - * @since 2.3.3
3392
+ */
3393
+ public function setup_ld_json_transient( $page_id, $taxonomy = '', $type = null ) {
3394
+
3395
+ @@ -220,19 +222,15 @@
3396
+ /**
3397
+ * Generate transient key based on query vars.
3398
+ *
3399
+ - * @param int|string|bool $page_id the Taxonomy or Post ID.
3400
+ - * @param string $taxonomy The Taxonomy name.
3401
+ - * @param string $type The Post Type
3402
+ - *
3403
+ - * @staticvar array $cached_id : contains cache strings.
3404
+ - *
3405
+ * @global int $blog_id;
3406
+ *
3407
+ * @since 2.3.3
3408
+ + * @since 2.6.0 Refactored.
3409
+ + * @staticvar array $cached_id : contains cache strings.
3410
+ *
3411
+ - * @refactored
3412
+ - * @since 2.6.0
3413
+ - *
3414
+ + * @param int|string|bool $page_id the Taxonomy or Post ID.
3415
+ + * @param string $taxonomy The Taxonomy name.
3416
+ + * @param string $type The Post Type
3417
+ * @return string The generated page id key.
3418
+ */
3419
+ public function generate_cache_key( $page_id, $taxonomy = '', $type = null ) {
3420
+ @@ -253,7 +251,7 @@
3421
+ //* Author page.
3422
+ $the_id = 'author_' . $page_id;
3423
+ } else if ( 'frontpage' === $type ) {
3424
+ - //* Home Page.
3425
+ + //* Front/HomePage.
3426
+ $the_id = $this->generate_front_page_cache_key();
3427
+ } else {
3428
+ $this->_doing_it_wrong( __CLASS__ . '::' . __FUNCTION__, __( 'Third parameter must be a known type.', 'autodescription' ), '2.6.5' );
3429
+ @@ -260,10 +258,9 @@
3430
+ $the_id = esc_sql( $type . '_' . $page_id . '_' . $t );
3431
+ }
3432
+ } else if ( $this->is_404() ) {
3433
+ - //* 404.
3434
+ $the_id = '_404_';
3435
+ } else if ( ( $this->is_front_page( $page_id ) ) || ( $this->is_admin() && $this->is_menu_page( $this->pagehook ) ) ) {
3436
+ - //* Fetch Home key.
3437
+ + //* Front/HomePage.
3438
+ $the_id = $this->generate_front_page_cache_key();
3439
+ } else if ( $this->is_blog_page( $page_id ) ) {
3440
+ $the_id = 'blog_' . $page_id;
3441
+ @@ -455,7 +452,6 @@
3442
+ * @since 2.2.9
3443
+ *
3444
+ * @param int $post_id The Post ID that has been updated.
3445
+ - *
3446
+ * @return bool|null True when sitemap is flushed. False on revision. Null
3447
+ * when sitemaps are deactivated.
3448
+ */
3449
+ @@ -540,10 +536,9 @@
3450
+ * Delete transient for the automatic description for blog on save request.
3451
+ * Returns old option, since that's passed for sanitation within WP Core.
3452
+ *
3453
+ - * @param string $old_option The previous blog description option.
3454
+ - *
3455
+ * @since 2.3.3
3456
+ *
3457
+ + * @param string $old_option The previous blog description option.
3458
+ * @return string Previous option.
3459
+ */
3460
+ public function delete_auto_description_blog_transient( $old_option ) {
3461
+ @@ -558,12 +553,11 @@
3462
+ /**
3463
+ * Delete transient for the automatic description on requests.
3464
+ *
3465
+ + * @since 2.3.3
3466
+ + *
3467
+ * @param mixed $page_id The page ID or identifier.
3468
+ * @param string $taxonomy The tt name.
3469
+ * @param string $type The Post Type
3470
+ - *
3471
+ - * @since 2.3.3
3472
+ - *
3473
+ * @return bool true
3474
+ */
3475
+ public function delete_auto_description_transient( $page_id, $taxonomy = '', $type = null ) {
3476
+ @@ -578,12 +572,11 @@
3477
+ /**
3478
+ * Delete transient for the LD+Json scripts on requests.
3479
+ *
3480
+ + * @since 2.4.2
3481
+ + *
3482
+ * @param mixed $page_id The page ID or identifier.
3483
+ * @param string $taxonomy The tt name.
3484
+ * @param string|null $type The post type.
3485
+ - *
3486
+ - * @since 2.4.2
3487
+ - *
3488
+ * @return bool true
3489
+ */
3490
+ public function delete_ld_json_transient( $page_id, $taxonomy = '', $type = null ) {
3491
+ @@ -635,7 +628,7 @@
3492
+ * Expiration time, 3 days.
3493
+ * 60s * 60m * 24d * 3d
3494
+ */
3495
+ - $expiration = 60 * 60 * 24 * 3;
3496
+ + $expiration = DAY_IN_SECONDS * 3;
3497
+
3498
+ set_transient( $this->theme_doing_it_right_transient, $dir, $expiration );
3499
+ }
3500
+ @@ -645,8 +638,8 @@
3501
+ /**
3502
+ * Flushes the home page LD+Json transient.
3503
+ *
3504
+ - * @staticvar bool $flushed
3505
+ * @since 2.6.0
3506
+ + * @staticvar bool $flushed Prevents second flush.
3507
+ *
3508
+ * @return bool Whether it's flushed on current call.
3509
+ */
3510
+ Index: inc/deprecated/deprecated.class.php
3511
+ ===================================================================
3512
+ --- inc/deprecated/deprecated.class.php (revision 1433389)
3513
+ +++ inc/deprecated/deprecated.class.php (working copy)
3514
+ @@ -707,4 +707,23 @@
3515
+ return $path;
3516
+ }
3517
+
3518
+ + /**
3519
+ + * Doing it Wrong The SEO Framework version wrapper.
3520
+ + *
3521
+ + * @since 2.3.0
3522
+ + *
3523
+ + * @deprecated
3524
+ + * @since 2.6.5
3525
+ + *
3526
+ + * @return string The SEO Framework version.
3527
+ + */
3528
+ + public function the_seo_framework_version( $version = '' ) {
3529
+ +
3530
+ + $this->_deprecated_function( 'AutoDescription_Load::' . __FUNCTION__, '', '2.6.6' );
3531
+ +
3532
+ + $output = $version ? sprintf( __( '%s of The SEO Framework', 'autodescription' ), esc_attr( $version ) ) : '';
3533
+ +
3534
+ + return $output;
3535
+ + }
3536
+ +
3537
+ }
3538
+ Index: load.class.php
3539
+ ===================================================================
3540
+ --- load.class.php (revision 1427876)
3541
+ +++ load.class.php (working copy)
3542
+ @@ -98,7 +98,6 @@
3543
+
3544
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'search.class.php' );
3545
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'doingitright.class.php' );
3546
+ -require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'pageoptions.class.php' );
3547
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'inpost.class.php' );
3548
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'adminpages.class.php' );
3549
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH_CLASS . 'sanitize.class.php' );
3550
+ @@ -111,7 +110,7 @@
3551
+ require_once( THE_SEO_FRAMEWORK_DIR_PATH . 'inc/deprecated/deprecated.class.php' );
3552
+
3553
+ /**
3554
+ - * God class.
3555
+ + * Facade class.
3556
+ *
3557
+ * Extending upon parent classes.
3558
+ *
3559
+ @@ -124,13 +123,12 @@
3560
+ *
3561
+ * @since 2.2.9
3562
+ *
3563
+ - * @var bool The SEO Framework Debug/Profile constants is/are defined.
3564
+ + * @var bool The SEO Framework Debug/Profile states.
3565
+ */
3566
+ public $the_seo_framework_debug = false;
3567
+ public $the_seo_framework_debug_hidden = false;
3568
+ public $the_seo_framework_use_transients = true;
3569
+ public $script_debug = false;
3570
+ - public $debug_time;
3571
+
3572
+ /**
3573
+ * Constructor, setup debug vars and then load parent constructor.
3574
+ @@ -164,23 +162,17 @@
3575
+
3576
+ /**
3577
+ * Wrapper for function calling through parameters. The golden nugget.
3578
+ - * Is this function not working properly? Send me your code through the WordPress support forums.
3579
+ - * I'll adjust if possible.
3580
+ *
3581
+ - * @param array|string $callback the method array or function string.
3582
+ - * @param string $version the version of AutoDescription the function is used.
3583
+ - *
3584
+ * @since 2.2.2
3585
+ - *
3586
+ - * @return mixed $output The function called.
3587
+ - *
3588
+ * @NOTE _doing_it_wrong notices go towards the callback. Unless this
3589
+ * function is used wrongfully. Then the notice is about this function.
3590
+ *
3591
+ - * @param array|string $params The arguments passed to the function.
3592
+ - * @since 2.2.4
3593
+ + * @param array|string $callback the method array or function string.
3594
+ + * @param string $version the version of AutoDescription the function is used.
3595
+ + * @param array|string $args The arguments passed to the function.
3596
+ + * @return mixed $output The function called.
3597
+ */
3598
+ - public function call_function( $callback, $version = '', $params = array() ) {
3599
+ + public function call_function( $callback, $version = '', $args = array() ) {
3600
+
3601
+ $output = '';
3602
+
3603
+ @@ -196,10 +188,10 @@
3604
+ /**
3605
+ * Convert string/object to array
3606
+ */
3607
+ - if ( is_object( $params ) ) {
3608
+ - $args = array( $params, '' );
3609
+ + if ( is_object( $args ) ) {
3610
+ + $args = array( $args, '' );
3611
+ } else {
3612
+ - $args = (array) $params;
3613
+ + $args = (array) $args;
3614
+ }
3615
+
3616
+ $class = reset( $function );
3617
+ @@ -261,16 +253,4 @@
3618
+ return $output;
3619
+ }
3620
+
3621
+ - /**
3622
+ - * Helper function for Doing it Wrong
3623
+ - *
3624
+ - * @since 2.3.0
3625
+ - */
3626
+ - public function the_seo_framework_version( $version = '' ) {
3627
+ -
3628
+ - $output = $version ? sprintf( __( '%s of The SEO Framework', 'autodescription' ), esc_attr( $version ) ) : '';
3629
+ -
3630
+ - return $output;
3631
+ - }
3632
+ -
3633
+ }
patch/index.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * If you're reading this...
4
+ * Congratulations, you're alive.
5
+ * If that's not something to smile about,
6
+ * then I don't know what is.
7
+ * - Chad Sugg
8
+ *
9
+ * :)
10
+ */
readme.txt ADDED
@@ -0,0 +1,753 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === The SEO Framework ===
2
+ Contributors: Cybr
3
+ Donate link: https://theseoframework.com/donate/
4
+ Tags: open graph, description, automatic, generate, generator, title, breadcrumbs, ogtype, meta, metadata, search, engine, optimization, seo, framework, canonical, redirect, bbpress, twitter, facebook, google, bing, yahoo, jetpack, genesis, woocommerce, multisite, robots, icon, cpt, custom, post, types, pages, taxonomy, tag, sitemap, sitemaps, screenreader, rtl, feed
5
+ Requires at least: 3.8.0
6
+ Tested up to: 4.5.2
7
+ Stable tag: 2.6.6
8
+ License: GPLv3
9
+ License URI: http://www.gnu.org/licenses/gpl-3.0.html
10
+
11
+ The SEO Framework plugin provides an automated and advanced SEO solution for your WordPress website.
12
+
13
+ == Description ==
14
+
15
+ = The SEO Framework =
16
+
17
+ **Easy for beginners, awesome for experts. WordPress SEO for Everyone.**
18
+
19
+ An accessible, unbranded and extremely fast SEO solution for any WordPress website.
20
+
21
+ > <strong>This plugin strongly helps you create better SEO value for your content.</strong><br>
22
+ > But at the end of the day, it all depends on how entertaining or well-constructed your content or product is.
23
+ >
24
+ > No SEO plugin does the magic thing to be found instantly. But doing it right helps a lot.<br>
25
+ > The SEO Framework helps you doing it right. Give it a try!
26
+ >
27
+ > The Default Settings are recommended within the SEO Settings page. If you know what you're doing, go ahead and change them! Each option is also documented.
28
+
29
+ = What this plugin does, in a few lines =
30
+
31
+ * Automatically configures SEO for every page, post, taxonomy and term.
32
+ * Allows you to adjust the SEO globally.
33
+ * Allows you to adjust the SEO for every applicable page, post, taxonomy and term.
34
+ * Shows you how to improve your SEO with a beautiful SEO bar for each supported Post, Page and Taxonomy.
35
+ * Helps your pages get ranked distinctively through various Metatag and scripting techniques.
36
+ * Helps your pages get shared more beautiful through Facebook, Twitter and other social sites.
37
+ * Allows plugin authors to easily extend this plugin.
38
+ * Supports custom post types, like WooCommerce and bbPress.
39
+ * Automatically upgrades itself from Genesis SEO.
40
+ * Allows for easy SEO plugin switch using a tool.
41
+
42
+ *Read **Transferring SEO Content using SEO Data Transporter** below for SEO plugin transitioning instructions.*
43
+
44
+ = Unbranded, Free and for the Professional =
45
+ This plugin is unbranded! This even means that we don't even put the name "The SEO Framework" anywhere within the WordPress interface, aside from the plugin activation page.
46
+ This plugin makes great use of the default WordPress interface elements, like as if this plugin is part of WordPress. No ads, no nags.
47
+ The small and hidden HTML comment can easily be disabled with the use of a filter.
48
+
49
+ Nobody has to know about the tools you've used to create your or someone else's website. A clean interface, for everyone.
50
+
51
+ = Numbers don't lie, performance matters =
52
+ Optimizing SEO is a fundamental process for any website. So we try to be non-intrusive with The SEO Framework.
53
+ The SEO Framework is byte and process optimized on PHP level, with each update the optimization is improved when possible.
54
+ Page rendering time matters in SEO. This is where we lay focus on.
55
+
56
+ * This plugin is written with massive and busy (multi-)sites in mind.
57
+ * This plugin uses various caching methods which store heavy calculations in memory and the database.
58
+ * This plugin is on average 1.49x to 1.95x faster compared to other popular SEO plugins.
59
+ * This plugin consumes on average 1.42x more server resources than other popular SEO plugins in exchange for improved performance.
60
+ * This plugin has on average 1.30 to 1.60x more database interactions in exchange for improved performance.
61
+ * And last but not least, this plugin always has 100% fewer advertisements. Let's keep it that way.
62
+
63
+ *Numbers may vary per installation and version. Last checked: 14th May 2016.*
64
+ *The numbers are based on actual plugin code runtime.*
65
+
66
+ = Completely pluggable =
67
+ The SEO Framework also features pluggable functions. All functions are active and can be called within the WordPress Loop.
68
+ This allows other developers to extend the plugin wherever needed.
69
+ We have also provided an API documentation located at [The SEO Framework API Docs](http://theseoframework.com/docs/api/).
70
+
71
+ = Still not convinced? Let's dive deeper =
72
+
73
+ **By default, this plugin automatically generates:**
74
+
75
+ * Title, with super-fast 'wrong themes' support.
76
+ * Description, with anti-spam techniques.
77
+ * A canonical URL.
78
+ * Various Open Graph, Facebook and Twitter tags.
79
+ * Special Open Graph description, which organically integrates with the Facebook and Twitter snippets.
80
+ * Extended Open Graph Images support, including image manipulation.
81
+ * Canonical, with full WPMUdev Domain Mapping, subdomain and HTTPS support to prevent duplicated content.
82
+ * Schema.org LD+Json script that adds extended search support for Google Search and Chrome.
83
+ * Schema.org LD+Json script for Knowledge Graph (Personal/Business site relations, name and logo).
84
+ * Advanced Schema.org LD+Json script for Breadcrumbs (just like the visual one) which extends page relation support in Google Search.
85
+ * Schema.org LD+Json script to show the correct site name in Google Breadcrumbs.
86
+ * Publishing and editing dates, accurate to the day.
87
+ * Link relationships, with full WPMUdev Domain Mapping and HTTPS support.
88
+ * Simple Sitemap with Pages, Posts and Custom Post Types (CPT), which listens to the in-post settings.
89
+ * Feed excerpts and backlinks to prevent content scraping.
90
+
91
+ **This plugin goes further, behind the screens it:**
92
+
93
+ * Prevents canonical errors with categories, pages, subdomains and Multisite Domain Mapping.
94
+ * Disables 404 pages and empty categories from being indexed, even if they don't send a 404 response.
95
+ * Automatically notifies Google, Bing and Yandex on Post or Page update and deletion when sitemaps are enabled.
96
+
97
+ **This plugin allows you to manually set these values for each post, page, supported CPT and term:**
98
+
99
+ * Title
100
+ * Description
101
+ * Canonical URL
102
+ * Robots (nofollow, noindex, noarchive)
103
+ * Redirect, with optional Multisite spam filter (Post/Page/CPT only)
104
+ * Local on-site search settings (Post/Page/CPT only)
105
+
106
+ **This plugin allows you to adjust over 90 site settings, including:**
107
+
108
+ * Title and Description Separators and additions.
109
+ * Automated description output.
110
+ * Schema.org output, including Knowledge Graph options.
111
+ * Various robots options.
112
+ * Many home page specific options.
113
+ * Facebook, Twitter and Pinterest social integration
114
+ * Shortlink tag output.
115
+ * Link relationships
116
+ * Google, Bing, Pinterest and Yandex Webmaster verification
117
+ * Sitemap integration.
118
+ * Robots.txt sitemap integration.
119
+ * Feed anti-scraper options.
120
+ * And much, much more.
121
+
122
+ **This plugin helps you to create better content, at a glance. By showing you:**
123
+
124
+ * If the title is too long, too short, duplicated, and/or automatically generated.
125
+ * If the description is too long, too short, duplicated, has too many repeated words and/or automatically generated.
126
+ * If the page is indexed, redirected, followed and/or archived, while looking at other WordPress settings.
127
+
128
+ **We call this The SEO Bar. Check out the [Screenshots](https://wordpress.org/plugins/autodescription/screenshots/#plugin-info) to see how it helps you!**
129
+
130
+ > This plugin is fully compatible with the [Domain Mapping plugin by WPMUdev](https://premium.wpmudev.org/project/domain-mapping/) and the [Domain Mapping plugin by Donncha](https://wordpress.org/plugins/wordpress-mu-domain-mapping/).<br>
131
+ > This compatibility ensures **prevention of canonical errors**. This way your site will always be correctly indexed, no matter what you use!<br>
132
+
133
+ = Caching =
134
+
135
+ This plugin's code is highly optimized on PHP-level and uses variable, object and transient caching. This means that there's little extra page load time from this plugin, even with more Meta tags used.
136
+ A caching plugin isn't even needed for this plugin as you won't notice a difference, however it's supported wherever best suited.
137
+
138
+ **If you use object caching:**
139
+ The output will be stored for each page, if you've edited a page the page output Meta will stay the same until the object cache expires. So be sure to clear your object cache or wait until it expires.
140
+
141
+ **Used Caches:**
142
+
143
+ * Server-level Opcode (optimized).
144
+ * Staticvar functions (prevents running code twice or more).
145
+ * Staticvar class (instead of discouraged globals, prevents constructors running multiple times).
146
+ * Object caching for unique database calls and full front-end output.
147
+ * Transients for process intensive operations and persistent communication with front-and back end.
148
+
149
+ **All caching plugins are supported. If you use one, be sure to clear your cache when you want to robots to notice your changes.**
150
+
151
+ = Compatibility =
152
+
153
+ **Basics:**
154
+
155
+ * Full internationalization support through WordPress.org.
156
+ * Extended Multibyte support (CJK).
157
+ * Full Right to Left (RTL) language support.
158
+ * Extended Color vision deficiency accessibility.
159
+ * Screen reader accessibility.
160
+ * MultiSite, this plugin is in fact built upon one.
161
+ * Detection of robots.txt and sitemap.xml files.
162
+ * Detection of theme Title output "doing it right" (or wrong).
163
+
164
+ **Plugins:**
165
+
166
+ * W3 Total Cache, WP Super Cache, Batcache, etc.
167
+ * WooCommerce: Shop Page, Products, Product Breadcrumbs, Product Galleries, Product Categories and Product Tags.
168
+ * Custom Post Types, (all kinds of plugins) with automatic integration.
169
+ * WPMUdev and Donncha's Domain Mapping with full HTTPS support.
170
+ * WPMUdev Avatars for og:image and twitter:image if no other image is found.
171
+ * bbPress: Forums, Topics, Replies.
172
+ * BuddyPress profiles.
173
+ * Ultimate Member profiles.
174
+ * AnsPress Questions, Profiles and Pages, also Canonical errors have been fixed.
175
+ * StudioPress SEO Data Transporter for Posts and Pages.
176
+ * WPML, URLs, full sitemap and per-page/post SEO settings (Documentation is coming soon).
177
+ * qTranslate X, URLs, per-page/post SEO settings, the main language's sitemap (Documentation is coming soon).
178
+ * Polylang, URLs, per-page/post SEO settings, the main language's sitemap.
179
+ * Confirmed Jetpack modules: Custom Content Types (Testimonials, Portfolio), Infinite Scroll, Photon, Sitemaps, Publicize.
180
+ * Most popular SEO plugins, let's not get in each other's way.
181
+ * Many, many more plugins, yet to be confirmed.
182
+ * Divi Builder by Elegant Themes
183
+ * Visual Composer by WPBakery
184
+ * Page Builder by SiteOrigin
185
+ * Beaver Builder by Fastline Media
186
+
187
+ **Themes:**
188
+
189
+ * All themes.
190
+ * Special extended support for Genesis & Genesis SEO. This plugin takes all Post, Page, Category and Tag SEO values from Genesis and uses them within The SEO Framework Options. The easiest upgrade!
191
+
192
+ If you have other popular SEO plugins activated, this plugin will most likely automatically prevent SEO mistakes by deactivating itself on almost every part.
193
+
194
+ = Transferring SEO data using SEO Data Transporter =
195
+
196
+ Because this plugin was initially written to extend the Genesis SEO, it uses the same option name values. This makes transferring from Genesis SEO to The SEO Framework work automatically.
197
+
198
+ > If you didn't use Genesis SEO previously, Nathan Rice (StudioPress) has created an awesome plugin for your needs to transfer your SEO data.
199
+ >
200
+ > Get the [SEO Data Transporter from WordPress.org](https://wordpress.org/plugins/seo-data-transporter/).
201
+ >
202
+ > Usage:<br>
203
+ > 1. Install and activate SEO Data Transporter.<br>
204
+ > 2. Go to the <strong>SEO Data Transporter menu within Tools</strong>.<br>
205
+ > 3. Select your <strong>previous SEO plugin</strong> within the first dropdown menu.<br>
206
+ > 4. Select <strong>Genesis</strong> within the second dropdown menu.<br>
207
+ > 5. Click <strong>Analyze</strong> for extra information about the data transport.<br>
208
+ > 6. Click <strong>Convert</strong> to convert the data.
209
+ >
210
+ > The SEO Framework now uses the same data from the new Genesis SEO settings on Posts, Pages and Taxonomies.
211
+
212
+ = About the Sitemap =
213
+
214
+ The Sitemap generated with The SEO Framework is sufficient for Search Engines to find Posts, Pages and supported Custom Post Types throughout your website.
215
+ It also listens to the noindex settings on each of the items.
216
+ If you however require a more expanded Sitemap, feel free to activate a dedicated Sitemap plugin. The SEO Framework will automatically deactivate its Sitemap functionality when another (known) Sitemap plugin is found.
217
+ If it is not automatically detected and no notification has been provided on the Sitemap Settings, feel free to open a support ticket and it will be addressed carefully.
218
+
219
+ The Breadcrumb script generated by this plugin on Posts will also make sure Google easily finds related categories which aren't included within the Sitemap of this plugin.
220
+
221
+ = Other notes =
222
+
223
+ *Genesis SEO will be disabled upon activating this plugin. This plugin takes over and extends Genesis SEO.*
224
+
225
+ ***The Automatic Description Generation will work with any installation, but it will exclude shortcodes. This means that if you use shortcodes or a page builder, be sure to enter your custom description or the description will fall short.***
226
+
227
+ ***The home page tagline settings won't have any effect on the title output if your theme's title output is not written according to the WordPress standards, which luckily are enforced strongly on new WordPress.org themes since recently.***
228
+
229
+ > <strong>Check out the "[Other Notes](https://wordpress.org/plugins/autodescription/other_notes/#Other-Notes)" tab for the API documentation.</strong>
230
+
231
+ == Installation ==
232
+
233
+ 1. Install The SEO Framework either via the WordPress.org plugin directory, or by uploading the files to your server.
234
+ 1. Either Network Activate this plugin or activate it on a single site.
235
+ 1. That's it!
236
+
237
+ 1. Let the plugin automatically work or fine-tune each page with the metaboxes beneath the content or on the taxonomy pages.
238
+ 1. Adjust the SEO settings through the SEO settings page if desired. Red checkboxes are rather left unchecked. Green checkboxes are default enabled.
239
+
240
+ == Screenshots ==
241
+
242
+ 1. This plugin shows you what you can improve, at a glance. With full color vision deficiency support.
243
+ 2. Hover over any of the SEO Bar's items to see how you can improve the page's SEO. Red is bad, orange is okay, green is good. Blue is situational.
244
+ 3. The dynamic Post/Page SEO settings Metabox. This box is also neatly implemented in Categories and Tags.
245
+ 4. The SEO Settings Page. With over 90 settings, you are in full control. Using the Default Settings and filling in the Knowledge Graph Settings and Social Meta Settings is recommended to do.
246
+
247
+ == Frequently Asked Questions ==
248
+
249
+ = Is The SEO Framework Free? =
250
+
251
+ Absolutely! It will stay free as well, without ads or nags!
252
+
253
+ = I have a feature request, I've found a bug, a plugin is incompatible... =
254
+
255
+ Please visit [the support forums](https://wordpress.org/support/plugin/autodescription) and kindly tell me about it. I try to get back to you within 48 hours. :)
256
+
257
+ = Is this really a Framework? =
258
+
259
+ This plugin is not in particular a framework in a technical sense, but it is built with a framework's mindset. It is however a framework for your website's SEO, a building block that keeps everything together.
260
+ This means that this plugin will do all the great Search Engine Optimization, and also allows for extensions and real-time alterations. For when you really want or need to change something.
261
+ Extensions built for this plugin might just as well work as a standalone. The SEO Framework provides an easier and cached way of doing so, however.
262
+
263
+ = I am a developer, how can I help? =
264
+
265
+ The SEO Framework is currently a one-man project. However, any input is greatly appreciated and everything will be considered.
266
+ Please leave feature requests in the Support Forums and I will talk you through the process of implementing it if necessary.
267
+
268
+ = I'm not a developer, how can I help? =
269
+
270
+ A way of donating is available through the donation link on the plugin website.
271
+ However, you can also greatly help by telling your friends about this plugin :).
272
+
273
+ = I want to remove or change stuff, but I can't find an option! =
274
+
275
+ The SEO Framework is very pluggable on many fields. Please refer to the [Other Notes](https://wordpress.org/plugins/autodescription/other_notes/).
276
+ Please note that a free plugin is underway which will allow you to change all filters from the dashboard. No ETA yet.
277
+
278
+ = No ads! Why? =
279
+
280
+ Nope, no ads! No nags! No links! Never!
281
+ Why? Because I hate them, probably more than you do.
282
+ I also don't want to taint your website from the inside, like many popular plugins do.
283
+ Read more about this on the [Plugin Guidelines, Section 7](https://wordpress.org/plugins/about/guidelines/).
284
+
285
+ ***But how do you make a living?***
286
+
287
+ Currently, The SEO Framework is non-profit.
288
+ This plugin was first released to the public in March 15th, 2015. From there it has grown, from 179 lines of code, to more than 17100 lines.
289
+ With over 600,000 characters of code written, this plugin is absolutely a piece of art in my mind.
290
+ And that's what it should stay, (functional) art.
291
+ I trust that people are good at heart and will tell their friends and family about the things they enjoy the most, what they're excited about, what they find beneficial or even beautiful.
292
+
293
+ With The SEO Framework I try to achieve exactly that. It's made with <3.
294
+
295
+ = Does this plugin collect my data? =
296
+
297
+ Absolutely not! Read more about this on the [Plugin Guidelines, Section 7](https://wordpress.org/plugins/about/guidelines/).
298
+
299
+ = Premium version? =
300
+
301
+ Nope! Only premium extensions. These are planned and being developed.
302
+
303
+ = If a premium extensions is released, what will happen to this plugin? =
304
+
305
+ This plugin is built to be an all-in-one SEO solution for professional environments, so:
306
+
307
+ 1. No advertisements about the premium extensions will be placed within this plugin.
308
+ 1. No features will be removed or replaced for premium-only features.
309
+ 1. The premium extensions will most likely only be used for big-business SEO. Which are very difficult to develop and which will confuse most users anyway.
310
+
311
+ = I've heard about an extension manager, what's that? =
312
+
313
+ Currently it's not available. When it is, it will allow you to download and activate extensions for The SEO Framework. It will support both multisite and single-site and the registration will be based on the Akismet plugin.
314
+
315
+ = The sitemap doesn't contain categories, images, news, etc. is this OK? =
316
+
317
+ This is not a problem. Search Engines love crawling WordPress because its structure is consistent and well known.
318
+ If a visitor can't find a page, why would a Search Engine? Don't rely on your sitemap, but on your content and website's usability.
319
+
320
+ = What's does the application/ld+json script do? =
321
+
322
+ The LD+Json scripts are Search Engine helpers which tell Search Engines how to connect and index the site. They tell the Search Engine if your site contains an internal search engine, what sites you're socially connected to and what page structure you're using.
323
+
324
+ = The (home page) title is different from the og:title, or doesn't do what I want or told it to. =
325
+
326
+ The theme you're using is using outdated standards and is therefore doing it wrong. Inform your theme author about this issue.
327
+
328
+ Give the theme author these two links: https://codex.wordpress.org/Title_Tag https://make.wordpress.org/themes/2015/08/25/title-tag-support-now-required/
329
+
330
+ If you know your way around PHP, you can speed up this process by replacing the `<title>some code here</title>` code with `<title><?php wp_title('') ?></title>` within the `header.php` file of the theme you're using.
331
+
332
+ = The meta data is not being updated, and I'm using a caching plugin. =
333
+
334
+ All The SEO Framework's metadata is put into Object cache when a caching plugin is available. The descriptions and schema.org scripts are put into Transients. Please be sure to clear your cache.
335
+ If you're using W3 Total Cache you might be interested in [this free plugin](https://wordpress.org/plugins/w3tc-auto-pilot/) to do it for you.
336
+
337
+ = Ugh, I don't want anyone to know I'm using The SEO Framework! =
338
+
339
+ Aww :(
340
+ Oh well, here's the filter you need to remove the HTML tags saying where the Meta tags are coming from:
341
+ `add_filter( 'the_seo_framework_indicator', '__return_false' );`
342
+
343
+ = I'm fine with The SEO Framework, but not with you! =
344
+
345
+ Well then! D: We got off on the wrong foot, I guess..
346
+ If you wish to remove only my name from your HTML code, here's the filter:
347
+ `add_filter( 'sybre_waaijer_<3', '__return_false' );`
348
+
349
+ = I want to transport SEO data from other plugins to The SEO Framework, how do I do this? =
350
+
351
+ Please refer to this small guide: [SEO Data Migration](http://theseoframework.com/docs/seo-data-migration/).
352
+ Transporting Terms and Taxonomies SEO data isn't supported.
353
+
354
+ == Changelog ==
355
+
356
+ = 2.6.6 - Semantic Structures =
357
+
358
+ **Release date:**
359
+
360
+ * June 14th 2016
361
+
362
+ **Summarized**
363
+
364
+ * Page builders are great for if you want to style your website, and when you want to do it fast.
365
+ * So from this update the Divi Builder, Visual Composer, Beaver Builder, and the Page Builder by SiteOrigin are now fully supported.
366
+ * Various bugs have also been fixed, to improve your experience. One particular bugfix is related to Polylang; this bugfix should improve performance and reduce other bugs from happening as well.
367
+ * The Automated and Manual description output has been improved in several ways as well.
368
+ * For developers, a class has been removed. All functions within have been moved to more suitable classes. This change will reduce server resource usage and increase overall performance.
369
+
370
+ **SEO Tip of the Update - Know your Keywords:**
371
+
372
+ * To know how your website is found, sign up for Google Search Console. Over a couple of days (or weeks), elegant data has been accumulated about your website.
373
+ * When you go to the Search Analytics within the Search Console, you'll see a list of queries people have used to find your website.
374
+ * The queries can be used as keywords, they are excellent starting points for new post titles and subjects. Go ahead, use them!
375
+
376
+ **For developers - About the class removal:**
377
+
378
+ * Because the plugin makes use of a "[Facade pattern](https://en.wikipedia.org/wiki/Facade_pattern)", all functions within the plugin are available at all times.
379
+ * In trade for increased resource usage this does make the plugin very compatible, faster, and easier to work with.
380
+ * This plugin serves one single responsibility: Outputting SEO data, and determining the reason why. Therefore, although it looks like a "[God object](https://en.wikipedia.org/wiki/God_object)", it's not.
381
+ * Please keep in mind that all functions from any class are available through the "Facade object". Because of this, the class removal shouldn't cause issues for developers.
382
+ * You can call this "Facade object" through a single cached function (rather than a global variable), this cached function is `the_seo_framework()`.
383
+ * If you wish to extend this plugin, feel free to ask me for any details or suggestions at the [Support forums](https://wordpress.org/support/plugin/autodescription).
384
+
385
+ **Detailed Log:**
386
+
387
+ ***There's something to be said about [all these details](https://theseoframework.com/?p=1365#detailed).***
388
+
389
+ = 2.6.5.1 - Schematic Hotfix =
390
+
391
+ **Release date:**
392
+
393
+ * June 9th 2016
394
+
395
+ **Summarized:**
396
+
397
+ * A typo in the Knowledge Graph settings check caused the Knowledge Graph not to be output, even if the option was enabled.
398
+ * Because of this, the LD+Json revisional cache key has been bumped up as well.
399
+ * For developers, as this update is so (relatively) minor, it's not documented on the plugin changelog page, nor a tag will be made.
400
+
401
+ = 2.6.5 - Systematic Support =
402
+
403
+ **Release date:**
404
+
405
+ * June 8th 2016
406
+
407
+ **Summarized:**
408
+
409
+ * Another maintenance release for The SEO Framework 2.6 is coming right at you!
410
+ * While Polylang creates Taxonomies from Pages without acknowledging it being an Archive, I have found a workaround for the Title not to show up correctly.
411
+ * Also, bbPress' Original Post within a topic is now shown correctly, although the bbPress 2.6.0 update will resolve this as well.
412
+ * On special pages like the Forum page of bbPress, no more cache key conflicts will be present, this makes it a recommended update.
413
+ * WPML Multilingual domains are now also fully supported. So instead of just adding a subdomain, this plugin now also takes whole new domains into consideration.
414
+ * The URL generation has also been improved once more. It now contains Canonical Pagination support, and expanded Plain Permalink structure support.
415
+ * And just when you thought new options were available, now you won't be fooled again.
416
+ * For developers it's now much easier to debug, and there are also three (actually four) brand new filters.
417
+ * And last but not least, 404 pages and Search Pages now have a Canonical URL, right towards your Homepage.
418
+
419
+ **SEO Tip of the Update - Skyscraper Technique:**
420
+
421
+ * Have you found something that greatly interests you, and do you think you can write a better article than you've found?
422
+ * Go write it, in your own words and style. Don't forget to make it much longer, better and more detailed. Don't forget to add images.
423
+ * Finally, share the content, reach out to the public, and of course your public. This will increase your rankings significantly.
424
+ * This method is known as the Skyscraper Technique; it's used for and as this SEO tip, minus the images, details or length.
425
+
426
+ **Detailed Log:**
427
+
428
+ ***Sometimes, you just have to look [a little harder](https://theseoframework.com/?p=1339#detailed).***
429
+
430
+ = 2.6.4 - Biographic Consistency =
431
+
432
+ **Release date:**
433
+
434
+ * May 31st 2016
435
+
436
+ **Summarized:**
437
+
438
+ * This update is another maintenance release. It's highly recommended to install this update to prevent incorrect meta values.
439
+ * The fix that has been applied now set at the root cause for the issues that have persisted since the release of 2.6.0, rather than later on.
440
+ * I also bring you Author Descriptions. This description is pulled from the user's profile biography input and is handled the same way as other automated descriptions.
441
+
442
+ ***Sorry, no SEO Tip of the Update this time.***
443
+
444
+ **Detailed log:**
445
+
446
+ ***The smaller things in life can be [found here](https://theseoframework.com/?p=1284#detailed).***
447
+
448
+ = 2.6.3 - Plain Loops =
449
+
450
+ **Release date:**
451
+
452
+ * May 29th 2016
453
+
454
+ **Summarized:**
455
+
456
+ * This update is another maintenance release. This essentially means that bugs have been fixed.
457
+ * Most of the bugs that have been fixed are regarding URL generation for the shortlink and relationships.
458
+ * Next to that, the default "plain" permalink structure has now obtained a Canonical URL output.
459
+ * A much more reliable hook has been found to be used prior to caching the query. This prevents not only an infinite redirect loop in conjunction with some plugins, but will also prevent incorrect metadata being used on some themes.
460
+
461
+ **SEO Tip of the Updated - Keep it Alive:**
462
+
463
+ * Have you written about something already, and is the subject coming up again? Link back to the older publication.
464
+ * About 2 to 5 internal links within the content to older pages will help your visitors understand your hierarchy and find more related content (of which they're already interested in).
465
+ * It will also inform Search Engines on other important pages within your site; this is also known as link juice and will increase your Search Rankings overall.
466
+
467
+ **Detailed log:**
468
+
469
+ ***Want to know which specific bugs have been fixed? Go ahead, you can [read them here](https://theseoframework.com/?p=1269#detailed).***
470
+
471
+ = 2.6.2 - Condensed Associations =
472
+
473
+ **Release date:**
474
+
475
+ * May 26th 2016
476
+
477
+ **Summarized:**
478
+
479
+ * Simply put, this update fixes a few bugs. One of these bugs caused all recognized Post Types to be judged wrongfully for supporting SEO.
480
+ * A very small bug with a major impact has also been fixed. This bug caused WooCommerce Shop Pages to canonicalized to the latest product.
481
+ * This update also makes sure all translations of 2.6.0 and later are put into effect.
482
+ * UTF-8 required languages are now correctly rendered on PHP5.3 and lower within the SEO bar, like Russian.
483
+ * And for developers, the URL generation has been slightly refactored. With more reliable and lighter variables being used throughout the generation.
484
+
485
+ **SEO Tip of the Updated - Geo Targeting:**
486
+
487
+ * Is your website about a local business? Then be sure to sign up for Google Businesses. This will massively increase your search presence, by an artificial result.
488
+ * It also greatly helps to have your server located near your target audience's location. The website will not only respond faster, but Search Engines can pick up descriptive signals from it.
489
+ * If your business resides and only serves in Belgium, for example, it's also better to have a ".be" domain name extension rather than a ".com" domain name extension.
490
+
491
+ **Detailed log:**
492
+
493
+ ***Say hello to [my little friend](https://theseoframework.com/?p=1254#detailed).***
494
+
495
+ = 2.6.1 - Pacified Handling =
496
+
497
+ **Release date:**
498
+
499
+ * May 19th 2016
500
+
501
+ **Summarized:**
502
+
503
+ * The new plugin detection features from 2.6.0 "Pragmatic Foundation" was done in a manner that any arbitrary plugin could be detected with conflicting namespaces.
504
+ * For this reason, I've taken JetPack's philosophical standpoint on this and implemented it within The SEO Framework.
505
+ * Also, the new special Greek/Latin duplicated word counter now also works great on PHP versions 5.2 and 5.3.
506
+ * And in some configurations, the Home Page Title could have been rendered empty, so I got that fixed as well!
507
+ * For developers, many new filters have been added for plugin detection, be sure to check them out in the detailed log.
508
+
509
+ **Feature highlights:**
510
+
511
+ * **New:**
512
+ * Open Graph Product types are now supported on WooCommerce products.
513
+ * **Improved:**
514
+ * The Twitter Image tag has been updated to the latest standards.
515
+ * Revised plugin detection.
516
+ * A probable memory leak on archives has been fixed.
517
+
518
+ **SEO Tip of the Update - Image Descriptions:**
519
+
520
+ * Do you use Images in your Posts and Pages? Be sure to describe them! This way people are able to find your website through the Image Results.
521
+ * This can be easily done when editing the Image through WordPress' Media Library or when inserting an Image in the content. When you click on an Image, you can define various details.
522
+ * The Alt Text is to be used. If that's not found, the Image Caption is used. And if that's also not found, then the Image Title will be used.
523
+ * Make sure it clearly describes the Image, and be aware that it will fall back to it when the browser can't render the Image. It's also a great additions for people who are vision impaired.
524
+
525
+ **Detailed log:**
526
+
527
+ ***Love details? Then head onto [the detailed changelog](https://theseoframework.com/?p=1231#detailed).***
528
+
529
+ = 2.6.0.2 - Tough Understructure =
530
+
531
+ **Release date:**
532
+
533
+ * May 18th 2016
534
+
535
+ **Summarized:**
536
+
537
+ * The SEO Framework 2.6.0 "Pragmatic Foundation" (released May 17th) brought a whole new way for determining the Page Type, synchronous with both the admin side and the front-end.
538
+ * However, in some installations, a few WordPress variables may have not yet been assigned when called within this plugin.
539
+ * This caused a wrong cache initialization, and therefore SEO values and settings were incorrectly rendered.
540
+ * This minor update adds determination for if the WordPress Query or Admin Page Type are accessible before caching anything to make sure no wrong status values are being used throughout the plugin.
541
+
542
+ **Detailed log:**
543
+
544
+ ***Want to read the verbose "detailed" log? Please refer to [the detailed changelog](https://theseoframework.com/?p=1222#detailed).***
545
+
546
+ = 2.6.0.1 - The real Pragmatic Foundation =
547
+
548
+ * This minor update fixes a fatal error after updating to 2.6.0. Sorry about that!
549
+
550
+ = 2.6.0 - Pragmatic Foundation =
551
+
552
+ **Release date:**
553
+
554
+ * May 17th 2016
555
+
556
+ **Preamble:**
557
+
558
+ * This is a dot version bump update, which is done so as the core code has been changed drastically. Nine new classes have been added to maintain structured code, including many more function to fetch data easily and consistently.
559
+ * With hundreds of changes, I had to find a new way to present this update in an understandable manner. So here goes!
560
+
561
+ **Summarized:**
562
+
563
+ * With over 200 notable changes, I bring you a new Pragmatic Foundation.
564
+ * Most importantly, this update allows you to be better informed about your website's index status, through the much improved SEO bar.
565
+ * As the issue of the incorrect title length has finally been found, this update glorifies and updates its plugin's title counter once more.
566
+ * Many new options have been included within the SEO Settings page. Including much desired Title, Description and Schema.org options.
567
+ * WPML compatibility has received a rework, now all canonical URLs in the sitemap and front-end are always correct. The qTranslate X settings are now also being taken into account when outputting canonical URLs.
568
+ * A new script has been added on the front-page. This will make sure the Breadcrumb homepage name will be correct in the Search Engine Results Page.
569
+ * The breadcrumb script has been expanded to work on posts with multiple and nested categories. These scripts now also work on WooCommerce products. Don't be surprised if you suddenly have all kinds of scripts in the header! These scripts help Google and other Search Engines better understand your website.
570
+ * And for developers, with the code expanding rapidly, this update brings new light to the code by reorganizing the code into dedicated classes and is including many major refactorizations.
571
+
572
+ **Feature highlights:**
573
+
574
+ * **New:**
575
+ * WooCommerce Schema.org breadcrumbs.
576
+ * Intelligently nested Schema.org breadcrumbs.
577
+ * Schema.org website name.
578
+ * Vibrant character counters for when you need extra visual assistance.
579
+ * The SEO Bar's tooltip is now able to speak to you through accessiblity tools.
580
+ * More Automated Description options.
581
+ * New Archive Title, Sitemap and Schema.org options.
582
+ * Yandex Sitemap pinging support.
583
+ * Automatic option merging on update.
584
+ * AJAX integration when adding tags.
585
+ * Personalized error handling for developers.
586
+ * Over 150 new public functions for developers.
587
+ * More than 20 brand new filters for developers.
588
+ * WP Query Admin synchronization for developers.
589
+ * Automated setting navigation tabs for developers.
590
+ * **Improved:**
591
+ * Better Automated Description sentence punctuations.
592
+ * Modernized and Smarter SEO bar, with many more conditional checks.
593
+ * Many linguistic improvements, with more flow in the SEO Bar.
594
+ * Extended Title Fix extension support.
595
+ * More efficient cache key generation.
596
+ * Adaptive WPML & qTranslate X URL generation.
597
+ * Better editorial translations.
598
+ * JetPack compatibility.
599
+
600
+ **SEO Tip of the Update: Redirects & Canonical**
601
+
602
+ * A change in the Canonical URL or the use of a 301 Redirect URL indicate that your page has moved.
603
+ * These can be seen by Search Engines as the same. However, the 301 Redirect enforces the relocation of the page to everyone, whereas the Canonical URL softly indicates.
604
+ * When changing the Canonical URL of a Page, you're telling robots to look and elsewhere, be sure to include a link to the new Page on the canonicalized Page to indicate where everything has moved to.
605
+ * However, it's even better to enforce a 301 redirect. This makes sure both your visitors as Search Engines know where to be instantaneously.
606
+ * If you've changed the permalink of a popular post, you should create an empty post that follows one of the said examples.
607
+
608
+ **Announcements:**
609
+
610
+ * Back when I started developing this update, I announced a new extension plugin for The SEO Framework! [Title Fix - The SEO Framework](https://wordpress.org/plugins/the-seo-framework-title-fix/).
611
+ * This update ensures extra compatibility with the Title Fix plugin, this will add back removed title features for if the theme is doing it wrong and when the Title Fix plugin is active.
612
+
613
+ **About: Plugin progression and help wanted:**
614
+
615
+ * This dear project has taken me over 2500 hours to perfect and maintain. This update alone has cost me over 300 hours to complete.
616
+ * I really want to keep this project free. In fact, the upcoming Author SEO is actually planned to be a premium extension, but it will be free.
617
+ * I also want to turn this project into my full-time job, even if it's free from monetization and/or premium content.
618
+ * And I will keep my promises, always: This plugin will stay free of ads, (self-)reference and this plugin will continue to be maintained.
619
+ * All with all, this means I can't do it without your help!
620
+ * Please consider making a donation. The link can be found on the WordPress.org plugin page, or [right here](https://theseoframework.com/?p=572).
621
+ * Thank you so much for your support!
622
+
623
+ **About: Changes in Changelog**
624
+
625
+ * I love to push many changes at once every update, as I'm only happy when everything works. If I find a bug, I'll be sure to fix it!
626
+ * So to clean up the massive all-inclusive changelogs, detailed information on updates are put aside and are visible on the plugin's website.
627
+ * With each update, I try to find better ways for presenting information and I try to minimize confusion.
628
+
629
+ **No Stone Left Unturned:**
630
+
631
+ * This massive update has touched almost every aspect of this plugin.
632
+ * With this, many fixes are put into effect for all known and possibly many unknown bugs. Better standards have been put into place, both for WordPress and the coding world.
633
+ * All changes have been tested thoroughly. However, it's always possible something has been overlooked. If you find anything out of place, let me know in the [plugin support forums](https://wordpress.org/support/plugin/autodescription)!
634
+
635
+ **For everyone - Squared SEO Bar:**
636
+
637
+ * From now on, the SEO bar is flat and squared. This gives a more modern look and feel.
638
+ * Screen Readers are now able to enunciate the tooltip when shown.
639
+
640
+ **For everyone - New Options Merging:**
641
+
642
+ * From this plugin update, new default options are automatically merged with the previous options on update.
643
+ * This way, you don't have to update each site you own with the new recommended features.
644
+ * No options will be overwritten in this process, and it only happens once in the admin area when the plugin is active and the one who can edit the site's SEO options is loading the site.
645
+ * This new feature is multisite compatible and can be disabled through a filter.
646
+ * When this happens, a dismissible update notification will show whenever this happens.
647
+ * The notification is unbranded, it will say "New SEO options have been updated." on the SEO Settings Page and will add "View the new options here." on all other Admin Pages.
648
+
649
+ **For everyone - Schema Markup:**
650
+
651
+ * New schema markup has been added, this helps Search Engines better understand your website.
652
+ * Breadcrumbs have been expanded, to support nested categories and multiple categories. Now you can see multiple breadcrumb scripts to help Search Engines better understand your website's structure.
653
+ * Breadcrumbs scripts now also work on WooCommerce products, enjoy!
654
+
655
+ **For translators - High priority translations:**
656
+
657
+ * Please look for **Front-end output** comments within the translation page to find high-priority translations.
658
+
659
+ **For translators - Linguistic improvements and other changes:**
660
+
661
+ * Objective translations for grammatically gendered noun types like "this post" (male in Dutch) and "this page" (genderless in Dutch) within sentences which are fetched dynamically (like "Product" and "Product Tag" for WooCommerce) couldn't be translated correctly.
662
+ * Therefore, I've changed these types of sentences without any loss of understanding.
663
+ * Small changes within translations happen over time, although I try to keep these to the necessary minimum. Nevertheless, as this is an ongoing project you can expect continuous improvements wherever possible. Translating WordPress and its plugins are a team effort :).
664
+ * Other small changes include conversion of WordPress slang to real English. Like "Paged" to "Paginated".
665
+ * Over time, inconsistencies have appeared within the language used within this plugin. These have been fixed. If you still find any, please notify me through the support forums and I'll address them.
666
+ * Thanks @pidengmor for many (over 40) linguistic improvements, they're really appreciated! Thank you so much for your time!
667
+ * I also want to make a big shout out to all the other translators who have contributed to this plugin! <3
668
+ * And if you wish to become a language editor, just let me know!
669
+
670
+ **For developers - Performance:**
671
+
672
+ * Because this plugin has grown massively in size with this update, the memory use has been increased marginally (to 4MB from 2.7MB). This shouldn't cause issues. And to improve this, singleton methods are going to be considered.
673
+ * Because many more items are being rendered, the plugin is a tad slower than before, even though overall performance has been improved. This won't affect your website's performance noticeably.
674
+ * This plugin has been benchmarked with PHP Xdebug. With it I found that some functions affected the performance marginally, and have thus been fixed accordingly.
675
+
676
+ **For developers - Refactoring classes:**
677
+
678
+ * The classes `AutoDescription_DoingItRight` and `AutoDescription_Generate` among others have been greatly refactored to improve performance and maintainability.
679
+ * Almost all public functions before this overhaul have maintained their initial behavior, the generation of the output has just been split over multiple functions. If this is not the case, and unexpected variables are input, a deprecation notice is output.
680
+
681
+ **For developers - WordPress Query Sync:**
682
+
683
+ * The current WordPress query only works on the front-end.
684
+ * `/inc/classes/query.class.php` contains alternative functions based on the WordPress query. However, although these functions work just like WordPress Core query functions, they also look for the screens within the admin area.
685
+ * These functions do not work on custom post types, yet. But they do work on WooCommerce products and product categories.
686
+ * This resulted in easing the whole code base as it doesn't have to check for admin/front-end per-function anymore. Further improvements are planned, but this update already contains most of the overhaul.
687
+
688
+ **Looking forward - Upcoming features:**
689
+
690
+ Not all planned features made it into 2.6.0. The following features are planned to be released in a future version.
691
+
692
+ * Author SEO (Title, Description, Twitter, Facebook, Google+, etc.).
693
+ * Google+ output options.
694
+ * The SEO Bar options.
695
+ * Per page title additions options.
696
+ * Canonical SEO (URL scheme options).
697
+ * Article modified time output options, just like the Sitemap time output options.
698
+ * Image Description as an excerpt for the attachment's page meta descriptions.
699
+ * Twitter card plugin detection and subtle notification of such.
700
+ * WP.me shortlink integration.
701
+
702
+ **Detailed log:**
703
+
704
+ ***There are a lot more changes in this update which did not make it to this page. Please refer to [the detailed changelog](https://theseoframework.com/?p=1196#detailed).***
705
+
706
+ *What do you think of this change? Let me know at [Slack](https://wordpress.slack.com/messages/@cybr/) (no support questions there, please)!*
707
+
708
+ = Full changelog =
709
+
710
+ **The full changelog can be found [here](http://theseoframework.com/?cat=3).**
711
+
712
+ == Upgrade Notice ==
713
+
714
+ = 2.6.4 =
715
+ Highly recommended update that fixes various query checks and caches.
716
+
717
+ = 2.6.3 =
718
+ This update resolves an issue with the Home Page (blog) Title and Description output.
719
+
720
+ = 2.6.2 =
721
+ This update resolves an issue with the WooCommerce Shop Page Canonical URL. Installing this update is therefore highly recommended.
722
+
723
+ == Other Notes ==
724
+
725
+ = Filters =
726
+
727
+ = Add any of the filters to your theme's functions.php or a plugin to change this plugin's output. =
728
+
729
+ Learn about them here: [The SEO Framework filters](http://theseoframework.com/docs/api/filters/)
730
+
731
+ = Constants =
732
+
733
+ = Overwrite any of these constants in your theme's functions.php or a plugin to change this plugin's output by simply defining the constants. =
734
+
735
+ View them here: [The SEO Framework constants](http://theseoframework.com/docs/api/constants/)
736
+
737
+ = Actions =
738
+
739
+ = Use any of these actions to add your own output. =
740
+
741
+ They are found here: [The SEO Framework actions](http://theseoframework.com/docs/api/actions/)
742
+
743
+ = Settings API =
744
+
745
+ = Add settings to and interact with The SEO Framework. =
746
+
747
+ Read how to here: [The SEO Framework Settings API](http://theseoframework.com/docs/api/settings/)
748
+
749
+ = Beta Version =
750
+
751
+ = Want to test the latest version before it's released? =
752
+
753
+ If there's a beta, it will be available [on Github](https://github.com/sybrew/the-seo-framework). Please note that changes there might not reflect the final outcome of the full version release. Use at own risk.
seotips/index.php ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <?php
2
+ //* Well-lit streets discourage sin, but don't overdo it. - William J. Kennedy
seotips/seotips.txt ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ = 2.3.3 - Domain Age is important =
2
+
3
+ * Is your domain name just registered? Give it some time. It can take from three to six months before Google thinks you deserve a better spot on the search results page. Google doesn't really like new domains because new domains often contain spam. While waiting for a higher spot, be creative! Get awesome content out there for when your domain does get some authority :).
4
+ * Bing and Yahoo, however, don't (really) look at domain age, but rather at domain name. So it's much easier to rank higher early on those search engines. DuckDuckGo is still a mystery but it seems that they look at Geo location, backlinks and titles.
5
+
6
+ = 2.3.4 - Descriptions are organic & Limiting scripts =
7
+
8
+ * Search engines do not always pick out the description you've provided, but sometimes take an organic part based on your search query. This only proves more that your content is more important than SEO optimization. But every little part helps!
9
+ * One little part that helps, a lot, is that you reduce the amount of styles and scripts you push to the visitor. Use a great theme from the WordPress repository or use a premium theme from Genesis or WooThemes. They work according to the standards and are following guidelines set by Automattic. Also avoid using many plugins that provide front-end goodies, but only use what you really need. Your visitors and Search Engines alike will love your site for it!
10
+
11
+ = 2.3.5 - Go Social & Use Knowledge Graph =
12
+
13
+ * Go social! Search Engines don't just look at your website anymore these days, but also take a look at social activity. Most importantly: Having a Facebook, Twitter and/or Google+ page increases your chances to be found.
14
+ * Use the Knowledge Graph options to improve Search Engine presence on your company or trademark by connecting your site socially, be sure to engage with your audience by linking back to content on your site. But don't spam!
15
+
16
+ = 2.3.6 - Plenty of Content, but not too much =
17
+
18
+ * Make sure you have plenty of content for your visitors to read. About 600 to 1900 words of content per page is recommended.
19
+ * Your content is for your visitors, not for search engines.
20
+ * So, do not split up content if it exceeds the recommended amount. If a piece of text can't support itself, readers won't understand the subject and will leave your website.
21
+ * If you have fewer words but you've covered everything that needed to be covered for the page, don't extend it just for the sake of SEO. Do not over-optimize.
22
+
23
+ = 2.3.7 - Sitemaps, just wait and see =
24
+
25
+ * Nowadays, sitemaps are a tool for Search Engines, not for users. This means that styling the sitemap just adds unnecessary resource usage.
26
+ * Having a sitemap available will make sure your content is reached out faster to the Search Index since the Search Engines know what has changed and when. So be sure to enable the lastmod feature if you frequently post new posts or pages!
27
+ * No sitemap? Not a problem! Search Engines will just follow links within your site to find new pages. This default behavior will also continue with a sitemap available, so no pages are missed even if your sitemap has reached its limit.
28
+
29
+ = 2.3.8 - Title Tag binds your site together, answer questions with it =
30
+
31
+ * The Title tag is considered the most important part for SEO. It's what binds the whole site together. That's why this plugin outputs the blog name on every page.
32
+ * Having a well-constructed Title consists of the Blog Name and an addition. This addition should describe the whole page in a few words.
33
+ * Referring back to the Title within the first or last paragraph, or within a subheader as well as in the Description will improve your chances to be found for those words.
34
+ * Alternatively, a question as the title where you give an answer in the first paragraph is considered rich content. It might even put your page in the rich content results on Google.
35
+
36
+ = 2.3.9 - Following visitors, hook them with an introduction =
37
+
38
+ * Search engines follow visitor interaction throughout the pages. If a visitor doesn't stay on a page long enough and hits the back button, your content probably isn't what the visitor was looking for and the Search Engine gets notified and adjusts for future queries.
39
+ * So be sure to create great titles and build a related and constructive first paragraph. If the visitor is hooked, the visitor will stay, read on or click through. Search Engines take note of this and your SERP value goes up.
40
+
41
+ = 2.4.0 - Invalid HTML, Valid Content and use HTML5 =
42
+
43
+ * Invalid HTML doesn't affect your site's SEO value. However, valid HTML is considered best practice, and with a good reason.
44
+ * The good reason for valid HTML is that you can be certain that your website (almost) looks the same on every browser, so you're certain that the visitor will see your website as you intended.
45
+ * However, it doesn't affect your site's SEO value because Search Engines want to put out the best content for your search, not the most 'valid' content.
46
+ * With so many "invalid" websites out there, Search Engines try to read the HTML as many web browsers would: by fixing common mistakes.
47
+ * Still using HTML4 or xHTML? Try to find a way to upgrade to HTML5, its semantic elements are very SEO friendly.
48
+
49
+ = 2.4.1 - Navigation and Naming internal links =
50
+
51
+ * Navigation is very important for your website. Depending on your content, create an easy menu on what visitors need the most.
52
+ * Do not depend on your sitemap too much, if a user can't find a page, then why should a Search Engine?
53
+ * Internal links with aptly named titles are therefore considered best practice. Naming a link "read more here" is for this reason not advised for links that lead to landing pages.
54
+ * Rather use: Read all about "my product name here". Do not forget to make everything natural to read.
55
+
56
+ = 2.4.2 - Canonical URLs =
57
+
58
+ * Canonical URLs are the URLs that lead to exactly where a page is located. It's very important for Search Engines to find the right page, to prevent duplicated content mistakes.
59
+ * The Search Engine will therefore always follow the c