initialize_table();
// the tab for export.
$tab = isset( $_GET['tab'] ) ? sanitize_text_field( wp_unslash( $_GET['tab'] ) ) : '';
// the visible columns list, id => title.
$columns = $this->get_visible_columns();
if ( $export->export_data_tab( $tab, $columns ) ) {
exit();
}
$error = $export->get_error() ?? '';
} else {
$error = __( 'Sorry, you are not allowed to export the content audit data.', 'ahrefs-seo' );
}
Ahrefs_Seo::notify( new Ahrefs_Seo_Exception( sprintf( 'Error during CSV export: %s(%s)', __METHOD__, $error ), 0 ) );
}
}
/**
* Show a page
*/
public function show() : void {
if ( current_user_can( Ahrefs_Seo::CAP_CONTENT_AUDIT_VIEW ) ) {
if ( isset( $_POST['run_new_audit'] ) && current_user_can( Ahrefs_Seo::CAP_CONTENT_AUDIT_RUN ) && check_admin_referer( self::ACTION_NEW_AUDIT_RUN, 'scope_updated_nonce' ) ) {
$this->new_audit_run( false );
}
$content_audit = new Content_Audit();
// if audit paused, check, maybe some plugin already deactivated and we can run?
if ( Content_Audit::audit_is_paused() && $content_audit->require_update() ) {
Ahrefs_Seo_Compatibility::recheck_saved_incompatibility();
if ( empty( Content_Audit::audit_get_paused_messages() ) ) { // no saved reasons exists.
Content_Audit::audit_resume();
}
}
$params = [
'last_audit_stopped' => Content_Audit::audit_get_paused_messages( true ), // Message[] or null type.
'stop' => Ahrefs_Seo_Errors::check_stop_status( false ), // Message[] or null type.
'header_class' => $this->get_header_classes( [ 'content' ] ),
'is_first_audit' => $content_audit->show_first_audit(),
];
// manage new 'added since' items.
( new Ahrefs_Seo_Data_Content() )->update_added_since_items();
$this->view->show( 'content', __( 'Content Audit', 'ahrefs-seo' ), $params, $this, 'content' );
}
}
/**
* Get prefix for ajax requests to tables
*
* @return string
*/
protected function get_ajax_table_prefix() : string {
return 'ahrefs_seo_table_content';
}
/**
* Create new table
*
* @return Ahrefs_Seo_Table_Content
*/
protected function new_table_instance() : Ahrefs_Seo_Table_Content {
return new Ahrefs_Seo_Table_Content();
}
/**
* Print navigation and placeholder for future table
*
* @return void
*/
public function show_table_placeholder() : void {
if ( ! is_null( $this->table ) ) {
?>
is_tax_or_published() ) {
$action = $post_tax->load_action() ?? Ahrefs_Seo_Data_Content::ACTION4_ADDED_SINCE_LAST;
if ( in_array( $action, [ Ahrefs_Seo_Data_Content::ACTION4_ANALYZING_INITIAL, Ahrefs_Seo_Data_Content::ACTION4_OUT_OF_SCOPE_INITIAL, Ahrefs_Seo_Data_Content::ACTION4_ANALYZING_FINAL, Ahrefs_Seo_Data_Content::ACTION4_OUT_OF_SCOPE_ANALYZING ], true ) ) {
$action = Ahrefs_Seo_Data_Content::ACTION4_ANALYZING; // substitute correct template for initial actions.
}
ob_start();
$this->view->show_part( 'actions/' . $action, [ 'post_tax' => $post_tax ] );
$result = ob_get_clean();
} else {
$result = __( 'This page cannot be found. It is possible that you’ve archived the page or changed the page ID. Please reload the page & try again.', 'ahrefs-seo' );
}
wp_send_json_success( $result );
}
}
/**
* Handler for 'Run Content Audit again'.
* Created new snapshot if it was not exists.
*
* @return void
*/
public function ajax_content_manual_update() : void {
Ahrefs_Seo::thread_id( 'run_manual' );
if ( check_ajax_referer( Ahrefs_Seo_Table::ACTION ) ) {
if ( current_user_can( Ahrefs_Seo::CAP_CONTENT_AUDIT_RUN ) ) {
$this->new_audit_run( true );
} else {
wp_send_json_error( [ 'error' => __( 'Sorry, you are not allowed to run content audits.', 'ahrefs-seo' ) ] );
}
}
}
/**
* Try to run new audit
*
* @since 0.8.0
*
* @param bool $exit_with_json_answer Output JSON answer and terminate execution.
* @return void
*/
private function new_audit_run( bool $exit_with_json_answer ) : void {
if ( ! current_user_can( Ahrefs_Seo::CAP_CONTENT_AUDIT_RUN ) ) {
$message = Message::action_not_allowed( __( 'Sorry, you are not allowed to run content audits.', 'ahrefs-seo' ) );
if ( $exit_with_json_answer ) {
wp_send_json_error( [ 'tips' => $message ] );
} else {
Ahrefs_Seo_Errors::save_message( 'WordPress', $message->get_text(), Message::TYPE_ERROR_SINGLE );
}
return;
}
// check compatibility issues.
$tips = [];
Ahrefs_Seo_Api::get()->get_subscription_info(); // update Ahrefs account details (update 'is limited account' value).
if ( ! Ahrefs_Seo_Errors::has_stop_error( true ) ) {
$snapshots = new Snapshot();
if ( is_null( $snapshots->get_new_snapshot_id() ) ) {
$snapshots->create_new_snapshot();
if ( $exit_with_json_answer ) {
$fields = [ 'ok' => true ];
if ( ! empty( $_POST['info'] ) && current_user_can( Ahrefs_Seo::CAP_ROLE_ADMIN ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce already checked before this method called.
$fields['_'] = Ahrefs_Seo::get_breadcrumbs_data(); // return breadcrumbs details for debugging purposes.
}
wp_send_json_success( $fields );
}
return;
}
} else {
if ( ! $exit_with_json_answer ) {
return;
}
// fill message with all stop errors.
ob_start();
Ahrefs_Seo_Errors::show_stop_errors( Ahrefs_Seo_Errors::check_stop_status( true ) );
$tips['stop'] = ob_get_clean();
wp_send_json_error( [ 'tips' => $tips ] );
}
if ( ! $exit_with_json_answer ) {
return;
}
ob_start();
$this->view->show_part( 'content-tips/already-run' );
$tips['stop'] = ob_get_clean();
wp_send_json_error( [ 'tips' => $tips ] );
}
/**
* Bulk actions handler.
*
* @return void
*/
public function ajax_bulk_actions() : void {
Ahrefs_Seo::thread_id( 'bulk_actions' );
if ( check_ajax_referer( Ahrefs_Seo_Table::ACTION ) && current_user_can( Ahrefs_Seo::CAP_CONTENT_AUDIT_VIEW ) && isset( $_REQUEST['doaction'] ) && isset( $_REQUEST['ids'] ) && is_array( $_REQUEST['ids'] ) ) {
$doaction = sanitize_text_field( wp_unslash( $_REQUEST['doaction'] ) );
$ids = array_map( 'sanitize_text_field', wp_unslash( $_REQUEST['ids'] ) );
$this->bulk_actions( $doaction, Post_Tax::create_from_strings( $ids ) );
}
}
/**
* Receive ping message with items shown at the current moment and their versions ('ver' field of query).
* Run update of all pending items.
* Check current version of items and return all updated items (rows) at the 'data.updated' field of json answer.
* If 'data.status' is true - should run next ajax request.
* If nothing updated, simply return json success.
*/
public function ajax_content_ping() : void {
Ahrefs_Seo::breadcrumbs( __METHOD__ );
Ahrefs_Seo::thread_id( 'ping' );
if ( check_ajax_referer( Ahrefs_Seo_Table::ACTION ) && current_user_can( Ahrefs_Seo::CAP_CONTENT_AUDIT_VIEW ) && isset( $_REQUEST['items'] ) && ( is_array( $_REQUEST['items'] ) || 'false' === $_REQUEST['items'] ) ) {
$fields = [];
$fields['timeout'] = 2 + ( intval( ini_get( 'max_execution_time' ) ) ?: 120 ); // set this value before update_table call (it can modify default value).
$stop = isset( $_POST['stop'] ) ? sanitize_text_field( wp_unslash( $_POST['stop'] ) ) : ''; // what tip is displayed in stop block.
$content = Ahrefs_Seo_Data_Content::get();
$content_audit = new Content_Audit();
$content_audit_current = new Content_Audit_Current();
$something_updated = true; // send new request by default.
$cancel_audit = ! empty( $_POST['cancel_audit'] );
if ( $cancel_audit ) { // try to cancel audit.
if ( current_user_can( Ahrefs_Seo::CAP_CONTENT_AUDIT_RUN ) ) {
Content_Audit::audit_cancel();
}
} elseif ( ! empty( $_POST['unpause_audit'] ) ) { // try to resume audit.
if ( current_user_can( Ahrefs_Seo::CAP_CONTENT_AUDIT_RUN ) ) {
Content_Audit::audit_resume();
}
}
if ( ! $cancel_audit && ! Content_Audit::audit_is_paused() ) { // if not paused or cancelled.
// 1. Do update if we have any pending items.
// return value is true if we updated something and can run next request from JS.
$something_updated = $content_audit_current->maybe_update();
$waiting_time = $content_audit_current->get_waiting_time(); // from current snapshot.
if ( ! $something_updated ) {
$something_updated = $content_audit->update_table();
$waiting_time_new = $content_audit->get_waiting_time(); // from new snapshot.
if ( ! is_null( $waiting_time_new ) && ( is_null( $waiting_time ) || ( $waiting_time_new > $waiting_time ) ) ) {
$waiting_time = $waiting_time_new;
}
} elseif ( is_null( $waiting_time ) ) {
$waiting_time = $content_audit->get_waiting_time(); // from new snapshot.
}
$fields['paused'] = Content_Audit::audit_is_paused(); // update after audit executed.
} else {
$waiting_time = 2 * MINUTE_IN_SECONDS;
$fields['paused'] = true;
if ( $cancel_audit ) {
$fields['reload'] = 1;
}
}
if ( is_null( $waiting_time ) ) {
$waiting_time = max( [ 10, Worker::get_min_waiting_time() ] );
}
$fields['delayed'] = $fields['paused'] ? false : Content_Audit::audit_is_delayed(); // do not show "audit delayed" message when audit is paused.
$fields['new-request'] = $something_updated || ( new Content_Audit_Current() )->require_update() || ( $content_audit->require_update() );
$fields['audit'] = Ahrefs_Seo_Data_Content::get()->get_statistics();
$fields['waiting_time'] = round( $waiting_time, 1 ); // All active workers are paused during this amount of time.
$fields['tips'] = $this->prepare_tips( $stop );
// 2. Return updated items.
$post_tax_strings_with_ver = []; // array with [ post_tax_string => version_string ] items.
if ( is_array( $_REQUEST['items'] ) ) {
foreach ( $_REQUEST['items'] as $key => $value ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- we are sanitizing both key and value one line below as string and integers.
$post_tax_strings_with_ver[ sanitize_text_field( wp_unslash( $key ) ) ] = intval( $value ); // key is post_tax_string, value is (int)version.
}
}
$updated_ids = count( $post_tax_strings_with_ver ) ? $content->get_updated_items( $post_tax_strings_with_ver ) : [];
// is the current snapshot different from displayed one?
if ( $this->is_current_snapshot_different( $post_tax_strings_with_ver ) ) {
$fields['reload'] = 1;
}
unset( $post_tax_strings_with_ver );
$charts = Ahrefs_Seo_Charts::maybe_return_charts();
if ( ! empty( $_POST['info'] ) && current_user_can( Ahrefs_Seo::CAP_ROLE_ADMIN ) ) {
$fields['_'] = Ahrefs_Seo::get_breadcrumbs_data(); // return breadcrumbs details for debugging purposes.
}
if ( count( $updated_ids ) || ! empty( $charts ) ) {
// this will return ajax response and terminate.
if ( ! empty( $charts ) ) {
$fields['charts'] = $charts;
}
$this->initialize_table()->ajax_response_updated( $updated_ids, $fields );
}
// Nothing to update: send default ajax response with success and maybe messages.
wp_send_json_success( $fields );
}
}
/**
* Is the current snapshot different from snapshot in parameters?
*
* @since 0.8.0
*
* @param array