Commit realizado el 12:13:52 08-04-2024

This commit is contained in:
Pagina Web Monito
2024-04-08 12:13:55 -04:00
commit 0c33094de9
7815 changed files with 1365694 additions and 0 deletions

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,10 @@
/**
* jquery-circle-progress - jQuery Plugin to draw animated circular progress bars:
* {@link http://kottenator.github.io/jquery-circle-progress/}
*
* @author Rostyslav Bryzgunov <kottenator@gmail.com>
* @version 1.2.2
* @licence MIT
* @preserve
*/
!function(i){if("function"==typeof define&&define.amd)define(["jquery"],i);else if("object"==typeof module&&module.exports){var t=require("jquery");i(t),module.exports=t}else i(jQuery)}(function(i){function t(i){this.init(i)}t.prototype={value:0,size:100,startAngle:-Math.PI,thickness:"auto",fill:{gradient:["#3aeabb","#fdd250"]},emptyFill:"rgba(0, 0, 0, .1)",animation:{duration:1200,easing:"circleProgressEasing"},animationStartValue:0,reverse:!1,lineCap:"butt",insertMode:"prepend",constructor:t,el:null,canvas:null,ctx:null,radius:0,arcFill:null,lastFrameValue:0,init:function(t){i.extend(this,t),this.radius=this.size/2,this.initWidget(),this.initFill(),this.draw(),this.el.trigger("circle-inited")},initWidget:function(){this.canvas||(this.canvas=i("<canvas>")["prepend"==this.insertMode?"prependTo":"appendTo"](this.el)[0]);var t=this.canvas;if(t.width=this.size,t.height=this.size,this.ctx=t.getContext("2d"),window.devicePixelRatio>1){var e=window.devicePixelRatio;t.style.width=t.style.height=this.size+"px",t.width=t.height=this.size*e,this.ctx.scale(e,e)}},initFill:function(){function t(){var t=i("<canvas>")[0];t.width=e.size,t.height=e.size,t.getContext("2d").drawImage(g,0,0,r,r),e.arcFill=e.ctx.createPattern(t,"no-repeat"),e.drawFrame(e.lastFrameValue)}var e=this,a=this.fill,n=this.ctx,r=this.size;if(!a)throw Error("The fill is not specified!");if("string"==typeof a&&(a={color:a}),a.color&&(this.arcFill=a.color),a.gradient){var s=a.gradient;if(1==s.length)this.arcFill=s[0];else if(s.length>1){for(var l=a.gradientAngle||0,o=a.gradientDirection||[r/2*(1-Math.cos(l)),r/2*(1+Math.sin(l)),r/2*(1+Math.cos(l)),r/2*(1-Math.sin(l))],h=n.createLinearGradient.apply(n,o),c=0;c<s.length;c++){var d=s[c],u=c/(s.length-1);i.isArray(d)&&(u=d[1],d=d[0]),h.addColorStop(u,d)}this.arcFill=h}}if(a.image){var g;a.image instanceof Image?g=a.image:(g=new Image,g.src=a.image),g.complete?t():g.onload=t}},draw:function(){this.animation?this.drawAnimated(this.value):this.drawFrame(this.value)},drawFrame:function(i){this.lastFrameValue=i,this.ctx.clearRect(0,0,this.size,this.size),this.drawEmptyArc(i),this.drawArc(i)},drawArc:function(i){if(0!==i){var t=this.ctx,e=this.radius,a=this.getThickness(),n=this.startAngle;t.save(),t.beginPath(),this.reverse?t.arc(e,e,e-a/2,n-2*Math.PI*i,n):t.arc(e,e,e-a/2,n,n+2*Math.PI*i),t.lineWidth=a,t.lineCap=this.lineCap,t.strokeStyle=this.arcFill,t.stroke(),t.restore()}},drawEmptyArc:function(i){var t=this.ctx,e=this.radius,a=this.getThickness(),n=this.startAngle;i<1&&(t.save(),t.beginPath(),i<=0?t.arc(e,e,e-a/2,0,2*Math.PI):this.reverse?t.arc(e,e,e-a/2,n,n-2*Math.PI*i):t.arc(e,e,e-a/2,n+2*Math.PI*i,n),t.lineWidth=a,t.strokeStyle=this.emptyFill,t.stroke(),t.restore())},drawAnimated:function(t){var e=this,a=this.el,n=i(this.canvas);n.stop(!0,!1),a.trigger("circle-animation-start"),n.css({animationProgress:0}).animate({animationProgress:1},i.extend({},this.animation,{step:function(i){var n=e.animationStartValue*(1-i)+t*i;e.drawFrame(n),a.trigger("circle-animation-progress",[i,n])}})).promise().always(function(){a.trigger("circle-animation-end")})},getThickness:function(){return i.isNumeric(this.thickness)?this.thickness:this.size/14},getValue:function(){return this.value},setValue:function(i){this.animation&&(this.animationStartValue=this.lastFrameValue),this.value=i,this.draw()}},i.circleProgress={defaults:t.prototype},i.easing.circleProgressEasing=function(i){return i<.5?(i=2*i,.5*i*i*i):(i=2-2*i,1-.5*i*i*i)},i.fn.circleProgress=function(e,a){var n="circle-progress",r=this.data(n);if("widget"==e){if(!r)throw Error('Calling "widget" method on not initialized instance is forbidden');return r.canvas}if("value"==e){if(!r)throw Error('Calling "value" method on not initialized instance is forbidden');if("undefined"==typeof a)return r.getValue();var s=arguments[1];return this.each(function(){i(this).data(n).setValue(s)})}return this.each(function(){var a=i(this),r=a.data(n),s=i.isPlainObject(e)?e:{};if(r)r.init(s);else{var l=i.extend({},a.data());"string"==typeof l.fill&&(l.fill=JSON.parse(l.fill)),"string"==typeof l.animation&&(l.animation=JSON.parse(l.animation)),s=i.extend(l,s),s.el=a,r=new t(s),a.data(n,r)}})}});

View File

@@ -0,0 +1 @@
!function(){"use strict";var e,r={n:function(e){var a=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(a,{a:a}),a},d:function(e,a){for(var t in a)r.o(a,t)&&!r.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:a[t]})},o:function(e,r){return Object.prototype.hasOwnProperty.call(e,r)}},a=jQuery;(e=r.n(a)())((function(){({init:function(){this.wrap=e(".rank-math-seo-analysis-wrap"),this.results=this.wrap.find(".rank-math-results-wrapper"),this.progress=this.wrap.find(".progress"),this.counter=this.wrap.find(".progress-bar span"),this.events(),this.graphs(),this.single(),this.filters()},events:function(){var r=this;this.wrap.on("click",".rank-math-recheck",(function(a){e(this),a.preventDefault(),r.wrap.addClass("is-loading").removeClass("is-loaded"),r.results.empty();var t=function(a){e(".notice-seo-analysis-error").remove(),0!==a.length&&(r.wrap.find(".rank-math-analyzer-result").first().prepend(a),a.slideDown())},n={action:"rank_math_analyze",security:rankMath.security,u:r.wrap.find(".rank-math-analyze-url").val()};r.wrap.find('input[type="hidden"]').length>0&&r.wrap.find('input[type="hidden"]').each((function(){n[e(this).attr("name")]=e(this).val()})),e.ajax({url:ajaxurl,type:"POST",dataType:"html",data:n,beforeSend:function(){r.renderProgressBar()},complete:function(){clearInterval(r.interval),r.progress.css("width","100%"),r.counter.html("100%")},error:function(){var a=e('<div class="notice notice-error is-dismissible notice-seo-analysis-error"><p>An error occured.</p></div>').hide();t(a),r.wrap.addClass("is-loaded").removeClass("is-loading")},success:function(a){r.results.html(a);var n=r.results.find(".notice");0!==e(a).find("#rank-math-circle-progress").length?(r.wrap.addClass("is-loaded").removeClass("is-loading"),r.graphs()):(r.wrap.removeClass("is-loaded is-loading"),r.progress.css("width","0%"),r.counter.html("0%")),t(n)}})})),r.wrap.on("click",".result-action",(function(r){r.preventDefault(),e(this).parent("div").toggleClass("expanded")})),r.wrap.on("click",".enable-auto-update",(function(r){r.preventDefault(),e.ajax({url:ajaxurl,type:"POST",data:{action:"rank_math_enable_auto_update",security:rankMath.security}}),e(this).closest(".auto-update-disabled").addClass("hidden").siblings(".auto-update-enabled").removeClass("hidden").closest(".row-description").find(".status-icon").removeClass("status-warning dashicons-warning").addClass("status-ok dashicons-yes")}))},renderProgressBar:function(){var e=this,r=0;e.progress.css("width",r),e.interval=setInterval((function(){100==++r&&clearInterval(e.interval),e.counter.html(r+"%"),e.progress.css("width",r+"%")}),30)},graphs:function(){var r=e("#rank-math-circle-progress");if(!(0>r.length)){var a=r.data("result"),t=["#10AC84","#10AC84"];.5>a?t=["#ed5e5e","#ed5e5e"]:.7>a&&(t=["#FF9F43","#FF9F43"]),r.circleProgress({value:a,size:207,thickness:15,lineCap:"round",emptyFill:"#e9e9ea",fill:{gradient:t},startAngle:-Math.PI/2})}},single:function(){var e=this,r=e.wrap.find(".rank-math-current-url"),a=e.wrap.find(".rank-math-analyze-url"),t=e.wrap.find(".rank-math-recheck"),n=e.wrap.find(".rank-math-changeurl"),s=e.wrap.find(".rank-math-changeurl-ok");if(!a.length)return e;n.on("click",(function(){return r.hide(),n.hide(),a.show(),s.show(),!1})),s.on("click",(function(){return a.hide(),s.hide(),r.show(),n.show(),r.html(a.val()),t.trigger("click"),!1})),a.on("keypress",(function(e){13===e.keyCode&&s.trigger("click")})),t.not(".no-autostart").trigger("click")},filters:function(){var r=this,a=r.wrap.find(".rank-math-result-filter");r.wrap.on("click",a,(function(a){var t=e(a.target).data("filter"),n=r.wrap.find(".rank-math-result-filter");if(void 0!==t){a.preventDefault();var s=r.wrap.find(".rank-math-result-table"),i=r.wrap.find(".table-row");if(n.removeClass("active"),e(a.target).addClass("active"),s.addClass("hidden"),i.addClass("hidden"),"all"===t)return s.removeClass("hidden"),void i.removeClass("hidden");s.filter(".rank-math-result-statuses-"+t).removeClass("hidden"),i.filter(".rank-math-result-status-"+t).removeClass("hidden")}}))}}).init()}))}();

View File

