define_tables();
$this->init();
} catch ( Error $e ) {
Ahrefs_Seo_Compatibility::on_type_error( $e, __METHOD__, __FILE__, __( 'Plugin initialization failed.', 'ahrefs-seo' ) );
} catch ( Exception $e ) {
self::notify( $e, 'Plugin initialization failed.' );
Ahrefs_Seo_Errors::save_message( 'general', __( 'Plugin initialization failed.', 'ahrefs-seo' ) . ' ' . $e->getMessage(), Message::TYPE_ERROR ); // show error to user if we can't submit it.
}
}
/**
* Initialize plugin
*/
private function init() {
add_action( 'admin_init', [ $this, 'admin_init' ] );
add_action( 'admin_init', [ Ahrefs_Seo_Deprecated::class, 'admin_init' ] );
if ( is_admin() ) {
add_action( 'init', [ $this, 'load_textdomain' ], 13 ); // first of init actions.
add_action( 'init', [ $this, 'init_screens' ], 15 );
add_action( 'admin_menu', [ $this, 'add_admin_menu' ] );
add_filter( 'wp_refresh_nonces', [ $this, 'wp_refresh_nonces' ], 10, 3 );
}
add_action( 'init', [ $this, 'init_cron' ], 15 );
add_action( 'init', [ $this, 'quick_updates_check' ], 14 );
add_action( 'switch_blog', [ $this, 'switch_blog' ], 9, 0 );
add_action( self::ACTION_TOKEN_CHANGED, [ $this, 'clear_caches_on_events' ] );
add_action( self::ACTION_DOMAIN_CHANGED, [ $this, 'clear_caches_on_events' ] );
// initialize earlier.
/** @psalm-suppress TypeDoesNotContainType */
if ( self::allow_reports() && ( ! defined( 'AHREFS_SEO_BUGSNAG_OFF' ) || ! AHREFS_SEO_BUGSNAG_OFF ) ) {
self::$bugsnag = Ahrefs_Seo_Bugsnag::get()->create_client();
\Bugsnag\Handler::register( self::$bugsnag );
}
if ( $this->initialized_get() ) {
if ( is_admin() ) {
add_action( 'admin_init', [ new Custom_Traffic_Column(), 'add_post_columns' ] );
add_action( 'init', [ Ahrefs_Seo_Screen_With_Table::class, 'add_table_and_post_actions' ], 15 ); // "per page" options.
}
// initialize Content Audit hooks.
Content_Hooks::get();
}
}
/**
* Initialize cron jobs.
*/
public function init_cron() {
if ( is_null( $this::$fatal_info ) ) {
if ( $this->initialized_get() || get_option( self::OPTION_IS_INITIALIZED_IN_PROGRESS ) ) { // allow cron jobs if initialized or after the Wizard update is in progress.
Ahrefs_Seo_Cron::get();
}
}
}
/**
* Initialize admin part
*/
public function admin_init() {
$this->token = Ahrefs_Seo_Token::get();
if ( ! get_option( self::OPTION_IS_INITIALIZED_WIZARD_COMPLETED ) ) {
Ahrefs_Seo_Data_Wizard::get(); // ajax handlers for wizard progress.
}
add_filter( 'plugin_action_links_' . AHREFS_SEO_BASENAME, [ $this, 'add_action_links' ] );
add_action( 'after_plugin_row_' . AHREFS_SEO_BASENAME, [ $this, 'after_plugin_row' ], 20, 1 );
add_filter( 'plugin_row_meta', [ $this, 'plugin_row_meta' ], 20, 2 );
}
/**
* Load plugin text domain
*
* @since 0.8.4
* @return void
*/
public function load_textdomain() {
load_plugin_textdomain( 'ahrefs-seo', false, dirname( plugin_basename( AHREFS_SEO_DIR . '/ahrefs-plugin.php' ) ) . '/languages' );
}
/**
* Initialize screens
*/
public function init_screens() {
$this->view = new Ahrefs_Seo_View();
if ( ! is_null( $this::$fatal_info ) ) {
$this->settings = new Ahrefs_Seo_Screen_Settings( $this->view );
} elseif ( ! $this->initialized_get() ) {
$this->wizard = new Ahrefs_Seo_Screen_Wizard( $this->view );
} else {
$this->content = new Ahrefs_Seo_Screen_Content( $this->view );
$this->settings = new Ahrefs_Seo_Screen_Settings( $this->view );
}
}
// phpcs:disable Generic.CodeAnalysis.EmptyStatement.DetectedCatch, Squiz.Commenting.EmptyCatchComment.Missing
/**
* Called on plugin activation.
* Add cron tasks.
*/
public static function plugin_activate() {
try {
Ahrefs_Seo_Cron::get()->add_tasks();
} catch ( Exception $e ) {
} catch ( Error $e ) {
}
}
/**
* Called on plugin deactivation.
* Remove cron tasks.
*/
public static function plugin_deactivate() {
try {
Ahrefs_Seo_Cron::get()->remove_tasks();
( new Snapshot() )->clean_cache();
} catch ( Exception $e ) {
} catch ( Error $e ) {
}
}
// phpcs:enable Generic.CodeAnalysis.EmptyStatement.DetectedCatch, Squiz.Commenting.EmptyCatchComment.Missing
/**
* Add items to admin menu
*/
public function add_admin_menu() {
$icon = 'data:image/svg+xml;base64,' . base64_encode( '' ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
if ( ! is_null( $this::$fatal_info ) ) {
// if already initialized - then same as for Content audit screen, otherwise same as for Wizard view.
$this->view->add_admin_screen( $this->settings, add_menu_page( __( 'Ahrefs SEO for WordPress', 'ahrefs-seo' ), __( 'Ahrefs SEO', 'ahrefs-seo' ), $this->initialized_get() ? self::CAP_CONTENT_AUDIT_VIEW : self::CAP_WIZARD_VIEW, self::SLUG, [ $this, 'error_menu' ], $icon, 81 ) );
return;
}
if ( ! $this->initialized_get() ) {
// show wizard.
$this->view->add_admin_screen( $this->wizard, add_menu_page( __( 'Ahrefs SEO for WordPress', 'ahrefs-seo' ), __( 'Ahrefs SEO', 'ahrefs-seo' ), self::CAP_WIZARD_VIEW, self::SLUG, [ $this, 'wizard_menu' ], $icon, 81 ) );
} else {
if ( get_option( self::OPTION_IS_INITIALIZED_FIRST_TIME ) && ! get_option( self::OPTION_IS_INITIALIZED_WIZARD_COMPLETED ) ) {
$this->initialized_set( null, null, null, null, true );
}
// subpages (for all) + settings (editor+admin).
$this->view->add_admin_screen( $this->content, add_menu_page( __( 'Ahrefs SEO for WordPress', 'ahrefs-seo' ), __( 'Ahrefs SEO', 'ahrefs-seo' ), self::CAP_CONTENT_AUDIT_VIEW, self::SLUG, array( $this, 'content_menu' ), $icon, 81 ) );
$page = add_submenu_page( self::SLUG, __( 'Content Audit', 'ahrefs-seo' ), __( 'Content Audit', 'ahrefs-seo' ), self::CAP_CONTENT_AUDIT_VIEW, self::SLUG_CONTENT, array( $this, 'content_menu' ) );
if ( $page ) {
$this->view->add_admin_screen( $this->content, $page );
}
$page = add_submenu_page( self::SLUG, __( 'Ahrefs SEO for WordPress', 'ahrefs-seo' ), __( 'Settings', 'ahrefs-seo' ), self::CAP_SETTINGS_MENU, self::SLUG_SETTINGS, array( $this, 'settings_menu' ) );
if ( $page ) {
$this->view->add_admin_screen( $this->settings, $page );
}
}
}
/**
* Handler for Wizard menu item.
* It used until Wizard will be finished.
*/
public function wizard_menu() {
$this->wizard->show();
}
/**
* Handler for Settings menu item
*/
public function settings_menu() {
$this->settings->show();
}
/**
* Handler for Content Audit menu item
*/
public function content_menu() {
$this->content->show();
}
/**
* Handler for Error menu item
*/
public function error_menu() {
$this->view->show( 'fatal', __( 'Content Audit', 'ahrefs-seo' ), array_merge( [ 'header_class' => $this->settings ? $this->settings->get_header_classes( [ 'content' ] ) : [ 'content' ] ], isset( self::$fatal_info ) ? self::$fatal_info : [] ), $this->settings, 'error' );
}
/**
* Clear vary caches.
*/
public function clear_caches_on_events() {
// need to call clear cache for those instances, because they may be not initialized before.
Ahrefs_Seo_Api::get()->clear_cache();
}
/**
* Check DB version and run update if current version is different from version value saved at the DB.
* Do same for Content Audit rules.
*/
public function quick_updates_check() {
try {
if ( get_option( self::OPTION_TABLE_VERSION ) !== self::CURRENT_TABLE_VERSION ) {
$previous_version = intval( get_option( self::OPTION_TABLE_VERSION, 0 ) );
self::breadcrumbs( sprintf( 'Update DB from version %d to %d.', $previous_version, self::CURRENT_TABLE_VERSION ) );
if ( 0 === $previous_version ) { // not an update from some previous version.
add_action( 'init', [ new Ahrefs_Seo_Content_Settings(), 'maybe_turn_on_products' ], 9999 ); // custom post type 'product' is not registered at the current moment.
}
// reset stop error if requested.
if ( isset( $_GET['reset_error'] ) && check_admin_referer( 'reset_error', 'reset_error' ) ) {
Ahrefs_Seo_Db::reset_stop_error();
Helper_Content::wp_redirect( remove_query_arg( [ 'reset_error', 'r' ] ) );
die;
}
if ( Ahrefs_Seo_Db::create_table( $previous_version ) ) {
update_option( self::OPTION_TABLE_VERSION, self::CURRENT_TABLE_VERSION );
update_option( self::OPTION_LAST_HASH, [] ); // reset last reported error if any.
} else {
return;
}
}
$prev_rules_ver = get_option( self::OPTION_CONTENT_RULES_VERSION );
if ( self::CURRENT_CONTENT_RULES !== $prev_rules_ver ) {
update_option( self::OPTION_CONTENT_RULES_VERSION, self::CURRENT_CONTENT_RULES );
if ( (int) $prev_rules_ver > 0 ) {
Ahrefs_Seo_Data_Content::get()->update_options( (int) $prev_rules_ver );
// run new analysis on rules version update.
if ( (int) $prev_rules_ver < 4 ) {
if ( Ahrefs_Seo_Compatibility::quick_compatibility_check() ) {
( new Snapshot() )->create_new_snapshot();
}
}
if ( (int) $prev_rules_ver < 5 ) { // prev version may show incorrect duplicated keywords.
( new Duplicated_Keywords() )->fill_duplicated_for_snapshot( ( new Snapshot() )->get_current_snapshot_id() );
}
}
}
} catch ( Error $e ) {
Ahrefs_Seo_Compatibility::on_type_error( $e, __METHOD__, __FILE__, __( 'Quick database update failed', 'ahrefs-seo' ) );
} catch ( Exception $e ) {
self::notify( $e, 'Quick Database update failed' );
}
}
/**
* Set vary initialized statuses to on or off.
* Update status only if not null.
*
* @param null|bool $is_initialized_ahrefs Is ahrefs initialized (has valid token).
* @param null|bool $is_initialized_analytics Is analytics initialized (has valid token AND ua_id profile chosen).
* @param null|bool $analysis_in_progress Analysis started: user is seeing step 3.2 with progress bar.
* @param null|bool $is_initialized_first_time Load or do not load wizard screens.
* Turned off after initial backlinks transfer and content analysis completed.
* @param null|bool $is_initialized_wizard_completed Load or do not load wizard screen ajax stuff.
* Turned off after any normal page opened first time.
* (It turned on a bit longer then previous option, because we want that already opened wizard page receive updates using ajax).
* @return void
*/
public function initialized_set( $is_initialized_ahrefs = null, $is_initialized_analytics = null, $analysis_in_progress = null, $is_initialized_first_time = null, $is_initialized_wizard_completed = null ) {
self::breadcrumbs( sprintf( 'initialized_set(%s)', (string) wp_json_encode( func_get_args() ) ) );
if ( ! is_null( $is_initialized_ahrefs ) ) {
update_option( self::OPTION_IS_INITIALIZED, $is_initialized_ahrefs );
if ( ! $is_initialized_ahrefs ) {
// reset current token.
$this->token->token_save( '' );
}
}
if ( ! is_null( $is_initialized_analytics ) ) {
if ( (bool) get_option( self::OPTION_IS_INITIALIZED_ANALYTICS ) !== $is_initialized_analytics ) {
update_option( self::OPTION_IS_INITIALIZED_ANALYTICS, $is_initialized_analytics );
if ( ! $is_initialized_analytics ) {
// reset current token if any.
Ahrefs_Seo_Analytics::get()->disconnect();
}
}
}
if ( ! is_null( $analysis_in_progress ) ) {
update_option( self::OPTION_IS_INITIALIZED_IN_PROGRESS, $analysis_in_progress );
}
if ( ! is_null( $is_initialized_first_time ) ) {
update_option( self::OPTION_IS_INITIALIZED_FIRST_TIME, $is_initialized_first_time );
}
if ( ! is_null( $is_initialized_wizard_completed ) ) {
update_option( self::OPTION_IS_INITIALIZED_WIZARD_COMPLETED, $is_initialized_wizard_completed );
}
}
/**
* Is Wizard already initialized?
* No need to show it again if already did.
*
* @return bool
*/
public function initialized_wizard() {
$value = get_option( self::OPTION_IS_INITIALIZED_FIRST_TIME );
return ! empty( $value );
}
/**
* Is plugin initialized
*
* @return bool
*/
public function initialized_get() {
static $result = null;
// function called twice, must return same result.
if ( is_null( $result ) ) {
$value2 = get_option( self::OPTION_IS_INITIALIZED_FIRST_TIME );
$result = ! empty( $value2 );
}
return $result;
}
/**
* Register custom tables within $wpdb object.
*/
private function define_tables() {
global $wpdb;
// List of tables without prefixes [ name for use inside $wpdb => real table name ].
$tables = array(
'ahrefs_content' => self::TABLE_CONTENT,
'ahrefs_snapshots' => self::TABLE_SNAPSHOTS,
);
foreach ( $tables as $name => $table ) {
$wpdb->{$name} = $wpdb->prefix . $table;
$wpdb->tables[] = $table;
}
}
/**
* Callback for switch blog action.
*
* @since 0.9.8
*
* @return void
*/
public function switch_blog() {
$this->define_tables();
}
/**
* Should finish code execution?
*
* @param int|null $seconds_to_end Exit when x seconds left before max allowed limit.
* @param int|null $percents_to_end Exit when x percents of max allowed time left.
* @return bool
*/
public static function should_finish( $seconds_to_end = null, $percents_to_end = null ) {
if ( ! is_null( $percents_to_end ) ) {
$seconds_to_end = ( 100 - $percents_to_end ) / 100.0 * self::$time_limit;
if ( $seconds_to_end > 10 ) {
$seconds_to_end = 10;
}
}
if ( is_null( $seconds_to_end ) ) {
$seconds_to_end = self::$time_limit > 30 ? 10 : 5;
}
return microtime( true ) - self::$time_start - ( defined( 'AHREFS_SEO_IGNORE_DELAY' ) && AHREFS_SEO_IGNORE_DELAY ? 5 * 60 : 0 ) >= self::$time_limit - $seconds_to_end;
}
/**
* Get time for transient options
*
* @return int Time in seconds.
*/
public static function transient_time() {
return intval( self::$time_limit );
}
/**
* Set fatal error or notice reason
*
* @param string|null $header Header to show instead of default "contact ahrefs...".
* @param string $reason Message to show.
* @param bool $is_error Show ooops... part of screen if error.
* @param bool $has_reset_button Show button for error reset.
* @return void
*/
public static function set_fatal_error( $header = null, $reason, $is_error = true, $has_reset_button = false ) {
self::breadcrumbs( 'Fatal error: ' . (string) wp_json_encode( func_get_args() ) );
self::$fatal_info = [
'header' => $header,
'error' => $reason,
'is_error' => $is_error,
'has_reset_button' => $has_reset_button,
];
}
/**
* Define and apply max execution time limit
*
* @return void
*/
private static function use_time_limit() {
self::$time_limit = intval( ini_get( 'max_execution_time' ) );
$expected_limit = self::is_doing_real_cron() ? 55 : 15;
if ( self::$time_limit <= 0 ) {
self::$time_limit = $expected_limit;
}
if ( self::$time_limit > $expected_limit ) {
self::$time_limit = $expected_limit;
}
if ( $expected_limit > self::$time_limit ) {
self::set_time_limit( $expected_limit );
}
}
/**
* A real cron job is running now.
*
* @return bool
*/
public static function is_doing_real_cron() {
/** @psalm-suppress RedundantCondition */
return wp_doing_cron() && defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON;
}
/**
* Check if set_time_limit allowed and call it.
* Update internal time limit value.
* Try to avoid possible php warning "set_time_limit() has been disabled for security reasons".
*
* @param int $seconds Value, in seconds.
*
* @return bool
*/
public static function set_time_limit( $seconds ) {
if ( $seconds > self::$time_limit ) { // no need to decrease allowed time.
if ( function_exists( 'set_time_limit' ) && ! self::function_disabled( 'set_time_limit' ) ) {
if ( @set_time_limit( $seconds ) ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- the checking is passed, but function call may produce warning: "set_time_limit(): Cannot set max execution time limit due to system policy" anyway.
self::use_time_limit();
}
}
return false;
}
return true;
}
/**
* Delay execution in microseconds.
*
* @since 0.7.3
*
* @param int $micro_seconds Value, in micro seconds.
* @return void
*/
public static function usleep( $micro_seconds ) {
if ( ! defined( 'AHREFS_SEO_IGNORE_DELAY' ) ) {
if ( $micro_seconds > 0 ) {
usleep( $micro_seconds );
}
}
}
/**
* Check if ignore_user_abort allowed and call it.
* Try to avoid possible php warning "ignore_user_abort() has been disabled for security reasons".
*
* @param bool $ignore New value.
*
* @return void
*/
public static function ignore_user_abort( $ignore ) {
if ( function_exists( 'ignore_user_abort' ) && ! self::function_disabled( 'ignore_user_abort' ) ) {
ignore_user_abort( $ignore );
}
}
/**
* Is function disabled?
*
* @param string $function_name Function name to check.
* @return bool
*/
private static function function_disabled( $function_name ) {
$disabled = explode( ',', (string) ini_get( 'disable_functions' ) );
return in_array( $function_name, $disabled, true );
}
/**
* Set breadcrumbs for current code execution.
* No need to translate it.
*
* @param string $string String to save.
* @param bool $is_error Is it an error.
*
* @return void
*/
public static function breadcrumbs( $string, $is_error = false ) {
if ( ! is_null( self::$bugsnag ) ) {
self::$bugsnag->leaveBreadcrumb( $string, $is_error ? \Bugsnag\Breadcrumbs\Breadcrumb::ERROR_TYPE : \Bugsnag\Breadcrumbs\Breadcrumb::MANUAL_TYPE );
} else {
if ( defined( 'AHREFS_SEO_RELEASE' ) && 'development' === AHREFS_SEO_RELEASE && ! defined( 'AHREFS_SEO_SILENT' ) ) {
error_log( '** ' . self::thread_id() . ( $is_error ? " Error {$string}" : " Log {$string}" ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
}
}
}
/**
* Get previously collected breadcrumbs data
*
* @since 0.9.5
*
* @return array
*/
public static function get_breadcrumbs_data() {
if ( ! is_null( self::$bugsnag ) ) {
$report = \Bugsnag\Report::fromNamedError( self::$bugsnag->getConfig(), 'debug' );
self::$bugsnag->getPipeline()->execute(
$report,
function () {
}
);
$result = $report->toArray();
unset( $result['app'], $result['context'], $result['severityReason'], $result['unhandled'], $result['user'] );
return $result;
}
return [ 'disabled' ];
}
/**
* Notify (if allowed) about exception happened.
* No need to translate it.
*
* @param Throwable $e Exception to report.
* @param null|string $type Type of notification, any string.
*
* @return void
*/
public static function notify( Throwable $e, $type = null ) {
$hash = (string) $e;
if ( $hash === self::$last_hash ) { // do not report same error twice.
return;
}
self::$last_hash = $hash;
$events = get_option( self::OPTION_LAST_HASH, [] );
$count = 0;
if ( is_array( $events ) && isset( $events['hash'] ) && $events['hash'] === $hash ) {
$count = absint( $events['count'] );
if ( $count > 5 && 0 !== $count % 100 ) { // do not report same error many times.
return;
}
}
if ( $count >= 5 ) {
self::breadcrumbs( sprintf( 'No more reports (repeated %d times).', $count ) );
}
update_option(
self::OPTION_LAST_HASH,
[
'hash' => $hash,
'count' => ++$count,
]
);
if ( ! is_null( self::$bugsnag ) ) {
self::$bugsnag->notifyException(
$e,
function ( $report ) use ( $type ) {
if ( ! is_null( $type ) ) {
/** @var \Bugsnag\Report $report */
$report->addMetaData( [ 'type' => [ 'type' => $type ] ] );
}
return $report;
}
);
} elseif ( defined( 'AHREFS_SEO_RELEASE' ) && ! defined( 'AHREFS_SEO_SILENT' ) ) {
error_log( sprintf( '** Exception %s', (string) $e ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
}
}
/**
* Set reports allowed or not.
*
* @param bool $enable Enable reports.
* @return void
*/
public static function allow_reports_set( $enable = true ) {
update_option( self::OPTION_ALLOW_REPORTS, $enable );
}
/**
* Is reports allowed?
*
* @return bool
*/
public static function allow_reports() {
return (bool) get_option( self::OPTION_ALLOW_REPORTS, false );
}
/**
* Get current domain.
*
* @return string
*/
public static function get_current_domain() {
$domain = apply_filters( 'ahrefs_seo_domain', ! empty( wp_parse_url( get_home_url(), PHP_URL_HOST ) ) ? wp_parse_url( get_home_url(), PHP_URL_HOST ) : '' );
return is_string( $domain ) ? $domain : '';
}
/**
* Return unique id for current thread.
* Set source of current thread execution when called first time.
*
* @since 0.7.3
*
* @param string|null $source Thread source.
* @return string
*/
public static function thread_id( $source = null ) {
static $result = null;
if ( ! is_null( $source ) && is_null( self::$thread_source ) ) {
self::$thread_source = $source;
}
if ( is_null( $result ) ) {
$result = self::$time_start . '-' . rand( 1000000, 9999999 ) . ( wp_doing_cron() ? self::is_doing_real_cron() ? 'cron' : 'wpcron' : 'user' );
}
return $result . ( is_null( self::$thread_source ) ? '' : '-' . self::$thread_source ) . self::$time_limit;
}
/**
* @return Ahrefs_Seo_View
*/
public function get_view() {
return $this->view;
}
/**
* Get url to contact Ahrefs support team.
*
* @since 0.8.4
*
* @param bool $with_details Link will have body filled with some details.
* @return string
*/
public static function get_support_url( $with_details = false ) {
$result = 'mailto:support@ahrefs.com?subject=' . rawurlencode( 'WordPress plugin' );
if ( $with_details ) {
$analytics = Ahrefs_Seo_Analytics::get();
$google_connected = $analytics->get_data_tokens()->is_token_set();
$result .= '&body=' . rawurlencode( "\n" . 'Site: ' . self::get_current_domain() . "\n" . 'Ahrefs account: ' . ( Ahrefs_Seo_Api::get()->is_disconnected() ? 'not connected' : ( Ahrefs_Seo_Api::get()->is_free_account( true ) ? ' free token connected' : ' connected' ) ) . "\n" . 'Google accounts: ' . ( $google_connected ? 'connected' . "\n" . ( $analytics->is_analytics_enabled( false ) ? 'GA enabled' . ( $analytics->is_ua_set() ? ' and set' : ' but not set' ) : 'GA not enabled' ) . "\n" . ( $analytics->is_gsc_enabled( false ) ? 'GSC enabled' . ( $analytics->is_gsc_set() ? ' and set' : ' but not set' ) : 'GSC not enabled' ) . "\n" : 'not connected' ) );
}
return $result;
}
/**
* Add action links for the plugins page.
*
* @since 0.9.7
*
* @param string[] $actions An array of plugin action links.
* @return string[]
*/
public function add_action_links( $actions ) {
if ( ! is_array( $actions ) ) {
$actions = [];
}
if ( $this->initialized_get() ) {
$actions[] = sprintf( '%s>', esc_attr( Links::settings( Ahrefs_Seo_Screen_Settings::TAB_DATA ) . '&from-plugins-list=1' ), esc_html__( 'My audit data', 'ahrefs-seo' ) );
}
return $actions;
}
/**
* Show notice below a plugin row at multisite installation
*
* @since 0.9.7
*
* @param string $plugin_file Path to the plugin file relative to the plugin's directory.
* @return void
*/
public function after_plugin_row( $plugin_file ) {
if ( is_multisite() && is_network_admin() && is_plugin_active_for_network( $plugin_file ) ) {
try {
/** @var \WP_Plugins_List_Table $wp_list_table */
$wp_list_table = _get_list_table( '\\WP_Plugins_List_Table', [ 'screen' => get_current_screen() ] );
$columns = $wp_list_table ? $wp_list_table->get_column_count() : 4;
?>
|
%s>', esc_attr( 'https://ahrefs.canny.io/wordpress-plugin' ), esc_html__( 'Suggest a feature', 'ahrefs-seo' ) );
}
return $plugin_meta;
}
/**
* Checks nonce expiration on the ahrefs seo screen and refresh if needed.
*
* @since 0.10.2
*
* @param array $response The Heartbeat response.
* @param array $data The $_POST data sent.
* @param string $screen_id The screen ID.
* @return array The Heartbeat response.
*/
public function wp_refresh_nonces( $response, $data, $screen_id ) {
if ( 'toplevel_page_' . self::SLUG_CONTENT === $screen_id && current_user_can( self::CAP_CONTENT_AUDIT_VIEW ) ) {
$response['ahrefs-nonce'] = [ 'table_nonce' => wp_create_nonce( Ahrefs_Seo_Table::ACTION ) ];
}
return $response;
}
}