386 lines
10 KiB
PHP
386 lines
10 KiB
PHP
<?php
|
|
namespace AIOSEO\Plugin\Common\QueryArgs;
|
|
|
|
// Exit if accessed directly.
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
use AIOSEO\Plugin\Common\Models;
|
|
|
|
/**
|
|
* Class to control Crawl Cleanup.
|
|
*
|
|
* @since 4.5.8
|
|
*/
|
|
class CrawlCleanup {
|
|
|
|
/**
|
|
* Construct method.
|
|
*
|
|
* @since 4.5.8
|
|
*/
|
|
public function __construct() {
|
|
// Add action to clear crawl cleanup logs.
|
|
add_action( 'aioseo_crawl_cleanup_clear_logs', [ $this, 'clearLogs' ] );
|
|
|
|
if ( aioseo()->options->searchAppearance->advanced->blockArgs->optimizeUtmParameters ) {
|
|
add_action( 'template_redirect', [ $this, 'maybeRedirectUtmParameters' ], 50 );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Redirects the UTM parameters to with (#) equivalent.
|
|
*
|
|
* @since 4.8.0
|
|
*
|
|
* @return void
|
|
*/
|
|
public function maybeRedirectUtmParameters() {
|
|
$requestUri = aioseo()->helpers->getRequestUrl();
|
|
if ( empty( $requestUri ) ) {
|
|
return;
|
|
}
|
|
|
|
$parsed = wp_parse_url( $requestUri );
|
|
if ( empty( $parsed['query'] ) ) {
|
|
return;
|
|
}
|
|
|
|
$args = [];
|
|
wp_parse_str( $parsed['query'], $args );
|
|
|
|
// Reset query to reconstruct without utm_ parameters.
|
|
$parsed['query'] = '';
|
|
|
|
// Initialize the fragment key if it's not set.
|
|
if ( ! isset( $parsed['fragment'] ) ) {
|
|
$parsed['fragment'] = '';
|
|
}
|
|
|
|
// Check if there are any utm_ parameters and redirect accordingly.
|
|
$utmFound = false;
|
|
foreach ( $args as $key => $value ) {
|
|
$keyValue = $key . '=' . $value;
|
|
if ( 0 === stripos( $key, 'utm_' ) ) {
|
|
$utmFound = true;
|
|
// Rebuild the URL with # instead of ?.
|
|
$parsed['fragment'] .= ! empty( $parsed['fragment'] ) ? '&' . $keyValue : $keyValue;
|
|
} else {
|
|
$parsed['query'] .= ! empty( $parsed['query'] ) ? '&' . $keyValue : $keyValue;
|
|
}
|
|
}
|
|
|
|
if ( $utmFound ) {
|
|
aioseo()->helpers->redirect( aioseo()->helpers->buildUrl( $parsed ), 301, 'Optimize UTM parameters' );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Schedule clearing of the logs.
|
|
* This runs when the logs retention option is changed.
|
|
*
|
|
* @since 4.5.8
|
|
*
|
|
* @return void
|
|
*/
|
|
public function scheduleClearingLogs() {
|
|
aioseo()->actionScheduler->unschedule( 'aioseo_crawl_cleanup_clear_logs' );
|
|
|
|
$optionLength = json_decode( aioseo()->options->searchAppearance->advanced->blockArgs->logsRetention )->value;
|
|
if (
|
|
aioseo()->options->searchAppearance->advanced->blockArgs->enable &&
|
|
'forever' !== $optionLength
|
|
) {
|
|
aioseo()->actionScheduler->scheduleRecurrent( 'aioseo_crawl_cleanup_clear_logs', 0, HOUR_IN_SECONDS );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clears the logs.
|
|
*
|
|
* @since 4.5.8
|
|
*
|
|
* @return void
|
|
*/
|
|
public function clearLogs() {
|
|
$optionLength = json_decode( aioseo()->options->searchAppearance->advanced->blockArgs->logsRetention )->value;
|
|
if ( 'forever' === $optionLength ) {
|
|
return;
|
|
}
|
|
|
|
$date = gmdate( 'Y-m-d H:i:s', strtotime( '-1 ' . $optionLength ) );
|
|
aioseo()->core->db
|
|
->delete( 'aioseo_crawl_cleanup_logs' )
|
|
->where( 'updated <', $date )
|
|
->run();
|
|
}
|
|
|
|
/**
|
|
* Fetch Crawl Cleanup Logs.
|
|
*
|
|
* @since 4.5.8
|
|
*
|
|
* @param \WP_REST_Request $request The REST Request.
|
|
* @return \WP_REST_Response The response.
|
|
*/
|
|
public static function fetchLogs( $request ) {
|
|
$filter = $request->get_param( 'filter' );
|
|
$body = $request->get_json_params();
|
|
$orderByUnblocked = ! empty( $body['orderBy'] ) ? sanitize_text_field( $body['orderBy'] ) : 'logs.updated';
|
|
$orderByBlocked = ! empty( $body['orderBy'] ) ? sanitize_text_field( $body['orderBy'] ) : 'b.id';
|
|
$orderDir = ! empty( $body['orderDir'] ) && ! empty( $body['orderBy'] ) ? strtoupper( sanitize_text_field( $body['orderDir'] ) ) : 'DESC';
|
|
$limit = ! empty( $body['limit'] ) ? intval( $body['limit'] ) : aioseo()->settings->tablePagination['queryArgs'];
|
|
$offset = ! empty( $body['offset'] ) ? intval( $body['offset'] ) : 0;
|
|
$searchTerm = ! empty( $body['searchTerm'] ) ? sanitize_text_field( $body['searchTerm'] ) : null;
|
|
$keyValueSeparator = Models\CrawlCleanupBlockedArg::getKeyValueSeparator();
|
|
$dateFormat = get_option( 'date_format' );
|
|
$timeFormat = get_option( 'time_format' );
|
|
$dateTimeFormat = $dateFormat . ' ' . $timeFormat;
|
|
|
|
// Query to get Arg Logs (unblocked) and the total.
|
|
$queryUnblocked = aioseo()->core->db
|
|
->start( 'aioseo_crawl_cleanup_logs as logs' )
|
|
->select( ' logs.id,
|
|
logs.slug,
|
|
logs.key,
|
|
logs.value,
|
|
logs.hits,
|
|
logs.updated' )
|
|
->leftJoin( 'aioseo_crawl_cleanup_blocked_args as blocked',
|
|
'blocked.key_value_hash = sha1(logs.key) OR
|
|
blocked.key_value_hash = sha1(concat(logs.key, "' . $keyValueSeparator . '", logs.value))' )
|
|
->limit( $limit, $offset );
|
|
|
|
if ( ! empty( $searchTerm ) ) {
|
|
// Apply escape to the search term.
|
|
$searchTerm = esc_sql( aioseo()->core->db->db->esc_like( $searchTerm ) );
|
|
$where = '
|
|
(
|
|
logs.slug LIKE \'%' . $searchTerm . '%\' OR
|
|
logs.slug LIKE \'%' . str_replace( '%20', '-', $searchTerm ) . '%\' OR
|
|
logs.slug LIKE \'%' . str_replace( '%20', '+', $searchTerm ) . '%\'
|
|
)
|
|
';
|
|
|
|
$queryUnblocked->whereRaw( $where );
|
|
}
|
|
|
|
$queryUnblocked->where( 'blocked.id', null );
|
|
$queryUnblocked->orderBy( "$orderByUnblocked $orderDir" );
|
|
|
|
$rowsUnblocked = $queryUnblocked->run( false )->result();
|
|
$totalUnblocked = $queryUnblocked->reset( [ 'limit' ] )->count();
|
|
|
|
// Test logs (unblocked) to see if have some regex block.
|
|
$regexMatches = [];
|
|
foreach ( $rowsUnblocked as $unblocked ) {
|
|
$blockedRegex = Models\CrawlCleanupBlockedArg::matchRegex( $unblocked->key, $unblocked->value );
|
|
if ( $blockedRegex->exists() ) {
|
|
$regexMatches[ $unblocked->id ] = $blockedRegex->regex;
|
|
}
|
|
}
|
|
|
|
// Query to get Blocked Args and the total.
|
|
$queryBlocked = aioseo()->core->db
|
|
->select( ' b.id,
|
|
b.key,
|
|
b.value,
|
|
b.regex,
|
|
b.hits,
|
|
b.updated' )
|
|
->start( 'aioseo_crawl_cleanup_blocked_args as b' )
|
|
->limit( $limit, $offset );
|
|
|
|
if ( ! empty( $searchTerm ) ) {
|
|
// Escape (esc_like) has already been applied.
|
|
$searchTerms = [
|
|
$searchTerm,
|
|
str_replace( '%20', '-', $searchTerm ),
|
|
str_replace( '%20', '+', $searchTerm )
|
|
];
|
|
|
|
$comparisons = [
|
|
'b.key',
|
|
'b.value',
|
|
'b.regex',
|
|
'CONCAT(b.key, \'' . $keyValueSeparator . '\', IF(b.value, b.value, \'*\'))'
|
|
];
|
|
|
|
$where = '';
|
|
foreach ( $comparisons as $comparison ) {
|
|
foreach ( $searchTerms as $s ) {
|
|
if ( ! empty( $where ) ) {
|
|
$where .= ' OR ';
|
|
}
|
|
|
|
$where .= aioseo()->core->db->db->prepare( " $comparison LIKE %s ", '%' . $s . '%' );
|
|
}
|
|
}
|
|
|
|
$where = "( $where )";
|
|
$queryBlocked->whereRaw( $where );
|
|
}
|
|
|
|
$queryBlocked->orderBy( "$orderByBlocked $orderDir" );
|
|
|
|
$rowsBlocked = $queryBlocked->run( false )->result();
|
|
$totalBlocked = $queryBlocked->reset( [ 'limit' ] )->count();
|
|
|
|
switch ( $filter ) {
|
|
case 'blocked':
|
|
$total = $totalBlocked;
|
|
$rows = $rowsBlocked;
|
|
break;
|
|
case 'unblocked':
|
|
$total = $totalUnblocked;
|
|
$rows = $rowsUnblocked;
|
|
break;
|
|
default:
|
|
return new \WP_REST_Response( [
|
|
'success' => false
|
|
], 404 );
|
|
}
|
|
|
|
foreach ( $rows as $row ) {
|
|
$row->updated = get_date_from_gmt( $row->updated, $dateTimeFormat );
|
|
}
|
|
|
|
return new \WP_REST_Response( [
|
|
'success' => true,
|
|
'rows' => $rows,
|
|
'regex' => $regexMatches,
|
|
'totals' => [
|
|
'total' => $total,
|
|
'pages' => 0 === $total ? 1 : ceil( $total / $limit ),
|
|
'page' => 0 === $offset ? 1 : ( $offset / $limit ) + 1
|
|
],
|
|
'filters' => [
|
|
[
|
|
'slug' => 'unblocked',
|
|
'name' => __( 'Unblocked', 'all-in-one-seo-pack' ),
|
|
'count' => $totalUnblocked,
|
|
'active' => 'unblocked' === $filter
|
|
],
|
|
[
|
|
'slug' => 'blocked',
|
|
'name' => __( 'Blocked', 'all-in-one-seo-pack' ),
|
|
'count' => $totalBlocked,
|
|
'active' => 'blocked' === $filter
|
|
]
|
|
]
|
|
], 200 );
|
|
}
|
|
|
|
/**
|
|
* Set block Arg Query.
|
|
*
|
|
* @since 4.5.8
|
|
*
|
|
* @param \WP_REST_Request $request The REST Request.
|
|
* @return \WP_REST_Response The response.
|
|
*/
|
|
public static function blockArg( $request ) {
|
|
$body = $request->get_json_params();
|
|
$return = true;
|
|
$listSaved = [];
|
|
$exists = [];
|
|
$error = 0;
|
|
|
|
try {
|
|
foreach ( $body as $block ) {
|
|
if ( $block ) {
|
|
$blocked = Models\CrawlCleanupBlockedArg::getByKeyValue( $block['key'], $block['value'] );
|
|
if ( ! $blocked->exists() && ! empty( $block['regex'] ) ) {
|
|
$blocked = Models\CrawlCleanupBlockedArg::getByRegex( $block['regex'] );
|
|
}
|
|
|
|
if ( $blocked->exists() ) {
|
|
$exists[] = [
|
|
'key' => $block['key'],
|
|
'value' => $block['value']
|
|
];
|
|
|
|
$keyValue = sha1( Models\CrawlCleanupBlockedArg::getKeyValueString( $block['key'], $block['value'] ) );
|
|
if ( ! in_array( $keyValue, $listSaved, true ) ) {
|
|
$return = false;
|
|
$error = 1;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
$blocked = new Models\CrawlCleanupBlockedArg();
|
|
$blocked->set( $block );
|
|
$blocked->save();
|
|
|
|
$listSaved[] = $blocked->key_value_hash;
|
|
}
|
|
}
|
|
} catch ( \Throwable $th ) {
|
|
$return = false;
|
|
}
|
|
|
|
return new \WP_REST_Response( [
|
|
'success' => $return,
|
|
'error' => $error,
|
|
'exists' => $exists
|
|
], 200 );
|
|
}
|
|
|
|
/**
|
|
* Delete Blocked Arg.
|
|
*
|
|
* @since 4.5.8
|
|
*
|
|
* @param \WP_REST_Request $request The REST Request.
|
|
* @return \WP_REST_Response The response.
|
|
*/
|
|
public static function deleteBlocked( $request ) {
|
|
$body = $request->get_json_params();
|
|
$return = true;
|
|
|
|
try {
|
|
foreach ( $body as $block ) {
|
|
$blocked = new Models\CrawlCleanupBlockedArg( $block );
|
|
if ( $blocked->exists() ) {
|
|
$blocked->delete();
|
|
}
|
|
}
|
|
} catch ( \Throwable $th ) {
|
|
$return = false;
|
|
}
|
|
|
|
return new \WP_REST_Response( [
|
|
'success' => $return
|
|
], 200 );
|
|
}
|
|
|
|
/**
|
|
* Delete Log.
|
|
*
|
|
* @since 4.5.8
|
|
*
|
|
* @param \WP_REST_Request $request The REST Request.
|
|
* @return \WP_REST_Response The response.
|
|
*/
|
|
public static function deleteLog( $request ) {
|
|
$body = $request->get_json_params();
|
|
$return = true;
|
|
|
|
try {
|
|
foreach ( $body as $block ) {
|
|
$log = new Models\CrawlCleanupLog( $block );
|
|
if ( $log->exists() ) {
|
|
$log->delete();
|
|
}
|
|
}
|
|
} catch ( \Throwable $th ) {
|
|
$return = false;
|
|
}
|
|
|
|
return new \WP_REST_Response( [
|
|
'success' => $return
|
|
], 200 );
|
|
}
|
|
} |