@@ -0,0 +1,104 @@
<?php
/**
* The nav tabs on the Dashboard page.
*
* @since 1.0.40
* @package RankMath
* @subpackage RankMath\Admin
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\SEO_Analysis;
use RankMath\Helper;
use RankMath\Helpers\Param;
defined( 'ABSPATH' ) || exit;
/**
* Admin Tabs class.
*
* @codeCoverageIgnore
*/
class Admin_Tabs {
/**
* Display dashboard tabs.
*/
public function display() {
$nav_links = $this->get_nav_links();
if ( empty( $nav_links ) ) {
return;
}
?>
<div class="rank-math-tab-nav" role="tablist" aria-orientation="horizontal">
<?php
foreach ( $nav_links as $id => $link ) {
$this->nav_link( $link );
}
?>
</div>
<?php
}
/**
* Get URL for dashboard nav links.
*
* @param array $link Link data.
* @return string Link URL.
*/
public function get_link_url( $link ) {
return Helper::get_admin_url( $link['url'], $link['args'] );
}
/**
* Output dashboard nav link.
*
* @param array $link Link data.
* @return void
*/
public function nav_link( $link ) {
if ( isset( $link['cap'] ) && ! current_user_can( $link['cap'] ) ) {
return;
}
$default_tab = 'seo_analyzer';
?>
<a
class="rank-math-tab<?php echo Param::get( 'view', $default_tab ) === sanitize_html_class( $link['id'] ) ? ' is-active' : ''; ?>"
href="<?php echo esc_url( $this->get_link_url( $link ) ); ?>"
title="<?php echo esc_attr( $link['title'] ); ?>">
<?php echo ! empty( $link['icon'] ) ? '<i class="' . esc_attr( $link['icon'] ) . '"></i>' : ''; ?>
<?php echo esc_html( $link['title'] ); ?>
</a>
<?php
}
/**
* Get dashbaord navigation links
*
* @return array
*/
private function get_nav_links() {
$links = [
'seo_analyzer' => [
'id' => 'seo_analyzer',
'url' => 'seo-analysis',
'args' => 'view=seo_analyzer',
'cap' => 'rank_math_site_analysis',
'title' => esc_html__( 'SEO Analyzer', 'rank-math' ),
'icon' => 'rm-icon rm-icon-analyzer',
],
'competitor_analyzer' => [
'id' => 'competitor_analyzer',
'url' => 'seo-analysis',
'args' => 'view=competitor_analyzer',
'cap' => 'rank_math_site_analysis',
'title' => esc_html__( 'Competitor Analyzer', 'rank-math' ),
'icon' => 'rm-icon rm-icon-users',
],
];
return apply_filters( 'rank_math/seo_analysis/admin_tab_links', $links );
}
}

View File

@@ -0,0 +1,121 @@
<?php
/**
* The SEO Analyzer module - admin side functionality.
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\SEO_Analysis
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\SEO_Analysis;
use RankMath\Module\Base;
use RankMath\Admin\Page;
use RankMath\KB;
defined( 'ABSPATH' ) || exit;
/**
* Admin class.
*/
class Admin extends Base {
/**
* Module ID.
*
* @var string
*/
public $id = '';
/**
* Module directory.
*
* @var string
*/
public $directory = '';
/**
* Module page.
*
* @var object
*/
public $page;
/**
* SEO Analyzer object.
*
* @var object
*/
public $analyzer;
/**
* The Constructor.
*/
public function __construct() {
$directory = dirname( __FILE__ );
$this->config(
[
'id' => 'seo-analysis',
'directory' => $directory,
]
);
parent::__construct();
if ( ( defined( 'DOING_AJAX' ) && DOING_AJAX ) || $this->page->is_current_page() ) {
include_once 'seo-analysis-tests.php';
$this->analyzer = new SEO_Analyzer();
}
$this->action( 'rank_math/analyzer/results_header', 'add_print_button', 15, 0 );
}
/**
* Register admin page.
*/
public function register_admin_page() {
$uri = untrailingslashit( plugin_dir_url( __FILE__ ) );
$new_label = '';
if ( ! get_option( 'rank_math_viewed_seo_analyer', false ) && strtotime( '28 December 2022' ) > get_option( 'rank_math_install_date' ) ) {
$new_label = '<span class="rank-math-new-label" style="color:#ed5e5e;font-size:10px;font-weight:normal;">' . esc_html__( 'New!', 'rank-math' ) . '</span>';
}
$this->page = new Page(
'rank-math-seo-analysis',
// Translators: placeholder is the new Rank Math label.
sprintf( esc_html__( 'SEO Analyzer %s', 'rank-math' ), $new_label ),
[
'position' => 60,
'parent' => 'rank-math',
'capability' => 'rank_math_site_analysis',
'classes' => [ 'rank-math-page' ],
'render' => $this->directory . '/views/main.php',
'assets' => [
'styles' => [
'rank-math-common' => '',
'rank-math-seo-analysis' => $uri . '/assets/css/seo-analysis.css',
],
'scripts' => [
'circle-progress' => $uri . '/assets/js/circle-progress.min.js',
'rank-math-seo-analysis' => $uri . '/assets/js/seo-analysis.js',
],
],
]
);
}
/**
* Add print button.
*/
public function add_print_button() {
?>
<a href="<?php KB::the( 'pro', 'SEO Analyzer Print Button' ); ?>" class="button button-secondary rank-math-print-results disabled" target="_blank">
<span class="dashicons dashicons-printer"></span>
<?php esc_html_e( 'Print', 'rank-math' ); ?>
<span class="rank-math-pro-badge">PRO</span>
</a>
<?php
}
}

View File

@@ -0,0 +1,399 @@
<?php
/**
* The SEO Analyzer result of each test.
*
* @since 1.0.24
* @package RankMath
* @subpackage RankMath\SEO_Analysis
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\SEO_Analysis;
use RankMath\Helper;
defined( 'ABSPATH' ) || exit;
/**
* Result class.
*/
class Result {
/**
* Result ID.
*
* @var string
*/
private $id;
/**
* Hold result data.
*
* @var array
*/
private $result;
/**
* Is sub-page.
*
* @var array
*/
private $is_subpage;
/**
* The Constructor.
*
* @param string $id Result id.
* @param object $data Result data.
* @param bool $is_subpage Is sub-page result.
*/
public function __construct( $id, $data, $is_subpage ) {
if ( is_a( $data, 'RankMath\\SEO_Analysis\\Result' ) ) {
$data = $data->result;
}
$this->id = $id;
$this->result = $data;
$this->is_subpage = $is_subpage;
}
/**
* Magic method: convert object to string.
*/
public function __toString() {
$kb_link = 'https://rankmath.com/kb/seo-analysis/';
if ( ! empty( $this->result['kb_link'] ) ) {
$kb_link = $this->result['kb_link'];
}
ob_start();
?>
<div class="row-title">
<?php $this->the_status(); ?>
<h3><?php echo esc_html( $this->result['title'] ); ?>
<?php if ( ! empty( $this->result['tooltip'] ) ) : ?>
<a href="<?php echo esc_url( $kb_link ); ?>" target="_blank" class="rank-math-tooltip"><em class="dashicons-before dashicons-editor-help"></em><span><?php echo esc_html( $this->result['tooltip'] ); ?></span></a>
<?php endif; ?>
</h3>
</div>
<div class="row-description">
<div class="row-content">
<?php if ( $this->has_fix() ) : ?>
<a href="#" class="button button-secondary button-small result-action"><?php esc_html_e( 'How to fix', 'rank-math' ); ?></a>
<?php endif; ?>
<?php echo wp_kses_post( $this->result['message'] ); ?>
<?php if ( $this->has_fix() ) : ?>
<div class="how-to-fix-wrapper">
<div class="analysis-test-how-to-fix">
<?php echo wp_kses_post( $this->result['fix'] ); ?>
<?php if ( ! preg_match( '#<\/a><\/p>$#i', trim( $this->result['fix'] ) ) ) : ?>
<p><a href="<?php echo esc_url( $kb_link ); ?>" target="_blank" class="analysis-read-more"><?php esc_html_e( 'Read more', 'rank-math' ); ?></a></p>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<div class="clear"></div>
<?php
if ( isset( $this->result['data'] ) && ! empty( $this->result['data'] ) ) {
$this->the_content();
}
?>
</div>
</div>
<?php
return ob_get_clean();
}
/**
* Get result ID.
*
* @return string
*/
public function get_id() {
return $this->id;
}
/**
* Get result category.
*
* @return string
*/
public function get_category() {
return is_array( $this->result ) && isset( $this->result['category'] ) ? $this->result['category'] : '';
}
/**
* Get result status.
*
* @return string
*/
public function get_status() {
return is_array( $this->result ) && isset( $this->result['status'] ) ? $this->result['status'] : '';
}
/**
* Has "how to fix" content.
*
* @return bool
*/
private function has_fix() {
return is_array( $this->result ) && in_array( $this->result['status'], [ 'fail', 'warning' ], true ) && ! empty( $this->result['fix'] );
}
/**
* Output test result status.
*/
private function the_status() {
if ( ! is_array( $this->result ) ) {
return;
}
$status = $this->result['status'];
if ( ! empty( $this->result['is_info'] ) ) {
$status = 'info';
}
$icons = [
'ok' => 'dashicons dashicons-yes',
'fail' => 'dashicons dashicons-no-alt',
'warning' => 'dashicons dashicons-warning',
'info' => 'dashicons',
];
$labels = [
'ok' => esc_html__( 'OK', 'rank-math' ),
'fail' => esc_html__( 'Failed', 'rank-math' ),
'warning' => esc_html__( 'Warning', 'rank-math' ),
'info' => esc_html__( 'Info', 'rank-math' ),
];
printf(
'<div class="status-icon status-%1$s %3$s" title="%2$s"></div>',
sanitize_html_class( $status ),
esc_attr( $labels[ $status ] ),
esc_attr( $icons[ $status ] )
);
}
/**
* Output test data.
*/
private function the_content() {
if ( ! is_array( $this->result ) ) {
return;
}
$data = $this->result['data'];
if ( 'common_keywords' === $this->id ) {
$this->the_tag_cloud( $data );
return;
}
if ( $this->is_list() || $this->is_reverse_heading() ) {
$this->the_list( $data );
return;
}
$explode = [ 'title_length', 'description_length', 'canonical' ];
if ( in_array( $this->id, $explode, true ) ) {
echo '<code class="full-width">' . wp_kses_post( join( ', ', (array) $data ) ) . '</code>';
return;
}
}
/**
* Render results list.
*
* @param array $data Keywords.
*/
private function the_list( $data ) {
$is_reverse_heading = $this->is_reverse_heading();
$html = '<ul class="info-list">';
foreach ( $data as $label => $text ) {
$text = is_array( $text ) ? join( ', ', $text ) : $text;
$html .= $is_reverse_heading ? '<li><strong>' . $label . ': </strong> ' . esc_html( $text ) . '</li>' :
'<li>' . esc_html( ( is_string( $label ) ? $label . ' (' . $text . ')' : $text ) ) . '</li>';
}
echo wp_kses_post( $html ) . '</ul>';
}
/**
* Check if result data should be rendered as a list or not.
*
* @return bool
*/
private function is_list() {
return in_array( $this->id, [ 'img_alt', 'minify_css', 'minify_js', 'active_plugins', 'h1_heading', 'h2_headings' ], true );
}
/**
* Check if result data should be rendered with reversed heading or not.
*
* @return bool
*/
private function is_reverse_heading() {
return in_array( $this->id, [ 'links_ratio', 'keywords_meta', 'page_objects' ], true );
}
/**
* Render tag cloud.
*
* @param array $data Keywords.
*/
private function the_tag_cloud( $data ) {
echo wp_kses_post( $this->get_tag_cloud( $data ) );
}
/**
* Get tag cloud HTML.
*
* @param array $data Keywords.
*/
private function get_tag_cloud( $data ) {
$font_size_max = 22;
$font_size_min = 10;
$max = max( $data );
$html = '<div class="wp-tag-cloud">';
foreach ( $data as $keyword => $occurrences ) {
$size = ( $occurrences / $max ) * ( $font_size_max - $font_size_min ) + $font_size_min;
$size = round( $size, 2 );
$html .= sprintf( '<span class="keyword-cloud-item" style="font-size: %.2fpx">%s</span> ', $size, htmlspecialchars( $keyword, ENT_QUOTES | ENT_SUBSTITUTE, 'utf-8' ) );
}
$html = rtrim( $html );
$html .= '</div>';
return apply_filters( 'rank_math/seo_analysis/tag_cloud_html', $html, $data );
}
/**
* Is test excluded.
*
* @return bool
*/
public function is_excluded() {
$exclude_tests = [
'active_plugins',
'active_theme',
'dirlist',
'libwww_perl_access',
'robots_txt',
'safe_browsing',
'xmlrpc',
// Local tests.
'comment_pagination',
'site_description',
'permalink_structure',
'cache_plugin',
'search_console',
'focus_keywords',
'post_titles',
];
return $this->is_subpage && in_array( $this->id, $exclude_tests, true );
}
/**
* Is test hidden.
*
* @return bool
*/
public function is_hidden() {
$always_hidden = [
'serp_preview',
'mobile_serp_preview',
];
// Hidden when not in advanced mode.
$hidden_tests = [
// Performance.
'image_header',
'minify_css',
'minify_js',
'page_objects',
'page_size',
'response_time',
// Security.
'directory_listing',
'safe_browsing',
'ssl',
'active_plugins',
'active_theme',
];
$is_hidden = in_array( $this->id, $always_hidden, true ) || ( ! Helper::is_advanced_mode() && in_array( $this->id, $hidden_tests, true ) );
return apply_filters( 'rank_math/seo_analysis/is_test_hidden', $is_hidden, $this->id );
}
/**
* Get test score.
*
* @return int
*/
public function get_score() {
$score = [
'h1_heading' => 5,
'h2_headings' => 2,
'img_alt' => 4,
'keywords_meta' => 5,
'links_ratio' => 3,
'title_length' => 4,
'permalink_structure' => 7,
'focus_keywords' => 3,
'post_titles' => 4,
// Advanced SEO.
'canonical' => 5,
'noindex' => 7,
'non_www' => 4,
'opengraph' => 2,
'robots_txt' => 3,
'schema' => 3,
'sitemaps' => 3,
'search_console' => 1,
// Performance.
'image_header' => 3,
'minify_css' => 2,
'minify_js' => 1,
'page_objects' => 2,
'page_size' => 3,
'response_time' => 3,
// Security.
'directory_listing' => 1,
'safe_browsing' => 8,
'ssl' => 7,
];
return isset( $score[ $this->id ] ) ? $score[ $this->id ] : 0;
}
/**
* Get test result data.
*
* @return array
*/
public function get_result() {
return $this->result;
}
}

