The SEO Framework - Version 2.9.0

Version Description

  • Equitable Erudition =

Courage is found in unlikely places - J.R.R. Tolkien

Release date:

  • 25th March 2017

Summarized:

  • This update focuses on fixing bugs and expanding the API for developers.
  • To expand the API for per page (in-post) SEO settings, tabs had to be added in the SEO settings metabox.
  • The JavaScript code has also been overhauled to improve performance and allow third party implementation.
  • Moreover, the home page settings now allow you to upload a social image.
  • Structured data markup has also been improved, like the inclusion of an alternative Sitename and fixes for Breadcrumb images.
  • The sitemap can now include the blog page. The lastmod value of which listens to multiple pages to automatically determines what's best.
  • Unfortunately, Twitter has removed support for photo cards. This update makes sure that the regarding settings correctly convert to the current format.

Only one week left for the earliest of early-bird discounts:

  • With The SEO Framework - Extension Manager's release, a celebratory discount has been introduced.
  • If you wish to benefit from this lifetime discount, visit the shop for more information.
  • More awesome extensions are coming soon!

Survey:

  • After updating, please fill in our Update Survey: tell us what you like or can be done better.

For everyone: Tabbed In-post SEO layout

  • This release brings tabbed layout to the in-post SEO settings.
  • With this change I've also altered the overall looks, with the help from Daniel.
  • This layout allows extensions from the Extension Manager to be implemented much easier.
  • This layout is self-responsive! This means it's not dependent on the browser-size, but on how much content will fit.
  • This layout therefore allows the SEO settings to be placed in the right sidebar! Take a look at the new screenshots.

For everyone: Browser support

  • This release brings CSS Flexbox to the new in-post SEO settings metabox.
  • The flexbox module is still experimental, in a way that not all browsers correctly support this to the fullest extend.
  • However, support for as many browsers feasible has been implemented. Check out the list. Enjoy!
  • The gist is that Chrome, Firefox, IE10 and Safari 6 or later are supported.

SEO Tip of the Update - Trust in layout

  • Is your website glitchy and has placeholder images, unmatched colors or weirdly contrasting borders all over the place?
  • Consider going for a more modern and clean layout. Strip out what isn't necessary. A clean layout implies professionalism and increases visitors' trust.
  • Even if you're just starting out with your website, remove all that could be but really isn't. Because if you leave in placeholders for the future, it implies amateurism.
  • People want their truth to be confirmed or their problems to be solved. If you can't confirm or solve the layout of your website, then they won't trust you to do so for their issues.
  • Simply put: It doesn't have to be perfect, it just must be done.

Detailed log:

It is some miles, but it will shorten your journey tomorrow.

Download this release

Release Info

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

Code changes from version 2.8.2 to 2.9.0

Files changed (50) hide show
  1. autodescription.php +3 -3
  2. inc/classes/admin-init.class.php +196 -15
  3. inc/classes/admin-pages.class.php +43 -3
  4. inc/classes/cache.class.php +27 -3
  5. inc/classes/core.class.php +25 -21
  6. inc/classes/debug.class.php +6 -3
  7. inc/classes/deprecated.class.php +240 -1
  8. inc/classes/detect.class.php +36 -0
  9. inc/classes/doing-it-right.class.php +42 -21
  10. inc/classes/feed.class.php +93 -52
  11. inc/classes/generate-description.class.php +47 -8
  12. inc/classes/generate-image.class.php +110 -76
  13. inc/classes/generate-ldjson.class.php +195 -82
  14. inc/classes/generate-title.class.php +2 -2
  15. inc/classes/generate-url.class.php +29 -19
  16. inc/classes/generate.class.php +57 -19
  17. inc/classes/init.class.php +60 -34
  18. inc/classes/inpost.class.php +351 -70
  19. inc/classes/load.class.php +5 -5
  20. inc/classes/metaboxes.class.php +17 -9
  21. inc/classes/post-data.class.php +2 -2
  22. inc/classes/query.class.php +64 -27
  23. inc/classes/render.class.php +5 -4
  24. inc/classes/sanitize.class.php +47 -0
  25. inc/classes/site-options.class.php +185 -209
  26. inc/classes/sitemaps.class.php +114 -24
  27. inc/classes/term-data.class.php +8 -6
  28. inc/compat/plugin-bbpress.php +173 -0
  29. inc/compat/plugin-wpmudev-dm.php +7 -0
  30. inc/functions/upgrade.php +68 -2
  31. inc/views/debug/output.php +1 -1
  32. inc/views/inpost/seo-settings-singular.php +286 -0
  33. inc/views/inpost/seo-settings-tt.php +157 -0
  34. inc/views/inpost/seo-settings.php +0 -423
  35. inc/views/metaboxes/feed-metabox.php +3 -3
  36. inc/views/metaboxes/homepage-metabox.php +64 -0
  37. inc/views/metaboxes/sitemaps-metabox.php +14 -5
  38. inc/views/metaboxes/social-metabox.php +22 -17
  39. language/autodescription.pot +336 -293
  40. lib/css/tsf-rtl.css +283 -12
  41. lib/css/tsf-rtl.min.css +1 -1
  42. lib/css/tsf.css +280 -8
  43. lib/css/tsf.min.css +1 -1
  44. lib/js/tsf.externs.js +5 -0
  45. lib/js/tsf.externs.protected.js +0 -6
  46. lib/js/tsf.js +578 -159
  47. lib/js/tsf.min.js +37 -32
  48. load.php +3 -3
  49. readme.txt +62 -160
  50. seotips/seotips.txt +8 -0
