initial
This commit is contained in:
@@ -0,0 +1,388 @@
|
||||
<?php
|
||||
|
||||
namespace Vrts\Core;
|
||||
|
||||
use Exception;
|
||||
use WP_Filesystem_Direct;
|
||||
use Vrts\Core\Settings\Manager;
|
||||
use Vrts\Core\Traits\Singleton;
|
||||
use Vrts\Core\Traits\Macroable;
|
||||
|
||||
/**
|
||||
* Main class responsible for defining all plugin functionalities.
|
||||
*
|
||||
* It has methods that are definied in other classes and made available here using the Macroable.
|
||||
*
|
||||
* @method string thisMenuHasMacro() Added in Vrts\Features\Menu
|
||||
*/
|
||||
class Plugin {
|
||||
use Singleton;
|
||||
use Macroable;
|
||||
|
||||
/**
|
||||
* Plugin file.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $plugin_file;
|
||||
|
||||
/**
|
||||
* Plugin identifier.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $plugin_identifier;
|
||||
|
||||
/**
|
||||
* Modules and objects instances list
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $factory = [];
|
||||
|
||||
/**
|
||||
* Load plugin functions and features.
|
||||
*
|
||||
* @param string $plugin_identifier Plugin identifier.
|
||||
* @param array $features Plugin features.
|
||||
*/
|
||||
public function setup( $plugin_identifier, $features ) {
|
||||
$this->plugin_identifier = $plugin_identifier;
|
||||
$this->plugin_file = VRTS_PLUGIN_FILE;
|
||||
|
||||
$this->features( $features );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Plugin identifier.
|
||||
*
|
||||
* @return string Plugin identifier.
|
||||
*/
|
||||
public function get_plugin_identifier() {
|
||||
return str_replace( '-', '_', $this->plugin_identifier );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plugin url.
|
||||
*
|
||||
* @param string $file File to return the url for in the plugin directory.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_plugin_url( $file = '' ) {
|
||||
$file = ltrim( $file, '/' );
|
||||
|
||||
if ( empty( $file ) ) {
|
||||
$url = plugin_dir_url( $this->plugin_file );
|
||||
} else {
|
||||
$url = plugin_dir_url( $this->plugin_file ) . $file;
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plugin path.
|
||||
*
|
||||
* @param string $file File to return the path for in the plugin directory.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_plugin_path( $file = '' ) {
|
||||
$file = ltrim( $file, '/' );
|
||||
|
||||
if ( empty( $file ) ) {
|
||||
$path = plugin_dir_path( $this->plugin_file );
|
||||
} else {
|
||||
$path = plugin_dir_path( $this->plugin_file ) . $file;
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the plugin file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_plugin_file() {
|
||||
return $this->plugin_file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto include files from defined directories.
|
||||
*
|
||||
* @param array $directories Directories to auto include files from.
|
||||
*/
|
||||
protected function includes( $directories ) {
|
||||
foreach ( $directories as $directory ) {
|
||||
foreach ( glob( $this->get_plugin_path( "{$directory}/*.php" ) ) as $filename ) {
|
||||
require_once $filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load plugin features.
|
||||
*
|
||||
* @param array $features Plugin features.
|
||||
*/
|
||||
protected function features( $features ) {
|
||||
foreach ( $features as $namespace => $directory ) {
|
||||
foreach ( glob( $this->get_plugin_path( "{$directory}/class-*.php" ) ) as $filename ) {
|
||||
$dir = $this->get_plugin_path( $directory );
|
||||
$filename = str_replace( $dir, '', $filename );
|
||||
$class_name = str_replace( [ '/class-', '.php' ], '', $filename );
|
||||
$class_name = ucwords( $class_name, '-' );
|
||||
$class_name = str_replace( '-', '_', $class_name );
|
||||
$class_name = $namespace . $class_name;
|
||||
new $class_name();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create objects.
|
||||
*
|
||||
* @param string $key Array key that the object will be accessible.
|
||||
* @param object $instance object that needs to created.
|
||||
*
|
||||
* @return object Created object.
|
||||
*/
|
||||
protected function factory( $key, $instance ) {
|
||||
if ( ! isset( $this->factory[ $key ] ) ) {
|
||||
$this->factory[ $key ] = $instance;
|
||||
}
|
||||
|
||||
return $this->factory[ $key ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Settings Manager.
|
||||
*
|
||||
* @return Manager Settings Manager.
|
||||
*/
|
||||
public function settings() {
|
||||
return $this->factory( 'settings', new Manager() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plugin informations.
|
||||
*
|
||||
* @param string $info What information do we want.
|
||||
*
|
||||
* @return string Desired plugin information.
|
||||
*/
|
||||
public function get_plugin_info( $info ) {
|
||||
if ( ! function_exists( 'get_plugin_data' ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||
}
|
||||
$plugin = get_plugin_data( $this->plugin_file );
|
||||
|
||||
$infos = [
|
||||
'name' => 'Name',
|
||||
'version' => 'Version',
|
||||
'uri' => 'PluginURI',
|
||||
'author' => 'Author',
|
||||
'author_uri' => 'AuthorURI',
|
||||
'description' => 'Description',
|
||||
'requires_wp' => 'RequiresWP',
|
||||
'requires_php' => 'RequiresPHP',
|
||||
'text_domain' => 'TextDomain',
|
||||
'domain_path' => 'DomainPath',
|
||||
];
|
||||
|
||||
return isset( $infos[ $info ] ) ? $plugin[ $infos[ $info ] ] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a component with passing arguments.
|
||||
*
|
||||
* Makes it easy for a plugin to reuse sections of code.
|
||||
*
|
||||
* @param string $name The component name.
|
||||
* @param array $data Pass data with the component load.
|
||||
*
|
||||
* @return string Component markup
|
||||
*
|
||||
* @throws Exception If there is no file found.
|
||||
*/
|
||||
public function get_component( $name, $data = [] ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed -- $data is used by included template files.
|
||||
ob_start();
|
||||
|
||||
if ( file_exists( $this->get_plugin_path( "components/{$name}/index.php" ) ) ) {
|
||||
include $this->get_plugin_path( "components/{$name}/index.php" );
|
||||
} else {
|
||||
throw new Exception( esc_html( "Components file 'components/{$name}/index.php' cannot be located" ) );
|
||||
}
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a component with passing arguments.
|
||||
*
|
||||
* Makes it easy for a plugin to reuse sections of code.
|
||||
*
|
||||
* @param string $name The component name.
|
||||
* @param array $data Pass data with the component load.
|
||||
*
|
||||
* @throws Exception If there is no file found.
|
||||
*/
|
||||
public function component( $name, $data = [] ) {
|
||||
$safe_escaped_component = $this->get_component( $name, $data );
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- It's escaped.
|
||||
echo $safe_escaped_component;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an icon.
|
||||
*
|
||||
* @param string $icon The icon name.
|
||||
* @param bool $escape If true it will escape the icon.
|
||||
*
|
||||
* @return string Icon.
|
||||
*/
|
||||
public function get_icon( $icon, $escape = true ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php';
|
||||
require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php';
|
||||
|
||||
$filesystem = new WP_Filesystem_Direct( null );
|
||||
$icon = $filesystem->get_contents( $this->get_plugin_path( "assets/icons/{$icon}.svg" ) );
|
||||
|
||||
if ( $escape ) {
|
||||
return wp_kses( $icon, $this->wp_kses_svg() );
|
||||
}
|
||||
|
||||
return $icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an icon.
|
||||
*
|
||||
* @param string $icon The icon name.
|
||||
*/
|
||||
public function icon( $icon ) {
|
||||
echo wp_kses(
|
||||
$this->get_icon( $icon, false ),
|
||||
$this->wp_kses_svg()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the plugin logo.
|
||||
*
|
||||
* @param bool $escape If true it will escape the logog.
|
||||
*
|
||||
* @return string the logo as string.
|
||||
*/
|
||||
public function get_logo( $escape = true ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php';
|
||||
require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php';
|
||||
|
||||
$filesystem = new WP_Filesystem_Direct( null );
|
||||
$logo = $filesystem->get_contents( $this->get_plugin_path( 'assets/images/vrts-logo.svg' ) );
|
||||
|
||||
if ( $escape ) {
|
||||
return wp_kses( $logo, $this->wp_kses_svg() );
|
||||
}
|
||||
|
||||
return $logo;
|
||||
}
|
||||
|
||||
/**
|
||||
* The plugin logo.
|
||||
*/
|
||||
public function logo() {
|
||||
echo wp_kses(
|
||||
$this->get_logo( false ),
|
||||
$this->wp_kses_svg()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get public post types.
|
||||
*
|
||||
* @return array public post types.
|
||||
*/
|
||||
public function get_public_post_types() {
|
||||
$default_post_types = [ 'post', 'page' ];
|
||||
$custom_post_types = get_post_types( [
|
||||
'public' => true,
|
||||
'_builtin' => false,
|
||||
] );
|
||||
|
||||
return array_merge( $default_post_types, $custom_post_types );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get snapshot placeholder image.
|
||||
*
|
||||
* @param boolean $base64 return base 64 encoded or not.
|
||||
*
|
||||
* @return string the placeholder image as string.
|
||||
*/
|
||||
public function get_snapshot_placeholder_image( $base64 = true ) {
|
||||
$icon_path = $this->get_plugin_path( 'assets/images/vrts-snapshot-placeholder.svg' );
|
||||
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- It's a file.
|
||||
$svg = file_get_contents( $icon_path );
|
||||
|
||||
if ( $base64 ) {
|
||||
// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- It's a file.
|
||||
return 'data:image/svg+xml;base64,' . base64_encode( $svg );
|
||||
}
|
||||
|
||||
return $svg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get allowed SVG tags and attributes.
|
||||
*
|
||||
* @return array Allowed SVG tags and attributes.
|
||||
*/
|
||||
private function wp_kses_svg() {
|
||||
return apply_filters( 'vrts_wp_kses_svg', [
|
||||
'svg' => [
|
||||
'class' => true,
|
||||
'width' => true,
|
||||
'height' => true,
|
||||
'version' => true,
|
||||
'fill' => true,
|
||||
'viewbox' => true,
|
||||
'xmlns' => true,
|
||||
'aria-hidden' => true,
|
||||
'focusable' => true,
|
||||
'style' => [],
|
||||
'xml:space' => [],
|
||||
],
|
||||
'path' => [
|
||||
'd' => true,
|
||||
'fill' => true,
|
||||
'stroke' => true,
|
||||
'stroke-linecap' => true,
|
||||
'stroke-linejoin' => true,
|
||||
'stroke-width' => true,
|
||||
'vector-effect' => true,
|
||||
'transform-origin' => true,
|
||||
],
|
||||
'circle' => [
|
||||
'fill' => true,
|
||||
'stroke' => true,
|
||||
'cx' => true,
|
||||
'cy' => true,
|
||||
'r' => true,
|
||||
'vector-effect' => true,
|
||||
],
|
||||
'rect' => [
|
||||
'x' => true,
|
||||
'y' => true,
|
||||
'width' => true,
|
||||
'height' => true,
|
||||
'fill' => true,
|
||||
'class' => true,
|
||||
'rx' => true,
|
||||
],
|
||||
] );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
namespace Vrts\Core\Settings;
|
||||
|
||||
class Manager {
|
||||
/**
|
||||
* Sections
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $sections = [];
|
||||
|
||||
/**
|
||||
* Settings.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $settings = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action( 'admin_init', [ $this, 'register_settings' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register settings.
|
||||
*/
|
||||
public function register_settings() {
|
||||
$this->add_sections();
|
||||
$this->add_settings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get option.
|
||||
*
|
||||
* @param string $id Settings setting ID.
|
||||
* @param bool $transform_value Transform value.
|
||||
*
|
||||
* @return mixed Option value.
|
||||
*/
|
||||
public function get_option( $id, $transform_value = true ) {
|
||||
$default_value = isset( $this->settings[ $id ] ) ? $this->settings[ $id ]['default'] ?? '' : false;
|
||||
$value = get_option( $id, $default_value );
|
||||
|
||||
if ( $transform_value && isset( $this->settings[ $id ]['return_value_callback'] ) ) {
|
||||
$value = call_user_func( $this->settings[ $id ]['return_value_callback'], $value );
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get settings section page.
|
||||
*
|
||||
* @param string $id Setting section ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_section_page( $id ) {
|
||||
return isset( $this->sections[ $id ] ) ? $this->sections[ $id ]['page'] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output field.
|
||||
*
|
||||
* @param array $args Setting args.
|
||||
*/
|
||||
private function get_field( $args ) {
|
||||
$value = $args['value'] ?? $this->get_option( $args['id'], false );
|
||||
include vrts()->get_plugin_path( "includes/core/settings/field-{$args['type']}/index.php" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all sections.
|
||||
*/
|
||||
private function add_sections() {
|
||||
foreach ( $this->sections as $id => $args ) {
|
||||
add_settings_section(
|
||||
$id,
|
||||
$args['title'] ?? '',
|
||||
$args['callback'] ?? '__return_false',
|
||||
$args['page'] ?? 'general'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all settings.
|
||||
*/
|
||||
private function add_settings() {
|
||||
foreach ( $this->settings as $id => $args ) {
|
||||
register_setting(
|
||||
$this->get_section_page( $args['section'] ),
|
||||
$id,
|
||||
[
|
||||
'type' => $args['value_type'] ?? 'string',
|
||||
'sanitize_callback' => $args['sanitize_callback'] ?? null,
|
||||
'show_in_rest' => $args['show_in_rest'] ?? false,
|
||||
'default' => $args['default'] ?? '',
|
||||
]
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
$id,
|
||||
$args['title'] ?? '',
|
||||
function () use ( $args ) {
|
||||
$this->get_field( $args );
|
||||
},
|
||||
$this->get_section_page( $args['section'] ),
|
||||
$args['section'],
|
||||
[
|
||||
'class' => "vrts-settings-{$args['type']}",
|
||||
'label_for' => ! in_array( $args['type'], [ 'info', 'checkbox', 'radio' ], true ) ? $id : '',
|
||||
]
|
||||
);
|
||||
}//end foreach
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a settings section.
|
||||
*
|
||||
* @param array $args Array of properties for the new section object.
|
||||
*/
|
||||
public function add_section( $args = [] ) {
|
||||
$this->sections[ $args['id'] ] = $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a settings setting.
|
||||
*
|
||||
* @param array $args Array of properties for the new setting and control object.
|
||||
*/
|
||||
public function add_setting( $args = [] ) {
|
||||
$this->settings[ $args['id'] ] = $args;
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
<fieldset <?php echo isset( $args['is_pro'] ) && false === $args['is_pro'] ? 'data-a11y-dialog-show="vrts-modal-pro-settings"' : ''; ?>>
|
||||
<legend class="screen-reader-text"><?php echo esc_html( $args['title'] ); ?></legend>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="<?php echo esc_attr( $args['id'] ); ?>"
|
||||
name="<?php echo esc_html( $args['id'] ); ?>"
|
||||
value="1"
|
||||
<?php checked( $value, 1 ); ?>
|
||||
<?php wp_readonly( isset( $args['readonly'] ) && $args['readonly'] ); ?>
|
||||
<?php disabled( isset( $args['disabled'] ) && $args['disabled'] ); ?>>
|
||||
<?php echo wp_kses_post( $args['label'] ); ?>
|
||||
</label>
|
||||
<?php
|
||||
if ( isset( $args['is_pro'] ) && false === $args['is_pro'] ) :
|
||||
?>
|
||||
<span class="vrts-settings__pro-label"><?php esc_html_e( 'Pro', 'visual-regression-tests' ); ?></span>
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
</fieldset>
|
||||
|
||||
<?php
|
||||
if ( isset( $args['description'] ) ) :
|
||||
?>
|
||||
<p class="description"><?php echo wp_kses_post( $args['description'] ); ?></p>
|
||||
<?php
|
||||
endif;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<fieldset>
|
||||
<?php echo wp_kses_post( $args['description'] ); ?>
|
||||
<?php
|
||||
if ( isset( $args['is_pro'] ) && false === $args['is_pro'] ) :
|
||||
?>
|
||||
<span class="vrts-settings__pro-label"><?php esc_html_e( 'Pro', 'visual-regression-tests' ); ?></span>
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
</fieldset>
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<fieldset <?php echo isset( $args['is_pro'] ) && false === $args['is_pro'] ? 'data-a11y-dialog-show="vrts-modal-pro-settings"' : ''; ?>>
|
||||
<input
|
||||
type="password" id="<?php echo esc_attr( $args['id'] ); ?>"
|
||||
class="regular-text"
|
||||
name="<?php echo esc_attr( $args['id'] ); ?>"
|
||||
value="<?php echo esc_attr( $value ); ?>"
|
||||
placeholder="<?php echo esc_attr( $args['placeholder'] ); ?>"
|
||||
<?php wp_readonly( isset( $args['readonly'] ) && $args['readonly'] ); ?>
|
||||
<?php disabled( isset( $args['disabled'] ) && $args['disabled'] ); ?>>
|
||||
<?php
|
||||
if ( isset( $args['is_pro'] ) && false === $args['is_pro'] ) :
|
||||
?>
|
||||
<span class="vrts-settings__pro-label"><?php esc_html_e( 'Pro', 'visual-regression-tests' ); ?></span>
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
</fieldset>
|
||||
<?php
|
||||
if ( isset( $args['description'] ) ) :
|
||||
?>
|
||||
<p class="description"><?php echo wp_kses_post( $args['description'] ); ?></p>
|
||||
<?php
|
||||
endif;
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
<fieldset <?php echo isset( $args['is_pro'] ) && false === $args['is_pro'] ? 'data-a11y-dialog-show="vrts-modal-pro-settings"' : ''; ?>>
|
||||
<select
|
||||
name="<?php echo esc_attr( $args['id'] ); ?>"
|
||||
id="<?php echo esc_attr( $args['id'] ); ?>"
|
||||
<?php wp_readonly( isset( $args['readonly'] ) && $args['readonly'] ); ?>
|
||||
<?php disabled( isset( $args['disabled'] ) && $args['disabled'] ); ?>>
|
||||
<?php
|
||||
foreach ( $args['choices'] as $key => $name ) :
|
||||
?>
|
||||
<option value="<?php echo esc_attr( $key ); ?>" <?php selected( $key, $value ); ?>><?php echo esc_html( $name ); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<?php
|
||||
if ( isset( $args['is_pro'] ) && false === $args['is_pro'] ) :
|
||||
?>
|
||||
<span class="vrts-settings__pro-label"><?php esc_html_e( 'Pro', 'visual-regression-tests' ); ?></span>
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
</fieldset>
|
||||
<?php
|
||||
if ( isset( $args['description'] ) ) :
|
||||
?>
|
||||
<p class="description"><?php echo wp_kses_post( $args['description'] ); ?></p>
|
||||
<?php
|
||||
endif;
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<fieldset <?php echo isset( $args['is_pro'] ) && false === $args['is_pro'] ? 'data-a11y-dialog-show="vrts-modal-pro-settings"' : ''; ?>>
|
||||
<input
|
||||
type="text" id="<?php echo esc_attr( $args['id'] ); ?>"
|
||||
class="regular-text"
|
||||
name="<?php echo esc_attr( $args['id'] ); ?>"
|
||||
value="<?php echo esc_attr( $value ); ?>"
|
||||
placeholder="<?php echo esc_attr( $args['placeholder'] ); ?>"
|
||||
<?php wp_readonly( isset( $args['readonly'] ) && $args['readonly'] ); ?>
|
||||
<?php disabled( isset( $args['disabled'] ) && $args['disabled'] ); ?>>
|
||||
<?php
|
||||
if ( isset( $args['is_pro'] ) && false === $args['is_pro'] ) :
|
||||
?>
|
||||
<span class="vrts-settings__pro-label"><?php esc_html_e( 'Pro', 'visual-regression-tests' ); ?></span>
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
</fieldset>
|
||||
<?php
|
||||
if ( isset( $args['description'] ) ) :
|
||||
?>
|
||||
<p class="description"><?php echo wp_kses_post( $args['description'] ); ?></p>
|
||||
<?php
|
||||
endif;
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace Vrts\Core\Traits;
|
||||
|
||||
use Closure;
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
use BadMethodCallException;
|
||||
|
||||
trait Macroable {
|
||||
/**
|
||||
* Macros.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $macros = [];
|
||||
|
||||
/**
|
||||
* Registerd macro.
|
||||
*
|
||||
* @param string $name Name of macro function.
|
||||
* @param callable $macro Callback function.
|
||||
*/
|
||||
public static function macro( $name, $macro ) {
|
||||
static::$macros[ $name ] = $macro;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register mixins.
|
||||
*
|
||||
* @param class $mixin Class with mixins.
|
||||
*/
|
||||
public static function mixin( $mixin ) {
|
||||
$methods = ( new ReflectionClass( $mixin ) )->getMethods(
|
||||
ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED
|
||||
);
|
||||
|
||||
foreach ( $methods as $method ) {
|
||||
$method->setAccessible( true );
|
||||
|
||||
static::macro( $method->name, $method->invoke( $mixin ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if has macro.
|
||||
*
|
||||
* @param string $name Name of macro function.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function has_macro( $name ) {
|
||||
return isset( static::$macros[ $name ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Call static method.
|
||||
*
|
||||
* @param string $method The function to be called.
|
||||
* @param array $parameters The parameters to be passed to the function, as an indexed array.
|
||||
|
||||
* @return callable The function to be called.
|
||||
*
|
||||
* @throws BadMethodCallException When method doesn't exist.
|
||||
*/
|
||||
public static function __callStatic( $method, $parameters ) {
|
||||
if ( ! static::has_macro( $method ) ) {
|
||||
throw new BadMethodCallException( esc_html( "Method {$method} does not exist." ) );
|
||||
}
|
||||
|
||||
$macro = static::$macros[ $method ];
|
||||
|
||||
if ( $macro instanceof Closure ) {
|
||||
return call_user_func_array( Closure::bind( $macro, null, static::class ), $parameters );
|
||||
}
|
||||
|
||||
return call_user_func_array( $macro, $parameters );
|
||||
}
|
||||
|
||||
/**
|
||||
* Call method.
|
||||
*
|
||||
* @param string $method The function to be called.
|
||||
* @param array $parameters The parameters to be passed to the function, as an indexed array.
|
||||
|
||||
* @return callable The function to be called.
|
||||
*
|
||||
* @throws BadMethodCallException When method doesn't exist.
|
||||
*/
|
||||
public function __call( $method, $parameters ) {
|
||||
if ( ! static::has_macro( $method ) ) {
|
||||
throw new BadMethodCallException( esc_html( "Method {$method} does not exist." ) );
|
||||
}
|
||||
|
||||
$macro = static::$macros[ $method ];
|
||||
|
||||
if ( $macro instanceof Closure ) {
|
||||
return call_user_func_array( $macro->bindTo( $this, static::class ), $parameters );
|
||||
}
|
||||
|
||||
return call_user_func_array( $macro, $parameters );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Vrts\Core\Traits;
|
||||
|
||||
trait Singleton {
|
||||
/**
|
||||
* The reference to Singleton instance of this class.
|
||||
*
|
||||
* @var Singleton
|
||||
*/
|
||||
protected static $instance;
|
||||
|
||||
/**
|
||||
* Returns the Singleton instance of this class.
|
||||
*
|
||||
* @return Singleton The Singleton instance.
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( is_null( self::$instance ) ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Protected constructor to prevent creating a new instance of the
|
||||
* Singleton via the `new` operator from outside of this class.
|
||||
*/
|
||||
protected function __construct() {}
|
||||
|
||||
/**
|
||||
* Private clone method to prevent cloning of the instance of the
|
||||
* Singleton instance.
|
||||
*/
|
||||
private function __clone() {}
|
||||
|
||||
/**
|
||||
* Public unserialize method to prevent unserializing of the Singleton
|
||||
* instance.
|
||||
*/
|
||||
public function __wakeup() {}
|
||||
}
|
||||
+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