View File

@@ -0,0 +1,98 @@
<?php
/**
* The SEO Analyzer module.
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\SEO_Analysis
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\SEO_Analysis;
use RankMath\Helper;
use RankMath\Traits\Hooker;
use RankMath\Helpers\Param;
defined( 'ABSPATH' ) || exit;
/**
* SEO_Analysis class.
*/
class SEO_Analysis {
use Hooker;
/**
* Admin object.
*
* @var Admin
*/
public $admin;
/**
* The Constructor.
*/
public function __construct() {
if ( Helper::is_heartbeat() ) {
return;
}
if ( is_admin() ) {
$this->admin = new Admin();
}
if ( Helper::has_cap( 'rank_math_site_analysis' ) ) {
$this->action( 'rank_math/admin_bar/items', 'admin_bar_items', 11 );
}
$this->filter( 'rank_math/seo_analysis/admin_tab_view', 'add_tab_previews', 10, 2 );
}
/**
* Add admin bar item.
*
* @param Admin_Bar_Menu $menu Menu class instance.
*/
public function admin_bar_items( $menu ) {
$menu->add_sub_menu(
'seo-analysis',
[
'title' => esc_html__( 'SEO Analyzer', 'rank-math' ),
'href' => Helper::get_admin_url( 'seo-analysis' ),
'meta' => [ 'title' => esc_html__( 'Site-wide analysis', 'rank-math' ) ],
'priority' => 50,
]
);
if ( ! is_admin() && ! is_404() ) {
$link = is_front_page() ? '' : ( is_ssl() ? 'https' : 'http' ) . '://' . Param::server( 'HTTP_HOST' ) . Param::server( 'REQUEST_URI' );
$menu->add_sub_menu(
'analyze',
[
'title' => $link ? esc_html__( 'Analyze this Page', 'rank-math' ) : esc_html__( 'SEO Analyzer', 'rank-math' ),
'href' => Helper::get_admin_url( 'seo-analysis' ) . ( $link ? '&u=' . rawurlencode( $link ) : '' ),
'meta' => [ 'title' => esc_html__( 'SEO Analysis for this page', 'rank-math' ) ],
],
'seo-analysis'
);
}
}
/**
* Add PRO tab previews.
*
* @param string $file Include file.
* @param string $current_tab Current tab.
*
* @return string
*/
public function add_tab_previews( $file, $current_tab ) {
if ( 'competitor_analyzer' === $current_tab ) {
$file = dirname( __FILE__ ) . '/views/competitor-analysis.php';
}
return $file;
}
}

View File

