3rdparty
6 days ago
abilities
6 days ago
admin
6 days ago
cli
5 years ago
frontend
2 weeks ago
helpers
2 weeks ago
metaboxes
2 years ago
module
2 weeks ago
modules
6 days ago
opengraph
2 weeks ago
replace-variables
2 weeks ago
rest
6 days ago
settings
2 weeks ago
traits
2 weeks ago
updates
2 weeks ago
class-auto-updater.php
5 years ago
class-cmb2.php
2 weeks ago
class-common.php
5 months ago
class-compatibility.php
1 year ago
class-data-encryption.php
5 months ago
class-defaults.php
6 years ago
class-frontend-seo-score.php
2 weeks ago
class-helper.php
10 months ago
class-installer.php
2 weeks ago
class-json-manager.php
1 year ago
class-kb.php
7 months ago
class-metadata.php
2 months ago
class-post.php
1 year ago
class-rewrite.php
5 months ago
class-settings.php
1 year ago
class-term.php
1 year ago
class-thumbnail-overlay.php
1 year ago
class-tracking.php
6 days ago
class-update-email.php
2 weeks ago
class-updates.php
3 months ago
class-user.php
8 months ago
index.php
7 years ago
interface-runner.php
7 years ago
template-tags.php
1 year ago
class-frontend-seo-score.php
291 lines
| 1 | <?php |
| 2 | /** |
| 3 | * Show SEO Score on the front end. |
| 4 | * |
| 5 | * @since 0.9.0 |
| 6 | * @package RankMath |
| 7 | * @author Rank Math <support@rankmath.com> |
| 8 | */ |
| 9 | |
| 10 | namespace RankMath; |
| 11 | |
| 12 | use RankMath\KB; |
| 13 | use RankMath\Post; |
| 14 | use RankMath\Helper; |
| 15 | use RankMath\Traits\Hooker; |
| 16 | use RankMath\Traits\Shortcode; |
| 17 | use RankMath\Admin\Admin_Helper; |
| 18 | |
| 19 | defined( 'ABSPATH' ) || exit; |
| 20 | |
| 21 | /** |
| 22 | * Frontend_SEO_Score class. |
| 23 | */ |
| 24 | class Frontend_SEO_Score { |
| 25 | |
| 26 | use Hooker; |
| 27 | use Shortcode; |
| 28 | |
| 29 | /** |
| 30 | * SEO Score. |
| 31 | * |
| 32 | * @var array |
| 33 | */ |
| 34 | private $score = 0; |
| 35 | |
| 36 | /** |
| 37 | * Flag to only add CSS once. |
| 38 | * |
| 39 | * @var array |
| 40 | */ |
| 41 | private $css_added = false; |
| 42 | |
| 43 | /** |
| 44 | * Convenience method to output as string. |
| 45 | * |
| 46 | * @return string |
| 47 | */ |
| 48 | public function __toString() { |
| 49 | return $this->get_output(); |
| 50 | } |
| 51 | |
| 52 | /** |
| 53 | * The Constructor |
| 54 | * |
| 55 | * @codeCoverageIgnore |
| 56 | */ |
| 57 | public function __construct() { |
| 58 | $this->filter( 'the_content', 'insert_score' ); |
| 59 | |
| 60 | $this->add_shortcode( 'rank_math_seo_score', 'shortcode' ); |
| 61 | } |
| 62 | |
| 63 | /** |
| 64 | * Insert score before/after content. |
| 65 | * |
| 66 | * @param string $content Original post content. |
| 67 | * @return string $content New content. |
| 68 | */ |
| 69 | public function insert_score( $content ) { |
| 70 | |
| 71 | if ( ! $this->score_enabled() ) { |
| 72 | return $content; |
| 73 | } |
| 74 | |
| 75 | $score_location = Helper::get_settings( 'general.frontend_seo_score_position' ); |
| 76 | if ( 'custom' === $score_location ) { |
| 77 | return $content; |
| 78 | } |
| 79 | |
| 80 | if ( 'top' === $score_location || 'both' === $score_location ) { |
| 81 | $content = $this->get_output( [ 'class' => 'before-content' ] ) . $content; |
| 82 | } |
| 83 | |
| 84 | if ( 'bottom' === $score_location || 'both' === $score_location ) { |
| 85 | $content = $content . $this->get_output( [ 'class' => 'after-content' ] ); |
| 86 | } |
| 87 | |
| 88 | return $content; |
| 89 | } |
| 90 | |
| 91 | /** |
| 92 | * Check if front end SEO score is enabled for this post. |
| 93 | * |
| 94 | * @return bool |
| 95 | */ |
| 96 | public function score_enabled() { |
| 97 | /* |
| 98 | * The loop_start check ensures this only runs after wp_head. |
| 99 | */ |
| 100 | if ( is_front_page() || ! is_singular() || ! did_action( 'loop_start' ) ) { |
| 101 | return false; |
| 102 | } |
| 103 | |
| 104 | $post_type = get_post_type(); |
| 105 | $post_id = get_the_ID(); |
| 106 | $score_enabled = Helper::get_settings( 'general.frontend_seo_score' ) |
| 107 | && Helper::is_score_enabled() |
| 108 | && in_array( $post_type, (array) Helper::get_settings( 'general.frontend_seo_score_post_types' ), true ) |
| 109 | && get_post_meta( $post_id, 'rank_math_dont_show_seo_score', true ) !== 'on'; |
| 110 | |
| 111 | return $score_enabled; |
| 112 | } |
| 113 | |
| 114 | /** |
| 115 | * Get the SEO score HTML. |
| 116 | * |
| 117 | * @param array $args Arguments. |
| 118 | * @return string |
| 119 | */ |
| 120 | public function get_output( $args = [] ) { |
| 121 | $args = $this->do_filter( |
| 122 | 'frontend/seo_score/args', |
| 123 | wp_parse_args( |
| 124 | $args, |
| 125 | [ |
| 126 | 'template' => Helper::get_settings( 'general.frontend_seo_score_template' ), |
| 127 | 'backlink' => Helper::get_settings( 'general.support_rank_math' ), |
| 128 | 'post_id' => '0', |
| 129 | 'class' => '', |
| 130 | ] |
| 131 | ) |
| 132 | ); |
| 133 | |
| 134 | $score = (int) $this->get_score( $args['post_id'] ); |
| 135 | $rating = $this->get_rating( $score ); |
| 136 | |
| 137 | if ( ! $score ) { |
| 138 | return $this->do_filter( 'frontend/seo_score/html', '', $args, $score ); |
| 139 | } |
| 140 | |
| 141 | // If template is empty we output $score value directly. |
| 142 | $html = $score; |
| 143 | $backlink = '<a href="' . KB::get( 'seo-suite', 'Frontend SEO score' ) . '" target="_blank" rel="noopener">Rank Math SEO</a>'; |
| 144 | if ( ! empty( $args['template'] ) ) { |
| 145 | ob_start(); |
| 146 | |
| 147 | ?> |
| 148 | <div class="rank-math-seo-score template-<?php echo sanitize_html_class( $args['template'], 'circle' ); ?> <?php echo sanitize_html_class( $rating, 'unknown' ); ?>-seo <?php echo esc_attr( $args['class'] ); ?>"> |
| 149 | |
| 150 | <span class="score"> |
| 151 | <?php echo esc_html( absint( $score ) ); ?> |
| 152 | <span class="outof"> |
| 153 | / 100 |
| 154 | </span> |
| 155 | </span> |
| 156 | |
| 157 | <?php if ( $args['backlink'] ) : ?> |
| 158 | <div class="backlink"> |
| 159 | <span class="poweredby"> |
| 160 | <?php |
| 161 | echo wp_kses_post( |
| 162 | sprintf( |
| 163 | /* translators: %s is a Rank Math link. */ |
| 164 | __( 'Powered by %s', 'seo-by-rank-math' ), |
| 165 | $this->do_filter( 'frontend/seo_score/backlink', $backlink ) |
| 166 | ) |
| 167 | ); |
| 168 | ?> |
| 169 | </span> |
| 170 | </div> |
| 171 | <?php endif; ?> |
| 172 | |
| 173 | <span class="label"> |
| 174 | <?php echo esc_html__( 'SEO Score', 'seo-by-rank-math' ); ?> |
| 175 | </span> |
| 176 | |
| 177 | </div> |
| 178 | <?php |
| 179 | $this->add_css(); |
| 180 | |
| 181 | $html = ob_get_clean(); |
| 182 | } |
| 183 | |
| 184 | return $this->do_filter( 'frontend/seo_score/html', $html, $args, $score ); |
| 185 | } |
| 186 | |
| 187 | /** |
| 188 | * Turn numeric score into textual rating. |
| 189 | * |
| 190 | * @param int $score SEO Score. |
| 191 | * @return string |
| 192 | */ |
| 193 | public function get_rating( $score ) { |
| 194 | $hash = [ |
| 195 | 'unknown' => 0, |
| 196 | 'bad' => 50, |
| 197 | 'good' => 80, |
| 198 | 'great' => 100, |
| 199 | ]; |
| 200 | |
| 201 | foreach ( $hash as $key => $value ) { |
| 202 | if ( $score <= $value ) { |
| 203 | return $key; |
| 204 | } |
| 205 | } |
| 206 | |
| 207 | return array_keys( $hash )[0]; |
| 208 | } |
| 209 | |
| 210 | /** |
| 211 | * Get the SEO score for given post. |
| 212 | * |
| 213 | * @param int $post_id Post ID. |
| 214 | * @return int |
| 215 | */ |
| 216 | public function get_score( $post_id = 0 ) { |
| 217 | global $post; |
| 218 | if ( empty( $post_id ) ) { |
| 219 | $post_id = $post->ID; |
| 220 | } |
| 221 | |
| 222 | return get_post_meta( $post_id, 'rank_math_seo_score', true ); |
| 223 | } |
| 224 | |
| 225 | /** |
| 226 | * Show field check callback. |
| 227 | * |
| 228 | * @return boolean |
| 229 | */ |
| 230 | public static function show_on() { |
| 231 | // Early Bail if is sttic homepage. |
| 232 | if ( Admin_Helper::is_home_page() ) { |
| 233 | return false; |
| 234 | } |
| 235 | |
| 236 | $post_type = get_post_type(); |
| 237 | return Helper::get_settings( 'general.frontend_seo_score' ) && |
| 238 | in_array( $post_type, (array) Helper::get_settings( 'general.frontend_seo_score_post_types' ), true ); |
| 239 | } |
| 240 | |
| 241 | /** |
| 242 | * Shortcode output. |
| 243 | * |
| 244 | * @param array $atts Shortcode attributes. |
| 245 | */ |
| 246 | public function shortcode( $atts ) { |
| 247 | if ( ! $this->score_enabled() ) { |
| 248 | return ''; |
| 249 | } |
| 250 | |
| 251 | $atts = shortcode_atts( |
| 252 | [ |
| 253 | 'class' => 'as-shortcode', |
| 254 | ], |
| 255 | $atts, |
| 256 | 'rank-math-seo-score' |
| 257 | ); |
| 258 | |
| 259 | return $this->get_output( $atts ); |
| 260 | } |
| 261 | |
| 262 | /** |
| 263 | * Add CSS inline, once. |
| 264 | */ |
| 265 | public function add_css() { |
| 266 | if ( $this->css_added ) { |
| 267 | return; |
| 268 | } |
| 269 | ?> |
| 270 | <style type="text/css"> |
| 271 | .rank-math-seo-score{font-family:sans-serif;position:relative;display:inline-block;height:96px;width:96px;margin:20px 20px 30px;text-align:center;color:#fff;border:none;border-radius:50%;background:#eee;-webkit-box-shadow:1px 1px 1px #bbb;box-shadow:1px 1px 1px #bbb}.rank-math-seo-score.before-content{margin:0 0 30px 20px;float:right}.rank-math-seo-score.after-content{margin:20px 0 30px 20px}.rank-math-seo-score.as-shortcode{display:inline-block}.rank-math-seo-score .label{font-size:12px;position:absolute;top:100px;left:0;display:block;width:100%;color:#979ea5}.rank-math-seo-score .score{font-size:42px;font-weight:bold;line-height:42px;display:block}.rank-math-seo-score .outof{font-size:12px;font-weight:normal;line-height:12px;display:block;color:rgba(255,255,255,0.7)}.rank-math-seo-score .backlink{font-size:12px;position:absolute;top:-94px;left:-12px;display:block;visibility:hidden;width:120px;padding:8px 10px;-webkit-transition:.25s all ease;transition:.25s all ease;-webkit-transition-delay:.25s;transition-delay:.25s;opacity:0;color:#a8a8a8;border:none;border-radius:8px;background:#fff;-webkit-box-shadow:0 4px 14px rgba(60,60,90,0.2);box-shadow:0 4px 12px rgba(60,60,90,0.15)}.rank-math-seo-score .backlink:after{position:absolute;bottom:-8px;left:calc(50% - 7px);width:0;height:0;content:'';border-width:8px 7.5px 0 7.5px;border-style:solid;border-color:#fff transparent transparent transparent}.rank-math-seo-score:hover .backlink{top:-74px;visibility:visible;opacity:1}.rank-math-seo-score .poweredby{font-size:13px;color:#a8a8a8}.rank-math-seo-score .poweredby a{display:block;font-weight:normal;text-decoration:none;color:#6372b6;border:none}.rank-math-seo-score.unknown-seo{background:#eee;background:linear-gradient(135deg, #b9b9b9 0%, #989898 100%);-webkit-box-shadow:1px 1px 1px #bbb;box-shadow:1px 1px 1px #bbb}.rank-math-seo-score.bad-seo{background:#f8b0a2;background:linear-gradient(135deg, #f8b0a2 0%, #f1938c 100%);-webkit-box-shadow:1px 1px 1px #e48982;box-shadow:1px 1px 1px #e48982;filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#f8b0a2', endColorstr='#f1938c',GradientType=1 )}.rank-math-seo-score.good-seo{background:#fdd07a;background:linear-gradient(135deg, #fdd07a 0%, #fcbe6c 100%);-webkit-box-shadow:1px 1px 1px #efb463;box-shadow:1px 1px 1px #efb463;filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#fdd07a', endColorstr='#fcbe6c',GradientType=1 )}.rank-math-seo-score.great-seo{background:#99d484;background:linear-gradient(135deg, #99d484 0%, #83c97f 100%);-webkit-box-shadow:1px 1px 1px #5ba857;box-shadow:1px 1px 1px #5ba857;filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#99d484', endColorstr='#83c97f',GradientType=1 )}.rank-math-seo-score.template-circle .score{margin-top:22px !important}.rank-math-seo-score.template-square{height:80px;width:110px;border-radius:12px}.rank-math-seo-score.template-square .score{margin:10px 12px;text-align:left}.rank-math-seo-score.template-square .outof{display:inline-block;margin-left:-8px}.rank-math-seo-score.template-square .label{font-size:13px;top:52px;left:14px;text-align:left;color:rgba(255,255,255,0.8)}.rank-math-seo-score.template-square .backlink{left:-5px}.rank-math-seo-score.template-square.before-content{margin-bottom:20px}.rank-math-seo-score.template-square.after-content{margin-bottom:0}.theme-twentytwenty .rank-math-seo-score{width:96px !important}.theme-twentytwenty .rank-math-seo-score.template-square{width:110px !important}.theme-twentytwenty .rank-math-seo-score.before-content{margin:0 auto 30px auto;display:inherit;float:none}.theme-twentytwenty .rank-math-seo-score.template-circle .score,.theme-twentytwenty .rank-math-seo-score.template-square .score{transform:translateY(22px)} |
| 272 | </style> |
| 273 | <?php |
| 274 | $this->css_added = true; |
| 275 | } |
| 276 | |
| 277 | /** |
| 278 | * Settings field default callback. |
| 279 | */ |
| 280 | public static function post_types_field_default() { |
| 281 | $seo_score = Helper::get_settings( 'general.frontend_seo_score' ); |
| 282 | $post_types = Helper::get_settings( 'general.frontend_seo_score_post_types' ); |
| 283 | |
| 284 | if ( 'on' === $seo_score && '' === $post_types ) { |
| 285 | return []; |
| 286 | } |
| 287 | |
| 288 | return [ 'post' ]; |
| 289 | } |
| 290 | } |
| 291 |