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,395 @@
<?php
namespace Vrts\Features;
use Vrts\Models\Alert;
use Vrts\Models\Test;
use Vrts\Services\Test_Service;
class Service {
const DB_VERSION = '1.2';
const SERVICE = 'vrts_service';
const BASE_URL = VRTS_SERVICE_ENDPOINT;
/**
* Connect current website to VRTs Service.
*/
public static function connect_service() {
$option_name = self::SERVICE . '_version';
$installed_version = get_option( $option_name );
if ( self::DB_VERSION !== $installed_version ) {
update_option( $option_name, self::DB_VERSION );
}//end if
if ( ! self::is_connected() ) {
self::create_site();
}
if ( self::is_connected() && ! self::has_secret() ) {
self::create_secret();
}
if ( $installed_version && version_compare( $installed_version, '1.2', '<' ) ) {
$service_project_id = get_option( 'vrts_project_id' );
$service_api_route = 'sites/' . $service_project_id;
self::rest_service_request( $service_api_route, [], 'put' );
}
}
/**
* Rerty connection.
*/
public static function retry_connection() {
return static::create_site( true );
}
/**
* Helper to create site on service.
*
* @return bool
*/
private static function create_site() {
if ( static::is_connected() ) {
return;
}
$time = current_time( 'mysql' );
$rest_base_url = self::get_rest_url();
$service_api_route = 'sites';
$create_token = md5( 'verysecret' . $time );
$parameters = [
'create_token' => $create_token,
'rest_url' => $rest_base_url,
'admin_ajax_url' => admin_url( 'admin-ajax.php' ),
'requested_at' => $time,
];
if ( ! empty( get_option( 'vrts_project_id' ) ) && ! empty( get_option( 'vrts_project_token' ) ) ) {
$parameters['project_id'] = get_option( 'vrts_project_id' );
$parameters['project_token'] = get_option( 'vrts_project_token' );
$parameters['project_secret'] = get_option( 'vrts_project_secret' );
$parameters['tests'] = Test::get_all_service_test_ids();
}
$service_request = self::rest_service_request( $service_api_route, $parameters, 'post' );
delete_option( 'vrts_disconnected' );
if ( 201 === $service_request['status_code'] || 200 === $service_request['status_code'] ) {
$data = $service_request['response'];
update_option( 'vrts_project_id', $data['id'] );
update_option( 'vrts_project_token', $data['token'] );
update_option( 'vrts_project_secret', $data['secret'] ?? null );
Subscription::update_available_tests( $data['remaining_credits'], $data['total_credits'], $data['has_subscription'], $data['tier_id'] );
self::add_homepage_test();
return true;
} else {
update_option( 'vrts_disconnected', 1 );
}
return false;
}
/**
* Connect current website to VRTs Service.
*
* @param string $service_api_route the service api route.
* @param array $parameters the parameters.
* @param string $request_type the request type.
*/
public static function rest_service_request( $service_api_route, $parameters = [], $request_type = '' ) {
$request_url = self::BASE_URL . $service_api_route;
$service_project_id = get_option( 'vrts_project_id' );
$service_project_token = get_option( 'vrts_project_token' );
$response = [];
$args = [
'project_id' => $service_project_id,
'headers' => [
'Content-Type' => 'application/json; charset=utf-8',
'Authorization' => 'Bearer ' . $service_project_token,
],
'body' => wp_json_encode( $parameters ),
'data_format' => 'body',
];
// If project already created, attach project id and service token.
if ( $service_project_id && $service_project_token ) {
$args['project_id'] = $service_project_id;
$args['headers']['Authorization'] = 'Bearer ' . $service_project_token;
}
add_filter( 'http_headers_useragent', [ static::class, 'set_user_agent' ], 10 );
switch ( $request_type ) {
case 'get':
$args = [
'method' => 'GET',
'project_id' => $service_project_id,
'headers' => [
'Authorization' => 'Bearer ' . $service_project_token,
],
'body' => $parameters,
'data_format' => 'body',
];
$data = wp_remote_post( $request_url, $args );
$response = [
'response' => json_decode( wp_remote_retrieve_body( $data ), true ),
'status_code' => wp_remote_retrieve_response_code( $data ),
];
break;
case 'delete':
$args['method'] = 'DELETE';
$data = wp_remote_post( $request_url, $args );
break;
case 'put':
$args['method'] = 'PUT';
$data = wp_remote_post( $request_url, $args );
break;
default:
$data = wp_remote_post( $request_url, $args );
$response = [
'response' => json_decode( wp_remote_retrieve_body( $data ), true ),
'status_code' => wp_remote_retrieve_response_code( $data ),
];
break;
}//end switch
if ( empty( $response ) ) {
$response = [
'response' => $data,
'status_code' => wp_remote_retrieve_response_code( $data ),
];
}
remove_filter( 'http_headers_useragent', [ static::class, 'set_user_agent' ], 10 );
return $response;
}
/**
* Send request to server to resume test.
*
* @param int $post_id the post id.
*/
public static function resume_test( $post_id ) {
$service_test_id = Test::get_service_test_id_by_post_id( $post_id );
if ( $service_test_id ) {
$service_api_route = 'tests/' . $service_test_id . '/resume';
$response = self::rest_service_request( $service_api_route, [], 'post' );
}
}
/**
* Send request to server to delete test.
*
* @param int $service_test_id the service test id.
*/
public static function delete_test( $service_test_id ) {
$service_api_route = 'tests/' . $service_test_id;
$response = self::rest_service_request( $service_api_route, [], 'delete' );
return 200 === $response['status_code'];
}
/**
* Send request to server to update test.
*
* @param int $service_test_id the service test id.
* @param array $data the data.
*/
public static function update_test( $service_test_id, $data ) {
$service_api_route = 'tests/' . $service_test_id;
$response = self::rest_service_request( $service_api_route, $data, 'put' );
return 200 === $response['status_code'];
}
/**
* Send request to server to resume tests.
*/
public static function resume_tests() {
$service_project_id = get_option( 'vrts_project_id' );
$service_api_route = 'sites/' . $service_project_id;
self::rest_service_request( $service_api_route . '/resume', [], 'post' );
}
/**
* Add homepage as a default test.
*/
public static function add_homepage_test() {
$option_name = 'vrts_homepage_added';
$homepage_added = get_option( $option_name );
// If plugin was previously activated, dont add homepage again.
if ( ! $homepage_added ) {
$homepage_id = get_option( 'page_on_front' );
// Save data to custom database table.
$test_service = new Test_Service();
$test_service->create_test( $homepage_id );
update_option( $option_name, 1 );
}
}
/**
* Get rest url for default language if WPML is installed.
*/
private static function get_rest_url() {
// Exlusion for WPML installations.
global $sitepress;
$rest_url = rest_url( 'vrts/v1/service' );
if ( $sitepress ) {
// WPML Get languages.
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- It's ok.
$wpml_current_lang = apply_filters( 'wpml_current_language', null );
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- It's ok.
$wpml_default_lang = apply_filters( 'wpml_default_language', null );
// If current language is not default, switch to default language to get the rest url.
if ( $wpml_current_lang !== $wpml_default_lang ) {
$sitepress->switch_lang( $wpml_default_lang );
$rest_url = rest_url( 'vrts/v1/service' );
// Switch back to the current language.
$sitepress->switch_lang( $wpml_current_lang );
}
}
return $rest_url;
}
/**
* Run manual tests.
*
* @param string[] $service_test_ids the service test ids.
* @param array $options the options.
*/
public static function run_manual_tests( $service_test_ids, $options = [] ) {
$service_project_id = get_option( 'vrts_project_id' );
$service_api_route = 'sites/' . $service_project_id . '/trigger';
return self::rest_service_request( $service_api_route, array_merge( $options, [
'ids' => $service_test_ids,
]), 'post' );
}
/**
* Mark alert as false positive.
*
* @param int $alert_id Alert id.
*/
public static function mark_alert_as_false_positive( $alert_id ) {
$alert = Alert::get_item( $alert_id );
$service_api_route = 'tests/' . $alert->screenshot_test_id . '/false-positives';
return self::rest_service_request( $service_api_route, [
'comparison_id' => $alert->comparison_id,
], 'post' );
}
/**
* Unmark alert as false positive.
*
* @param int $alert_id Alert id.
*/
public static function unmark_alert_as_false_positive( $alert_id ) {
$alert = Alert::get_item( $alert_id );
$service_api_route = 'tests/' . $alert->screenshot_test_id . '/false-positives/' . $alert->comparison_id;
return self::rest_service_request( $service_api_route, [], 'delete' );
}
/**
* Get project id from the service.
*/
public static function fetch_updates() {
$service_project_id = get_option( 'vrts_project_id' );
$service_api_route = 'sites/' . $service_project_id . '/updates';
return self::rest_service_request( $service_api_route, [], 'get' );
}
/**
* Get test runs from the service.
*
* @param string[] $test_run_ids the test run ids.
*/
public static function fetch_test_runs( $test_run_ids ) {
$service_project_id = get_option( 'vrts_project_id' );
$service_api_route = 'sites/' . $service_project_id . '/runs';
$service_api_route = add_query_arg( 'ids', implode( ',', $test_run_ids ), $service_api_route );
return self::rest_service_request( $service_api_route, [], 'get' );
}
/**
* Delete project from the service.
*/
public static function disconnect_service() {
$service_project_id = get_option( 'vrts_project_id' );
$service_api_route = 'sites/' . $service_project_id;
$response = self::rest_service_request( $service_api_route, [], 'delete' );
if ( 200 === $response['status_code'] ) {
update_option( 'vrts_disconnected', 1 );
}
}
/**
* Drop the database table for tests.
*/
public static function delete_option() {
delete_option( 'vrts_project_id' );
delete_option( 'vrts_project_token' );
delete_option( 'vrts_project_secret' );
delete_option( 'vrts_create_token' );
delete_option( 'vrts_access_token' );
delete_option( 'vrts_homepage_added' );
delete_option( 'vrts_site_urls' );
delete_option( 'vrts_connection_inactive' );
delete_option( self::SERVICE . '_version' );
}
/**
* Check if external service was able to connect
*/
public static function is_connected() {
return ! (bool) get_option( 'vrts_disconnected' ) && (bool) get_option( 'vrts_project_id' ) && (bool) get_option( 'vrts_project_token' );
}
/**
* Check if secret was created
*/
public static function has_secret() {
return (bool) get_option( 'vrts_project_secret' );
}
/**
* Set user agent for the request.
*
* @param string $user_agent the user agent.
*/
public static function set_user_agent( $user_agent ) {
if ( function_exists( 'vrts' ) ) {
return 'VRTs/' . vrts()->get_plugin_info( 'version' ) . ';' . $user_agent;
} else {
return $user_agent;
}
}
/**
* Create secret for the project
*/
private static function create_secret() {
$service_project_id = get_option( 'vrts_project_id' );
$service_api_route = 'sites/' . $service_project_id . '/secret';
$service_request = self::rest_service_request( $service_api_route, [], 'post' );
if ( 200 === $service_request['status_code'] ) {
update_option( 'vrts_project_secret', $service_request['response']['secret'] ?? null );
}
}
}