@@ -0,0 +1,653 @@
<?php
/**
* The SEO Analyzer class runs the tests and handles the results.
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\SEO_Analysis
* @author Rank Math <support@rankmath.com>
*/
namespace RankMath\SEO_Analysis;
use RankMath\Traits\Ajax;
use RankMath\Traits\Hooker;
use RankMath\Helpers\Security;
use RankMath\Helpers\Param;
use RankMath\Helper;
defined( 'ABSPATH' ) || exit;
/**
* SEO_Analyzer class.
*/
class SEO_Analyzer {
use Ajax, Hooker;
/**
* Rank Math SEO Checkup API.
*
* @var string
*/
private $api_url = '';
/**
* URL to analyze.
*
* @var string
*/
public $analyse_url = '';
/**
* Sub-page URL to analyze.
*
* @var string
*/
public $analyse_subpage = false;
/**
* Hold analysis results.
*
* @var null|array
*/
public $results = null;
/**
* Hold analysis result date.
*
* @var mixed
*/
public $results_date = null;
/**
* Hold any api error.
*
* @var array
*/
private $api_error = '';
/**
* Hold local test data.
*
* @var array
*/
private $local_tests = [];
/**
* The constructor.
*/
public function __construct() {
$this->analyse_url = home_url();
$this->action( 'init', 'set_url' );
$this->maybe_clear_storage();
$this->ajax( 'analyze', 'analyze_me' );
$this->ajax( 'enable_auto_update', 'enable_auto_update' );
}
/**
* Set URL and other properties on init.
*
* @return void
*/
public function set_url() {
update_option( 'rank_math_viewed_seo_analyer', true, false ); // Code to update the viewed value to remove the New label.
$this->api_url = $this->do_filter( 'seo_analysis/api_endpoint', 'https://rankmath.com/analyze/v2/json/' );
if ( ! empty( $_REQUEST['u'] ) && $this->is_allowed_url( Param::request( 'u' ) ) ) { // phpcs:ignore
$this->analyse_url = esc_url_raw( Param::request( 'u' ) );
$this->analyse_subpage = true;
}
/**
* Action: 'rank_math/seo_analysis/after_set_url' - Fires after setting the URL.
*/
$this->do_action( 'seo_analysis/after_set_url', $this );
if ( $this->analyse_subpage ) {
return;
}
$this->get_results_from_storage();
$this->local_tests = $this->do_filter( 'seo_analysis/tests', [] );
}
/**
* Output results.
*/
public function display() {
if ( empty( $this->results ) ) {
return;
}
?>
<?php $this->display_graphs(); ?>
<?php $this->display_result_filters(); ?>
<div class="rank-math-result-tables rank-math-box">
<?php $this->display_results(); ?>
</div>
<?php
}
/**
* Show buttons to filter results: All, Passed Tests, Warnings, Failed Tests.
* Also show the number of tests in each category.
*/
private function display_result_filters() {
$data = $this->get_graph_metrices();
extract( $data ); // phpcs:ignore
?>
<div id="analysis-result" class="rank-math-result-filters">
<a href="#all" class="rank-math-result-filter rank-math-result-filter-all active" data-filter="all" data-count="<?php echo esc_attr( $total ); ?>">
<?php esc_html_e( 'All', 'rank-math' ); ?>
<span class="rank-math-result-filter-count"><?php echo esc_html( $total ); ?></span>
</a>
<a href="#passed" class="rank-math-result-filter rank-math-result-filter-passed" data-filter="ok" data-count="<?php echo esc_attr( $statuses['ok'] ); ?>">
<?php esc_html_e( 'Passed Tests', 'rank-math' ); ?>
<span class="rank-math-result-filter-count"><?php echo esc_html( $statuses['ok'] ); ?></span>
</a>
<a href="#warning" class="rank-math-result-filter rank-math-result-filter-warnings" data-filter="warning" data-count="<?php echo esc_attr( $statuses['warning'] ); ?>">
<?php esc_html_e( 'Warnings', 'rank-math' ); ?>
<span class="rank-math-result-filter-count"><?php echo esc_html( $statuses['warning'] ); ?></span>
</a>
<a href="#failed" class="rank-math-result-filter rank-math-result-filter-failed" data-filter="fail" data-count="<?php echo esc_attr( $statuses['fail'] ); ?>">
<?php esc_html_e( 'Failed Tests', 'rank-math' ); ?>
<span class="rank-math-result-filter-count"><?php echo esc_html( $statuses['fail'] ); ?></span>
</a>
</div>
<?php
}
/**
* Output graphs.
*/
private function display_graphs() {
$data = $this->get_graph_metrices();
extract( $data ); // phpcs:ignore
include dirname( __FILE__ ) . '/views/graphs.php';
}
/**
* Output the SERP Preview.
*/
public function display_serp_preview() {
include dirname( __FILE__ ) . '/views/serp-preview.php';
}
/**
* Get graph metrices.
*
* @return array
*/
private function get_graph_metrices() {
$total = 0;
$percent = 0;
$total_score = 0;
$statuses = [
'ok' => 0,
'fail' => 0,
'info' => 0,
'warning' => 0,
];
foreach ( $this->results as $id => $result ) {
if ( false === $this->can_count_result( $result ) ) {
continue;
}
if ( $result->is_hidden() ) {
continue;
}
$statuses[ $result->get_status() ]++;
$total++;
$total_score = $total_score + $result->get_score();
if ( 'ok' !== $result->get_status() ) {
continue;
}
$percent = $percent + $result->get_score();
}
$percent = round( ( $percent / $total_score ) * 100 );
$grade = $this->get_graph_grade( $percent );
return compact( 'total', 'percent', 'statuses', 'grade' );
}
/**
* Can count result.
*
* @param Result $result Result instance.
*
* @return bool
*/
private function can_count_result( $result ) {
return ( ! is_object( $result ) || 'info' === $result->get_status() || $result->is_excluded() ) ? false : true;
}
/**
* Format grade result.
*
* @param int $percent Total percentage.
*
* @return string
*/
private function get_graph_grade( $percent ) {
if ( $percent < 70 ) {
return 'average';
}
if ( $percent < 50 ) {
return 'bad';
}
return 'good';
}
/**
* Output results in tables.
*/
private function display_results() {
foreach ( $this->sort_results_by_category() as $category => $results ) :
$label = $this->get_category_label( $category );
?>
<div class="rank-math-result-table rank-math-result-category-<?php echo esc_attr( $category ); ?> <?php echo esc_attr( $this->get_status_class( $results ) ); ?>">
<div class="category-title">
<?php echo $label; // phpcs:ignore ?>
</div>
<?php foreach ( $results as $result ) : ?>
<div class="table-row rank-math-result-status-<?php echo esc_attr( $result->get_status() ); ?>" data-status="<?php echo esc_attr( $result->get_status() ); ?>">
<?php echo $result; // phpcs:ignore ?>
</div>
<?php endforeach; ?>
</div>
<?php
endforeach;
}
/**
* Get class for the result category wrapper element.
* This is needed for the filter buttons.
*
* @param array $results Results array.
*
* @return string
*/
private function get_status_class( $results ) {
$status_classes = [];
foreach ( $results as $result ) {
if ( false === $this->can_count_result( $result ) ) {
continue;
}
if ( $result->is_hidden() ) {
continue;
}
$status_classes[] = $result->get_status();
}
$status_class = implode(
' ',
array_map(
function( $status ) {
return 'rank-math-result-statuses-' . $status;
},
array_unique( $status_classes )
)
);
return $status_class;
}
/**
* Get result from storage.
*
* @param string $option Option name.
*/
public function get_results_from_storage( $option = 'rank_math_seo_analysis' ) {
if ( ! is_null( $this->results ) ) {
return;
}
$this->results = get_option( $option . '_results' );
$this->results_date = get_option( $option . '_date' );
$url = get_option( $option . '_url' );
if ( false !== $url ) {
$this->analyse_url = $url;
}
$this->build_results();
}
/**
* Return formatted date.
*/
public function get_last_checked_date() {
if ( ! $this->results_date ) {
return;
}
$date = date_i18n( get_option( 'date_format' ), $this->results_date );
$time = date_i18n( get_option( 'time_format' ), $this->results_date );
// translators: 1: Date, 2: Time.
return '<span>' . esc_attr__( 'Last checked:', 'rank-math' ) . '</span> ' . sprintf( esc_html__( '%1$s at %2$s', 'rank-math' ), $date, $time );
}
/**
* Clear stored results if needed.
*/
private function maybe_clear_storage() {
if ( '1' === Param::request( 'clear_results' ) ) {
delete_option( 'rank_math_seo_analysis_results' );
delete_option( 'rank_math_seo_analysis_date' );
Helper::redirect( Security::remove_query_arg_raw( 'clear_results' ) );
exit;
}
}
/**
* Convert result into object.
*/
private function build_results() {
if ( ! is_array( $this->results ) ) {
return;
}
$this->move_priority_results_to_top();
foreach ( $this->results as $id => $result ) {
$this->results[ $id ] = new Result( $id, $result, $this->analyse_subpage );
}
}
/**
* Move all tests in "priority" category to top.
*/
private function move_priority_results_to_top() {
$priority = [];
foreach ( $this->results as $id => $result ) {
if ( is_array( $result ) && 'priority' === $result['category'] ) {
$priority[ $id ] = $result;
unset( $this->results[ $id ] );
}
}
$this->results = array_replace( $priority, $this->results );
}
/**
* Analyze page.
*/
public function analyze_me() {
$success = true;
$directory = dirname( __FILE__ );
check_ajax_referer( 'rank-math-ajax-nonce', 'security' );
$this->has_cap_ajax( 'site_analysis' );
if ( ! $this->analyse_subpage ) {
delete_option( 'rank_math_seo_analysis_results' );
delete_option( 'rank_math_seo_analysis_date' );
}
if ( ! $this->run_api_tests() ) {
/* translators: API error */
echo '<div class="notice notice-error is-dismissible notice-seo-analysis-error rank-math-notice"><p>' . sprintf( __( '<strong>API Error:</strong> %s', 'rank-math' ), $this->api_error ) . '</p></div>'; // phpcs:ignore
$success = false;
die;
}
if ( ! $this->analyse_subpage ) {
$this->run_local_tests();
update_option( 'rank_math_seo_analysis_results', $this->results, false );
update_option( 'rank_math_seo_analysis_date', time(), false );
}
/**
* Action: 'rank_math/seo_analysis/after_analyze' - Fires after the SEO analysis is done.
*/
$this->do_action( 'seo_analysis/after_analyze', $this );
$this->build_results();
$this->display();
die;
}
/**
* Get page score.
*
* @param string $url Url to get score for.
*
* @return int
*/
public function get_page_score( $url ) {
$this->analyse_url = $url;
$this->analyse_subpage = true;
if ( ! $this->run_api_tests() ) {
error_log( __( 'Rank Math SEO Analyzer error: ', 'rank-math' ) . $this->api_error ); // phpcs:ignore
return 0;
}
$this->build_results();
if ( empty( $this->results ) ) {
return 0;
}
$total = 0;
foreach ( $this->results as $id => $result ) {
if (
$result->is_hidden() ||
'ok' !== $result->get_status() ||
false === $this->can_count_result( $result )
) {
continue;
}
$total = $total + $result->get_score();
}
return $total;
}
/**
* Ajax handler for the Enable auto update button.
*/
public function enable_auto_update() {
check_ajax_referer( 'rank-math-ajax-nonce', 'security' );
$this->has_cap_ajax( 'general' );
$this->enable_auto_update_in_stored_data();
Helper::toggle_auto_update_setting( 'on' );
echo '1';
die;
}
/**
* Update the auto update value in the stored results.
*/
public function enable_auto_update_in_stored_data() {
$results = get_option( 'rank_math_seo_analysis_results' );
if ( ! isset( $results['auto_update'] ) ) {
return;
}
$results['auto_update']['status'] = 'ok';
$results['auto_update']['message'] = __( 'Rank Math auto-update option is enabled on your site.', 'rank-math' );
update_option( 'rank_math_seo_analysis_results', $results, false );
}
/**
* Run test through the Rank Math Analysis API.
*
* @return boolean
*/
private function run_api_tests() {
$response = $this->get_api_results();
if ( false === $response ) {
return false;
}
$this->process_api_results( $response );
return true;
}
/**
* Process results as needed.
*
* @param array $response Response.
*
* @return boolean
*/
private function process_api_results( $response ) {
foreach ( $response as $id => $results ) {
$this->results[ $id ] = wp_parse_args(
$results,
[
'test_id' => $id,
'api_test' => true,
]
);
}
return true;
}
/**
* Get API results.
*
* @return bool|array
*/
private function get_api_results() {
$api_url = Security::add_query_arg_raw(
[
'u' => $this->analyse_url,
'locale' => get_locale(),
'is_subpage' => $this->analyse_subpage,
],
$this->api_url
);
$request = wp_remote_get( $api_url, [ 'timeout' => 30 ] );
if ( is_wp_error( $request ) ) {
$this->api_error = wp_strip_all_tags( $request->get_error_message() );
return false;
}
$status = absint( wp_remote_retrieve_response_code( $request ) );
if ( 200 !== $status ) {
// Translators: placeholder is a HTTP error code.
$this->api_error = sprintf( __( 'HTTP %d error.', 'rank-math' ), $status );
return false;
}
$response = wp_remote_retrieve_body( $request );
$response = json_decode( $response, true );
if ( ! is_array( $response ) ) {
$this->api_error = __( 'Unexpected API response.', 'rank-math' );
return false;
}
return $response;
}
/**
* Run local site tests.
*/
private function run_local_tests() {
foreach ( $this->local_tests as $id => $test ) {
$this->results[ $id ] = array_merge(
[
'test_id' => $id,
'api_test' => false,
'title' => $test['title'],
'description' => $test['description'],
'fix' => isset( $test['how_to_fix'] ) ? $test['how_to_fix'] : '',
'category' => $test['category'],
'info' => [],
'kb_link' => isset( $test['kb_link'] ) ? $test['kb_link'] : 'https://rankmath.com/kb/seo-analysis',
'tooltip' => ! empty( $test['tooltip'] ) ? $test['tooltip'] : '',
],
call_user_func( $test['callback'], $this )
);
}
}
/**
* Check if it is a valid URL on this site.
*
* @param string $url Check url if it is allowed.
* @return bool
*/
private function is_allowed_url( $url ) {
$allowed = true;
$home = get_home_url();
if ( strpos( $url, $home ) !== 0 ) {
$allowed = false;
}
// wp-admin pages are not allowed.
if ( strpos( substr( $url, strlen( $home ) ), '/wp-admin' ) === 0 ) {
$allowed = false;
}
return $this->do_filter( 'analysis/is_allowed_url', $allowed, $url );
}
/**
* Sort results by category.
*
* @return array
*/
private function sort_results_by_category() {
$data = [];
foreach ( $this->results as $result ) {
if ( $result->is_hidden() ) {
continue;
}
$category = $result->get_category();
if ( ! isset( $data[ $category ] ) ) {
$data[ $category ] = [];
}
$data[ $category ][ $result->get_id() ] = $result;
}
return $data;
}
/**
* Get category label by slug.
*
* @param string $category Current category slug.
* @return string
*/
private function get_category_label( $category ) {
$category_map = [
'priority' => esc_html__( 'Priority', 'rank-math' ),
'advanced' => esc_html__( 'Advanced SEO', 'rank-math' ),
'basic' => esc_html__( 'Basic SEO', 'rank-math' ),
'performance' => esc_html__( 'Performance', 'rank-math' ),
'security' => esc_html__( 'Security', 'rank-math' ),
];
return isset( $category_map[ $category ] ) ? $category_map[ $category ] : '';
}
/**
* Get admin tabs.
*/
public function admin_tabs() {
$tabs = new Admin_Tabs();
$tabs->display();
}
}