autodescription.php CHANGED
@@ -3,7 +3,7 @@
3
* Plugin Name: The SEO Framework
4
* Plugin URI: https://theseoframework.com/
5
* Description: An automated, advanced, accessible, unbranded and extremely fast SEO solution for any WordPress website.
6
- * Version: 2.8.2
7
* Author: Sybre Waaijer
8
* Author URI: https://cyberwire.nl/
9
* License: GPLv3
@@ -46,13 +46,13 @@ defined( 'ABSPATH' ) or die;
46
* Not many caching plugins use CDN in dashboard. What a shame. Firefox does cache.
47
* @since 1.0.0
48
*/
49
- define( 'THE_SEO_FRAMEWORK_VERSION', '2.8.2' );
50
51
/**
52
* Plugin Database version for lightweight version upgrade comparing.
53
* @since 2.7.0
54
*/
55
- define( 'THE_SEO_FRAMEWORK_DB_VERSION', '2804' );
56
57
/**
58
* Plugin options filter.
3
* Plugin Name: The SEO Framework
4
* Plugin URI: https://theseoframework.com/
5
* Description: An automated, advanced, accessible, unbranded and extremely fast SEO solution for any WordPress website.
6
+ * Version: 2.9.0
7
* Author: Sybre Waaijer
8
* Author URI: https://cyberwire.nl/
9
* License: GPLv3
46
* Not many caching plugins use CDN in dashboard. What a shame. Firefox does cache.
47
* @since 1.0.0
48
*/
49
+ define( 'THE_SEO_FRAMEWORK_VERSION', '2.9.0' );
50
51
/**
52
* Plugin Database version for lightweight version upgrade comparing.
53
* @since 2.7.0
54
*/
55
+ define( 'THE_SEO_FRAMEWORK_DB_VERSION', '2903' );
56
57
/**
58
* Plugin options filter.
inc/classes/admin-init.class.php CHANGED
@@ -210,6 +210,7 @@ class Admin_Init extends Init {
210
* 2. Reworked output.
211
* 3. Removed unused caching.
212
* 4. Added dynamic output control.
213
*
214
* @return array $strings The l10n strings.
215
*/
@@ -227,10 +228,10 @@ class Admin_Init extends Init {
227
$counter_type = (int) $this->get_user_option( 0, 'counter_type', 3 );
228
229
//* Enunciate the lenghts of Titles and Descriptions.
230
- $good = __( 'Good', 'autodescription' );
231
- $okay = __( 'Okay', 'autodescription' );
232
- $bad = __( 'Bad', 'autodescription' );
233
- $unknown = __( 'Unknown', 'autodescription' );
234
235
$title_separator = $this->get_separator( 'title' );
236
$description_separator = $this->get_separator( 'description' );
@@ -305,10 +306,21 @@ class Admin_Init extends Init {
305
$additions = $home_tagline ?: $description;
306
}
307
308
$nonce = \wp_create_nonce( 'autodescription-ajax-nonce' );
309
310
return array(
311
'nonce' => $nonce,
312
'i18n' => array(
313
'saveAlert' => \esc_html__( 'The changes you made will be lost if you navigate away from this page.', 'autodescription' ),
314
'confirmReset' => \esc_html__( 'Are you sure you want to reset all SEO settings to their defaults?', 'autodescription' ),
@@ -323,6 +335,7 @@ class Admin_Init extends Init {
323
'hasInput' => $this->is_term_edit() || $this->is_post_edit() || $this->is_seo_settings_page(),
324
'counterType' => \absint( $counter_type ),
325
'titleTagline' => $tagline,
326
),
327
'params' => array(
328
'siteTitle' => \esc_html( \wp_kses_decode_entities( $title ) ),
@@ -339,13 +352,13 @@ class Admin_Init extends Init {
339
/**
340
* Maintains and Returns additional JS l10n.
341
*
342
- * They are put under object 'tsfemL10n.other.$key.[ $val ]'.
343
*
344
* @since 2.8.0
345
* @staticvar object $strings The cached strings object.
346
*
347
- * @param null|string $key The object key. Requires escape.
348
- * @param null|array $val The object val. Requires escape.
349
* @param bool $get Whether to return the cached strings.
350
* @param bool $escape Whether to escape the input.
351
* @return object Early when $get is true
@@ -369,6 +382,73 @@ class Admin_Init extends Init {
369
$strings->$key = $val;
370
}
371
372
/**
373
* CSS for the AutoDescription Bar
374
*
@@ -442,9 +522,9 @@ class Admin_Init extends Init {
442
*
443
* @since 2.2.2
444
*
445
- * @param string $page Menu slug.
446
- * @param array $query_args Optional. Associative array of query string arguments
447
- * (key => value). Default is an empty array.
448
* @return null Return early if first argument is false.
449
*/
450
public function admin_redirect( $page, array $query_args = array() ) {
@@ -469,17 +549,24 @@ class Admin_Init extends Init {
469
* Handles counter option update on AJAX request.
470
*
471
* @since 2.6.0
472
* @access private
473
*/
474
public function wp_ajax_update_counter_type() {
475
476
- if ( $this->is_admin() && defined( 'DOING_AJAX' ) && DOING_AJAX ) {
477
478
//* If current user isn't allowed to edit posts, don't do anything and kill PHP.
479
- if ( ! \current_user_can( 'publish_posts' ) )
480
- exit;
481
482
- \check_ajax_referer( 'autodescription-ajax-nonce', 'nonce' );
483
484
/**
485
* Count up, reset to 0 if needed. We have 4 options: 0, 1, 2, 3
@@ -507,6 +594,100 @@ class Admin_Init extends Init {
507
508
//* Kill PHP.
509
exit;
510
- }
511
}
512
}
210
* 2. Reworked output.
211
* 3. Removed unused caching.
212
* 4. Added dynamic output control.
213
+ * @since 2.9.0 Added boolean $returnValue['states']['isSettingsPage']
214
*
215
* @return array $strings The l10n strings.
216
*/
228
$counter_type = (int) $this->get_user_option( 0, 'counter_type', 3 );
229
230
//* Enunciate the lenghts of Titles and Descriptions.
231
+ $good = \__( 'Good', 'autodescription' );
232
+ $okay = \__( 'Okay', 'autodescription' );
233
+ $bad = \__( 'Bad', 'autodescription' );
234
+ $unknown = \__( 'Unknown', 'autodescription' );
235
236
$title_separator = $this->get_separator( 'title' );
237
$description_separator = $this->get_separator( 'description' );
306
$additions = $home_tagline ?: $description;
307
}
308
309
+ //* @TODO deprecate
310
$nonce = \wp_create_nonce( 'autodescription-ajax-nonce' );
311
312
+ $this->set_js_nonces( array(
313
+ /**
314
+ * Use $this->settings_capability() ?... might conflict with other nonces.
315
+ */
316
+ // 'manage_options' => \current_user_can( 'manage_options' ) ? \wp_create_nonce( 'tsf-ajax-manage_options' ) : false,
317
+ 'upload_files' => \current_user_can( 'upload_files' ) ? \wp_create_nonce( 'tsf-ajax-upload_files' ) : false,
318
+ 'edit_posts' => \current_user_can( 'edit_posts' ) ? \wp_create_nonce( 'tsf-ajax-edit_posts' ) : false,
319
+ ) );
320
+
321
return array(
322
'nonce' => $nonce,
323
+ 'nonces' => $this->get_js_nonces(),
324
'i18n' => array(
325
'saveAlert' => \esc_html__( 'The changes you made will be lost if you navigate away from this page.', 'autodescription' ),
326
'confirmReset' => \esc_html__( 'Are you sure you want to reset all SEO settings to their defaults?', 'autodescription' ),
335
'hasInput' => $this->is_term_edit() || $this->is_post_edit() || $this->is_seo_settings_page(),
336
'counterType' => \absint( $counter_type ),
337
'titleTagline' => $tagline,
338
+ 'isSettingsPage' => $this->is_seo_settings_page(),
339
),
340
'params' => array(
341
'siteTitle' => \esc_html( \wp_kses_decode_entities( $title ) ),
352
/**
353
* Maintains and Returns additional JS l10n.
354
*
355
+ * They are put under object 'tsfemL10n.other[ $key ] = $val'.
356
*
357
* @since 2.8.0
358
* @staticvar object $strings The cached strings object.
359
*
360
+ * @param null|string $key The object key.
361
+ * @param array $val The object val.
362
* @param bool $get Whether to return the cached strings.
363
* @param bool $escape Whether to escape the input.
364
* @return object Early when $get is true
382
$strings->$key = $val;
383
}
384
385
+ /**
386
+ * Sets up additional JS l10n values for nonces.
387
+ *
388
+ * They are put under object 'tsfemL10n.nonces[ $key ] = $val'.
389
+ *
390
+ * @since 2.9.0
391
+ *
392
+ * @param string|array $key Required. The object key or array of keys and values. Requires escape.
393
+ * @param mixed $val The object value if $key is string. Requires escape.
394
+ */
395
+ public function set_js_nonces( $key, $val = null ) {
396
+ $this->get_js_nonces( $key, $val, false );
397
+ }
398
+
399
+ /**
400
+ * Maintains and Returns additional JS l10n.
401
+ *
402
+ * They are put under object 'tsfemL10n.nonces[ $key ] = $val'.
403
+ *
404
+ * If $key is an array, $val is ignored and $key's values are used instead.
405
+ *
406
+ * @since 2.9.0
407
+ * @staticvar object $nonces The cached nonces object.
408
+ *
409
+ * @param string|array $key The object key or array of keys and values. Requires escape.
410
+ * @param mixed $val The object value if $key is string. Requires escape.
411
+ * @param bool $get Whether to return the cached nonces.
412
+ * @return object Early when $get is true
413
+ */
414
+ public function get_js_nonces( $key = null, $val = null, $get = true ) {
415
+
416
+ static $nonces = null;
417
+
418
+ if ( null === $nonces )
419
+ $nonces = new \stdClass();
420
+
421
+ if ( $get )
422
+ return $nonces;
423
+
424
+ if ( is_string( $key ) ) {
425
+ $nonces->$key = $val;
426
+ } elseif ( is_array( $key ) ) {
427
+ foreach ( $key as $k => $v ) {
428
+ $nonces->$k = $v;
429
+ }
430
+ }
431
+ }
432
+
433
+ /**
434
+ * Checks ajax referred set by set_js_nonces based on capability.
435
+ *
436
+ * Performs die() on fail.
437
+ *
438
+ * @since 2.9.0
439
+ * @access private
440
+ * It uses an internally and manually created prefix.
441
+ * @uses WP Core check_ajax_referer()
442
+ * @see @link https://developer.wordpress.org/reference/functions/check_ajax_referer/
443
+ *
444
+ * @return false|int False if the nonce is invalid, 1 if the nonce is valid
445
+ * and generated between 0-12 hours ago, 2 if the nonce is
446
+ * valid and generated between 12-24 hours ago.
447
+ */
448
+ public function check_tsf_ajax_referer( $capability ) {
449
+ return \check_ajax_referer( 'tsf-ajax-' . $capability, 'nonce' );
450
+ }
451
+
452
/**
453
* CSS for the AutoDescription Bar
454
*
522
*
523
* @since 2.2.2
524
*
525
+ * @param string $page Menu slug.
526
+ * @param array $query_args Optional. Associative array of query string arguments
527
+ * (key => value). Default is an empty array.
528
* @return null Return early if first argument is false.
529
*/
530
public function admin_redirect( $page, array $query_args = array() ) {
549
* Handles counter option update on AJAX request.
550
*
551
* @since 2.6.0
552
+ * @since 2.9.0 : 1. Changed capability from 'publish_posts' to 'edit_posts'.
553
+ * 2. Added json header.
554
* @access private
555
*/
556
public function wp_ajax_update_counter_type() {
557
558
+ if ( $this->is_admin() && $this->doing_ajax() ) :
559
560
+ $this->check_tsf_ajax_referer( 'edit_posts' );
561
//* If current user isn't allowed to edit posts, don't do anything and kill PHP.
562
+ if ( ! \current_user_can( 'edit_posts' ) ) {
563
+ //* Remove output buffer.
564
+ $this->clean_reponse_header();
565
566
+ //* Encode and echo results. Requires JSON decode within JS.
567
+ echo json_encode( array( 'type' => 'failure', 'value' => '' ) );
568
+ exit;
569
+ }
570
571
/**
572
* Count up, reset to 0 if needed. We have 4 options: 0, 1, 2, 3
594
595
//* Kill PHP.
596
exit;
597
+ endif;
598
+ }
599
+
600
+ /**
601
+ * Handles cropping of images on AJAX request.
602
+ *
603
+ * Copied from WordPress Core wp_ajax_crop_image.
604
+ * Adjusted: 1. It accepts capability 'upload_files', instead of 'customize'.
605
+ * 2. It now only accepts TSF own AJAX nonces.
606
+ * 3. It now only accepts context 'tsf-image'
607
+ * 4. It no longer accepts a default context.
608
+ *
609
+ * @since 2.9.0
610
+ * @access private
611
+ */
612
+ public function wp_ajax_crop_image() {
613
+
614
+ $this->check_tsf_ajax_referer( 'upload_files' );
615
+ if ( ! \current_user_can( 'upload_files' ) )
616
+ \wp_send_json_error();
617
+
618
+ $attachment_id = \absint( $_POST['id'] );
619
+
620
+ $context = str_replace( '_', '-', $_POST['context'] );
621
+ $data = array_map( 'absint', $_POST['cropDetails'] );
622
+ $cropped = \wp_crop_image( $attachment_id, $data['x1'], $data['y1'], $data['width'], $data['height'], $data['dst_width'], $data['dst_height'] );
623
+
624
+ if ( ! $cropped || \is_wp_error( $cropped ) )
625
+ \wp_send_json_error( array( 'message' => \esc_js__( 'Image could not be processed.', 'autodescription' ) ) );
626
+
627
+ switch ( $context ) :
628
+ case 'tsf-image':
629
+
630
+ /**
631
+ * Fires before a cropped image is saved.
632
+ *
633
+ * Allows to add filters to modify the way a cropped image is saved.
634
+ *
635
+ * @since 4.3.0 WordPress Core
636
+ *
637
+ * @param string $context The Customizer control requesting the cropped image.
638
+ * @param int $attachment_id The attachment ID of the original image.
639
+ * @param string $cropped Path to the cropped image file.
640
+ */
641
+ \do_action( 'wp_ajax_crop_image_pre_save', $context, $attachment_id, $cropped );
642
+
643
+ /** This filter is documented in wp-admin/custom-header.php */
644
+ $cropped = \apply_filters( 'wp_create_file_in_uploads', $cropped, $attachment_id ); // For replication.
645
+
646
+ $parent_url = \wp_get_attachment_url( $attachment_id );
647
+ $url = str_replace( basename( $parent_url ), basename( $cropped ), $parent_url );
648
+
649
+ $size = @getimagesize( $cropped );
650
+ $image_type = ( $size ) ? $size['mime'] : 'image/jpeg';
651
+
652
+ $object = array(
653
+ 'post_title' => basename( $cropped ),
654
+ 'post_content' => $url,
655
+ 'post_mime_type' => $image_type,
656
+ 'guid' => $url,
657
+ 'context' => $context,
658
+ );
659
+
660
+ $attachment_id = \wp_insert_attachment( $object, $cropped );
661
+ $metadata = \wp_generate_attachment_metadata( $attachment_id, $cropped );
662
+
663
+ /**
664
+ * Filters the cropped image attachment metadata.
665
+ *
666
+ * @since 4.3.0 WordPress Core
667
+ *
668
+ * @see wp_generate_attachment_metadata()
669
+ *
670
+ * @param array $metadata Attachment metadata.
671
+ */
672
+ $metadata = \apply_filters( 'wp_ajax_cropped_attachment_metadata', $metadata );
673
+ \wp_update_attachment_metadata( $attachment_id, $metadata );
674
+
675
+ /**
676
+ * Filters the attachment ID for a cropped image.
677
+ *
678
+ * @since 4.3.0 WordPress Core
679
+ *
680
+ * @param int $attachment_id The attachment ID of the cropped image.
681
+ * @param string $context The Customizer control requesting the cropped image.
682
+ */
683
+ $attachment_id = \apply_filters( 'wp_ajax_cropped_attachment_id', $attachment_id, $context );
684
+ break;
685
+
686
+ default :
687
+ \wp_send_json_error( array( 'message' => \esc_js__( 'Image could not be processed.', 'autodescription' ) ) );
688
+ break;
689
+ endswitch;
690
+
691
+ \wp_send_json_success( \wp_prepare_attachment_for_js( $attachment_id ) );
692
}
693
}
inc/classes/admin-pages.class.php CHANGED
@@ -344,6 +344,7 @@ class Admin_Pages extends Inpost {
344
* Create a 'settings_boxes' method to add metaboxes.
345
*
346
* @since 2.2.2
347
*/
348
public function admin() {
349
@@ -394,6 +395,7 @@ class Admin_Pages extends Inpost {
394
* Display notices on the save or reset of settings.
395
*
396
* @since 2.2.2
397
*
398
* @return void
399
*/
@@ -463,8 +465,8 @@ class Admin_Pages extends Inpost {
463
* @since 2.2.2
464
* @uses $this->get_field_id() Constructs id attributes for use in form fields.
465
*
466
- * @param string $id Field id base
467
- * @param boolean $echo echo or return
468
* @return string Full field id
469
*/
470
public function field_id( $id, $echo = true ) {
@@ -508,7 +510,7 @@ class Admin_Pages extends Inpost {
508
* @since 2.6.0
509
*
510
* @param string $input The input to wrap. Should already be escaped.
511
- * @param bool $echo Whether to echo or return.
512
* @return Wrapped $input.
513
*/
514
public function wrap_fields( $input = '', $echo = false ) {
@@ -815,4 +817,42 @@ class Admin_Pages extends Inpost {
815
return $class;
816
}
817
}
818
}
344
* Create a 'settings_boxes' method to add metaboxes.
345
*
346
* @since 2.2.2
347
+ * @todo deprecate (method name is arbitrary) and convert to view.
348
*/
349
public function admin() {
350
395
* Display notices on the save or reset of settings.
396
*
397
* @since 2.2.2
398
+ * @todo convert the "request" into secure "error_notice" option. See TSF Extension Manager.
399
*
400
* @return void
401
*/
465
* @since 2.2.2
466
* @uses $this->get_field_id() Constructs id attributes for use in form fields.
467
*
468
+ * @param string $id Field id base.
469
+ * @param boolean $echo Whether to escape echo or just return.
470
* @return string Full field id
471
*/
472
public function field_id( $id, $echo = true ) {
510
* @since 2.6.0
511
*
512
* @param string $input The input to wrap. Should already be escaped.
513
+ * @param boolean $echo Whether to escape echo or just return.
514
* @return Wrapped $input.
515
*/
516
public function wrap_fields( $input = '', $echo = false ) {
817
return $class;
818
}
819
}
820
+
821
+ /**
822
+ * Returns social image uploader form button.
823
+ * Also registers additional i18n strings for JS.
824
+ *
825
+ * @since 2.8.0
826
+ * @todo optimize? Sanitation and translations are duplicated -> microseconds...
827
+ *
828
+ * @param string $input_id Required. The HTML input id to pass URL into.
829
+ * @return string The image uploader button.
830
+ */
831
+ public function get_social_image_uploader_form( $input_id ) {
832
+
833
+ if ( ! $input_id )
834
+ return '';
835
+
836
+ $content = sprintf( '<a href="%1$s" class="tsf-set-social-image button button-primary button-small" title="%2$s" id="%3$s-select" data-inputid="%3$s">%4$s</a>',
837
+ \esc_url( \get_upload_iframe_src( 'image', $this->get_the_real_ID() ) ),
838
+ \esc_attr_x( 'Select social image', 'Button hover', 'autodescription' ),
839
+ \esc_attr( $input_id ),
840
+ \esc_html__( 'Select Image', 'autodescription' )
841
+ );
842
+
843
+ $button_labels = array(
844
+ 'select' => \esc_attr__( 'Select Image', 'autodescription' ),
845
+ 'select_title' => \esc_attr_x( 'Select social image', 'Button hover', 'autodescription' ),
846
+ 'change' => \esc_attr__( 'Change Image', 'autodescription' ),
847
+ 'remove' => \esc_attr__( 'Remove Image', 'autodescription' ),
848
+ 'remove_title' => \esc_attr__( 'Remove selected social image', 'autodescription' ),
849
+ 'frame_title' => \esc_attr_x( 'Select Social Image', 'Frame title', 'autodescription' ),
850
+ 'frame_button' => \esc_attr__( 'Use this image', 'autodescription' ),
851
+ );
852
+
853
+ //* Already escaped. Turn off escaping.
854
+ $this->additional_js_l10n( \esc_attr( $input_id ), $button_labels, false, false );
855
+
856
+ return $content;
857
+ }
858
}
inc/classes/cache.class.php CHANGED
@@ -155,11 +155,14 @@ class Cache extends Sitemaps {
155
* Flushes front-page and global transients that can be affected by options.
156
*
157
* @since 2.8.0
158
*/
159
public function delete_main_cache() {
160
$this->delete_cache( 'front' );
161
$this->delete_cache( 'sitemap' );
162
$this->delete_cache( 'robots' );
163
}
164
165
/**
@@ -191,11 +194,25 @@ class Cache extends Sitemaps {
191
* @since 2.8.0
192
*
193
* @param int $user_id The User ID that has been updated.
194
*/
195
public function delete_author_cache( $user_id ) {
196
return $this->delete_cache( 'author', $user_id );
197
}
198
199
/**
200
* Handles all kinds of cache for removal.
201
* Main cache deletion function handler.
@@ -275,9 +292,14 @@ class Cache extends Sitemaps {
275
/**
276
* Flush whole object cache group.
277
* Set here for external functions to use. It works because of magic methods.
278
*/
279
case 'objectflush' :
280
- if ( $this->use_object_cache ) {
281
if ( isset( $GLOBALS['wp_object_cache']->cache['the_seo_framework'] ) ) {
282
$_cache = $GLOBALS['wp_object_cache']->cache;
283
unset( $_cache['the_seo_framework'] );
@@ -560,7 +582,7 @@ class Cache extends Sitemaps {
560
561
if ( $this->is_404() ) {
562
$the_id = '_404_';
563
- } elseif ( ( $this->is_front_page( $page_id ) ) || ( $this->is_admin() && $this->is_seo_settings_page( true ) ) ) {
564
//* Front/HomePage.
565
$the_id = $this->generate_front_page_cache_key();
566
} elseif ( $this->is_blog_page( $page_id ) ) {
@@ -838,7 +860,7 @@ class Cache extends Sitemaps {
838
839
if ( isset( $_POST['permalink_structure'] ) || isset( $_POST['category_base'] ) ) {
840
841
- if ( check_admin_referer( 'update-permalink' ) )
842
return $this->delete_cache( 'sitemap' );
843
}
844
@@ -965,6 +987,8 @@ class Cache extends Sitemaps {
965
*
966
* @since 2.5.2
967
* @since 2.7.0 : Will always set "doing it wrong" transient, even if it was "doing it right" earlier.
968
*
969
* @NOTE: Ignores transient debug constant and options.
970
*
155
* Flushes front-page and global transients that can be affected by options.
156
*
157
* @since 2.8.0
158
+ * @since 2.9.0 : Added object cache flush.
159
+ * @TODO make 2.9 note work.
160
*/
161
public function delete_main_cache() {
162
$this->delete_cache( 'front' );
163
$this->delete_cache( 'sitemap' );
164
$this->delete_cache( 'robots' );
165
+ // $this->delete_cache( 'objectflush' );
166
}
167
168
/**
194
* @since 2.8.0
195
*
196
* @param int $user_id The User ID that has been updated.
197
+ * @return bool True on success, false on failure.
198
*/
199
public function delete_author_cache( $user_id ) {
200
return $this->delete_cache( 'author', $user_id );
201
}
202
203
+ /**
204
+ * Deletes object cache.
205
+ *
206
+ * @since 2.9.0
207
+ * @TODO make this work.
208
+ *
209
+ * @return bool True on success, false on failure.
210
+ */
211
+ public function delete_object_cache() {
212
+ return false;
213
+ // return $this->delete_cache( 'objectflush' );
214
+ }
215
+
216
/**
217
* Handles all kinds of cache for removal.
218
* Main cache deletion function handler.
292
/**
293
* Flush whole object cache group.
294
* Set here for external functions to use. It works because of magic methods.
295
+ *
296
+ * @NOTE Other caching plugins can override these groups. Therefore this
297
+ * does NOT work.
298
+ * @TODO make this work.
299
*/
300
case 'objectflush' :
301
+ //* @NOTE false can't pass.
302
+ if ( false && $this->use_object_cache ) {
303
if ( isset( $GLOBALS['wp_object_cache']->cache['the_seo_framework'] ) ) {
304
$_cache = $GLOBALS['wp_object_cache']->cache;
305
unset( $_cache['the_seo_framework'] );
582
583
if ( $this->is_404() ) {
584
$the_id = '_404_';
585
+ } elseif ( ( $this->is_real_front_page() || $this->is_front_page_by_id( $page_id ) ) || ( $this->is_admin() && $this->is_seo_settings_page( true ) ) ) {
586
//* Front/HomePage.
587
$the_id = $this->generate_front_page_cache_key();
588
} elseif ( $this->is_blog_page( $page_id ) ) {
860
861
if ( isset( $_POST['permalink_structure'] ) || isset( $_POST['category_base'] ) ) {
862
863
+ if ( \check_admin_referer( 'update-permalink' ) )
864
return $this->delete_cache( 'sitemap' );
865
}
866
987
*
988
* @since 2.5.2
989
* @since 2.7.0 : Will always set "doing it wrong" transient, even if it was "doing it right" earlier.
990
+ * WordPress get_all_options will prevent multiple DB writes.
991
+ * Returning false on set_transient() as it was already set to '0'.
992
*
993
* @NOTE: Ignores transient debug constant and options.
994
*
inc/classes/core.class.php CHANGED
@@ -133,13 +133,17 @@ class Core {
133
* Destroys output buffer, if any. To be used with AJAX and XML to clear any PHP errors or dumps.
134
*
135
* @since 2.8.0
136
*
137
* @return bool True on clear. False otherwise.
138
*/
139
protected function clean_reponse_header() {
140
141
- if ( ob_get_level() && ob_get_contents() ) {
142
- ob_clean();
143
return true;
144
}
145
@@ -152,13 +156,12 @@ class Core {
152
*
153
* @since 2.7.0
154
* @access private
155
*
156
* @param string $view The file name.
157
* @param array $args The arguments to be supplied within the file name.
158
* Each array key is converted to a variable with its value attached.
159
* @param string $instance The instance suffix to call back upon.
160
- *
161
- * @credits Akismet For some code.
162
*/
163
public function get_view( $view, array $args = array(), $instance = 'main' ) {
164
@@ -721,6 +724,7 @@ class Core {
721
* Calculates the relative font color according to the background.
722
*
723
* @since 2.8.0
724
*
725
* @param string $hex The 3 to 6 character RGB hex. '#' prefix is supported.
726
* @return string The hexadecimal RGB relative font color, without '#' prefix.
@@ -747,12 +751,10 @@ class Core {
747
$rel_lum = 1 - ( $sr + $sg + $sb ) / 255;
748
749
//* Convert to relative intvals between 1 and 0 for L from HSL
750
- /*
751
- $rr = $r / 255;
752
- $rg = $g / 255;
753
- $rb = $b / 255;
754
- $luminance = ( min( $rr, $rg, $rb ) + max( $rr, $rg, $rb ) ) / 2;
755
- */
756
757
//* Get perceptive luminance (greyscale) according to W3C.
758
$gr = 0.2989 * $r;
@@ -763,14 +765,14 @@ class Core {
763
//* Invert colors if they hit luminance boundaries.
764
if ( $rel_lum < 0.5 ) {
765
//* Build dark. Add softness.
766
- $gr = $gr * $per_lum / 8 / 0.2989 + 8 * 0.2989;
767
- $gg = $gg * $per_lum / 8 / 0.5870 + 8 * 0.5870;
768
- $gb = $gb * $per_lum / 8 / 0.1140 + 8 * 0.1140;
769
} else {
770
//* Build light. Add (subtract) softness.
771
- $gr = 255 - $gr * $per_lum / 8 * 0.2989 - 8 * 0.2989;
772
- $gg = 255 - $gg * $per_lum / 8 * 0.5870 - 8 * 0.5870;
773
- $gb = 255 - $gb * $per_lum / 8 * 0.1140 - 8 * 0.1140;
774
}
775
776
//* Complete hexvals.
@@ -788,6 +790,9 @@ class Core {
788
* Note: This code has been rightfully stolen from the Extension Manager plugin (sorry Sybre!).
789
*
790
* @since 2.8.0
791
* @link https://wordpress.org/plugins/about/readme.txt
792
*
793
* @param string $text The text that might contain markdown. Expected to be escaped.
@@ -827,8 +832,7 @@ class Core {
827
foreach ( $md_types as $type ) :
828
switch ( $type ) :
829
case 'strong' :
830
- //* Considers word boundary. @TODO consider removing this?
831
- $count = preg_match_all( '/(?:\*{2})\b([^\*{2}]+)(?:\*{2})/', $text, $matches, PREG_PATTERN_ORDER );
832
833
for ( $i = 0; $i < $count; $i++ ) {
834
$text = str_replace(
@@ -840,7 +844,7 @@ class Core {
840
break;
841
842
case 'em' :
843
- $count = preg_match_all( '/(?:\*{1})([^\*{1}]+)(?:\*{1})/', $text, $matches, PREG_PATTERN_ORDER );
844
845
for ( $i = 0; $i < $count; $i++ ) {
846
$text = str_replace(
@@ -852,7 +856,7 @@ class Core {
852
break;
853
854
case 'code' :
855
- $count = preg_match_all( '/(?:`{1})([^`{1}]+)(?:`{1})/', $text, $matches, PREG_PATTERN_ORDER );
856
857
for ( $i = 0; $i < $count; $i++ ) {
858
$text = str_replace(
@@ -871,7 +875,7 @@ class Core {
871
case 'h1' :
872
$amount = filter_var( $type, FILTER_SANITIZE_NUMBER_INT );
873
//* Considers word non-boundary. @TODO consider removing this?
874
- $expression = "/(?:={{$amount}})\B([^={{$amount}}]+?)\B(?:={{$amount}})/";
875
$count = preg_match_all( $expression, $text, $matches, PREG_PATTERN_ORDER );
876
877
for ( $i = 0; $i < $count; $i++ ) {
133
* Destroys output buffer, if any. To be used with AJAX and XML to clear any PHP errors or dumps.
134
*
135
* @since 2.8.0
136
+ * @since 2.9.0 : Now flushes all levels rather than just the latest one.
137
*
138
* @return bool True on clear. False otherwise.
139
*/
140
protected function clean_reponse_header() {
141
142
+ if ( $level = ob_get_level() ) {
143
+ while ( $level ) {
144
+ ob_end_clean();
145
+ $level--;
146
+ }
147
return true;
148
}
149
156
*
157
* @since 2.7.0
158
* @access private
159
+ * @credits Akismet For some code.
160
*
161
* @param string $view The file name.
162
* @param array $args The arguments to be supplied within the file name.
163
* Each array key is converted to a variable with its value attached.
164
* @param string $instance The instance suffix to call back upon.
165
*/
166
public function get_view( $view, array $args = array(), $instance = 'main' ) {
167
724
* Calculates the relative font color according to the background.
725
*
726
* @since 2.8.0
727
+ * @since 2.9.0 Now adds a little more relative softness based on rel_lum.
728
*
729
* @param string $hex The 3 to 6 character RGB hex. '#' prefix is supported.
730
* @return string The hexadecimal RGB relative font color, without '#' prefix.
751
$rel_lum = 1 - ( $sr + $sg + $sb ) / 255;
752
753
//* Convert to relative intvals between 1 and 0 for L from HSL
754
+ // $rr = $r / 255;
755
+ // $rg = $g / 255;
756
+ // $rb = $b / 255;
757
+ // $luminance = ( min( $rr, $rg, $rb ) + max( $rr, $rg, $rb ) ) / 2;
758
759
//* Get perceptive luminance (greyscale) according to W3C.
760
$gr = 0.2989 * $r;
765
//* Invert colors if they hit luminance boundaries.
766
if ( $rel_lum < 0.5 ) {
767
//* Build dark. Add softness.
768
+ $gr = $gr * $per_lum / 8 / 0.2989 + 8 * 0.2989 / $rel_lum;
769
+ $gg = $gg * $per_lum / 8 / 0.5870 + 8 * 0.5870 / $rel_lum;
770
+ $gb = $gb * $per_lum / 8 / 0.1140 + 8 * 0.1140 / $rel_lum;
771
} else {
772
//* Build light. Add (subtract) softness.
773
+ $gr = 255 - $gr * $per_lum / 8 * 0.2989 - 8 * 0.2989 / $rel_lum;
774
+ $gg = 255 - $gg * $per_lum / 8 * 0.5870 - 8 * 0.5870 / $rel_lum;
775
+ $gb = 255 - $gb * $per_lum / 8 * 0.1140 - 8 * 0.1140 / $rel_lum;
776
}
777
778
//* Complete hexvals.
790
* Note: This code has been rightfully stolen from the Extension Manager plugin (sorry Sybre!).
791
*
792
* @since 2.8.0
793
+ * @since 2.9.0 : 1. Removed word boundary requirement for strong.
794
+ * 2. Now accepts regex count their numeric values in string.
795
+ * 3. Fixed header 1~6 calculation.
796
* @link https://wordpress.org/plugins/about/readme.txt
797
*
798
* @param string $text The text that might contain markdown. Expected to be escaped.
832
foreach ( $md_types as $type ) :
833
switch ( $type ) :
834
case 'strong' :
835
+ $count = preg_match_all( '/(?:\*{2})([^\*{\2}]+)(?:\*{2})/', $text, $matches, PREG_PATTERN_ORDER );
836
837
for ( $i = 0; $i < $count; $i++ ) {
838
$text = str_replace(
844
break;
845
846
case 'em' :
847
+ $count = preg_match_all( '/(?:\*{1})([^\*{\1}]+)(?:\*{1})/', $text, $matches, PREG_PATTERN_ORDER );
848
849
for ( $i = 0; $i < $count; $i++ ) {
850
$text = str_replace(
856
break;
857
858
case 'code' :
859
+ $count = preg_match_all( '/(?:`{1})([^`{\1}]+)(?:`{1})/', $text, $matches, PREG_PATTERN_ORDER );
860
861
for ( $i = 0; $i < $count; $i++ ) {
862
$text = str_replace(
875
case 'h1' :
876
$amount = filter_var( $type, FILTER_SANITIZE_NUMBER_INT );
877
//* Considers word non-boundary. @TODO consider removing this?
878
+ $expression = sprintf( '/(?:\={%1$s})\B([^\={\%1$s}]+)\B(?:\={%1$s})/', $amount );
879
$count = preg_match_all( $expression, $text, $matches, PREG_PATTERN_ORDER );
880
881
for ( $i = 0; $i < $count; $i++ ) {
inc/classes/debug.class.php CHANGED
@@ -895,7 +895,8 @@ final class Debug implements Debug_Interface {
895
//* Don't register this output.
896
$this->add_debug_output = false;
897
898
- $output = $tsf->the_description()
899
. $tsf->og_image()
900
. $tsf->og_locale()
901
. $tsf->og_type()
@@ -925,7 +926,7 @@ final class Debug implements Debug_Interface {
925
926
$timer = '<div style="display:inline-block;width:100%;padding:20px;border-bottom:1px solid #ccc;">Generated in: ' . number_format( $this->timer(), 5 ) . ' seconds</div>' ;
927
928
- $title = $tsf->is_admin() ? 'Expected SEO Output' : 'Current SEO Output';
929
$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>';
930
931
//* Escape it, replace EOL with breaks, and style everything between quotes (which are ending with space).
@@ -1020,6 +1021,7 @@ final class Debug implements Debug_Interface {
1020
$tsf = \the_seo_framework();
1021
1022
//* Only get true/false values.
1023
$is_404 = $tsf->is_404();
1024
$is_admin = $tsf->is_admin();
1025
$is_attachment = $tsf->is_attachment();
@@ -1034,7 +1036,8 @@ final class Debug implements Debug_Interface {
1034
$is_date = $tsf->is_date();
1035
$is_day = $tsf->is_day();
1036
$is_feed = $tsf->is_feed();
1037
- $is_front_page = $tsf->is_front_page();
1038
$is_home = $tsf->is_home();
1039
$is_month = $tsf->is_month();
1040
$is_page = $tsf->is_page();
895
//* Don't register this output.
896
$this->add_debug_output = false;
897
898
+ $output = $tsf->robots()
899
+ . $tsf->the_description()
900
. $tsf->og_image()
901
. $tsf->og_locale()
902
. $tsf->og_type()
926
927
$timer = '<div style="display:inline-block;width:100%;padding:20px;border-bottom:1px solid #ccc;">Generated in: ' . number_format( $this->timer(), 5 ) . ' seconds</div>' ;
928
929
+ $title = $tsf->is_admin() ? 'Expected SEO Output' : 'Determined SEO Output';
930
$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>';
931
932
//* Escape it, replace EOL with breaks, and style everything between quotes (which are ending with space).
1021
$tsf = \the_seo_framework();
1022
1023
//* Only get true/false values.
1024
+ $page_id = $tsf->get_the_real_ID();
1025
$is_404 = $tsf->is_404();
1026
$is_admin = $tsf->is_admin();
1027
$is_attachment = $tsf->is_attachment();
1036
$is_date = $tsf->is_date();
1037
$is_day = $tsf->is_day();
1038
$is_feed = $tsf->is_feed();
1039
+ $is_real_front_page = $tsf->is_real_front_page();
1040
+ $is_front_page_by_id = $tsf->is_front_page_by_id( $tsf->get_the_real_ID() );
1041
$is_home = $tsf->is_home();
1042
$is_month = $tsf->is_month();
1043
$is_page = $tsf->is_page();
inc/classes/deprecated.class.php CHANGED
@@ -97,7 +97,7 @@ final class Deprecated {
97
98
\the_seo_framework()->_deprecated_function( 'The_SEO_Framework_Transients::' . __FUNCTION__, '2.7.0', 'The_SEO_Framework_Transients::delete_auto_description_frontpage_transient()' );
99
100
- \the_seo_framework()->delete_auto_description_transient( the_seo_framework()->get_the_front_page_ID(), '', 'frontpage' );
101
102
return $old_option;
103
}
@@ -502,4 +502,243 @@ final class Deprecated {
502
503
return $data;
504
}
505
}
97
98
\the_seo_framework()->_deprecated_function( 'The_SEO_Framework_Transients::' . __FUNCTION__, '2.7.0', 'The_SEO_Framework_Transients::delete_auto_description_frontpage_transient()' );
99
100
+ \the_seo_framework()->delete_auto_description_transient( \the_seo_framework()->get_the_front_page_ID(), '', 'frontpage' );
101
102
return $old_option;
103
}
502
503
return $data;
504
}
505
+
506
+ /**
507
+ * Fetches og:image URL.
508
+ *
509
+ * @since 2.2.2
510
+ * @since 2.2.8 : Added theme icon detection.
511
+ * @since 2.5.2 : Added args filters.
512
+ * @since 2.8.0 : 1. Added theme logo detection.
513
+ * 2. Added inpost image selection detection.
514
+ * @since 2.8.2 : 1. Now returns something on post ID 0.
515
+ * 2. Added SEO settings fallback image selection detection.
516
+ * @since 2.9.0 : 1. Added 'skip_fallback' option to arguments.
517
+ * 2. Added 'escape' option to arguments.
518
+ * 3. First parameter is now arguments. Fallback for integer is added.
519
+ * 4. Second parameter is now deprecated.
520
+ * 5. Deprecated.
521
+ * @deprecated Use get_social_image instead.
522
+ *
523
+ * @param int|array $args The image arguments.
524
+ * Was: $post_id.
525
+ * Warning: Integer usage is only used for backwards compat.
526
+ * @param array $depr_args, Deprecated;
527
+ * Was $args The image arguments.
528
+ * @param bool $escape Whether to escape the image URL.
529
+ * Deprecated: You should use $args['escape'].
530
+ * @return string the image URL.
531
+ */
532
+ public function get_image( $args = array(), $depr_args = '', $depr_escape = true ) {
533
+
534
+ $tsf = \the_seo_framework();
535
+
536
+ $tsf->_deprecated_function( 'the_seo_framework()->get_image()', '2.9.0', 'the_seo_framework()->get_social_image()' );
537
+
538
+ if ( is_int( $args ) || is_array( $depr_args ) ) {
539
+ $tsf->_doing_it_wrong( __METHOD__, 'First parameter is now used for arguments. Second parameter is deprecated.', '2.9.0' );
540
+
541
+ $post_id = $args;
542
+ $args = array();
543
+
544
+ /**
545
+ * Backwards compat with parse args.
546
+ * @since 2.5.0
547
+ */
548
+ if ( ! isset( $depr_args['post_id'] ) ) {
549
+ $args['post_id'] = $post_id ?: ( $tsf->is_singular( $post_id ) ? $tsf->get_the_real_ID() : 0 );
550
+ }
551
+
552
+ if ( is_array( $depr_args ) ) {
553
+ $args = \wp_parse_args( $depr_args, $args );
554
+ }
555
+ }
556
+
557
+ if ( false === $depr_escape ) {
558
+ $tsf->_doing_it_wrong( __METHOD__, 'Third parameter has been deprecated. Use `$args["escape"] => false` instead.', '2.9.0' );
559
+ $args['escape'] = false;
560
+ }
561
+
562
+ $args = $tsf->reparse_image_args( $args );
563
+
564
+ //* 0. Image from argument.
565
+ pre_0 : {
566
+ if ( $image = $args['image'] )
567
+ goto end;
568
+ }
569
+
570
+ //* Check if there are no disallowed arguments.
571
+ $all_allowed = empty( $args['disallowed'] );
572
+
573
+ //* 1. Fetch image from homepage SEO meta upload.
574
+ if ( $all_allowed || false === in_array( 'homemeta', $args['disallowed'], true ) ) {
575
+ if ( $image = $tsf->get_social_image_url_from_home_meta( $args['post_id'], true ) )
576
+ goto end;
577
+ }
578
+
579
+ if ( $args['post_id'] ) {
580
+ //* 2. Fetch image from SEO meta upload.
581
+ if ( $all_allowed || false === in_array( 'postmeta', $args['disallowed'], true ) ) {
582
+ if ( $image = $tsf->get_social_image_url_from_post_meta( $args['post_id'], true ) )
583
+ goto end;
584
+ }
585
+
586
+ //* 3. Fetch image from featured.
587
+ if ( $all_allowed || false === in_array( 'featured', $args['disallowed'], true ) ) {
588
+ if ( $image = $tsf->get_image_from_post_thumbnail( $args, true ) )
589
+ goto end;
590
+ }
591
+ }
592
+
593
+ if ( $args['skip_fallback'] )
594
+ goto end;
595
+
596
+ //* 4. Fetch image from SEO settings
597
+ if ( $all_allowed || false === in_array( 'option', $args['disallowed'], true ) ) {
598
+ if ( $image = $tsf->get_social_image_url_from_seo_settings( true ) )
599
+ goto end;
600
+ }
601
+
602
+ //* 5. Fetch image from fallback filter 1
603
+ /**
604
+ * Applies filters 'the_seo_framework_og_image_after_featured' : string
605
+ * @since 2.5.2
606
+ */
607
+ fallback_1 : {
608
+ if ( $image = (string) \apply_filters( 'the_seo_framework_og_image_after_featured', '', $args['post_id'] ) )
609
+ goto end;
610
+ }
611
+
612
+ //* 6. Fallback: Get header image if exists
613
+ if ( ( $all_allowed || false === in_array( 'header', $args['disallowed'], true ) ) && \current_theme_supports( 'custom-header', 'default-image' ) ) {
614
+ if ( $image = $tsf->get_header_image( true ) )
615
+ goto end;
616
+ }
617
+
618
+ //* 7. Fetch image from fallback filter 2
619
+ /**
620
+ * Applies filters 'the_seo_framework_og_image_after_header' : string
621
+ * @since 2.5.2
622
+ */
623
+ fallback_2 : {
624
+ if ( $image = (string) \apply_filters( 'the_seo_framework_og_image_after_header', '', $args['post_id'] ) )
625
+ goto end;
626
+ }
627
+
628
+ //* 8. Get the WP 4.5 Site Logo
629
+ if ( ( $all_allowed || false === in_array( 'logo', $args['disallowed'], true ) ) && $tsf->can_use_logo() ) {
630
+ if ( $image = $tsf->get_site_logo( true ) )
631
+ goto end;
632
+ }
633
+
634
+ //* 9. Get the WP 4.3 Site Icon
635
+ if ( $all_allowed || false === in_array( 'icon', $args['disallowed'], true ) ) {
636
+ if ( $image = $tsf->get_site_icon( 'full', true ) )
637
+ goto end;
638
+ }
639
+
640
+ end :;
641
+
642
+ if ( $args['escape'] && $image )
643
+ $image = \esc_url( $image );
644
+
645
+ return (string) $image;
646
+ }
647
+
648
+ /**
649
+ * Fetches image from post thumbnail.
650
+ * Resizes the image between 1500px if bigger. Then it saves the image and
651
+ * Keeps dimensions relative.
652
+ *
653
+ * @since 2.3.0
654
+ * @since 2.9.0 Changed parameters.
655
+ * @since 2.9.0 Deprecated.
656
+ * @deprecated
657
+ *
658
+ * @param array $args The image args.
659
+ * Was: int $id The post/page ID.
660
+ * @param bool $set_og_dimensions Whether to set Open Graph image dimensions.
661
+ * Was: array $depr_args Deprecated. Image arguments.
662
+ * @return string|null the image url.
663
+ */
664
+ public function get_image_from_post_thumbnail( $args = array(), $set_og_dimensions = false ) {
665
+
666
+ $tsf = \the_seo_framework();
667
+
668
+ $tsf->_deprecated_function( 'the_seo_framework()->get_image_from_post_thumbnail()', '2.9.0', 'the_seo_framework()->get_social_image_url_from_post_thumbnail()' );
669
+
670
+ if ( is_array( $set_og_dimensions ) ) {
671
+ $tsf->_doing_it_wrong( __METHOD__, 'First parameter are now arguments, second parameter is for setting og dimensions.', '2.9.0' );
672
+ $args = $set_og_dimensions;
673
+ $set_og_dimensions = false;
674
+ }
675
+
676
+ $args = $tsf->reparse_image_args( $args );
677
+
678
+ $id = \get_post_thumbnail_id( $args['post_id'] );
679
+
680
+ $args['get_the_real_ID'] = true;
681
+
682
+ $image = $id ? $tsf->parse_og_image( $id, $args, $set_og_dimensions ) : '';
683
+
684
+ return $image;
685
+ }
686
+
687
+ /**
688
+ * Detects front page.
689
+ *
690
+ * Returns true on SEO settings page if ID is 0.
691
+ *
692
+ * @since 2.6.0
693
+ * @since 2.9.0: Deprecated.
694
+ * @deprecated
695
+ *
696
+ * @param int $id The Page or Post ID.
697
+ * @return bool
698
+ */
699
+ public function is_front_page( $id = 0 ) {
700
+
701
+ $tsf = \the_seo_framework();
702
+
703
+ $tsf->_deprecated_function( 'the_seo_framework()->is_front_page()', '2.9.0', 'the_seo_framework()->is_real_front_page() or the_seo_framework()->is_front_page_by_id()' );
704
+
705
+ static $cache = array();
706
+
707
+ if ( null !== $cache = $tsf->get_query_cache( __METHOD__, null, $id ) )
708
+ return $cache;
709
+
710
+ $is_front_page = false;
711
+
712
+ if ( \is_front_page() && empty( $id ) )
713
+ $is_front_page = true;
714
+
715
+ //* Elegant Themes Support. Yay.
716
+ if ( false === $is_front_page && empty( $id ) && $tsf->is_home() ) {
717
+ $sof = \get_option( 'show_on_front' );
718
+
719
+ if ( 'page' !== $sof && 'posts' !== $sof )
720
+ $is_front_page = true;
721
+ }
722
+
723
+ //* Compare against $id
724
+ if ( false === $is_front_page && $id ) {
725
+ $sof = \get_option( 'show_on_front' );
726
+
727
+ if ( 'page' === $sof && (int) \get_option( 'page_on_front' ) === $id )
728
+ $is_front_page = true;
729
+
730
+ if ( 'posts' === $sof && (int) \get_option( 'page_for_posts' ) === $id )
731
+ $is_front_page = true;
732
+ } elseif ( empty( $id ) && $tsf->is_seo_settings_page() ) {
733
+ $is_front_page = true;
734
+ }
735
+
736
+ $tsf->set_query_cache(
737
+ __METHOD__,
738
+ $is_front_page,
739
+ $id
740
+ );
741
+
742
+ return $is_front_page;
743
+ }
744
}
inc/classes/detect.class.php CHANGED
@@ -41,6 +41,23 @@ class Detect extends Render {
41
parent::__construct();
42
}
43
44
/**
45
* Returns list of active plugins.
46
*
@@ -500,6 +517,7 @@ class Detect extends Render {
500
*
501
* @since 2.6.0
502
* @since 2.8.0 Added check_option parameter.
503
*
504
* @param bool $check_option Whether to check for sitemap option.
505
* @return bool True when no conflicting plugins are detected or when The SEO Framework's Sitemaps are output.
@@ -520,6 +538,9 @@ class Detect extends Render {
520
return false;
521
}
522
523
return true;
524
}
525
@@ -1033,4 +1054,19 @@ class Detect extends Render {
1033
1034
return isset( $cache ) ? $cache : $this->wp_version( '4.5.0', '>=' ) && \current_theme_supports( 'custom-logo' );
1035
}
1036
}
41
parent::__construct();
42
}
43
44
+ /**
45
+ * Determines if we're doing ajax.
46
+ *
47
+ * @since 2.9.0
48
+ * @staticvar bool $cache
49
+ *
50
+ * @return bool True if AJAX
51
+ */
52
+ public function doing_ajax() {
53
+
54
+ static $cache = null;
55
+
56
+ null === $cache and $cache = defined( 'DOING_AJAX' ) && DOING_AJAX;
57
+
58
+ return $cache;
59
+ }
60
+
61
/**
62
* Returns list of active plugins.
63
*
517
*
518
* @since 2.6.0
519
* @since 2.8.0 Added check_option parameter.
520
+ * @since 2.9.0 Now also checks for subdirectory installations.
521
*
522
* @param bool $check_option Whether to check for sitemap option.
523
* @return bool True when no conflicting plugins are detected or when The SEO Framework's Sitemaps are output.
538
return false;
539
}
540
541
+ if ( $this->is_subdirectory_installation() )
542
+ return false;
543
+
544
return true;
545
}
546
1054
1055
return isset( $cache ) ? $cache : $this->wp_version( '4.5.0', '>=' ) && \current_theme_supports( 'custom-logo' );
1056
}
1057
+
1058
+ /**
1059
+ * Determines if the current installation is on a subdirectory.
1060
+ *
1061
+ * @since 2.9.0-
1062
+ * @staticvar $bool $cache
1063
+ *
1064
+ * @return bool
1065
+ */
1066
+ public function is_subdirectory_installation() {
1067
+
1068
+ static $cache = null;
1069
+
1070
+ return isset( $cache ) ? $cache : '' !== $this->get_home_path();
1071
+ }
1072
}
inc/classes/doing-it-right.class.php CHANGED
@@ -95,7 +95,7 @@ class Doing_It_Right extends Generate_Ldjson {
95
/**
96
* Securely check the referrer, instead of leaving holes everywhere.
97
*/
98
- if ( defined( 'DOING_AJAX' ) && DOING_AJAX && \check_ajax_referer( 'add-tag', '_wpnonce_add-tag', false ) ) {
99
100
$taxonomy = ! empty( $_POST['taxonomy'] ) ? $_POST['taxonomy'] : 'post_tag';
101
$tax = \get_taxonomy( $taxonomy );
@@ -106,28 +106,38 @@ class Doing_It_Right extends Generate_Ldjson {
106
}
107
108
/**
109
- * Initializes columns
110
- *
111
- * Applies filter the_seo_framework_show_seo_column : Boolean Show the SEO column in edit.php
112
*
113
* @since 2.1.9
114
*
115
* @param object|empty $screen WP_Screen
116
* @param bool $doing_ajax Whether we're doing an AJAX response.
117
*/
118
public function init_columns( $screen = '', $doing_ajax = false ) {
119
120
$show_seo_column = (bool) \apply_filters( 'the_seo_framework_show_seo_column', true );
121
122
if ( $doing_ajax ) {
123
$post_type = isset( $_POST['post_type'] ) ? $_POST['post_type'] : '';
124
} else {
125
$post_type = isset( $screen->post_type ) ? $screen->post_type : '';
126
}
127
128
- if ( $show_seo_column && $this->post_type_supports_custom_seo( $post_type ) ) {
129
if ( $doing_ajax ) {
130
131
$id = isset( $_POST['screen'] ) ? $_POST['screen'] : false;
132
$taxonomy = isset( $_POST['taxonomy'] ) ? $_POST['taxonomy'] : false;
133
@@ -509,7 +519,7 @@ class Doing_It_Right extends Generate_Ldjson {
509
510
$data = $this->get_term_data( $term, $term->term_id );
511
512
- $noindex = isset( $data['noindex'] ) && $this->is_checked( $data['noindex'] );
513
$redirect = false; // We don't apply redirect on taxonomies (yet)
514
515
//* Blocked SEO, return simple bar.
@@ -549,14 +559,11 @@ class Doing_It_Right extends Generate_Ldjson {
549
$is_term = false;
550
$is_front_page = $this->is_static_frontpage( $post_id );
551
552
- $redirect = $this->get_custom_field( 'redirect', $post_id );
553
- $redirect = empty( $redirect ) ? false : true;
554
-
555
- $noindex = $this->get_custom_field( '_genesis_noindex', $post_id );
556
- $noindex = $this->is_checked( $noindex );
557
558
if ( $is_front_page )
559
- $noindex = $this->is_option_checked( 'homepage_noindex' ) ? true : $noindex;
560
561
if ( $redirect || $noindex )
562
return $this->the_seo_bar_blocked( array( 'is_term' => $is_term, 'redirect' => $redirect, 'noindex' => $noindex, 'post_i18n' => $post ) );
@@ -609,6 +616,7 @@ class Doing_It_Right extends Generate_Ldjson {
609
* Fetch the term data for The SEO Bar.
610
*
611
* @since 2.6.0
612
* @staticvar array $data
613
*
614
* @param array $args The term args.
@@ -617,6 +625,7 @@ class Doing_It_Right extends Generate_Ldjson {
617
* 'title_is_from_custom_field' => $title_is_from_custom_field,
618
* 'description' => $description,
619
* 'description_is_from_custom_field' => $description_is_from_custom_field,
620
* 'nofollow' => $nofollow,
621
* 'noarchive' => $noarchive
622
* }
@@ -631,6 +640,7 @@ class Doing_It_Right extends Generate_Ldjson {
631
632
$title_custom_field = isset( $data['doctitle'] ) ? $data['doctitle'] : '';
633
$description_custom_field = isset( $data['description'] ) ? $data['description'] : '';
634
$nofollow = isset( $data['nofollow'] ) ? $data['nofollow'] : '';
635
$noarchive = isset( $data['noarchive'] ) ? $data['noarchive'] : '';
636
@@ -654,14 +664,19 @@ class Doing_It_Right extends Generate_Ldjson {
654
$description = $this->generate_description( '', $description_args );
655
}
656
657
- $nofollow = $this->is_checked( $nofollow );
658
- $noarchive = $this->is_checked( $noarchive );
659
660
return array(
661
'title' => $title,
662
'title_is_from_custom_field' => $title_is_from_custom_field,
663
'description' => $description,
664
'description_is_from_custom_field' => $description_is_from_custom_field,
665
'nofollow' => $nofollow,
666
'noarchive' => $noarchive,
667
);
@@ -671,6 +686,7 @@ class Doing_It_Right extends Generate_Ldjson {
671
* Fetch the post data for The SEO Bar.
672
*
673
* @since 2.6.0
674
* @staticvar array $data
675
*
676
* @param array $args The post args.
@@ -679,6 +695,7 @@ class Doing_It_Right extends Generate_Ldjson {
679
* 'title_is_from_custom_field' => $title_is_from_custom_field,
680
* 'description' => $description,
681
* 'description_is_from_custom_field' => $description_is_from_custom_field,
682
* 'nofollow' => $nofollow,
683
* 'noarchive' => $noarchive
684
* }
@@ -690,14 +707,16 @@ class Doing_It_Right extends Generate_Ldjson {
690
691
$title_custom_field = $this->get_custom_field( '_genesis_title', $post_id );
692
$description_custom_field = $this->get_custom_field( '_genesis_description', $post_id );
693
$nofollow = $this->get_custom_field( '_genesis_nofollow', $post_id );
694
$noarchive = $this->get_custom_field( '_genesis_noarchive', $post_id );
695
696
if ( $page_on_front ) {
697
- $title_custom_field = $this->get_option( 'homepage_title' ) ? $this->get_option( 'homepage_title' ) : $title_custom_field;
698
- $description_custom_field = $this->get_option( 'homepage_description' ) ? $this->get_option( 'homepage_description' ) : $description_custom_field;
699
- $nofollow = $this->get_option( 'homepage_nofollow' ) ? $this->get_option( 'homepage_nofollow' ) : $nofollow;
700
- $noarchive = $this->get_option( 'homepage_noarchive' ) ? $this->get_option( 'homepage_noarchive' ) : $noarchive;
701
}
702
703
$title_is_from_custom_field = (bool) $title_custom_field;
@@ -714,14 +733,16 @@ class Doing_It_Right extends Generate_Ldjson {
714
$description = $this->generate_description( '', array( 'id' => $post_id, 'get_custom_field' => false ) );
715
}
716
717
- $nofollow = $this->is_checked( $nofollow );
718
- $noarchive = $this->is_checked( $noarchive );
719
720
return array(
721
'title' => $title,
722
'title_is_from_custom_field' => $title_is_from_custom_field,
723
'description' => $description,
724
'description_is_from_custom_field' => $description_is_from_custom_field,
725
'nofollow' => $nofollow,
726
'noarchive' => $noarchive,
727
);
@@ -1548,7 +1569,7 @@ class Doing_It_Right extends Generate_Ldjson {
1548
'20%' => 'tsf-20',
1549
'16%' => 'tsf-16',
1550
'12.5%' => 'tsf-12-5',
1551
- '11%' => 'ad-11',
1552
'10%' => 'tsf-10',
1553
);
1554
}
95
/**
96
* Securely check the referrer, instead of leaving holes everywhere.
97
*/
98
+ if ( $this->doing_ajax() && \check_ajax_referer( 'add-tag', '_wpnonce_add-tag', false ) ) {
99
100
$taxonomy = ! empty( $_POST['taxonomy'] ) ? $_POST['taxonomy'] : 'post_tag';
101
$tax = \get_taxonomy( $taxonomy );
106
}
107
108
/**
109
+ * Initializes SEO bar columns.
110
*
111
* @since 2.1.9
112
*
113
* @param object|empty $screen WP_Screen
114
* @param bool $doing_ajax Whether we're doing an AJAX response.
115
+ * @return void If filter is set to false.
116
*/
117
public function init_columns( $screen = '', $doing_ajax = false ) {
118
119
+ /**
120
+ * Applies filters 'the_seo_framework_show_seo_column' : bool
121
+ *
122
+ * @since ???
123
+ *
124
+ * @param bool $show_seo_column
125
+ */
126
$show_seo_column = (bool) \apply_filters( 'the_seo_framework_show_seo_column', true );
127
128
+ if ( false === $show_seo_column )
129
+ return;
130
+
131
if ( $doing_ajax ) {
132
$post_type = isset( $_POST['post_type'] ) ? $_POST['post_type'] : '';
133
} else {
134
$post_type = isset( $screen->post_type ) ? $screen->post_type : '';
135
}
136
137
+ if ( $this->post_type_supports_custom_seo( $post_type ) ) {
138
if ( $doing_ajax ) {
139
140
+ //* Nonce is done in $this->init_columns_ajax()
141
$id = isset( $_POST['screen'] ) ? $_POST['screen'] : false;
142
$taxonomy = isset( $_POST['taxonomy'] ) ? $_POST['taxonomy'] : false;
143
519
520
$data = $this->get_term_data( $term, $term->term_id );
521
522
+ $noindex = isset( $data['noindex'] ) && $data['noindex'];
523
$redirect = false; // We don't apply redirect on taxonomies (yet)
524
525
//* Blocked SEO, return simple bar.
559
$is_term = false;
560
$is_front_page = $this->is_static_frontpage( $post_id );
561
562
+ $redirect = (bool) $this->get_custom_field( 'redirect', $post_id );
563
+ $noindex = (bool) $this->get_custom_field( '_genesis_noindex', $post_id );
564
565
if ( $is_front_page )
566
+ $noindex = $this->is_option_checked( 'homepage_noindex' ) ?: $noindex;
567
568
if ( $redirect || $noindex )
569
return $this->the_seo_bar_blocked( array( 'is_term' => $is_term, 'redirect' => $redirect, 'noindex' => $noindex, 'post_i18n' => $post ) );
616
* Fetch the term data for The SEO Bar.
617
*
618
* @since 2.6.0
619
+ * @since 2.9.0 Now also returns noindex value.
620
* @staticvar array $data
621
*
622
* @param array $args The term args.
625
* 'title_is_from_custom_field' => $title_is_from_custom_field,
626
* 'description' => $description,
627
* 'description_is_from_custom_field' => $description_is_from_custom_field,
628
+ * 'noindex' => $noindex,
629
* 'nofollow' => $nofollow,
630
* 'noarchive' => $noarchive
631
* }
640
641
$title_custom_field = isset( $data['doctitle'] ) ? $data['doctitle'] : '';
642
$description_custom_field = isset( $data['description'] ) ? $data['description'] : '';
643
+ $noindex = isset( $data['noindex'] ) ? $data['noindex'] : '';
644
$nofollow = isset( $data['nofollow'] ) ? $data['nofollow'] : '';
645
$noarchive = isset( $data['noarchive'] ) ? $data['noarchive'] : '';
646
664
$description = $this->generate_description( '', $description_args );
665
}
666
667
+ /**
668
+ * No longer uses is_checked. As it strict checks 1/0 strings.
669
+ */
670
+ $noindex = (bool) $noindex;
671
+ $nofollow = (bool) $nofollow;
672
+ $noarchive = (bool) $noarchive;
673
674
return array(
675
'title' => $title,
676
'title_is_from_custom_field' => $title_is_from_custom_field,
677
'description' => $description,
678
'description_is_from_custom_field' => $description_is_from_custom_field,
679
+ 'noindex' => $noindex,
680
'nofollow' => $nofollow,
681
'noarchive' => $noarchive,
682
);
686
* Fetch the post data for The SEO Bar.
687
*
688
* @since 2.6.0
689
+ * @since 2.9.0 Now also returns noindex value.
690
* @staticvar array $data
691
*
692
* @param array $args The post args.
695
* 'title_is_from_custom_field' => $title_is_from_custom_field,
696
* 'description' => $description,
697
* 'description_is_from_custom_field' => $description_is_from_custom_field,
698
+ * 'noindex' => $noindex,
699
* 'nofollow' => $nofollow,
700
* 'noarchive' => $noarchive
701
* }
707
708
$title_custom_field = $this->get_custom_field( '_genesis_title', $post_id );
709
$description_custom_field = $this->get_custom_field( '_genesis_description', $post_id );
710
+ $noindex = $this->get_custom_field( '_genesis_noindex', $post_id );
711
$nofollow = $this->get_custom_field( '_genesis_nofollow', $post_id );
712
$noarchive = $this->get_custom_field( '_genesis_noarchive', $post_id );
713
714
if ( $page_on_front ) {
715
+ $title_custom_field = $this->get_option( 'homepage_title' ) ?: $title_custom_field;
716
+ $description_custom_field = $this->get_option( 'homepage_description' ) ?: $description_custom_field;
717
+ $noindex = $this->get_option( 'homepage_noindex' ) ?: $nofollow;
718
+ $nofollow = $this->get_option( 'homepage_nofollow' ) ?: $nofollow;
719
+ $noarchive = $this->get_option( 'homepage_noarchive' ) ?: $noarchive;
720
}
721
722
$title_is_from_custom_field = (bool) $title_custom_field;
733
$description = $this->generate_description( '', array( 'id' => $post_id, 'get_custom_field' => false ) );
734
}
735
736
+ $noindex = (bool) $noindex;
737
+ $nofollow = (bool) $nofollow;
738
+ $noarchive = (bool) $noarchive;
739
740
return array(
741
'title' => $title,
742
'title_is_from_custom_field' => $title_is_from_custom_field,
743
'description' => $description,
744
'description_is_from_custom_field' => $description_is_from_custom_field,
745
+ 'noindex' => $noindex,
746
'nofollow' => $nofollow,
747
'noarchive' => $noarchive,
748
);
1569
'20%' => 'tsf-20',
1570
'16%' => 'tsf-16',
1571
'12.5%' => 'tsf-12-5',
1572
+ '11%' => 'tsf-11',
1573
'10%' => 'tsf-10',
1574
);
1575
}
inc/classes/feed.class.php CHANGED
@@ -43,9 +43,15 @@ class Feed extends Cache {
43
/**
44
* Initializes feed actions and hooks.
45
*
46
- * @since 2.6.0
47
*/
48
- public function init_feed() {
49
50
\add_filter( 'the_content_feed', array( $this, 'the_content_feed' ), 10, 2 );
51
@@ -67,75 +73,110 @@ class Feed extends Cache {
67
}
68
69
/**
70
- * Changes feed's content.
71
*
72
- * @param $content The feed's content.
73
- * @param $feed_type The feed type (not used in excerpted content)
74
*
75
* @since 2.5.2
76
*/
77
- public function the_content_feed( $content, $feed_type = null ) {
78
79
- if ( $content ) :
80
81
- /**
82
- * Don't alter already-excerpts or descriptions.
83
- * $feed_type is only set on 'the_content_feed' filter.
84
- */
85
- if ( isset( $feed_type ) && $this->get_option( 'excerpt_the_feed' ) ) {
86
- //* Strip all code and lines.
87
- $excerpt = $this->s_excerpt_raw( $content, false );
88
89
- $excerpt_len = (int) mb_strlen( $excerpt );
90
- /**
91
- * Applies filters the_seo_framework_max_content_feed_length : The max excerpt length.
92
- * @since 2.5.2
93
- */
94
- $max_len = (int) \apply_filters( 'the_seo_framework_max_content_feed_length', 400 );
95
96
- //* Generate excerpt.
97
- $excerpt = $this->trim_excerpt( $excerpt, $excerpt_len, $max_len );
98
99
- $h2_output = '';
100
101
- if ( 0 === strpos( $content, '<h2>' ) ) {
102
- //* Add the h2 title back
103
- $h2_end = mb_strpos( $content, '</h2>' );
104
105
- if ( false !== $h2_end ) {
106
- //* Start of content, plus <h2>
107
- $h2_start = 4;
108
- //* Remove the length of <h2>, again.
109
- $h2_end = $h2_end - $h2_start;
110
111
- //* Fetch h2 content.
112
- $h2_content = mb_substr( $content, $h2_start, $h2_end );
113
114
- //* Remove the H2 content from the excerpt.
115
- $count = 1;
116
- $excerpt = str_replace( $h2_content, '', $excerpt, $count );
117
118
- //* Wrap h2 content in h2 tags.
119
- $h2_output = '<h2>' . $h2_content . '</h2>' . PHP_EOL;
120
- }
121
- }
122
123
- $content = $h2_output . '<p>' . trim( $excerpt ) . '</p>';
124
- }
125
126
- if ( $this->get_option( 'source_the_feed' ) ) {
127
128
- //* Fetch permalink and add it to the content. Already escaped.
129
- $permalink = $this->the_url();
130
131
- /**
132
- * Applies filters 'the_seo_framework_feed_source_link' : string
133
- * @since 2.6.0
134
- */
135
- $source_i18n = (string) apply_filters( 'the_seo_framework_feed_source_link_text', \_x( 'Source', 'The content source', 'autodescription' ) );
136
- $content .= PHP_EOL . '<p><a href="' . $permalink . '" rel="external nofollow">' . \esc_html( $source_i18n ) . '</a></p>';
137
}
138
- endif;
139
140
return $content;
141
}
43
/**
44
* Initializes feed actions and hooks.
45
*
46
+ * @since 2.9.0
47
+ * @access private
48
+ *
49
+ * @return void Early if this request isn't for a feed.
50
*/
51
+ public function _init_feed_output() {
52
+
53
+ if ( ! $this->is_feed() )
54
+ return;
55
56
\add_filter( 'the_content_feed', array( $this, 'the_content_feed' ), 10, 2 );
57
73
}
74
75
/**
76
+ * Changes feed's content based on options.
77
*
78
+ * This method converts the input $content to an excerpt and is able to add
79
+ * a nofollow backlink at the end of the feed.
80
*
81
* @since 2.5.2
82
+ *
83
+ * @param $content The feed's content.
84
+ * @param $feed_type The feed type (not used in excerpted content)
85
+ * @return string The modified feed entry.
86
*/
87
+ public function the_content_feed( $content = '', $feed_type = null ) {
88
89
+ if ( ! $content )
90
+ return '';
91
92
+ /**
93
+ * Don't alter already-excerpts or descriptions.
94
+ * $feed_type is only set on 'the_content_feed' filter.
95
+ */
96
+ if ( isset( $feed_type ) && $this->get_option( 'excerpt_the_feed' ) ) {
97
+ $content = $this->convert_feed_entry_to_excerpt( $content );
98
+ }
99
100
+ if ( $this->get_option( 'source_the_feed' ) ) {
101
+ $content .= PHP_EOL . $this->get_feed_entry_source_link();
102
+ }
103
104
+ return $content;
105
+ }
106
107
+ /**
108
+ * Converts feed content to excerpt.
109
+ *
110
+ * @since 2.9.0
111
+ *
112
+ * @param string $content The full feed entry content.
113
+ * @return string The excerpted feed.
114
+ */
115
+ protected function convert_feed_entry_to_excerpt( $content = '' ) {
116
117
+ if ( ! $content )
118
+ return '';
119
120
+ //* Strip all code and lines.
121
+ $excerpt = $this->s_excerpt_raw( $content, false );
122
123
+ $excerpt_len = (int) mb_strlen( $excerpt );
124
+ /**
125
+ * Applies filters the_seo_framework_max_content_feed_length : The max excerpt length.
126
+ * @since 2.5.2
127
+ */
128
+ $max_len = (int) \apply_filters( 'the_seo_framework_max_content_feed_length', 400 );
129
130
+ //* Generate excerpt.
131
+ $excerpt = $this->trim_excerpt( $excerpt, $excerpt_len, $max_len );
132
133
+ $h2_output = '';
134
135
+ if ( 0 === strpos( $content, '<h2>' ) ) {
136
+ //* Add the h2 title back
137
+ $h2_end = mb_strpos( $content, '</h2>' );
138
+
139
+ if ( false !== $h2_end ) {
140
+ //* Start of content, plus <h2>
141
+ $h2_start = 4;
142
+ //* Remove the length of <h2>, again.
143
+ $h2_end = $h2_end - $h2_start;
144
145
+ //* Fetch h2 content.
146
+ $h2_content = mb_substr( $content, $h2_start, $h2_end );
147
148
+ //* Remove the H2 content from the excerpt.
149
+ $count = 1;
150
+ $excerpt = str_replace( $h2_content, '', $excerpt, $count );
151
152
+ //* Wrap h2 content in h2 tags.
153
+ $h2_output = '<h2>' . $h2_content . '</h2>' . PHP_EOL;
154
}
155
+ }
156
+
157
+ $content = $h2_output . '<p>' . trim( $excerpt ) . '</p>';
158
+
159
+ return $content;
160
+ }
161
+
162
+ /**
163
+ * Generates and returns feed source link.
164
+ *
165
+ * @since 2.9.0
166
+ *
167
+ * @return string The translatable feed entry source link.
168
+ */
169
+ protected function get_feed_entry_source_link() {
170
+
171
+ //* Fetch permalink and add it to the content. Already escaped.
172
+ $permalink = $this->the_url();
173
+
174
+ /**
175
+ * Applies filters 'the_seo_framework_feed_source_link' : string
176
+ * @since 2.6.0
177
+ */
178
+ $source_i18n = (string) \apply_filters( 'the_seo_framework_feed_source_link_text', \_x( 'Source', 'The content source', 'autodescription' ) );
179
+ $content = '<p><a href="' . $permalink . '" rel="external nofollow">' . \esc_html( $source_i18n ) . '</a></p>';
180
181
return $content;
182
}
inc/classes/generate-description.class.php CHANGED
@@ -43,6 +43,7 @@ class Generate_Description extends Generate {
43
* Creates description. Base function.
44
*
45
* @since 1.0.0
46
*
47
* @param string $description The optional description to simply parse.
48
* @param array $args description args : {
@@ -64,16 +65,41 @@ class Generate_Description extends Generate {
64
65
if ( $args['get_custom_field'] && empty( $description ) ) {
66
//* Fetch from options, if any.
67
- $description = (string) $this->description_from_custom_field( $args, false );
68
69
//* We've already checked the custom fields, so let's remove the check in the generation.
70
$args['get_custom_field'] = false;
71
}
72
73
//* Still no description found? Create an auto description based on content.
74
- if ( empty( $description ) || false === is_scalar( $description ) )
75
$description = $this->generate_description_from_id( $args, false );
76
77
/**
78
* Applies filters 'the_seo_framework_do_shortcodes_in_description' : Boolean
79
* @since 2.6.6
@@ -206,6 +232,8 @@ class Generate_Description extends Generate {
206
* Fetches HomePage Description from custom field.
207
*
208
* @since 2.6.0
209
* Use $this->description_from_custom_field() instead.
210
*
211
* @param array $args Description args.
@@ -215,9 +243,8 @@ class Generate_Description extends Generate {
215
216
$description = '';
217
218
- if ( $args['is_home'] || $this->is_front_page() || ( empty( $args['taxonomy'] ) && $this->is_static_frontpage( $args['id'] ) ) ) {
219
$description = $this->get_option( 'homepage_description' ) ?: '';
220
- }
221
222
return $description;
223
}
@@ -333,7 +360,7 @@ class Generate_Description extends Generate {
333
$args = $this->reparse_description_args( $args );
334
335
//* Home Page description
336
- if ( $args['is_home'] || $this->is_front_page() || $this->is_static_frontpage( $args['id'] ) )
337
return $this->generate_home_page_description( $args['get_custom_field'], $escape );
338
339
/**
@@ -638,14 +665,14 @@ class Generate_Description extends Generate {
638
* $sep => The separator
639
* }
640
*/
641
- public function generate_description_additions( $id = '', $term = '', $ignore = false ) {
642
643
static $title = array();
644
645
if ( $ignore || $this->add_description_additions( $id, $term ) ) {
646
647
if ( ! isset( $title[ $id ] ) ) {
648
- $title[ $id ] = $this->generate_description_title( $id, $term, $this->is_front_page( $id ) );
649
}
650
651
if ( $ignore || $this->is_option_checked( 'description_blogname' ) ) {
@@ -695,7 +722,7 @@ class Generate_Description extends Generate {
695
if ( '' === $id )
696
$id = $this->get_the_real_ID();
697
698
- if ( $page_on_front || $this->is_static_frontpage( $id ) ) :
699
$title = $this->get_option( 'homepage_title_tagline' ) ?: $this->get_blogdescription();
700
else :
701
/**
@@ -779,6 +806,18 @@ class Generate_Description extends Generate {
779
$excerpt = '';
780
}
781
782
$excerpt_cache[ $page_id ][ $term_id ] = $excerpt;
783
}
784
43
* Creates description. Base function.
44
*
45
* @since 1.0.0
46
+ * @since 2.9.0 Added two filters.
47
*
48
* @param string $description The optional description to simply parse.
49
* @param array $args description args : {
65
66
if ( $args['get_custom_field'] && empty( $description ) ) {
67
//* Fetch from options, if any.
68
+ $description = $this->description_from_custom_field( $args, false );
69
+
70
+ /**
71
+ * Applies filters 'the_seo_framework_custom_field_description' : string
72
+ *
73
+ * Filters the description from custom field, if any.
74
+ *
75
+ * @since 2.9.0
76
+ *
77
+ * @param string $description The description.
78
+ * @param array $args The description arguments.
79
+ */
80
+ $description = (string) \apply_filters( 'the_seo_framework_custom_field_description', $description, $args );
81
82
//* We've already checked the custom fields, so let's remove the check in the generation.
83
$args['get_custom_field'] = false;
84
}
85
86
//* Still no description found? Create an auto description based on content.
87
+ if ( empty( $description ) || false === is_scalar( $description ) ) {
88
$description = $this->generate_description_from_id( $args, false );
89
90
+ /**
91
+ * Applies filters 'the_seo_framework_generated_description' : string
92
+ *
93
+ * Filters the generated description, if any.
94
+ *
95
+ * @since 2.9.0
96
+ *
97
+ * @param string $description The description.
98
+ * @param array $args The description arguments.
99
+ */
100
+ $description = (string) \apply_filters( 'the_seo_framework_generated_description', $description, $args );
101
+ }
102
+
103
/**
104
* Applies filters 'the_seo_framework_do_shortcodes_in_description' : Boolean
105
* @since 2.6.6
232
* Fetches HomePage Description from custom field.
233
*
234
* @since 2.6.0
235
+ * @since 2.9.0 1. Removed $args['taxonomy'] check.
236
+ * 2. Added $this->is_archive() check.
237
* Use $this->description_from_custom_field() instead.
238
*
239
* @param array $args Description args.
243
244
$description = '';
245
246
+ if ( $args['is_home'] || $this->is_real_front_page() || ( ! $this->is_archive() && $this->is_static_frontpage( $args['id'] ) ) )
247
$description = $this->get_option( 'homepage_description' ) ?: '';
248
249
return $description;
250
}
360
$args = $this->reparse_description_args( $args );
361
362
//* Home Page description
363
+ if ( $args['is_home'] || $this->is_real_front_page() || $this->is_front_page_by_id( $args['id'] ) )
364
return $this->generate_home_page_description( $args['get_custom_field'], $escape );
365
366
/**
665
* $sep => The separator
666
* }
667
*/
668
+ public function generate_description_additions( $id = 0, $term = '', $ignore = false ) {
669
670
static $title = array();
671
672
if ( $ignore || $this->add_description_additions( $id, $term ) ) {
673
674
if ( ! isset( $title[ $id ] ) ) {
675
+ $title[ $id ] = $this->generate_description_title( $id, $term, $this->is_real_front_page() );
676
}
677
678
if ( $ignore || $this->is_option_checked( 'description_blogname' ) ) {
722
if ( '' === $id )
723
$id = $this->get_the_real_ID();
724
725
+ if ( $page_on_front || $this->is_front_page_by_id( $id ) ) :
726
$title = $this->get_option( 'homepage_title_tagline' ) ?: $this->get_blogdescription();
727
else :
728
/**
806
$excerpt = '';
807
}
808
809
+ /**
810
+ * Applies filters 'the_seo_framework_fetched_description_excerpt' : string
811
+ *
812
+ * @since 2.9.0
813
+ *
814
+ * @param string $excerpt The excerpt to use.
815
+ * @param bool $page_id The current page/term ID
816
+ * @param object|mixed $term The current term.
817
+ * @param int $max_char_length Determines the maximum length of excerpt after trimming.
818
+ */
819
+ $excerpt = (string) \apply_filters( 'the_seo_framework_fetched_description_excerpt', $excerpt, $page_id, $term, $max_char_length );
820
+
821
$excerpt_cache[ $page_id ][ $term_id ] = $excerpt;
822
}
823
inc/classes/generate-image.class.php CHANGED
@@ -49,33 +49,18 @@ class Generate_Image extends Generate_Url {
49
}
50
51
/**
52
- * Fetches og:image URL.
53
*
54
- * @since 2.2.2
55
- * @since 2.2.8 : Added theme icon detection.
56
- * @since 2.5.2 : Added args filters.
57
- * @since 2.8.0 : 1. Added theme logo detection.
58
- * 2. Added inpost image selection detection.
59
- * @since 2.8.2 : 1. Now returns something on post ID 0.
60
- * 2. Added SEO settings fallback image selection detection.
61
*
62
* @todo listen to attached images within post.
63
- * @todo set archive and front page image listener, now it simply fail on some calls.
64
- * @priority medium 2.7.0+
65
*
66
- * @param string $post_id The post ID.
67
* @param array $args The image arguments.
68
- * @param bool $escape Whether to escape the image URL.
69
- * @return string the image URL.
70
*/
71
- public function get_image( $post_id = '', $args = array(), $escape = true ) {
72
-
73
- /**
74
- * Backwards compat with parse args.
75
- * @since 2.5.0
76
- */
77
- if ( ! isset( $args['post_id'] ) )
78
- $args['post_id'] = $post_id ?: ( $this->is_singular() ? $this->get_the_real_ID() : 0 );
79
80
$args = $this->reparse_image_args( $args );
81
@@ -88,27 +73,36 @@ class Generate_Image extends Generate_Url {
88
//* Check if there are no disallowed arguments.
89
$all_allowed = empty( $args['disallowed'] );
90
91
if ( $args['post_id'] ) {
92
- //* 1. Fetch image from SEO meta upload.
93
if ( $all_allowed || false === in_array( 'postmeta', $args['disallowed'], true ) ) {
94
if ( $image = $this->get_social_image_url_from_post_meta( $args['post_id'], true ) )
95
goto end;
96
}
97
98
- //* 2. Fetch image from featured
99
if ( $all_allowed || false === in_array( 'featured', $args['disallowed'], true ) ) {
100
- if ( $image = $this->get_image_from_post_thumbnail( $args ) )
101
goto end;
102
}
103
}
104
105
- //* 3. Fetch image from SEO settings
106
if ( $all_allowed || false === in_array( 'option', $args['disallowed'], true ) ) {
107
if ( $image = $this->get_social_image_url_from_seo_settings( true ) )
108
goto end;
109
}
110
111
- //* 4. Fetch image from fallback filter 1
112
/**
113
* Applies filters 'the_seo_framework_og_image_after_featured' : string
114
* @since 2.5.2
@@ -118,13 +112,13 @@ class Generate_Image extends Generate_Url {
118
goto end;
119
}
120
121
- //* 5. Fallback: Get header image if exists
122
if ( ( $all_allowed || false === in_array( 'header', $args['disallowed'], true ) ) && \current_theme_supports( 'custom-header', 'default-image' ) ) {
123
if ( $image = $this->get_header_image( true ) )
124
goto end;
125
}
126
127
- //* 6. Fetch image from fallback filter 2
128
/**
129
* Applies filters 'the_seo_framework_og_image_after_header' : string
130
* @since 2.5.2
@@ -134,13 +128,13 @@ class Generate_Image extends Generate_Url {
134
goto end;
135
}
136
137
- //* 7. Get the WP 4.5 Site Logo
138
if ( ( $all_allowed || false === in_array( 'logo', $args['disallowed'], true ) ) && $this->can_use_logo() ) {
139
if ( $image = $this->get_site_logo( true ) )
140
goto end;
141
}
142
143
- //* 8. Get the WP 4.3 Site Icon
144
if ( $all_allowed || false === in_array( 'icon', $args['disallowed'], true ) ) {
145
if ( $image = $this->get_site_icon( 'full', true ) )
146
goto end;
@@ -148,16 +142,19 @@ class Generate_Image extends Generate_Url {
148
149
end :;
150
151
- if ( $escape && $image )
152
- return \esc_url( $image );
153
154
- return $image;
155
}
156
157
/**
158
* Parse and sanitize image args.
159
*
160
* @since 2.5.0
161
*
162
* The image set in the filter will always be used as fallback
163
*
@@ -175,8 +172,9 @@ class Generate_Image extends Generate_Url {
175
'image' => '',
176
'size' => 'full',
177
'icon' => false,
178
- 'attr' => array(),
179
'disallowed' => array(),
180
);
181
182
/**
@@ -184,7 +182,7 @@ class Generate_Image extends Generate_Url {
184
* @param string $image The image url
185
* @param mixed $size The image size
186
* @param bool $icon Fetch Image icon
187
- * @param array $attr Image attributes
188
* @param array $disallowed Disallowed image types : {
189
* array (
190
* string 'featured'
@@ -192,6 +190,7 @@ class Generate_Image extends Generate_Url {
192
* string 'icon'
193
* )
194
* }
195
* }
196
*
197
* @since 2.0.1
@@ -207,12 +206,13 @@ class Generate_Image extends Generate_Url {
207
return $defaults;
208
209
//* Array merge doesn't support sanitation. We're simply type casting here.
210
- $args['post_id'] = isset( $args['post_id'] ) ? (int) $args['post_id'] : $defaults['post_id'];
211
- $args['image'] = isset( $args['image'] ) ? (string) $args['image'] : $defaults['image'];
212
- $args['size'] = isset( $args['size'] ) ? $args['size'] : $defaults['size']; // Mixed.
213
- $args['icon'] = isset( $args['icon'] ) ? (bool) $args['icon'] : $defaults['icon'];
214
- $args['attr'] = isset( $args['attr'] ) ? (array) $args['attr'] : $defaults['attr'];
215
- $args['disallowed'] = isset( $args['disallowed'] ) ? (array) $args['disallowed'] : $defaults['disallowed'];
216
217
return $args;
218
}
@@ -229,43 +229,72 @@ class Generate_Image extends Generate_Url {
229
230
$default_args = $this->parse_image_args( '', '', true );
231
232
- if ( is_array( $args ) ) {
233
- if ( empty( $args ) ) {
234
- $args = $default_args;
235
- } else {
236
- $args = $this->parse_image_args( $args, $default_args );
237
- }
238
- } else {
239
- //* Old style parameters are used. Doing it wrong.
240
- $this->_doing_it_wrong( __METHOD__, 'Use $args = array() for parameters.', '2.5.0' );
241
$args = $default_args;
242
}
243
244
return $args;
245
}
246
247
/**
248
- * Returns unescaped URL from post ID input.
249
*
250
- * @since 2.8.0
251
* @uses $this->image_dimensions
252
*
253
* @param int $id The post ID.
254
- * @param bool $set_og_dimensions Whether to set open graph and twitter dimensions.
255
- * @return string The unescaped social image URL.
256
*/
257
- public function get_social_image_url_from_post_meta( $id = 0, $set_og_dimensions = false ) {
258
259
- if ( empty( $id ) )
260
- $id = $this->get_the_real_ID();
261
262
- $src = $this->get_custom_field( '_social_image_url', $id );
263
264
if ( ! $src )
265
return '';
266
267
//* Calculate image sizes.
268
- if ( $img_id = $this->get_custom_field( '_social_image_id', $id ) ) {
269
$_src = \wp_get_attachment_image_src( $img_id, 'full' );
270
271
$i = $_src[0]; // Source URL
@@ -312,27 +341,28 @@ class Generate_Image extends Generate_Url {
312
313
/**
314
* Fetches image from post thumbnail.
315
* Resizes the image between 1500px if bigger. Then it saves the image and
316
* Keeps dimensions relative.
317
*
318
- * @since 2.3.0
319
*
320
- * @param array $args Image arguments.
321
- * @return string|null the image url.
322
*/
323
- public function get_image_from_post_thumbnail( $args = array() ) {
324
-
325
- if ( empty( $args ) )
326
- $args = $this->reparse_image_args( $args );
327
328
- if ( ! isset( $args['post_id'] ) )
329
- $args['post_id'] = $this->get_the_real_ID();
330
331
- $id = \get_post_thumbnail_id( $args['post_id'] );
332
333
$args['get_the_real_ID'] = true;
334
335
- $image = $id ? $this->parse_og_image( $id, $args ) : '';
336
337
return $image;
338
}
@@ -372,24 +402,26 @@ class Generate_Image extends Generate_Url {
372
* @since 2.5.0
373
* @since 2.8.0 : 1. Removed staticvar.
374
* 2. Now adds ID call to OG image called listener.
375
*
376
* @todo create formula to fetch transient.
377
* @priority high 2.7.0
378
*
379
* @param int $id The attachment ID.
380
* @param array $args The image args
381
* @return string|empty Parsed image url or empty if already called
382
*/
383
- public function parse_og_image( $id, $args = array() ) {
384
385
//* Don't do anything if $id isn't given.
386
- if ( ! isset( $id ) || empty( $id ) )
387
return;
388
389
if ( empty( $args ) )
390
$args = $this->reparse_image_args( $args );
391
392
- $src = \wp_get_attachment_image_src( $id, $args['size'], $args['icon'], $args['attr'] );
393
394
$i = $src[0]; // Source URL
395
$w = $src[1]; // Width
@@ -451,10 +483,12 @@ class Generate_Image extends Generate_Url {
451
}
452
endif;
453
454
- //* Whether to use the post ID (Post Thumbnail) or input ID (ID was known beforehand)
455
- $usage_id = isset( $args['get_the_real_ID'] ) && $args['get_the_real_ID'] ? $this->get_the_real_ID() : $id;
456
457
- $this->image_dimensions = $this->image_dimensions + array( $usage_id => array( 'width' => $w, 'height' => $h ) );
458
459
return $i;
460
}
49
}
50
51
/**
52
+ * Returns social image URL and sets $this->image_dimensions.
53
*
54
+ * @since 2.9.0
55
*
56
* @todo listen to attached images within post.
57
+ * @todo listen to archive images.
58
*
59
* @param array $args The image arguments.
60
+ * @param bool $set_og_dimension Whether to set open graph dimensions.
61
+ * @return string The social image.
62
*/
63
+ public function get_social_image( $args = array(), $set_og_dimension = false ) {
64
65
$args = $this->reparse_image_args( $args );
66
73
//* Check if there are no disallowed arguments.
74
$all_allowed = empty( $args['disallowed'] );
75
76
+ //* 1. Fetch image from homepage SEO meta upload.
77
+ if ( $all_allowed || false === in_array( 'homemeta', $args['disallowed'], true ) ) {
78
+ if ( $image = $this->get_social_image_url_from_home_meta( $args['post_id'], true ) )
79
+ goto end;
80
+ }
81
+
82
if ( $args['post_id'] ) {
83
+ //* 2. Fetch image from SEO meta upload.
84
if ( $all_allowed || false === in_array( 'postmeta', $args['disallowed'], true ) ) {
85
if ( $image = $this->get_social_image_url_from_post_meta( $args['post_id'], true ) )
86
goto end;
87
}
88
89
+ //* 3. Fetch image from featured.
90
if ( $all_allowed || false === in_array( 'featured', $args['disallowed'], true ) ) {
91
+ if ( $image = $this->get_social_image_url_from_post_thumbnail( $args['post_id'], $args, true ) )
92
goto end;
93
}
94
}
95
96
+ if ( $args['skip_fallback'] )
97
+ goto end;
98
+
99
+ //* 4. Fetch image from SEO settings
100
if ( $all_allowed || false === in_array( 'option', $args['disallowed'], true ) ) {
101
if ( $image = $this->get_social_image_url_from_seo_settings( true ) )
102
goto end;
103
}
104
105
+ //* 5. Fetch image from fallback filter 1
106
/**
107
* Applies filters 'the_seo_framework_og_image_after_featured' : string
108
* @since 2.5.2
112
goto end;
113
}
114
115
+ //* 6. Fallback: Get header image if exists
116
if ( ( $all_allowed || false === in_array( 'header', $args['disallowed'], true ) ) && \current_theme_supports( 'custom-header', 'default-image' ) ) {
117
if ( $image = $this->get_header_image( true ) )
118
goto end;
119
}
120
121
+ //* 7. Fetch image from fallback filter 2
122
/**
123
* Applies filters 'the_seo_framework_og_image_after_header' : string
124
* @since 2.5.2
128
goto end;
129
}
130
131
+ //* 8. Get the WP 4.5 Site Logo
132
if ( ( $all_allowed || false === in_array( 'logo', $args['disallowed'], true ) ) && $this->can_use_logo() ) {
133
if ( $image = $this->get_site_logo( true ) )
134
goto end;
135
}
136
137
+ //* 9. Get the WP 4.3 Site Icon
138
if ( $all_allowed || false === in_array( 'icon', $args['disallowed'], true ) ) {
139
if ( $image = $this->get_site_icon( 'full', true ) )
140
goto end;
142
143
end :;
144
145
+ if ( $args['escape'] && $image )
146
+ $image = \esc_url( $image );
147
148
+ return (string) $image;
149
}
150
151
/**
152
* Parse and sanitize image args.
153
*
154
* @since 2.5.0
155
+ * @since 2.9.0 : 1. Removed 'attr' index, it was unused.
156
+ * 2. Added 'skip_fallback' option.
157
+ * 3. Added 'escape' option.
158
*
159
* The image set in the filter will always be used as fallback
160
*
172
'image' => '',
173
'size' => 'full',
174
'icon' => false,
175
+ 'skip_fallback' => false,
176
'disallowed' => array(),
177
+ 'escape' => true,
178
);
179
180
/**
182
* @param string $image The image url
183
* @param mixed $size The image size
184
* @param bool $icon Fetch Image icon
185
+ * @param bool 'skip_fallback' Whether to skip fallback images.
186
* @param array $disallowed Disallowed image types : {
187
* array (
188
* string 'featured'
190
* string 'icon'
191
* )
192
* }
193
+ * @param bool 'escape' Whether to escape output.
194
* }
195
*
196
* @since 2.0.1
206
return $defaults;
207
208
//* Array merge doesn't support sanitation. We're simply type casting here.
209
+ $args['post_id'] = isset( $args['post_id'] ) ? (int) $args['post_id'] : $defaults['post_id'];
210
+ $args['image'] = isset( $args['image'] ) ? (string) $args['image'] : $defaults['image'];
211
+ $args['size'] = isset( $args['size'] ) ? $args['size'] : $defaults['size']; // Mixed.
212
+ $args['icon'] = isset( $args['icon'] ) ? (bool) $args['icon'] : $defaults['icon'];
213
+ $args['skip_fallback'] = isset( $args['skip_fallback'] ) ? (bool) $args['skip_fallback'] : $defaults['skip_fallback'];
214
+ $args['disallowed'] = isset( $args['disallowed'] ) ? (array) $args['disallowed'] : $defaults['disallowed'];
215
+ $args['escape'] = isset( $args['escape'] ) ? (bool) $args['escape'] : $defaults['escape'];
216
217
return $args;
218
}
229
230
$default_args = $this->parse_image_args( '', '', true );
231
232
+ if ( empty( $args ) ) {
233
$args = $default_args;
234
+ } else {
235
+ $args = $this->parse_image_args( $args, $default_args );
236
}
237
238
return $args;
239
}
240
241
/**
242
+ * Returns unescaped HomePage settings image URL from post ID input.
243
*
244
+ * @since 2.9.0
245
* @uses $this->image_dimensions
246
*
247
* @param int $id The post ID.
248
+ * @param bool $set_og_dimensions Whether to set Open Graph and Twitter dimensions.
249
+ * @return string The unescaped HomePage social image URL.
250
*/
251
+ public function get_social_image_url_from_home_meta( $id = 0, $set_og_dimensions = false ) {
252
253
+ //* Don't output if not front page.
254
+ if ( false === $this->is_front_page_by_id( $id ) )
255
+ return '';
256
257
+ $src = $this->get_option( 'homepage_social_image_url' );
258
259
if ( ! $src )
260
return '';
261
262
//* Calculate image sizes.
263
+ if ( $set_og_dimensions && $img_id = $this->get_option( 'homepage_social_image_id' ) ) {
264
+ $_src = \wp_get_attachment_image_src( $img_id, 'full' );
265
+
266
+ $i = $_src[0]; // Source URL
267
+ $w = $_src[1]; // Width
268
+ $h = $_src[2]; // Height
269
+
270
+ if ( \esc_url( $this->set_preferred_url_scheme( $i ) ) === \esc_url( $this->set_preferred_url_scheme( $src ) ) )
271
+ $this->image_dimensions = $this->image_dimensions + array( $id => array( 'width' => $w, 'height' => $h ) );
272
+ }
273
+
274
+ return $src;
275
+ }
276
+
277
+ /**
278
+ * Returns unescaped Post settings image URL from post ID input.
279
+ *
280
+ * @since 2.8.0
281
+ * @since 2.9.0 1. The second parameter now works.
282
+ * 2. Fallback image ID has been removed.
283
+ * @uses $this->image_dimensions
284
+ *
285
+ * @param int $id The post ID. Required.
286
+ * @param bool $set_og_dimensions Whether to set Open Graph and Twitter dimensions.
287
+ * @return string The unescaped social image URL.
288
+ */
289
+ public function get_social_image_url_from_post_meta( $id, $set_og_dimensions = false ) {
290
+
291
+ $src = $id ? $this->get_custom_field( '_social_image_url', $id ) : '';
292
+
293
+ if ( ! $src )
294
+ return '';
295
+
296
+ //* Calculate image sizes.
297
+ if ( $set_og_dimensions && $img_id = $this->get_custom_field( '_social_image_id', $id ) ) {
298
$_src = \wp_get_attachment_image_src( $img_id, 'full' );
299
300
$i = $_src[0]; // Source URL
341
342
/**
343
* Fetches image from post thumbnail.
344
+ *
345
* Resizes the image between 1500px if bigger. Then it saves the image and
346
* Keeps dimensions relative.
347
*
348
+ * @since 2.9.0
349
*
350
+ * @param int $id The post ID. Required.
351
+ * @param array $args The image args.
352
+ * @param bool $set_og_dimensions Whether to set Open Graph image dimensions.
353
+ * @return string The social image URL.
354
*/
355
+ public function get_social_image_url_from_post_thumbnail( $id, $args = array(), $set_og_dimensions = false ) {
356
357
+ $image_id = $id ? \get_post_thumbnail_id( $id ) : '';
358
359
+ if ( ! $image_id )
360
+ return '';
361
362
+ $args = $this->reparse_image_args( $args );
363
$args['get_the_real_ID'] = true;
364
365
+ $image = $this->parse_og_image( $image_id, $args, $set_og_dimensions );
366
367
return $image;
368
}
402
* @since 2.5.0
403
* @since 2.8.0 : 1. Removed staticvar.
404
* 2. Now adds ID call to OG image called listener.
405
+ * @since 2.9.0 : Added $set_og_dimension parameter
406
*
407
* @todo create formula to fetch transient.
408
* @priority high 2.7.0
409
*
410
* @param int $id The attachment ID.
411
* @param array $args The image args
412
+ * @param bool $set_og_dimensions Whether to set OG dimensions.
413
* @return string|empty Parsed image url or empty if already called
414
*/
415
+ public function parse_og_image( $id, $args = array(), $set_og_dimensions = false ) {
416
417
//* Don't do anything if $id isn't given.
418
+ if ( empty( $id ) )
419
return;
420
421
if ( empty( $args ) )
422
$args = $this->reparse_image_args( $args );
423
424
+ $src = \wp_get_attachment_image_src( $id, $args['size'], $args['icon'] );
425
426
$i = $src[0]; // Source URL
427
$w = $src[1]; // Width
483
}
484
endif;
485
486
+ if ( $set_og_dimensions ) {
487
+ //* Whether to use the post ID (Post Thumbnail) or input ID (ID was known beforehand)
488
+ $usage_id = ! empty( $args['get_the_real_ID'] ) ? $this->get_the_real_ID() : $id;
489
490
+ $this->image_dimensions = $this->image_dimensions + array( $usage_id => array( 'width' => $w, 'height' => $h ) );
491
+ }
492
493
return $i;
494
}
inc/classes/generate-ldjson.class.php CHANGED
@@ -53,17 +53,17 @@ class Generate_Ldjson extends Generate_Image {
53
54
$this->setup_ld_json_transient( $this->get_the_real_ID() );
55
56
- if ( $this->the_seo_framework_debug ) $this->debug_init( __METHOD__, false, $debug_key = microtime( true ), array( 'LD Json transient' => $this->ld_json_transient, 'Output from transient' => false !== $this->get_transient( $this->ld_json_transient ) ) );
57
58
$use_cache = $this->is_option_checked( 'cache_meta_schema' );
59
60
$output = $use_cache ? $this->get_transient( $this->ld_json_transient ) : false;
61
- if ( false === $output ) {
62
63
$output = '';
64
65
//* Only display search helper and knowledge graph on front page.
66
- if ( $this->is_front_page() ) {
67
68
$sitename = $this->ld_json_name();
69
$sitelinks = $this->ld_json_search();
@@ -97,13 +97,9 @@ class Generate_Ldjson extends Generate_Image {
97
98
$this->set_transient( $this->ld_json_transient, $output, $expiration );
99
}
100
- }
101
102
- /**
103
- * Debug output.
104
- * @since 2.4.2
105
- */
106
- if ( $this->the_seo_framework_debug ) $this->debug_init( __METHOD__, false, $debug_key, array( 'LD Json transient output' => $output ) );
107
108
return $output;
109
}
@@ -221,8 +217,8 @@ class Generate_Ldjson extends Generate_Image {
221
*
222
* @staticvar array $images
223
* @since 2.7.0
224
- * @todo implement blog page image.
225
- * @priority low 2.7.0+ extension.
226
*
227
* @param int|string $id The page, post, product or term ID.
228
* @param bool $singular Whether the ID is singular.
@@ -239,12 +235,32 @@ class Generate_Ldjson extends Generate_Image {
239
$image = '';
240
241
if ( $singular ) {
242
- if ( $id === $this->get_the_real_ID() ) {
243
- $image = $this->get_image_from_cache( $id );
244
- } elseif ( $id ) {
245
- //* No ID (0) results in the home page being a blog. This will be handled in the future.
246
- $image = $this->get_image( $id );