initial
This commit is contained in:
+121
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
namespace Vrts\Core\Utilities;
|
||||
|
||||
class Array_Helpers {
|
||||
/**
|
||||
* Parses the string into variables without the max_input_vars limitation.
|
||||
*
|
||||
* @param string $str String.
|
||||
*
|
||||
* @return array Parsed array.
|
||||
*/
|
||||
public static function parse_str( $str ) {
|
||||
if ( '' === $str ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = [];
|
||||
$pairs = explode( '&', $str );
|
||||
|
||||
foreach ( $pairs as $key => $pair ) {
|
||||
|
||||
// use the original parse_str() on each element.
|
||||
parse_str( $pair, $params );
|
||||
|
||||
$k = key( $params );
|
||||
|
||||
if ( ! isset( $result[ $k ] ) ) {
|
||||
$result += $params;
|
||||
} else {
|
||||
$result[ $k ] = self::array_merge_recursive( $result[ $k ], $params[ $k ] );
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge arrays without converting values with duplicate keys to arrays as array_merge_recursive does.
|
||||
*
|
||||
* As seen here http://php.net/manual/en/function.array-merge-recursive.php#92195
|
||||
*
|
||||
* @param array $array1 First array.
|
||||
* @param array $array2 Second array.
|
||||
*
|
||||
* @return array Merged array.
|
||||
*/
|
||||
public static function array_merge_recursive( array $array1, array $array2 ) {
|
||||
$merged = $array1;
|
||||
|
||||
foreach ( $array2 as $key => $value ) {
|
||||
|
||||
if ( is_array( $value ) && isset( $merged[ $key ] ) && is_array( $merged[ $key ] ) ) {
|
||||
$merged[ $key ] = self::array_merge_recursive( $merged[ $key ], $value );
|
||||
} elseif ( is_numeric( $key ) && isset( $merged[ $key ] ) ) {
|
||||
$merged[] = $value;
|
||||
} else {
|
||||
$merged[ $key ] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $merged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive wp_parse_args for multidimensional arrays.
|
||||
*
|
||||
* @see http://mekshq.com/recursive-wp-parse-args-wordpress-function/.
|
||||
*
|
||||
* @param array $args Value to merge with $defaults.
|
||||
* @param array $defaults Array that serves as the defaults.
|
||||
*
|
||||
* @return array Merged user defined values with defaults.
|
||||
*/
|
||||
public static function parse_args( $args, $defaults ) {
|
||||
$args = (array) $args;
|
||||
$defaults = (array) $defaults;
|
||||
$result = $defaults;
|
||||
|
||||
foreach ( $args as $k => $v ) {
|
||||
if ( is_array( $v ) && isset( $result[ $k ] ) ) {
|
||||
$result[ $k ] = self::parse_args( $v, $result[ $k ] );
|
||||
} else {
|
||||
$result[ $k ] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implode array keys with desired value.
|
||||
*
|
||||
* @param array $arr Array to implode.
|
||||
* @param string $value Checked for this value.
|
||||
* @param string|array $remove Array key or keys to remove before implode.
|
||||
* @param string $before Value to prepend to array keys.
|
||||
* @param string $after Value to append to array keys.
|
||||
*
|
||||
* @return string Imploded array keys with value.
|
||||
*/
|
||||
public static function implode_array_keys( $arr, $value, $remove = false, $before = '', $after = '' ) {
|
||||
if ( is_array( $remove ) ) {
|
||||
foreach ( $remove as $key ) {
|
||||
unset( $arr[ $key ] );
|
||||
}
|
||||
} elseif ( $remove ) {
|
||||
unset( $arr[ $remove ] );
|
||||
}
|
||||
|
||||
$new_array = [];
|
||||
|
||||
foreach ( $arr as $key => $item ) {
|
||||
if ( $arr[ $key ] === $value ) {
|
||||
$new_array[ $before . $key . $after ] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
return implode( ' ', array_keys( $new_array ) );
|
||||
}
|
||||
}
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
namespace Vrts\Core\Utilities;
|
||||
|
||||
/**
|
||||
* Async Request.
|
||||
*
|
||||
* @see https://github.com/deliciousbrains/wp-background-processing
|
||||
*/
|
||||
abstract class Async_Request {
|
||||
/**
|
||||
* Prefix.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $prefix;
|
||||
|
||||
/**
|
||||
* Action.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $action = 'async_request';
|
||||
|
||||
/**
|
||||
* Identifier.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $identifier;
|
||||
|
||||
/**
|
||||
* Data.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $data = [];
|
||||
|
||||
/**
|
||||
* Initiate new async request.
|
||||
*/
|
||||
public function __construct() {
|
||||
// if the prefix hasn't been set explicity use the theme identifier.
|
||||
if ( is_null( $this->prefix ) ) {
|
||||
$this->prefix = vrts()->get_plugin_identifier();
|
||||
}
|
||||
|
||||
// Uses unique prefix per blog so each blog has separate queue.
|
||||
$this->prefix = $this->prefix . '_' . get_current_blog_id();
|
||||
$this->identifier = $this->prefix . '_' . $this->action;
|
||||
|
||||
add_action( 'wp_ajax_' . $this->identifier, [ $this, 'maybe_handle' ] );
|
||||
add_action( 'wp_ajax_nopriv_' . $this->identifier, [ $this, 'maybe_handle' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set data used during the request.
|
||||
*
|
||||
* @param array $data Data.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function data( $data ) {
|
||||
$this->data = $data;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch the async request.
|
||||
*
|
||||
* @return array|WP_Error
|
||||
*/
|
||||
public function dispatch() {
|
||||
$url = add_query_arg( $this->get_query_args(), $this->get_query_url() );
|
||||
$args = $this->get_post_args();
|
||||
|
||||
return wp_remote_post( esc_url_raw( $url ), $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get query args.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_query_args() {
|
||||
if ( property_exists( $this, 'query_args' ) ) {
|
||||
return $this->query_args;
|
||||
}
|
||||
|
||||
$args = [
|
||||
'action' => $this->identifier,
|
||||
'nonce' => wp_create_nonce( $this->identifier ),
|
||||
];
|
||||
|
||||
/**
|
||||
* Filters the post arguments used during an async request.
|
||||
*
|
||||
* @param array $url
|
||||
*/
|
||||
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound -- Prefix is theme identifier and action name.
|
||||
return apply_filters( $this->identifier . '_query_args', $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get query URL.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_query_url() {
|
||||
if ( property_exists( $this, 'query_url' ) ) {
|
||||
return $this->query_url;
|
||||
}
|
||||
|
||||
$url = admin_url( 'admin-ajax.php' );
|
||||
|
||||
/**
|
||||
* Filters the post arguments used during an async request.
|
||||
*
|
||||
* @param string $url
|
||||
*/
|
||||
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound -- Prefix is theme identifier and action name.
|
||||
return apply_filters( $this->identifier . '_query_url', $url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get post args.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_post_args() {
|
||||
if ( property_exists( $this, 'post_args' ) ) {
|
||||
return $this->post_args;
|
||||
}
|
||||
|
||||
$args = [
|
||||
'timeout' => 0.01,
|
||||
'blocking' => false,
|
||||
'body' => $this->data,
|
||||
'cookies' => $_COOKIE,
|
||||
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Using default wp hook.
|
||||
'sslverify' => apply_filters( 'https_local_ssl_verify', false ),
|
||||
];
|
||||
|
||||
/**
|
||||
* Filters the post arguments used during an async request.
|
||||
*
|
||||
* @param array $args
|
||||
*/
|
||||
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound -- Prefix is theme identifier and action name.
|
||||
return apply_filters( $this->identifier . '_post_args', $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe handle.
|
||||
*
|
||||
* Check for correct nonce and pass to handler.
|
||||
*/
|
||||
public function maybe_handle() {
|
||||
// Don't lock up other requests while processing.
|
||||
session_write_close();
|
||||
|
||||
check_ajax_referer( $this->identifier, 'nonce' );
|
||||
|
||||
$this->handle();
|
||||
|
||||
wp_die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle.
|
||||
*
|
||||
* Override this method to perform any actions required
|
||||
* during the async request.
|
||||
*/
|
||||
abstract protected function handle();
|
||||
}
|
||||
+480
@@ -0,0 +1,480 @@
|
||||
<?php
|
||||
|
||||
namespace Vrts\Core\Utilities;
|
||||
|
||||
/**
|
||||
* Background Process.
|
||||
*
|
||||
* @see https://github.com/deliciousbrains/wp-background-processing
|
||||
*/
|
||||
abstract class Background_Process extends Async_Request {
|
||||
/**
|
||||
* Action name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $action = 'background_process';
|
||||
|
||||
/**
|
||||
* Start time of current process.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $start_time = 0;
|
||||
|
||||
/**
|
||||
* Cron_hook_identifier.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $cron_hook_identifier;
|
||||
|
||||
/**
|
||||
* Cron_interval_identifier.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $cron_interval_identifier;
|
||||
|
||||
/**
|
||||
* Initiate new background process.
|
||||
*/
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
|
||||
$this->cron_hook_identifier = $this->identifier . '_cron';
|
||||
$this->cron_interval_identifier = $this->identifier . '_cron_interval';
|
||||
|
||||
add_action( $this->cron_hook_identifier, [ $this, 'handle_cron_healthcheck' ] );
|
||||
// phpcs:ignore WordPress.WP.CronInterval -- Verified 5 min.
|
||||
add_filter( 'cron_schedules', [ $this, 'schedule_cron_healthcheck' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch.
|
||||
*/
|
||||
public function dispatch() {
|
||||
// Schedule the cron healthcheck.
|
||||
$this->schedule_event();
|
||||
|
||||
// Perform remote post.
|
||||
return parent::dispatch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Push to queue.
|
||||
*
|
||||
* @param mixed $data Data.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function push_to_queue( $data ) {
|
||||
$this->data[] = $data;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save queue.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function save() {
|
||||
$key = $this->generate_key();
|
||||
|
||||
if ( ! empty( $this->data ) ) {
|
||||
update_site_option( $key, $this->data );
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update queue.
|
||||
*
|
||||
* @param string $key Key.
|
||||
* @param array $data Data.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function update( $key, $data ) {
|
||||
if ( ! empty( $data ) ) {
|
||||
update_site_option( $key, $data );
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete queue.
|
||||
*
|
||||
* @param string $key Key.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function delete( $key ) {
|
||||
delete_site_option( $key );
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a unique key based on microtime. Queue items are
|
||||
* given a unique key so that they can be merged upon save.
|
||||
*
|
||||
* @param int $length Length.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function generate_key( $length = 64 ) {
|
||||
$unique = md5( microtime() . wp_rand() );
|
||||
$prepend = $this->identifier . '_batch_';
|
||||
|
||||
return substr( $prepend . $unique, 0, $length );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether data exists within the queue and that
|
||||
* the process is not already running.
|
||||
*/
|
||||
public function maybe_handle() {
|
||||
// Don't lock up other requests while processing.
|
||||
session_write_close();
|
||||
|
||||
if ( $this->is_process_running() ) {
|
||||
// Background process already running.
|
||||
wp_die();
|
||||
}
|
||||
|
||||
if ( $this->is_queue_empty() ) {
|
||||
// No data to process.
|
||||
wp_die();
|
||||
}
|
||||
|
||||
check_ajax_referer( $this->identifier, 'nonce' );
|
||||
|
||||
$this->handle();
|
||||
|
||||
wp_die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is queue empty.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function is_queue_empty() {
|
||||
global $wpdb;
|
||||
|
||||
$table = $wpdb->options;
|
||||
$column = 'option_name';
|
||||
|
||||
if ( is_multisite() ) {
|
||||
$table = $wpdb->sitemeta;
|
||||
$column = 'meta_key';
|
||||
}
|
||||
|
||||
$key = $wpdb->esc_like( $this->identifier . '_batch_' ) . '%';
|
||||
|
||||
// Interpolated variables $table and $column are OK to use like this.
|
||||
// @codingStandardsIgnoreStart.
|
||||
$count = $wpdb->get_var( $wpdb->prepare( "
|
||||
SELECT COUNT(*)
|
||||
FROM {$table}
|
||||
WHERE {$column} LIKE %s
|
||||
", $key ) );
|
||||
// @codingStandardsIgnoreEnd.
|
||||
|
||||
return ( $count > 0 ) ? false : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the current process is already running
|
||||
* in a background process.
|
||||
*/
|
||||
protected function is_process_running() {
|
||||
if ( get_site_transient( $this->identifier . '_process_lock' ) ) {
|
||||
// Process already running.
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock the process so that multiple instances can't run simultaneously.
|
||||
* Override if applicable, but the duration should be greater than that
|
||||
* defined in the time_exceeded() method.
|
||||
*/
|
||||
protected function lock_process() {
|
||||
// Set start time of current process.
|
||||
$this->start_time = time();
|
||||
|
||||
// 1 minute
|
||||
$lock_duration = ( property_exists( $this, 'queue_lock_time' ) ) ? $this->queue_lock_time : 60;
|
||||
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound -- Prefix is theme identifier and action name.
|
||||
$lock_duration = apply_filters( $this->identifier . '_queue_lock_time', $lock_duration );
|
||||
|
||||
set_site_transient( $this->identifier . '_process_lock', microtime(), $lock_duration );
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlock the process so that other instances can spawn.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
protected function unlock_process() {
|
||||
delete_site_transient( $this->identifier . '_process_lock' );
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get batch.
|
||||
*
|
||||
* @return stdClass Return the first batch from the queue
|
||||
*/
|
||||
protected function get_batch() {
|
||||
global $wpdb;
|
||||
|
||||
$table = $wpdb->options;
|
||||
$column = 'option_name';
|
||||
$key_column = 'option_id';
|
||||
$value_column = 'option_value';
|
||||
|
||||
if ( is_multisite() ) {
|
||||
$table = $wpdb->sitemeta;
|
||||
$column = 'meta_key';
|
||||
$key_column = 'meta_id';
|
||||
$value_column = 'meta_value';
|
||||
}
|
||||
|
||||
$key = $wpdb->esc_like( $this->identifier . '_batch_' ) . '%';
|
||||
|
||||
// Interpolated variables $table, $column and $key_column are OK to use like this.
|
||||
// @codingStandardsIgnoreStart.
|
||||
$query = $wpdb->get_row( $wpdb->prepare( "
|
||||
SELECT *
|
||||
FROM {$table}
|
||||
WHERE {$column} LIKE %s
|
||||
ORDER BY {$key_column} ASC
|
||||
LIMIT 1
|
||||
", $key ) );
|
||||
// @codingStandardsIgnoreEnd.
|
||||
|
||||
$batch = new \stdClass();
|
||||
$batch->key = $query->$column;
|
||||
$batch->data = maybe_unserialize( $query->$value_column );
|
||||
|
||||
return $batch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass each queue item to the task handler, while remaining
|
||||
* within server memory and time limit constraints.
|
||||
*/
|
||||
protected function handle() {
|
||||
$this->lock_process();
|
||||
|
||||
do {
|
||||
$batch = $this->get_batch();
|
||||
|
||||
foreach ( $batch->data as $key => $value ) {
|
||||
$task = $this->task( $value );
|
||||
|
||||
if ( false !== $task ) {
|
||||
$batch->data[ $key ] = $task;
|
||||
} else {
|
||||
unset( $batch->data[ $key ] );
|
||||
}
|
||||
|
||||
if ( $this->time_exceeded() || $this->memory_exceeded() ) {
|
||||
// Batch limits reached.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Update or delete current batch.
|
||||
if ( ! empty( $batch->data ) ) {
|
||||
$this->update( $batch->key, $batch->data );
|
||||
} else {
|
||||
$this->delete( $batch->key );
|
||||
}
|
||||
} while ( ! $this->time_exceeded() && ! $this->memory_exceeded() && ! $this->is_queue_empty() );
|
||||
|
||||
$this->unlock_process();
|
||||
|
||||
// Start next batch or complete process.
|
||||
if ( ! $this->is_queue_empty() ) {
|
||||
$this->dispatch();
|
||||
} else {
|
||||
$this->complete();
|
||||
}
|
||||
|
||||
wp_die();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the batch process never exceeds 90%
|
||||
* of the maximum WordPress memory.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function memory_exceeded() {
|
||||
$memory_limit = $this->get_memory_limit() * 0.9;
|
||||
// 90% of max memory.
|
||||
$current_memory = memory_get_usage( true );
|
||||
$return = false;
|
||||
|
||||
if ( $current_memory >= $memory_limit ) {
|
||||
$return = true;
|
||||
}
|
||||
|
||||
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound -- Prefix is theme identifier and action name.
|
||||
return apply_filters( $this->identifier . '_memory_exceeded', $return );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get memory limit.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function get_memory_limit() {
|
||||
if ( function_exists( 'ini_get' ) ) {
|
||||
$memory_limit = ini_get( 'memory_limit' );
|
||||
} else {
|
||||
// Sensible default.
|
||||
$memory_limit = '128M';
|
||||
}
|
||||
|
||||
if ( ! $memory_limit || - 1 === intval( $memory_limit ) ) {
|
||||
// Unlimited, set to 32GB.
|
||||
$memory_limit = '32000M';
|
||||
}
|
||||
|
||||
return wp_convert_hr_to_bytes( $memory_limit );
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the batch never exceeds a sensible time limit.
|
||||
* A timeout limit of 30s is common on shared hosting.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function time_exceeded() {
|
||||
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound -- Prefix is theme identifier and action name.
|
||||
$finish = $this->start_time + apply_filters( $this->identifier . '_default_time_limit', 20 );
|
||||
// 20 seconds.
|
||||
$return = false;
|
||||
|
||||
if ( time() >= $finish ) {
|
||||
$return = true;
|
||||
}
|
||||
|
||||
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound -- Prefix is theme identifier and action name.
|
||||
return apply_filters( $this->identifier . '_time_exceeded', $return );
|
||||
}
|
||||
|
||||
/**
|
||||
* Override if applicable, but ensure that the below actions are
|
||||
* performed, or, call parent::complete().
|
||||
*/
|
||||
protected function complete() {
|
||||
// Unschedule the cron healthcheck.
|
||||
$this->clear_scheduled_event();
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule cron healthcheck.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @param mixed $schedules Schedules.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function schedule_cron_healthcheck( $schedules ) {
|
||||
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound -- Prefix is theme identifier and action name.
|
||||
$interval = apply_filters( $this->identifier . '_cron_interval', 5 );
|
||||
|
||||
if ( property_exists( $this, 'cron_interval' ) ) {
|
||||
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound -- Prefix is theme identifier and action name.
|
||||
$interval = apply_filters( $this->identifier . '_cron_interval', $this->cron_interval );
|
||||
}
|
||||
|
||||
// Adds every 5 minutes to the existing schedules.
|
||||
$schedules[ $this->identifier . '_cron_interval' ] = [
|
||||
'interval' => MINUTE_IN_SECONDS * $interval,
|
||||
/* translators: Cron Interval in minutes. */
|
||||
'display' => sprintf( esc_html_x( 'Every %d Minutes', 'cron interval description', 'colorio' ), $interval ),
|
||||
];
|
||||
|
||||
return $schedules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restart the background process if not already running
|
||||
* and data exists in the queue.
|
||||
*/
|
||||
public function handle_cron_healthcheck() {
|
||||
if ( $this->is_process_running() ) {
|
||||
// Background process already running.
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( $this->is_queue_empty() ) {
|
||||
// No data to process.
|
||||
$this->clear_scheduled_event();
|
||||
exit;
|
||||
}
|
||||
|
||||
$this->handle();
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule event.
|
||||
*/
|
||||
protected function schedule_event() {
|
||||
if ( ! wp_next_scheduled( $this->cron_hook_identifier ) ) {
|
||||
wp_schedule_event( time(), $this->cron_interval_identifier, $this->cron_hook_identifier );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear scheduled event.
|
||||
*/
|
||||
protected function clear_scheduled_event() {
|
||||
$timestamp = wp_next_scheduled( $this->cron_hook_identifier );
|
||||
|
||||
if ( $timestamp ) {
|
||||
wp_unschedule_event( $timestamp, $this->cron_hook_identifier );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop processing queue items, clear cronjob and delete batch.
|
||||
*/
|
||||
public function cancel_process() {
|
||||
if ( ! $this->is_queue_empty() ) {
|
||||
$batch = $this->get_batch();
|
||||
|
||||
$this->delete( $batch->key );
|
||||
|
||||
wp_clear_scheduled_hook( $this->cron_hook_identifier );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to perform any actions required on each
|
||||
* queue item. Return the modified item for further processing
|
||||
* in the next pass through. Or, return false to remove the
|
||||
* item from the queue.
|
||||
*
|
||||
* @param mixed $item Queue item to iterate over.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function task( $item );
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace Vrts\Core\Utilities;
|
||||
|
||||
class Color_Helpers {
|
||||
/**
|
||||
* Converts a color from hex to rgba.
|
||||
*
|
||||
* @param string $color The color to convert.
|
||||
* @param int $opacity The color opacity.
|
||||
* @param string $return_type The return format, string or array.
|
||||
*
|
||||
* @return string|array
|
||||
*/
|
||||
public static function hex_to_rgba( $color, $opacity = 1, $return_type = 'array' ) {
|
||||
$color = str_replace( '#', '', $color );
|
||||
|
||||
if ( strlen( $color ) === 3 ) {
|
||||
$r = hexdec( substr( $color, 0, 1 ) . substr( $color, 0, 1 ) );
|
||||
$g = hexdec( substr( $color, 1, 1 ) . substr( $color, 1, 1 ) );
|
||||
$b = hexdec( substr( $color, 2, 1 ) . substr( $color, 2, 1 ) );
|
||||
} else {
|
||||
$r = hexdec( substr( $color, 0, 2 ) );
|
||||
$g = hexdec( substr( $color, 2, 2 ) );
|
||||
$b = hexdec( substr( $color, 4, 2 ) );
|
||||
}
|
||||
|
||||
$rgba = [ $r, $g, $b, $opacity ];
|
||||
|
||||
if ( 'string' === $return_type ) {
|
||||
return 'rgba(' . implode( ',', $rgba ) . ')';
|
||||
}
|
||||
|
||||
return $rgba;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a color from rgba to hsla.
|
||||
*
|
||||
* @param string $color The color to convert.
|
||||
* @param int $opacity The color opacity.
|
||||
* @param string $return_type The return format, string or array.
|
||||
*
|
||||
* @return string|array
|
||||
*/
|
||||
public static function rgba_to_hsla( $color, $opacity = 1, $return_type = 'array' ) {
|
||||
$r = $color[0] / 255;
|
||||
$g = $color[1] / 255;
|
||||
$b = $color[2] / 255;
|
||||
|
||||
$min = min( $r, min( $g, $b ) );
|
||||
$max = max( $r, max( $g, $b ) );
|
||||
$delta = $max - $min;
|
||||
|
||||
$h = 0;
|
||||
$s = 0;
|
||||
$l = 0;
|
||||
|
||||
if ( $delta > 0 ) {
|
||||
if ( $max === $r ) {
|
||||
$h = fmod( ( ( $g - $b ) / $delta ), 6 );
|
||||
} elseif ( $max === $g ) {
|
||||
$h = ( $b - $r ) / $delta + 2;
|
||||
} else {
|
||||
$h = ( $r - $g ) / $delta + 4;
|
||||
}
|
||||
}
|
||||
|
||||
$h = round( $h * 60 );
|
||||
|
||||
if ( $h < 0 ) {
|
||||
$h += 360;
|
||||
}
|
||||
|
||||
$l = ( $min + $max ) / 2;
|
||||
$s = 0 === $delta ? 0 : $delta / ( 1 - abs( 2 * $l - 1 ) );
|
||||
|
||||
$s = round( $s * 100, 1 );
|
||||
$l = round( $l * 100, 1 );
|
||||
|
||||
$hsla = [ $h, "$s%", "$l%", $opacity ];
|
||||
|
||||
if ( 'string' === $return_type ) {
|
||||
return 'hsla(' . implode( ',', $hsla ) . ')';
|
||||
}
|
||||
|
||||
return $hsla;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a color from hex to hsla.
|
||||
*
|
||||
* @param string $color The color to convert.
|
||||
* @param int $opacity The color opacity.
|
||||
* @param string $return_type The return format, string or array.
|
||||
*
|
||||
* @return string|array
|
||||
*/
|
||||
public static function hex_to_hsla( $color, $opacity = 1, $return_type = 'array' ) {
|
||||
$rgba = self::hex_to_rgba( $color, $opacity );
|
||||
return self::rgba_to_hsla( $rgba, $opacity, $return_type );
|
||||
}
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace Vrts\Core\Utilities;
|
||||
|
||||
use DateTime;
|
||||
use DateTimeZone;
|
||||
|
||||
class Date_Time_Helpers {
|
||||
/**
|
||||
* Get the date and time WordPress native formatted.
|
||||
*
|
||||
* @param mixed $date a DateTime string.
|
||||
*
|
||||
* @return string Formatted date and time.
|
||||
*/
|
||||
public static function get_formatted_date_time( $date = null ) {
|
||||
if ( null === $date ) {
|
||||
return null;
|
||||
}
|
||||
$date = self::date_from_gmt( $date );
|
||||
|
||||
$formatted_date = sprintf(
|
||||
/* translators: 1: Date, 2: Time. */
|
||||
esc_html_x( '%1$s at %2$s', 'date at time', 'visual-regression-tests' ),
|
||||
/* translators: Date format. See https://www.php.net/manual/datetime.format.php */
|
||||
date_format( $date, esc_html_x( 'Y/m/d', 'date format', 'visual-regression-tests' ) ),
|
||||
/* translators: Time format. See https://www.php.net/manual/datetime.format.php */
|
||||
date_format( $date, esc_html_x( 'g:i a', 'time format', 'visual-regression-tests' ) )
|
||||
);
|
||||
return $formatted_date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the date and time WordPress native formatted.
|
||||
*
|
||||
* @param mixed $date a DateTime string.
|
||||
*
|
||||
* @return string Formatted date and time.
|
||||
*/
|
||||
public static function get_formatted_relative_date_time( $date = null ) {
|
||||
if ( null === $date ) {
|
||||
return null;
|
||||
}
|
||||
$date = self::date_from_gmt( $date );
|
||||
|
||||
$formatted_date = sprintf(
|
||||
/* translators: 1: Date, 2: Time. */
|
||||
esc_html_x( '%1$s at %2$s', 'date at time', 'visual-regression-tests' ),
|
||||
/* translators: Date format. See https://www.php.net/manual/datetime.format.php */
|
||||
static::extract_date( $date ),
|
||||
/* translators: Time format. See https://www.php.net/manual/datetime.format.php */
|
||||
static::extract_time( $date )
|
||||
);
|
||||
return '<time datetime="' . date_format( $date, 'c' ) . '">' . $formatted_date . '</time>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the date WordPress native formatted.
|
||||
*
|
||||
* @param mixed $date a DateTime string.
|
||||
*
|
||||
* @return DateTime DateTime instance.
|
||||
*/
|
||||
private static function date_from_gmt( $date ) {
|
||||
$date = date_create( $date, new DateTimeZone( 'UTC' ) );
|
||||
$date->setTimezone( wp_timezone() );
|
||||
|
||||
return $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the date from a DateTime object.
|
||||
*
|
||||
* @param DateTime $input_date a DateTime object.
|
||||
*
|
||||
* @return string Formatted date.
|
||||
*/
|
||||
private static function extract_date( $input_date ) {
|
||||
// Get today's date at midnight in the site's timezone.
|
||||
$today = new DateTime( 'today', wp_timezone() );
|
||||
|
||||
// Clone input date and set time to midnight.
|
||||
$comparison_date = clone $input_date;
|
||||
$comparison_date->setTime( 0, 0, 0 );
|
||||
|
||||
// Calculate the difference in days.
|
||||
$difference_in_seconds = $comparison_date->getTimestamp() - $today->getTimestamp();
|
||||
$difference_in_days = (int) round( $difference_in_seconds / ( 60 * 60 * 24 ) );
|
||||
|
||||
// Determine if the date is today, tomorrow, or yesterday.
|
||||
if ( 0 === $difference_in_days ) {
|
||||
return __( 'Today', 'visual-regression-testing' );
|
||||
} elseif ( 1 === $difference_in_days ) {
|
||||
return __( 'Tomorrow', 'visual-regression-testing' );
|
||||
} elseif ( -1 === $difference_in_days ) {
|
||||
return __( 'Yesterday', 'visual-regression-testing' );
|
||||
}
|
||||
return $input_date->format( 'D, Y/m/d' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the time from a DateTime object.
|
||||
*
|
||||
* @param DateTime $input_date a DateTime object.
|
||||
*
|
||||
* @return string Formatted time.
|
||||
*/
|
||||
private static function extract_time( $input_date ) {
|
||||
return $input_date->setTimezone( wp_timezone() )->format( 'g:i a' );
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace Vrts\Core\Utilities;
|
||||
|
||||
class Image_Helpers {
|
||||
|
||||
/**
|
||||
* Get the image height and width string.
|
||||
*
|
||||
* @param object $alert The alert object.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function alert_image_hwstring( $alert ) {
|
||||
$meta = maybe_unserialize( $alert->meta );
|
||||
$width = $meta['width'] ?? 1265;
|
||||
$height = $meta['height'] ?? 1800;
|
||||
return image_hwstring( $width, $height );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the image aspect ratio string.
|
||||
*
|
||||
* @param object $alert The alert object.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function alert_image_aspect_ratio( $alert ) {
|
||||
$meta = maybe_unserialize( $alert->meta );
|
||||
|
||||
if ( isset( $meta['width'], $meta['height'] ) ) {
|
||||
return round( $meta['width'] / $meta['height'], 2 );
|
||||
}
|
||||
|
||||
return 1.25;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get screenshot URL.
|
||||
*
|
||||
* @param object $item The alert or test object.
|
||||
* @param string $type Image type - base, target, comparison.
|
||||
* @param string $size The size of the image.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_screenshot_url( $item, $type, $size = 'full' ) {
|
||||
$property = "{$type}_screenshot_url";
|
||||
|
||||
if ( ! property_exists( $item, $property ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$url = 'preview' === $size ? maybe_unserialize( $item->meta )['preview_url'] ?? $item->$property : $item->$property;
|
||||
return self::get_cloudfront_url( $url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cloudfront URL.
|
||||
*
|
||||
* @param string $url The URL.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_cloudfront_url( $url ) {
|
||||
return str_replace( 'https://screenshotter-dev.s3.eu-central-1.amazonaws.com/', 'https://images.vrts.app/', $url );
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace Vrts\Core\Utilities;
|
||||
|
||||
class Sanitization {
|
||||
/**
|
||||
* Checkbox sanitization callback.
|
||||
*
|
||||
* @param bool $checked Whether the checkbox is checked.
|
||||
*
|
||||
* @return bool Whether the checkbox is checked.
|
||||
*/
|
||||
public static function sanitize_checkbox( $checked ) {
|
||||
return $checked ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTML sanitization callback.
|
||||
*
|
||||
* @param string $html HTML to sanitize.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function sanitize_html( $html ) {
|
||||
return wp_filter_post_kses( $html );
|
||||
}
|
||||
|
||||
/**
|
||||
* No-HTML sanitization callback.
|
||||
*
|
||||
* @param string $nohtml The no-HTML content to sanitize.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function sanitize_nohtml( $nohtml ) {
|
||||
return wp_filter_nohtml_kses( $nohtml );
|
||||
}
|
||||
|
||||
/**
|
||||
* Number sanitization callback.
|
||||
*
|
||||
* @param int $number Number to sanitize.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function sanitize_number_absint( $number ) {
|
||||
// Ensure $number is an absolute integer (whole number, zero or greater).
|
||||
return absint( $number );
|
||||
}
|
||||
|
||||
/**
|
||||
* URL sanitization callback.
|
||||
*
|
||||
* @param string $url URL to sanitize.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function sanitize_url( $url ) {
|
||||
return esc_url_raw( $url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiple emails sanitization callback.
|
||||
*
|
||||
* @param string $emails Comma-separated list of emails.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function sanitize_multiple_emails( $emails ) {
|
||||
$emails = array_filter( array_map( 'sanitize_email', explode( ',', $emails ) ) );
|
||||
return implode( ', ', $emails );
|
||||
}
|
||||
}
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
namespace Vrts\Core\Utilities;
|
||||
|
||||
class String_Helpers {
|
||||
/**
|
||||
* Converts a string from camel case to kebap case.
|
||||
*
|
||||
* @param string $str The string to convert.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function camel_case_to_kebap( $str ) {
|
||||
return strtolower( preg_replace( '/([a-zA-Z])(?=[A-Z])/', '$1-', $str ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips all HTML tags including script and style,
|
||||
* and trims text to a certain number of words.
|
||||
*
|
||||
* @param string $str The string to trim and strip.
|
||||
* @param int $length The string length to return.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function trim_strip( $str = '', $length = 25 ) {
|
||||
return wp_trim_words( wp_strip_all_tags( $str ), $length, '…' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits a camel case string.
|
||||
*
|
||||
* @param string $str The string to split.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function split_camel_case( $str ) {
|
||||
$a = preg_split(
|
||||
'/(^[^A-Z]+|[A-Z][^A-Z]+)/',
|
||||
$str,
|
||||
-1,
|
||||
// no limit for replacement count.
|
||||
PREG_SPLIT_NO_EMPTY
|
||||
// don't return empty elements.
|
||||
| PREG_SPLIT_DELIM_CAPTURE
|
||||
// don't strip anything from output array.
|
||||
);
|
||||
return implode( ' ', $a );
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a string from kebap case to camel case.
|
||||
*
|
||||
* @param string $str The string to convert.
|
||||
* @param boolean $capitalize_first_character Sets if the first character should be capitalized.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function kebap_case_to_camel_case( $str, $capitalize_first_character = false ) {
|
||||
$str = str_replace( ' ', '', ucwords( str_replace( '-', ' ', $str ) ) );
|
||||
if ( false === $capitalize_first_character ) {
|
||||
$str[0] = strtolower( $str[0] );
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a prefix from a string.
|
||||
*
|
||||
* @param string $prefix The prefix to be removed.
|
||||
* @param string $str The string to manipulate.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function remove_prefix( $prefix, $str ) {
|
||||
if ( self::starts_with( $prefix, $str ) ) {
|
||||
return substr( $str, strlen( $prefix ) );
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a string starts with a certain string.
|
||||
*
|
||||
* @param string $search The string to search for.
|
||||
* @param string $str The string to look into.
|
||||
*
|
||||
* @return boolean Returns true if the subject string starts with the search string.
|
||||
*/
|
||||
public static function starts_with( $search, $str ) {
|
||||
$starts_with = substr( $str, 0, strlen( $search ) );
|
||||
return $search === $starts_with;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a string ends with a certain string.
|
||||
*
|
||||
* @param string $search The string to search for.
|
||||
* @param string $str The string to look into.
|
||||
*
|
||||
* @return boolean Returns true if the subject string ends with the search string.
|
||||
*/
|
||||
public static function ends_with( $search, $str ) {
|
||||
$search_length = strlen( $search );
|
||||
$str_length = strlen( $str );
|
||||
if ( $search_length > $str_length ) {
|
||||
return false;
|
||||
}
|
||||
return 0 === substr_compare( $str, $search, $str_length - $search_length, $search_length );
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove white space in string.
|
||||
*
|
||||
* @param string $str The string to look into.
|
||||
*
|
||||
* @return string String without whitespace.
|
||||
*/
|
||||
public function remove_white_space( $str ) {
|
||||
$str = str_replace( "\t", ' ', $str );
|
||||
$str = str_replace( "\n", '', $str );
|
||||
$str = str_replace( "\r", '', $str );
|
||||
|
||||
while ( stristr( $str, ' ' ) ) {
|
||||
$str = str_replace( ' ', '', $str );
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format lines.
|
||||
*
|
||||
* @param array $lines Lines to format.
|
||||
* @param int $tabs Number of tabs for the offest.
|
||||
*
|
||||
* @return string Formated lines.
|
||||
*/
|
||||
public static function format_lines( $lines, $tabs = 1 ) {
|
||||
$line_tabs = str_repeat( "\t", $tabs );
|
||||
$end_tabs = str_repeat( "\t", $tabs - 1 );
|
||||
|
||||
$lines = array_map( function ( $line ) use ( $line_tabs ) {
|
||||
return "\n{$line_tabs}{$line}";
|
||||
}, $lines);
|
||||
|
||||
return implode( '', $lines ) . "\n{$end_tabs}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate a string.
|
||||
*
|
||||
* @param string $str The string to truncate.
|
||||
* @param int $length The length to truncate to.
|
||||
* @param string $append The string to append.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function truncate( $str, $length = 100, $append = '...' ) {
|
||||
$str = trim( $str );
|
||||
return ( strlen( $str ) > $length ) ? substr( $str, 0, $length - strlen( $append ) ) . $append : $str;
|
||||
}
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
namespace Vrts\Core\Utilities;
|
||||
|
||||
use Vrts\Models\Alert;
|
||||
use Vrts\Models\Test_Run;
|
||||
|
||||
class Url_Helpers {
|
||||
/**
|
||||
* Get the relative permalink of a post.
|
||||
*
|
||||
* @param int $post_id Post ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_relative_permalink( $post_id ) {
|
||||
$permalink = get_permalink( $post_id );
|
||||
return self::make_relative( $permalink );
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a permalink relative.
|
||||
*
|
||||
* @param int $permalink A permalink.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function make_relative( $permalink ) {
|
||||
$home_url = home_url();
|
||||
|
||||
if ( 0 === strpos( $permalink, $home_url ) ) {
|
||||
$permalink = str_replace( $home_url, '', $permalink );
|
||||
}
|
||||
|
||||
return $permalink;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the page URL.
|
||||
*
|
||||
* @param string $page Page.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_page_url( $page ) {
|
||||
$page = 'tests' === $page ? 'vrts' : 'vrts-' . $page;
|
||||
return admin_url( 'admin.php?page=' . $page );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the alert page URL.
|
||||
*
|
||||
* @param int|Alert $alert_id Alert ID.
|
||||
* @param int $test_run_id Test run ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_alert_page( $alert_id, $test_run_id = null ) {
|
||||
if ( is_null( $test_run_id ) ) {
|
||||
$alert = Alert::get_item( $alert_id );
|
||||
$test_run_id = $alert->test_run_id;
|
||||
}
|
||||
return add_query_arg( [
|
||||
'page' => 'vrts-runs',
|
||||
'run_id' => $test_run_id,
|
||||
'alert_id' => $alert_id,
|
||||
], admin_url( 'admin.php' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the alerts page URL.
|
||||
*
|
||||
* @param int|Test_Run $test_run Test run.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_alerts_page( $test_run ) {
|
||||
$test_run_id = is_object( $test_run ) ? $test_run->id : $test_run;
|
||||
return add_query_arg( [
|
||||
'page' => 'vrts-runs',
|
||||
'run_id' => $test_run_id,
|
||||
], admin_url( 'admin.php' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the run manual test page URL.
|
||||
*
|
||||
* @param int $test_id Test id.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_run_manual_test_url( $test_id ) {
|
||||
return add_query_arg( [
|
||||
'page' => 'vrts',
|
||||
'action' => 'run-manual-test',
|
||||
'test_id' => $test_id,
|
||||
], admin_url( 'admin.php' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the disable testing test page URL.
|
||||
*
|
||||
* @param int $test_id Test id.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_disable_testing_url( $test_id ) {
|
||||
return add_query_arg( [
|
||||
'page' => 'vrts',
|
||||
'action' => 'disable-testing',
|
||||
'test_id' => $test_id,
|
||||
], admin_url( 'admin.php' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the test run page URL.
|
||||
*
|
||||
* @param int $test_run_id Test run id.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_test_run_page( $test_run_id ) {
|
||||
if ( is_object( $test_run_id ) ) {
|
||||
$test_run_id = $test_run_id->id;
|
||||
}
|
||||
|
||||
return add_query_arg( [
|
||||
'page' => 'vrts-runs',
|
||||
'run_id' => $test_run_id,
|
||||
], admin_url( 'admin.php' ) );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user