View File

@@ -0,0 +1 @@
<?php // Silence is golden.

View File

@@ -0,0 +1,524 @@
<?php
/**
* The local tests for the SEO Analyzer.
*
* @since 0.9.0
* @package RankMath
* @subpackage RankMath\SEO_Analysis
* @author Rank Math <support@rankmath.com>
*/
use RankMath\KB;
use RankMath\Helper;
use RankMath\Google\Console;
use RankMath\Google\Authentication;
use RankMath\Helpers\DB;
defined( 'ABSPATH' ) || exit;
add_filter( 'rank_math/seo_analysis/tests', 'rank_math_register_seo_analysis_basic_tests' );
/**
* Register basic local tests for the SEO Analyzer.
*
* @param array $tests Array of tests.
*
* @return array
*/
function rank_math_register_seo_analysis_basic_tests( $tests ) {
$new_tests['site_description'] = [
'title' => esc_html__( 'Site Tagline', 'rank-math' ),
/* translators: link to general setting screen */
'description' => sprintf( esc_html__( 'Your theme may display the Site Tagline, and it can also be used in SEO titles &amp; descriptions. Set it to something unique. You can change it by navigating to <a href="%s">Settings &gt; General</a>.', 'rank-math' ), admin_url( 'options-general.php' ) ),
'how_to_fix' => '<p>' . esc_html__( 'Most WordPress themes place your site\'s tagline in a prominent position (inside header tags near the top of the page). Using the right tagline can give your site an SEO boost.', 'rank-math' ) . '</p>' .
'<p>' . esc_html__( 'Unfortunately, the standard WordPress tagline is "Just Another WordPress site." That\'s pretty sloppy looking, and it does nothing for your SEO. In fact, it\'s actually a security risk - it makes it easy for hackers with a WordPress exploit to locate your site with an automated search.', 'rank-math' ) . '</p>' .
/* translators: link to general setting screen */
'<p>' . sprintf( wp_kses_post( __( 'Changing your tagline is very easy. Just head on over to <a target="_blank" href="%1$s">Settings - General</a> in WordPress\'s admin menu (on the left), or click on the link in this sentence.', 'rank-math' ) ), esc_url( admin_url( 'options-general.php' ) ) ) . '</p>' .
'<p>' . esc_html__( 'The tagline is the second option. Choose a tagline that summarizes your site in a few words. The tagline is also a good place to use your main keyword.', 'rank-math' ) . '</p>',
'kb_link' => KB::get( 'analysis-site-tagline' ),
'tooltip' => esc_html__( 'Confirm custom tagline is set for your site', 'rank-math' ),
];
$new_tests['blog_public'] = [
'title' => esc_html__( 'Blog Public', 'rank-math' ),
/* translators: link to general setting screen */
'description' => esc_html__( 'Your site may not be visible to search engine.', 'rank-math' ),
'how_to_fix' => '<p>' .
sprintf(
/* translators: %1$s link to the reading settings, %2$s closing tag for the link */
esc_html__( 'You must %1$sgo to your Reading Settings%2$s and uncheck the box for Search Engine Visibility.', 'rank-math' ),
'<a href="' . esc_url( admin_url( 'options-reading.php' ) ) . '">',
'</a>'
) .
'</p>',
'kb_link' => KB::get( 'analysis-blog-public' ),
'tooltip' => esc_html__( "Check your site's visibility to search engines", 'rank-math' ),
];
$new_tests['permalink_structure'] = [
'title' => esc_html__( 'Permalink Structure', 'rank-math' ),
/* translators: link to permalink setting screen */
'description' => sprintf( __( 'For the best SEO results, use a custom permalink structure, preferably one that includes the post title (<code>%%postname%%</code>). You can change it by navigating to <a href="%s">Settings &gt; Permalinks</a>', 'rank-math' ), admin_url( 'options-permalink.php' ) ),
'how_to_fix' => '<p>' . esc_html__( 'The standard permalink structure is pretty ugly - WordPress generates offputting URLs like: http://www.yoursite.com/?p=99', 'rank-math' ) . '</p>' .
'<p>' . esc_html__( 'It\'s not very kind on the eyes, and it does nothing for your site\'s SEO. In fact, it can hurt it - Google\'s bot is quite cautious about crawling pages that look auto-generated.', 'rank-math' ) . '</p>' .
/* translators: link to permalink setting screen */
'<p>' . sprintf( wp_kses_post( __( 'Fortunately, it\'s very easy to fix. Just hop on over to <a target="_blank" href="%1$s">Settings - Permalinks</a>. Then chose the "Post Name" option.', 'rank-math' ) ), esc_url( admin_url( 'options-permalink.php' ) ) ) . '</p>' .
'<p>' . esc_html__( 'This option will replace the "?p=99" part of the URL with the post\'s title, like this: http://www.yoursite.com/my-amazing-post-title/', 'rank-math' ) . '</p>' .
'<p>' . esc_html__( 'This looks nice for readers - and it gets your keywords into the URL (keywords in the URL is a ranking factor).', 'rank-math' ) . '</p>',
'kb_link' => KB::get( 'analysis-permalink-structure' ),
'tooltip' => esc_html__( 'Check your site for SEO-friendly permalink structure', 'rank-math' ),
];
$new_tests['focus_keywords'] = [
'title' => esc_html__( 'Focus Keywords', 'rank-math' ),
'description' => esc_html__( 'Setting focus keywords for your posts allows Rank Math to analyse the content.', 'rank-math' ),
'how_to_fix' => '<p>' . esc_html__( 'Rank Math allows you to set a focus keyword for every post and page you write - the option is in the "Meta Box", which appears under the text editor in the screen where you write and edit content.', 'rank-math' ) . '</p>' .
'<p>' . esc_html__( 'Rank Math uses these focus keywords to analyze your on-page content. It can tell if you\'ve done a good job of optimizing your text to rank for these keywords.', 'rank-math' ) . '</p>' .
'<p>' . esc_html__( 'Of course, if you don\'t give Rank Math a focus keyword to work with, it can\'t give you any useful feedback.', 'rank-math' ) . '</p>' .
'<p>' . esc_html__( 'Fixing this issue is easy - just edit the post, and set a Focus Keyword. Then follow Rank Math\'s analysis to improve your rankings.', 'rank-math' ) . '</p>',
'kb_link' => KB::get( 'analysis-focus-keywords' ),
'tooltip' => esc_html__( 'Confirm focus keywords are set for all your posts', 'rank-math' ),
];
$new_tests['post_titles'] = [
'title' => esc_html__( 'Post Titles Missing Focus Keywords', 'rank-math' ),
'description' => esc_html__( 'Make sure the focus keywords you set for the posts appear in their titles.', 'rank-math' ),
'how_to_fix' => '<p>' . esc_html__( 'HTML Page Titles play a large role in Google\'s ranking algorithm. When you add a Focus Keyword to a post or page, Rank Math will check to see that you used the keyword in the title. If it finds any posts or pages that are missing the keyword in the title, it will tell you here.', 'rank-math' ) . '</p>' .
'<p>' . esc_html__( 'Fixing the issue is simple - just edit the post/page and add the focus keyword(s) to the title.', 'rank-math' ) . '</p>',
'kb_link' => KB::get( 'analysis-post-titles' ),
'tooltip' => esc_html__( 'Verify the presence of focus keywords in your post titles', 'rank-math' ),
];
foreach ( $new_tests as $key => $test ) {
$test['category'] = 'basic';
$test['callback'] = 'rank_math_analyze_' . $key;
$tests[ $key ] = $test;
}
return $tests;
}
add_filter( 'rank_math/seo_analysis/tests', 'rank_math_register_seo_analysis_advanced_tests' );
/**
* Register advanced local tests for the SEO Analyzer.
*
* @param array $tests Array of tests.
*
* @return array
*/
function rank_math_register_seo_analysis_advanced_tests( $tests ) {
$tests['search_console'] = [
'category' => 'advanced',
'title' => esc_html__( 'Search Console', 'rank-math' ),
'description' => sprintf(
/* translators: link to plugin setting screen */
esc_html__( 'Register at Google Search Console and verificate your site by adding the code to <a href="%1$s">Settings &gt; Verificate Tools</a>, then navigate to <a href="%2$s">Settings &gt; Search Console</a> to authenticate and link your site.', 'rank-math' ),
Helper::get_admin_url( 'options-general#setting-panel-webmaster' ),
Helper::get_admin_url( 'options-general#setting-panel-analytics' )
),
'how_to_fix' => '<p>' . esc_html__( 'Google\'s Search Console is a vital source of information concerning your rankings and click-through rates. Rank Math can import this data, so you don\'t have to log into your Google account to get the data you need.', 'rank-math' ) . '</p>' .
/* translators: link to plugin search console setting screen */
'<p>' . sprintf( wp_kses_post( __( 'You can integrate the Google Search Console with Rank math in the <a href="%1$s" target="_blank">Search Console tab</a>. of Rank Math\'s General Settings menu.', 'rank-math' ) ), esc_url( Helper::get_admin_url( 'options-general#setting-panel-analytics' ) ) ) . '</p>' .
/* translators: Link to Search Console KB article */
'<p>' . sprintf( wp_kses_post( __( 'Read <a href="%1$s" target="_blank">this article</a> for detailed instructions on setting up your Google Webmaster account and getting Rank Math to work with the Google Search Console.', 'rank-math' ) ), KB::get( 'help-analytics', 'SEO Analysis GSC Test' ) ) . '</p>',
'callback' => 'rank_math_analyze_search_console',
'kb_link' => KB::get( 'analysis-search-console' ),
'tooltip' => esc_html__( 'Confirm if Rank Math is connected to Search Console', 'rank-math' ),
];
$tests['sitemaps'] = [
'category' => 'advanced',
'title' => esc_html__( 'Sitemaps', 'rank-math' ),
'description' => esc_html__( 'XML sitemaps are a special type of text file that tells search engines about the structure of your site. They\'re a list of all the resources (pages and files) you would like the search engine to index. You can assign different priorities, so certain pages will be crawled first. Before XML sitemaps, search engines were limited to indexing the content they could find by following links. That\'s still an important feature for search engine spiders, but XML sitemaps have made it easier for content creators and search engines to collaborate.', 'rank-math' ),
'how_to_fix' => esc_html__( 'If you don\'t have an XML sitemap, the best option is to install a plugin that creates sitemaps for you. That way you\'ll know the sitemap will always be up-to-date. Plugins can also automatically ping the search engines when the XML file is updated. The Rank Math WordPress plugin gives you complete control over your site\'s XML sitemaps. You can control the settings for each page as you write or edit it, and Rank Math will ping Google as soon as you submit your edits. This results in fast crawls and indexing.', 'rank-math' ),
'callback' => 'rank_math_analyze_sitemap',
'kb_link' => KB::get( 'analysis-sitemaps' ),
'tooltip' => esc_html__( 'Check the presence of sitemaps on your website', 'rank-math' ),
];
return $tests;
}
add_filter( 'rank_math/seo_analysis/tests', 'rank_math_register_seo_analysis_auto_update_test', 20 );
/**
* Register test for the auto update option.
*
* @param array $tests Array of tests.
*
* @return array
*/
function rank_math_register_seo_analysis_auto_update_test( $tests ) {
$new_tests = [
'auto_update' => [
'category' => 'priority',
'title' => esc_html__( 'Automatic Updates', 'rank-math' ),
'description' => esc_html__( 'Enable automatic updates to ensure you are always using the latest version of Rank Math.', 'rank-math' ),
'callback' => 'rank_math_analyze_auto_update',
'kb_link' => KB::get( 'analysis-auto-update' ),
'tooltip' => esc_html__( 'Verify auto-updates are enabled for Rank Math', 'rank-math' ),
],
];
// Move to top.
$tests = array_merge( $new_tests, $tests );
return $tests;
}
/**
* Checks if auto update is enabled.
*
* @return array
*/
function rank_math_analyze_auto_update() {
if ( Helper::is_plugin_update_disabled() ) {
return [
'status' => 'warning',
'message' => __( 'Site wide plugins auto-update option is disabled on your site.', 'rank-math' ),
];
}
if ( Helper::get_auto_update_setting() ) {
return [
'status' => 'ok',
'message' => __( 'Rank Math auto-update option is enabled on your site.', 'rank-math' ),
];
}
return [
'status' => 'warning',
'message' => '<div class="auto-update-disabled">' . sprintf(
// Translators: placeholder is an activate button.
__( 'Automatic updates are not enabled on your site. %s', 'rank-math' ),
'<a href="#" class="button button-primary button-small enable-auto-update">' . __( 'Enable Auto Updates', 'rank-math' ) . '</a>'
) . '</div>' .
'<div class="auto-update-enabled hidden">' .
esc_html__( 'Rank Math auto-update option is enabled on your site.', 'rank-math' ) .
'</div>',
];
}
/**
* Checks if site description is empty or set to default.
*
* @return array
*/
function rank_math_analyze_site_description() {
$sitedesc = get_bloginfo( 'description' );
if ( '' === $sitedesc ) {
return [
'status' => 'warning',
'message' => sprintf(
/* translators: %1$s link to the customize settings, %2$s closing tag for the link */
esc_html__( 'You have not entered a tagline yet. It is a good idea to choose one. %1$sYou can fix this in the customizer%2$s.', 'rank-math' ),
'<a href="' . esc_url( admin_url( 'customize.php' ) ) . '" target="_blank">',
'</a>'
),
];
}
if ( rank_math_is_default_tagline() ) { // phpcs:ignore
return [
'status' => 'fail',
'message' => wp_kses_post( __( 'Your Site Tagline is set to the default value <em>Just another WordPress site</em>.', 'rank-math' ) ),
];
}
return [
'status' => 'ok',
'message' => esc_html__( 'Your Site Tagline is set to a custom value.', 'rank-math' ),
];
}
/**
* Check if the site uses the default WP tagline.
*
* @return bool
*/
function rank_math_is_default_tagline() {
$description = get_bloginfo( 'description' );
$translated_description = translate( 'Just another WordPress site' ); // phpcs:ignore
if ( $description === $translated_description ) {
return true;
}
// Also check untranslated version.
return 'Just another WordPress site' === $description;
}
/**
* Checks if pretty permalinks are enabled and if they contain %postname%.
*
* @return array
*/
function rank_math_analyze_permalink_structure() {
$permalink_structure = get_option( 'permalink_structure' );
if ( '' === $permalink_structure ) {
return [
'status' => 'fail',
'message' => wp_kses_post( __( 'Permalinks are set to the default value. <em>Pretty permalinks</em> are disabled. ', 'rank-math' ) ),
];
}
if ( ! rank_math_is_postname_in_permalink() ) {
return [
'status' => 'fail',
'message' => wp_kses_post( __( 'Permalinks are set to a custom structure but the post titles do not appear in the permalinks.', 'rank-math' ) ),
];
}
return [
'status' => 'ok',
/* translators: permalink structure */
'message' => sprintf( wp_kses_post( __( 'Post permalink structure is set to %s.', 'rank-math' ) ), '<code>' . $permalink_structure . '</code>' ),
];
}
/**
* Check if the post permalink includes %postname%.
*
* @return bool
*/
function rank_math_is_postname_in_permalink() {
return ( false !== strpos( get_option( 'permalink_structure' ), '%postname%' ) );
}
/**
* Checks if Search Console is linked.
*
* @return array
*/
function rank_math_analyze_search_console() {
$status = Authentication::is_authorized() && Console::get_site_url();
return [
'status' => $status ? 'ok' : 'fail',
'message' => $status ? esc_html__( 'Google Search Console has been linked.', 'rank-math' ) : esc_html__( 'You have not linked Google Search Console yet.', 'rank-math' ),
];
}
/**
* Checks for posts without a focus keyword.
*
* @return array
*/
function rank_math_analyze_focus_keywords() {
global $wpdb;
$postmeta_table_limit = apply_filters( 'rank_math/seo_analysis/postmeta_table_limit', 200000 );
if ( DB::table_size_exceeds( $wpdb->postmeta, $postmeta_table_limit ) ) {
return [
'status' => 'warning',
'message' => esc_html__( 'Could not check Focus Keywords in posts - the post meta table exceeds the size limit.', 'rank-math' ),
];
}
$in_search_post_types = Helper::get_allowed_post_types();
$in_search_post_types = empty( $in_search_post_types ) ? '' : " AND {$wpdb->posts}.post_type IN ('" . join( "', '", array_map( 'esc_sql', $in_search_post_types ) ) . "')";
$meta_query = new WP_Meta_Query(
[
'relation' => 'AND',
[
'key' => 'rank_math_focus_keyword',
'compare' => 'NOT EXISTS',
],
[
'relation' => 'OR',
[
'key' => 'rank_math_robots',
'value' => 'noindex',
'compare' => 'NOT LIKE',
],
[
'key' => 'rank_math_robots',
'compare' => 'NOT EXISTS',
],
],
]
);
$mq_sql = $meta_query->get_sql( 'post', $wpdb->posts, 'ID' );
$query = "SELECT {$wpdb->posts}.ID, {$wpdb->posts}.post_type FROM $wpdb->posts {$mq_sql['join']} WHERE 1 = 1 {$mq_sql['where']}{$in_search_post_types} AND ({$wpdb->posts}.post_status = 'publish') GROUP BY {$wpdb->posts}.ID";
$data = $wpdb->get_results( $query, ARRAY_A ); // phpcs:ignore
// Early Bail!
if ( empty( $data ) ) {
return [
'status' => 'ok',
'message' => esc_html__( 'All published posts have focus keywords set.', 'rank-math' ),
];
}
$rows = rank_math_analyze_group_result( $data );
$links = rank_math_get_post_type_links( $rows, '&focus_keyword=1' );
return [
'status' => 'fail',
/* translators: post type links */
'message' => sprintf( esc_html__( 'There are %s with no focus keyword set.', 'rank-math' ), join( ', ', $links ) ),
];
}
/**
* Checks for posts where the focus keyword doesn't appear in the title.
*
* @return array
*/
function rank_math_analyze_post_titles() {
$info = [];
$data = rank_math_get_posts_with_titles();
// Early Bail!
if ( empty( $data ) ) {
return [
'status' => 'ok',
'message' => esc_html__( 'Focus keywords appear in the titles of published posts where it is set.', 'rank-math' ),
];
}
$rows = rank_math_analyze_group_result( $data );
$links = rank_math_get_post_type_links( $rows, '&fk_in_title=1' );
$post_ids = wp_list_pluck( $data, 'ID' );
$post_ids_max = array_slice( $post_ids, 0, 20 );
foreach ( $post_ids_max as $postid ) {
$info[] = '<a href="' . get_permalink( $postid ) . '" target="_blank">' . get_the_title( $postid ) . '</a>';
}
$count = count( $post_ids ) - 20;
if ( $count > 0 ) {
/* translators: post ID count */
$info[] = sprintf( esc_html__( '+%d More...', 'rank-math' ), $count );
}
return [
'status' => 'fail',
/* translators: post type links */
'message' => sprintf( esc_html__( 'There are %s published posts where the primary focus keyword does not appear in the post title.', 'rank-math' ), join( ', ', $links ) ),
'info' => $info,
];
}
/**
* Get `post_type` links.
*
* @param array $rows Rows.
* @param string $extra_params Extra parameters.
*
* @return array
*/
function rank_math_get_post_type_links( $rows, $extra_params ) {
$links = [];
foreach ( $rows as $post_type => $row ) {
$post_type = get_post_type_object( $post_type );
$count = count( $row );
$links[] = sprintf(
'<a href="%1$s%2$s" target="_blank">%3$d %4$s</a>',
esc_url( admin_url( 'edit.php?post_type=' . $post_type->name ) ),
$extra_params,
$count,
$count > 1 ? $post_type->label : $post_type->labels->singular_name
);
}
return $links;
}
/**
* Get posts not set to noindex where the Focus Keyword has been added.
*
* @return mixed
*/
function rank_math_get_posts_with_titles() {
global $wpdb;
$in_post_types = Helper::get_allowed_post_types();
$in_post_types = empty( $in_post_types ) ? '' : " AND {$wpdb->posts}.post_type IN ('" . join( "', '", array_map( 'esc_sql', $in_post_types ) ) . "')";
$meta_query = new WP_Meta_Query(
[
'relation' => 'AND',
[
'key' => 'rank_math_focus_keyword',
'compare' => 'EXISTS',
],
[
'relation' => 'OR',
[
'key' => 'rank_math_robots',
'value' => 'noindex',
'compare' => 'NOT LIKE',
],
[
'key' => 'rank_math_robots',
'compare' => 'NOT EXISTS',
],
],
]
);
$mq_sql = $meta_query->get_sql( 'post', $wpdb->posts, 'ID' );
$query = "SELECT {$wpdb->posts}.ID, {$wpdb->posts}.post_type FROM $wpdb->posts {$mq_sql['join']} WHERE 1=1 {$mq_sql['where']}{$in_post_types} AND ({$wpdb->posts}.post_status = 'publish') AND {$wpdb->posts}.post_title NOT LIKE CONCAT( '%', SUBSTRING_INDEX( {$wpdb->postmeta}.meta_value, ',', 1 ), '%' ) GROUP BY {$wpdb->posts}.ID";
return $wpdb->get_results( $query, ARRAY_A ); // phpcs:ignore
}
/**
* Group result data by post type.
*
* @param array $data Result data.
*
* @return array
*/
function rank_math_analyze_group_result( $data ) {
foreach ( $data as $val ) {
$key = array_key_exists( 'post_type', $val ) ? $val['post_type'] : '';
$rows[ $key ][] = $val;
}
return $rows;
}
/**
* Check if sitemap module is active.
*
* @param SEO_Analyzer $analyzer Analyzer instance.
* @return array
*/
function rank_math_analyze_sitemap( $analyzer ) {
$found = Helper::is_module_active( 'sitemap' );
if ( ! $found ) {
$sitemap = wp_remote_get( RankMath\Sitemap\Router::get_base_url( 'sitemap.xml' ) );
$found = isset( $sitemap['response'] ) && 200 === $sitemap['response']['code'];
}
return [
'status' => $found ? 'ok' : 'fail',
'message' => $found ? esc_html__( 'Your site has one or more sitemaps.', 'rank-math' ) : esc_html__( 'No sitemaps found.', 'rank-math' ),
];
}
/**
* Check if the site is globally set to noindex.
*/
function rank_math_analyze_blog_public() {
$info_message = '<strong>' . esc_html__( 'Attention: Search Engines can\'t see your website.', 'rank-math' ) . '</strong> ';
$info_message .= sprintf(
/* translators: %1$s: opening tag of the link, %2$s: the closing tag */
esc_html__( 'Navigate to %1$sSettings > Reading%2$s and turn off this option: "Discourage search engines from indexing this site".', 'rank-math' ),
'<a href="' . esc_url( admin_url( 'options-reading.php' ) ) . '">',
'</a>'
);
$public = (bool) get_option( 'blog_public' );
return [
'status' => $public ? 'ok' : 'fail',
'message' => $public ? esc_html__( 'Your site is accessible by search engine.', 'rank-math' ) : $info_message,
];
}

