This commit is contained in:
2026-07-02 15:54:39 -06:00
commit 9883323161
17470 changed files with 4470592 additions and 0 deletions
@@ -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 ) );
}
}
@@ -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();
}
@@ -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 );
}
@@ -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 );
}
}
@@ -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' );
}
}
@@ -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 );
}
}
@@ -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 );
}
}
@@ -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, '&hellip;' );
}
/**
* 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;
}
}
@@ -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' ) );
}
}