View File

@@ -0,0 +1,219 @@
<?php
/**
* SEO Analyzer admin page contents.
*
* @package RANK_MATH
* @author Rank Math <support@rankmath.com>
* @license GPL-2.0+
* @link https://rankmath.com/wordpress/plugin/seo-suite/
* @copyright 2019 Rank Math
*/
use RankMath\KB;
defined( 'ABSPATH' ) || exit;
?>
<div class="rank-math-box blurred">
<h2>
Competitor Analyzer
</h2>
<p>Enter a site URL to see how it ranks for the same SEO criteria as site.</p>
<div class="url-form">
<input type="text" name="competitor_url" id="competitor_url" placeholder="https://rankmath.com" disabled="disabled" />
<button type="button" class="button button-primary disabled" id="competitor_url_submit" disabled="disabled">Start SEO Analyzer</button>
</div>
</div>
<div class="rank-math-box rank-math-analyzer-result blurred">
<span class="wp-header-end"></span>
<div class="rank-math-results-wrapper">
<div class="rank-math-result-graphs rank-math-box">
<div class="three-col">
<div class="graphs-main">
<div id="rank-math-circle-progress" data-result="0.85">
<div class="result-main-score">
<strong>85/100</strong>
<label>SEO Score</label>
</div>
</div>
</div>
<div class="graphs-side">
<ul class="chart">
<li class="chart-bar-good">
<div class="result-score">
<label>Passed Tests</label>
<strong>20/29</strong>
</div>
<div class="chart-bar">
<span style="width:69%"></span>
</div>
</li>
<li class="chart-bar-average">
<div class="result-score">
<label>Warnings</label>
<strong>2/29</strong>
</div>
<div class="chart-bar">
<span style="width:7%"></span>
</div>
</li>
<li class="chart-bar-bad">
<div class="result-score">
<label>Failed Tests</label>
<strong>7/29</strong>
</div>
<div class="chart-bar">
<span style="width:24%"></span>
</div>
</li>
</ul>
</div>
<div class="serp-preview">
<div class="serp-preview-body">
<div class="serp-url-wrapper">
<img src="https://t0.gstatic.com/faviconV2?client=SOCIAL&amp;type=FAVICON&amp;fallback_opts=TYPE,SIZE,URL&amp;url=https%3A%2F%2Fwww.rankmath.com%2F&amp;size=128" width="16" height="16" class="serp-favicon">
<span class="serp-url">https://www.rankmath.com/</span>
</div>
<h5 class="serp-title">Rank Math - Best Free WordPress SEO Tools in 2022</h5>
<p class="serp-description">Rank Math WordPress SEO plugin will help you rank higher in search engines. DOWNLOAD for FREE this plugin today to optimize WordPress website for higher ra...</p>
</div>
</div>
</div>
</div>
<div id="analysis-result" class="rank-math-result-filters">
<a href="#all" class="rank-math-result-filter rank-math-result-filter-all active" data-filter="all">All<span class="rank-math-result-filter-count">29</span></a>
<a href="#passed" class="rank-math-result-filter rank-math-result-filter-passed" data-filter="ok">Passed Tests<span class="rank-math-result-filter-count">20</span></a>
<a href="#warning" class="rank-math-result-filter rank-math-result-filter-warnings" data-filter="warning">Warnings<span class="rank-math-result-filter-count">2</span></a>
<a href="#failed" class="rank-math-result-filter rank-math-result-filter-failed" data-filter="fail">Failed Tests<span class="rank-math-result-filter-count">7</span></a>
</div>
<div class="rank-math-result-tables rank-math-box">
<div class="rank-math-result-table rank-math-result-category-basic rank-math-result-statuses-fail rank-math-result-statuses-ok rank-math-result-statuses-warning">
<div class="category-title">
Basic SEO
</div>
<div class="table-row rank-math-result-status-info" data-status="info">
<div class="row-title">
<div class="status-icon status-info dashicons dashicons-info" title="Info"></div>
<h3>
Common Keywords
<span class="rank-math-tooltip"><em class="dashicons-before dashicons-editor-help"></em><span>A list of keywords that appear frequently in the text of content.</span></span>
</h3>
</div>
<div class="row-description">
<div class="row-content">
Here are the most common keywords we found on page:
<div class="wp-tag-cloud"><span class="keyword-cloud-item" style="font-size: 22.00px">rank</span> <span class="keyword-cloud-item" style="font-size: 21.29px">math</span> <span class="keyword-cloud-item" style="font-size: 15.65px">wordpress</span> <span class="keyword-cloud-item" style="font-size: 13.18px">best</span> <span class="keyword-cloud-item" style="font-size: 12.82px">search</span> <span class="keyword-cloud-item" style="font-size: 12.82px">plugin</span> <span class="keyword-cloud-item" style="font-size: 12.47px">site</span> <span class="keyword-cloud-item" style="font-size: 12.47px">features</span> <span class="keyword-cloud-item" style="font-size: 12.47px">support</span> <span class="keyword-cloud-item" style="font-size: 12.47px">google</span></div>
<div class="clear"></div>
</div>
</div>
</div>
<div class="table-row rank-math-result-status-fail" data-status="fail">
<div class="row-title">
<div class="status-icon status-fail dashicons dashicons-no" title="Failed"></div>
<h3>
SEO Description
<span class="rank-math-tooltip"><em class="dashicons-before dashicons-editor-help"></em><span>SEO analysis of page\'s meta-description.</span></span>
</h3>
</div>
<div class="row-description">
<div class="row-content">
<a href="#" class="button button-secondary button-small result-action">How to fix</a>
The description of page has 184 characters. Most search engines will truncate meta description to 160 characters.
<code class="full-width">Rank Math WordPress SEO plugin will help you rank higher in search engines. DOWNLOAD for FREE this plugin today to optimize WordPress website for higher rankings and more traffic.</code>
<div class="clear"></div>
<div class="how-to-fix-wrapper">
<div class="analysis-test-how-to-fix">
<p>Write a meta-description for page. Use target keyword(s) (in a natural way) and write with human readers in mind. Summarize the content - describe the topics article discusses.</p>
<p>The description should stimulate reader interest and get them to click on the article. Think of it as a mini-advert for content.</p>
</div>
</div>
</div>
</div>
</div>
<div class="table-row rank-math-result-status-ok" data-status="ok">
<div class="row-title">
<div class="status-icon status-ok dashicons dashicons-yes" title="OK"></div>
<h3>
H1 Heading
<span class="rank-math-tooltip"><em class="dashicons-before dashicons-editor-help"></em><span>SEO Analysis of the H1 Tags on page.</span></span>
</h3>
</div>
<div class="row-description">
<div class="row-content">
One H1 tag was found on page.
<code class="full-width">WordPress SEO Made Easy</code>
<div class="clear"></div>
</div>
</div>
</div>
<div class="table-row rank-math-result-status-ok" data-status="ok">
<div class="row-title">
<div class="status-icon status-ok dashicons dashicons-yes" title="OK"></div>
<h3>
H2 Headings
<span class="rank-math-tooltip"><em class="dashicons-before dashicons-editor-help"></em><span>SEO analysis of the H2 headings on page.</span></span>
</h3>
</div>
<div class="row-description">
<div class="row-content">
One or more H2 tags were found on page.
<code class="full-width">Recommended By The Best SEOs On The Planet, What is Rank Math?
, What you can do with Rank Math, Top-performing World Leading Companies Use Rank Math SEO, Take The Guesswork Out Of WordPress SEO, all-in-one solution for all the SEO needs, Leading SEOs are Loving Rank Math!</code>
<div class="clear"></div>
</div>
</div>
</div>
<div class="table-row rank-math-result-status-ok" data-status="ok">
<div class="row-title">
<div class="status-icon status-ok dashicons dashicons-yes" title="OK"></div>
<h3>
Image ALT Attributes
<span class="rank-math-tooltip"><em class="dashicons-before dashicons-editor-help"></em><span>SEO analysis of the "alt" attribute for image tags.</span></span>
</h3>
</div>
<div class="row-description">
<div class="row-content">
All images on page have alt attributes.
<div class="clear"></div>
</div>
</div>
</div>
<div class="table-row rank-math-result-status-ok" data-status="ok">
<div class="row-title">
<div class="status-icon status-ok dashicons dashicons-yes" title="OK"></div>
<h3>
Keywords in Title &amp; Description
<span class="rank-math-tooltip"><em class="dashicons-before dashicons-editor-help"></em><span>SEO analysis of the HTML page\'s Title and meta description content.</span></span>
</h3>
</div>
<div class="row-description">
<div class="row-content">
One or more keywords were found in the title and description of page.
<ul class="info-list">
<li><strong>title: </strong> rank, math, wordpress, best</li>
<li><strong>description: </strong> rank, math, wordpress, search, plugin, site</li>
</ul>
<div class="clear"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="rank-math-pro-cta" class="center">
<div class="rank-math-cta-box blue-ticks width-50 top-20 less-padding">
<h3><?php esc_html_e( 'Competitor Analyzer', 'rank-math' ); ?></h3>
<ul>
<li><?php esc_html_e( 'Analyze competitor websites to gain an edge', 'rank-math' ); ?></li>
<li><?php esc_html_e( 'Evaluate strengths and weaknesses', 'rank-math' ); ?></li>
<li><?php esc_html_e( 'Explore new keywords and opportunities', 'rank-math' ); ?></li>
<li><?php esc_html_e( 'Make more informed decisions & strategy', 'rank-math' ); ?></li>
</ul>
<a href="<?php KB::the( 'pro', 'Competitor Analyzer Tab' ); ?>" target="_blank" rel="noreferrer" class="button button-primary is-green"><?php esc_html_e( 'Upgrade', 'rank-math' ); ?></a>
</div>
</div>

View File

@@ -0,0 +1,43 @@
<?php
/**
* SEO Analyzer form.
*
* @package RANK_MATH
* @author Rank Math <support@rankmath.com>
* @license GPL-2.0+
* @link https://rankmath.com/wordpress/plugin/seo-suite/
* @copyright 2019 Rank Math
*/
use RankMath\Helper;
defined( 'ABSPATH' ) || exit;
$analyzer = Helper::get_module( 'seo-analysis' )->admin->analyzer;
?>
<div class="rank-math-seo-analysis-header <?php echo empty( $analyzer->results ) || count( $analyzer->results ) < 30 ? '' : ' hidden'; ?>">
<?php if ( $analyzer->analyse_subpage ) { ?>
<p class="page-analysis-selected">
<?php echo sprintf( esc_html__( 'Selected page: %s', 'rank-math' ), '<a href="' . esc_url( $analyzer->analyse_url ) . '" class="rank-math-current-url" target="_blank">' . $analyzer->analyse_url . '</a>' ); // phpcs:ignore ?>
<input type="text" class="rank-math-analyze-url" value="<?php echo esc_url( $analyzer->analyse_url ); ?>">
<button class="button button-secondary rank-math-changeurl-ok"><?php esc_html_e( 'OK', 'rank-math' ); ?></button>
<button class="button button-secondary rank-math-changeurl"><?php esc_html_e( 'Change URL', 'rank-math' ); ?></button>
</p>
<button data-what="page" class="button button-primary button-xlarge rank-math-recheck"><?php esc_html_e( 'Start Page Analysis', 'rank-math' ); ?></button>
<h2><?php esc_html_e( 'Analysing Page&hellip;', 'rank-math' ); ?></h2>
<?php } else { ?>
<button data-what="website" class="button button-primary button-xlarge rank-math-recheck"><?php esc_html_e( 'Start SEO Analyzer', 'rank-math' ); ?></button>
<h2><?php esc_html_e( 'Analysing Website&hellip;', 'rank-math' ); ?></h2>
<?php } ?>
<div class="progress-bar">
<div class="progress"></div>
<div class="progress-text"><span>0%</span> <?php esc_html_e( 'Complete', 'rank-math' ); ?></div>
</div>
</div>

View File

@@ -0,0 +1,72 @@
<?php
/**
* SEO Analyzer graphs.
*
* @package RANK_MATH
* @author Rank Math <support@rankmath.com>
* @license GPL-2.0+
* @link https://rankmath.com/wordpress/plugin/seo-suite/
* @copyright 2019 Rank Math
*/
use RankMath\Helper;
$analyzer = Helper::get_module( 'seo-analysis' )->admin->analyzer;
defined( 'ABSPATH' ) || exit;
?>
<div class="rank-math-result-graphs rank-math-box">
<div class="rank-math-analysis-date">
<?php echo $analyzer->get_last_checked_date(); // phpcs:ignore ?>
</div>
<div class="three-col">
<div class="graphs-main">
<div id="rank-math-circle-progress" data-result="<?php echo floatval( $percent / 100 ); ?>">
<div class="result-main-score">
<strong><?php echo absint( $percent ); ?>/100</strong>
<label><?php esc_html_e( 'SEO Score', 'rank-math' ); ?></label>
</div>
</div>
</div>
<div class="graphs-side">
<ul class="chart">
<li class="chart-bar-good">
<div class="result-score">
<label><?php esc_html_e( 'Passed Tests', 'rank-math' ); ?></label>
<strong><?php echo absint( $statuses['ok'] ) . '/' . absint( $total ); ?></strong>
</div>
<div class="chart-bar">
<span style="width:<?php echo absint( round( $statuses['ok'] / $total * 100 ) ); ?>%"></span>
</div>
</li>
<li class="chart-bar-average">
<div class="result-score">
<label><?php esc_html_e( 'Warnings', 'rank-math' ); ?></label>
<strong><?php echo absint( $statuses['warning'] ) . '/' . absint( $total ); ?></strong>
</div>
<div class="chart-bar">
<span style="width:<?php echo absint( round( $statuses['warning'] / $total * 100 ) ); ?>%"></span>
</div>
</li>
<li class="chart-bar-bad">
<div class="result-score">
<label><?php esc_html_e( 'Failed Tests', 'rank-math' ); ?></label>
<strong><?php echo absint( $statuses['fail'] ) . '/' . absint( $total ); ?></strong>
</div>
<div class="chart-bar">
<span style="width:<?php echo absint( round( $statuses['fail'] / $total * 100 ) ); ?>%"></span>
</div>
</li>
</ul>
</div>
<?php $this->display_serp_preview(); ?>
</div>
</div>

View File

@@ -0,0 +1 @@
<?php // Silence is golden.

View File

@@ -0,0 +1,45 @@
<?php
/**
* SEO Analyzer admin page contents.
*
* @package RANK_MATH
* @author Rank Math <support@rankmath.com>
* @license GPL-2.0+
* @link https://rankmath.com/wordpress/plugin/seo-suite/
* @copyright 2019 Rank Math
*/
use RankMath\Helper;
use RankMath\Helpers\Param;
defined( 'ABSPATH' ) || exit;
$current_tab = Param::get( 'view', 'seo_analyzer' );
$allowed_tabs = [ 'seo_analyzer', 'competitor_analyzer' ];
if ( ! in_array( $current_tab, $allowed_tabs, true ) ) {
$current_tab = 'seo_analyzer';
}
$module = Helper::get_module( 'seo-analysis' );
$analyzer = $module->admin->analyzer;
$tab_file = apply_filters( 'rank_math/seo_analysis/admin_tab_view', '', $current_tab );
// Header.
rank_math()->admin->display_admin_header();
?>
<div class="wrap rank-math-wrap rank-math-seo-analysis-wrap dashboard">
<span class="wp-header-end"></span>
<?php $analyzer->admin_tabs(); ?>
<div class="rank-math-ui dashboard-wrapper seo-analysis <?php echo esc_attr( $current_tab ); ?>">
<?php
if ( $tab_file && file_exists( $tab_file ) ) {
include_once $tab_file;
} else {
include_once dirname( __FILE__ ) . '/seo-analyzer.php';
}
?>
</div><!--.rank-math-ui.module-listing.dashboard-wrapper-->
</div>

View File

@@ -0,0 +1,48 @@
<?php
/**
* SEO Analyzer admin page contents.
*
* @package RANK_MATH
* @author Rank Math <support@rankmath.com>
* @license GPL-2.0+
* @link https://rankmath.com/wordpress/plugin/seo-suite/
* @copyright 2019 Rank Math
*/
use RankMath\Helper;
defined( 'ABSPATH' ) || exit;
?>
<header class="rank-math-box">
<h2>
<span class="title-prefix"><?php esc_html_e( 'SEO Analysis for', 'rank-math' ); ?></span>
<span><?php echo esc_html( explode( '://', home_url() )[1] ); ?></span>
<?php if ( Helper::is_site_connected() && ! empty( $analyzer->results ) ) : ?>
<a href="#" data-what="website" class="rank-math-recheck"><?php esc_html_e( 'Restart SEO Analyzer', 'rank-math' ); ?> <span class="dashicons dashicons-update"></span></a>
<a href="#analysis-result" class="button button-primary rank-math-view-issues"><?php esc_html_e( 'View Issues', 'rank-math' ); ?></a>
<?php do_action( 'rank_math/analyzer/results_header' ); ?>
<?php endif; ?>
</h2>
</header>
<div class="rank-math-box rank-math-analyzer-result">
<span class="wp-header-end"></span>
<?php if ( Helper::is_site_connected() ) : ?>
<?php include dirname( __FILE__ ) . '/form.php'; ?>
<?php if ( ! $analyzer->analyse_subpage ) : ?>
<div class="rank-math-results-wrapper">
<?php $analyzer->display(); ?>
</div>
<?php endif; ?>
<?php else : ?>
<div class="rank-math-seo-analysis-header">
<?php // Translators: placeholders are opening and closing tag for link. ?>
<h3><?php echo wp_kses_post( sprintf( __( 'Analyze your site by %1$s linking your Rank Math account %2$s', 'rank-math' ), '<a href="' . Helper::get_connect_url() . '" target="_blank">', '</a>' ) ); ?>
</div>
<?php endif; ?>
</div>

View File

@@ -0,0 +1,56 @@
<?php
/**
* SEO Analyzer Google Preview.
*
* @package RANK_MATH
* @author Rank Math <support@rankmath.com>
* @license GPL-2.0+
* @link https://rankmath.com/wordpress/plugin/seo-suite/
* @copyright 2019 Rank Math
*/
defined( 'ABSPATH' ) || exit;
$src_format = 'https://t0.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=%%SITEURL%%&size=128';
$favicon = str_replace( '%%SITEURL%%', urlencode( $this->analyse_url ), $src_format );
if ( is_array( $this->results ) ) {
if ( isset( $this->results['title_length'] ) ) {
$title_data = $this->results['title_length']->get_result();
$title = $title_data['data'];
}
if ( isset( $this->results['description_length'] ) ) {
$description_data = $this->results['description_length']->get_result();
$description = $description_data['data'];
}
}
if ( empty( $title ) ) {
$title = __( '(No Title)', 'rank-math' );
}
// Cut title to 60 characters.
if ( strlen( $title ) > 60 ) {
$title = substr( $title, 0, 60 ) . '...';
}
if ( empty( $description ) ) {
$description = __( '(No Description)', 'rank-math' );
}
// Cut description to 160 characters.
if ( strlen( $description ) > 160 ) {
$description = substr( $description, 0, 160 ) . '...';
}
?>
<div class="serp-preview">
<div class="serp-preview-body">
<div class="serp-url-wrapper">
<img src="<?php echo $favicon; // phpcs:ignore ?>" width="16" height="16" class="serp-favicon" />
<span class="serp-url"><?php echo esc_url( $this->analyse_url ); ?></span>
</div>
<h5 class="serp-title"><?php echo esc_html( $title ); ?></h5>
<p class="serp-description"><?php echo esc_html( $description ); ?></p>
</div>
</div>