Initial commit: Atomaste website
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class Activator {
|
||||
public const INSTALLATION_OPTION_NAME = 'hts_new_installation';
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public static function activate(): void {
|
||||
$options = new DefaultOptions();
|
||||
$options->add_options();
|
||||
|
||||
self::update_installation_state_on_activation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves installation state.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function update_installation_state_on_activation(): void {
|
||||
$installation_state = get_option( self::INSTALLATION_OPTION_NAME, false );
|
||||
|
||||
if ( $installation_state !== 'old' ) {
|
||||
add_option( self::INSTALLATION_OPTION_NAME, 'new' );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger\Admin;
|
||||
|
||||
use Hostinger\Admin\Menu;
|
||||
use Hostinger\LlmsTxtGenerator\LlmsTxtFileHelper;
|
||||
use Hostinger\WpMenuManager\Menus;
|
||||
use Hostinger\Helper;
|
||||
use Hostinger\WpHelper\Utils;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Class Hostinger_Admin_Assets
|
||||
*
|
||||
* Handles the enqueueing of styles and scripts for the Hostinger admin pages.
|
||||
*/
|
||||
class Assets {
|
||||
/**
|
||||
* @var Helper Instance of the Hostinger_Helper class.
|
||||
*/
|
||||
private Helper $helper;
|
||||
|
||||
/**
|
||||
* @var LlmsTxtFileHelper
|
||||
*/
|
||||
private LlmsTxtFileHelper $llms_txt_file_helper;
|
||||
|
||||
/**
|
||||
* @var Utils
|
||||
*/
|
||||
private Utils $utils;
|
||||
|
||||
public function __construct() {
|
||||
$this->llms_txt_file_helper = new LlmsTxtFileHelper();
|
||||
$this->helper = new Helper();
|
||||
$this->utils = new Utils();
|
||||
$admin_path = parse_url( admin_url(), PHP_URL_PATH );
|
||||
|
||||
// Load assets only on Hostinger admin pages.
|
||||
if ( $this->utils->isThisPage( $admin_path . 'admin.php?page=' . Menu::MENU_SLUG ) || $this->utils->isThisPage( $admin_path . 'admin.php?page=' . Menus::MENU_SLUG ) ) {
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'admin_styles' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts' ) );
|
||||
}
|
||||
if ( is_admin() ) {
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'global_styles' ) );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues styles for the Hostinger admin pages.
|
||||
*/
|
||||
public function admin_styles(): void {
|
||||
// Vue frontend styles.
|
||||
wp_enqueue_style(
|
||||
'hostinger_tools_styles',
|
||||
HOSTINGER_VUE_ASSETS_URL . '/main.css',
|
||||
array(),
|
||||
HOSTINGER_VERSION
|
||||
);
|
||||
|
||||
// Plugin rating styles.
|
||||
wp_enqueue_style(
|
||||
'hostinger_rating_styles',
|
||||
HOSTINGER_ASSETS_URL . '/css/plugin-rating.min.css',
|
||||
array(),
|
||||
HOSTINGER_VERSION
|
||||
);
|
||||
}
|
||||
|
||||
public function global_styles(): void {
|
||||
wp_enqueue_style(
|
||||
'hostinger_tools_global_styles',
|
||||
HOSTINGER_ASSETS_URL . 'css/hostinger-global.min.css',
|
||||
array(),
|
||||
HOSTINGER_VERSION
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Enqueues scripts for the Hostinger admin pages.
|
||||
*/
|
||||
public function admin_scripts(): void {
|
||||
global $wp_version;
|
||||
|
||||
wp_enqueue_script(
|
||||
'hostinger_tools_main_scripts',
|
||||
HOSTINGER_VUE_ASSETS_URL . '/main.js',
|
||||
array(
|
||||
'jquery',
|
||||
'wp-i18n',
|
||||
),
|
||||
HOSTINGER_VERSION,
|
||||
false
|
||||
);
|
||||
|
||||
wp_localize_script(
|
||||
'hostinger_tools_main_scripts',
|
||||
'hostinger_tools_data',
|
||||
array(
|
||||
'home_url' => home_url(),
|
||||
'site_url' => get_site_url(),
|
||||
'plugin_url' => $this->helper->get_hostinger_plugin_url(),
|
||||
'asset_url' => HOSTINGER_PLUGIN_URL,
|
||||
'hplatform' => ! empty( $_SERVER['H_PLATFORM'] ) ? 1 : 0,
|
||||
'edit_site_url' => $this->helper->get_edit_site_url(),
|
||||
'llmstxt_file_url' => $this->llms_txt_file_helper->get_llmstxt_file_url(),
|
||||
'llmstxt_file_user_generated' => $this->llms_txt_file_helper->is_user_generated_file(),
|
||||
'translations' => array(
|
||||
'routes_tools' => __( 'Tools', 'hostinger' ),
|
||||
'hostinger_tools_open_guide' => __( 'Open guide', 'hostinger' ),
|
||||
'hostinger_tools_preview_site' => __( 'Preview site', 'hostinger' ),
|
||||
'hostinger_tools_edit_site' => __( 'Edit site', 'hostinger' ),
|
||||
'hostinger_tools_disable_public_access' => __( 'Disable public access to the site (WordPress admins will still be able to access)', 'hostinger' ),
|
||||
'hostinger_tools_skip_link_maintenance_mode' => __( 'Skip-link that bypasses the maintenance mode', 'hostinger' ),
|
||||
'hostinger_tools_reset_link' => __( 'Reset link', 'hostinger' ),
|
||||
'hostinger_tools_disable_xml_rpc' => __( 'Disable XML-RPC', 'hostinger' ),
|
||||
'hostinger_tools_xml_rpc_description' => __( 'XML-RPC allows apps to connect to your WordPress site, but might expose your site\'s security. Disable this feature if you don\'t need it', 'hostinger' ),
|
||||
'hostinger_tools_disable_authentication_password' => __( 'Disable application passwords', 'hostinger' ),
|
||||
'hostinger_tools_authentication_password_description' => __( 'WordPress application passwords allow users to authenticate API requests without using their main login credentials, allowing for third-party integrations.', 'hostinger' ),
|
||||
|
||||
'hostinger_tools_force_https' => __( 'Force HTTPS', 'hostinger' ),
|
||||
'hostinger_tools_force_https_description' => __( 'Redirects all HTTP URLs to HTTPS sites', 'hostinger' ),
|
||||
'hostinger_tools_force_www' => __( 'Force WWW', 'hostinger' ),
|
||||
'hostinger_tools_force_www_description' => __( 'Redirects all WWW URLs to non-WWW ones', 'hostinger' ),
|
||||
'hostinger_tools_force_www_description_not_available' => __( 'WWW and non-WWW domain records are not pointing to the same host. Redirect not possible.', 'hostinger' ),
|
||||
'hostinger_tools_php_version' => __( 'PHP version', 'hostinger' ),
|
||||
'hostinger_tools_wordpress_version' => __( 'WordPress version', 'hostinger' ),
|
||||
'hostinger_tools_php_version_description' => __( 'Various updates and fixes available in the newest version.', 'hostinger' ),
|
||||
'hostinger_tools_running_latest_version' => __( 'Running the latest version', 'hostinger' ),
|
||||
'hostinger_tools_update' => __( 'Update', 'hostinger' ),
|
||||
'hostinger_tools_update_to' => __( 'Update to', 'hostinger' ),
|
||||
'hostinger_tools_update_to_recommended' => __( 'is recommended', 'hostinger' ),
|
||||
'hostinger_tools_update_to_wordpress_version_description' => __( 'For improved security, ensure you use the latest version of WordPress', 'hostinger' ),
|
||||
'hostinger_tools_maintenance' => __( 'Maintenance', 'hostinger' ),
|
||||
'hostinger_tools_preview_my_website' => __( 'Preview my website', 'hostinger' ),
|
||||
'hostinger_tools_security' => __( 'Security', 'hostinger' ),
|
||||
'hostinger_tools_redirects' => __( 'Redirects', 'hostinger' ),
|
||||
'hostinger_tools_ai' => __( 'AI Tools', 'hostinger' ),
|
||||
'hostinger_tools_llms' => __( 'LLM Optimization', 'hostinger' ),
|
||||
'hostinger_tools_enable_llms_txt' => __( 'Create LLMs.txt file', 'hostinger' ),
|
||||
'hostinger_tools_llms_txt_description' => __( 'Let AI explore, understand, and interact with your WordPress site.', 'hostinger' ),
|
||||
'hostinger_tools_optin_mcp' => __( 'Web2Agent', 'hostinger' ),
|
||||
'hostinger_tools_optin_mcp_description' => __( 'Make your website easier for AI tools to understand. Website content updates will be tracked to keep the AI discovery service up to date.', 'hostinger' ),
|
||||
'hostinger_tools_llms_txt_learn_more' => __( 'Learn more', 'hostinger' ),
|
||||
'hostinger_tools_llms_txt_check_validity' => __( 'Check validity', 'hostinger' ),
|
||||
'hostinger_tools_llms_txt_llmstxt' => __( 'LLMS.txt', 'hostinger' ),
|
||||
'hostinger_tools_llms_txt_external_file_found' => __( 'An external LLMs.txt file was found. Switching on the toggle will replace it with a new one.', 'hostinger' ),
|
||||
'hostinger_tools_llms_txt_modal_title' => __( 'Create new LLMs.txt file?', 'hostinger' ),
|
||||
'hostinger_tools_llms_txt_modal_description' => __( 'This will replace the existing LLMs.txt file with a new one by Hostinger Tools. The original file can’t be restored.', 'hostinger' ),
|
||||
'hostinger_tools_llms_txt_modal_cancel' => __( 'Cancel', 'hostinger' ),
|
||||
'hostinger_tools_llms_txt_modal_create_file' => __( 'Create file', 'hostinger' ),
|
||||
'hostinger_tools_maintenance_mode' => __( 'Maintenance mode', 'hostinger' ),
|
||||
'hostinger_tools_bypass_link' => __( 'Bypass link', 'hostinger' ),
|
||||
'xml_security_modal_description' => __( ' Turning on XML-RPC might make your site less secure. Do you want to proceed?', 'hostinger' ),
|
||||
'xml_security_modal_title' => __( 'Disclaimer', 'hostinger' ),
|
||||
'xml_security_modal_cancel' => __( 'Cancel', 'hostinger' ),
|
||||
'xml_security_modal_proceed_anyway' => __( 'Proceed anyway', 'hostinger' ),
|
||||
'bypass_link_reset_modal_title' => __( 'Bypass link reset', 'hostinger' ),
|
||||
'bypass_link_reset_modal_description' => __( 'This will invalidate the currently generated link in use. This action cannot be undone, are you sure you want to proceed?', 'hostinger' ),
|
||||
'bypass_link_reset_modal_cancel' => __( 'Cancel', 'hostinger' ),
|
||||
'bypass_link_reset_modal_reset_link' => __( 'Reset link', 'hostinger' ),
|
||||
'bypass_link_reset_success' => __( 'Link has been reset', 'hostinger' ),
|
||||
'hostinger_tools_settings_updated' => __( 'Your settings have been updated', 'hostinger' ),
|
||||
'hostinger_tools_settings_error' => __( 'It was an error updating your settings', 'hostinger' ),
|
||||
'hostinger_tools_mcp_choice' => __( 'Allow Kodee to manage your site', 'hostinger' ),
|
||||
'hostinger_tools_mcp_description' => __( 'Let Kodee manage your site on your behalf. This allows Kodee to perform actions like creating pages or updating settings. We will install and pre-configure the WordPress MCP plugin for you.', 'hostinger' ),
|
||||
'hostinger_tools_copied_successfully' => __( 'Copied successfully', 'hostinger' ),
|
||||
'hostinger_tools_text_copied_successfully' => __( 'Text has been copied successfully', 'hostinger' ),
|
||||
'hostinger_tools_free_domain_llm_unavailable' => __( 'LLM optimization features are not available for temporary subdomains. Connect a domain to unlock these features.', 'hostinger' ),
|
||||
'hostinger_tools_connect_domain_cta' => __( 'Connect domain', 'hostinger' ),
|
||||
),
|
||||
'rest_base_url' => esc_url_raw( rest_url() ),
|
||||
'nonce' => wp_create_nonce( 'wp_rest' ),
|
||||
'wp_version' => $wp_version,
|
||||
'php_version' => phpversion(),
|
||||
'recommended_php_version' => $this->helper->get_recommended_php_version(),
|
||||
'mcp_choice' => get_option( 'hostinger_mcp_choice', 0 ),
|
||||
'ai_plugin_compatibility' => $this->check_ai_mcp_compatibility(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function check_ai_mcp_compatibility(): bool {
|
||||
if ( ! function_exists( 'is_plugin_active' ) ) {
|
||||
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
||||
}
|
||||
|
||||
$plugin_path = 'hostinger-ai-assistant/hostinger-ai-assistant.php';
|
||||
$required_version = '3.0.0';
|
||||
|
||||
if ( is_plugin_active( $plugin_path ) ) {
|
||||
$plugin_file = WP_PLUGIN_DIR . '/' . $plugin_path;
|
||||
if ( file_exists( $plugin_file ) ) {
|
||||
$plugin_data = get_plugin_data( $plugin_file, false, false );
|
||||
$active_version = isset( $plugin_data['Version'] ) ? $plugin_data['Version'] : '';
|
||||
|
||||
if ( $active_version && version_compare( $active_version, $required_version, '>=' ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger\Admin;
|
||||
|
||||
use Hostinger\Helper;
|
||||
use Hostinger\WpHelper\Utils;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class Hooks {
|
||||
/**
|
||||
* @var Helper
|
||||
*/
|
||||
private Helper $helper;
|
||||
|
||||
/**
|
||||
* @var Utils
|
||||
*/
|
||||
private Utils $utils;
|
||||
|
||||
public function __construct( $utils ) {
|
||||
$this->helper = new Helper();
|
||||
$this->utils = $utils ?? new Utils();
|
||||
add_action( 'admin_footer', array( $this, 'rate_plugin' ) );
|
||||
add_filter( 'wp_kses_allowed_html', array( $this, 'custom_kses_allowed_html' ), 10, 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function rate_plugin(): void {
|
||||
$admin_path = parse_url( admin_url(), PHP_URL_PATH );
|
||||
|
||||
if ( ! $this->utils->isThisPage( $admin_path . 'admin.php?page=' . Menu::MENU_SLUG ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
require_once HOSTINGER_ABSPATH . 'includes/Admin/Views/Partials/RateUs.php';
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function custom_kses_allowed_html( $allowed ) {
|
||||
$allowed['svg'] = array(
|
||||
'xmlns' => true,
|
||||
'width' => true,
|
||||
'height' => true,
|
||||
'viewBox' => true,
|
||||
'fill' => true,
|
||||
'style' => true,
|
||||
'class' => true,
|
||||
);
|
||||
$allowed['path'] = array(
|
||||
'd' => true,
|
||||
'fill' => true,
|
||||
);
|
||||
|
||||
return $allowed;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger\Admin\Jobs;
|
||||
|
||||
use Hostinger\LlmsTxtGenerator\LlmsTxtParser;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
abstract class AbstractBatchedJob extends AbstractJob {
|
||||
|
||||
public function init(): void {
|
||||
add_action( $this->get_create_batch_hook(), array( $this, 'handle_create_batch_action' ), 10, 2 );
|
||||
parent::init();
|
||||
}
|
||||
|
||||
protected function get_create_batch_hook(): string {
|
||||
return "{$this->get_hook_base_name()}create_batch";
|
||||
}
|
||||
|
||||
public function schedule( array $args = array() ): void {
|
||||
$this->schedule_create_batch_action( 1, $args );
|
||||
}
|
||||
|
||||
public function handle_create_batch_action( int $batch_number, array $args ): void {
|
||||
$items = $this->get_batch( $batch_number, $args );
|
||||
|
||||
if ( empty( $items ) ) {
|
||||
$this->handle_complete( $batch_number, $args );
|
||||
} else {
|
||||
$this->schedule_process_action( $items, $args );
|
||||
$this->schedule_create_batch_action( $batch_number + 1, $args );
|
||||
}
|
||||
}
|
||||
|
||||
protected function get_batch_size(): int {
|
||||
return apply_filters( 'hostinger_batch_item_limit', LlmsTxtParser::DEFAULT_LIMIT );
|
||||
}
|
||||
|
||||
protected function get_query_offset( int $batch_number ): int {
|
||||
return $this->get_batch_size() * ( $batch_number - 1 );
|
||||
}
|
||||
|
||||
protected function schedule_create_batch_action( int $batch_number, array $args ): void {
|
||||
if ( $this->can_schedule( array( $batch_number ) ) ) {
|
||||
$this->action_scheduler->schedule_immediate(
|
||||
$this->get_create_batch_hook(),
|
||||
array(
|
||||
$batch_number,
|
||||
$args,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function schedule_process_action( array $items = array(), array $args = array() ): void {
|
||||
$job_data = array(
|
||||
'items' => $items,
|
||||
'args' => $args,
|
||||
);
|
||||
if ( ! $this->is_processing( $job_data ) ) {
|
||||
$this->action_scheduler->schedule_immediate( $this->get_process_item_hook(), array( $job_data ) );
|
||||
}
|
||||
}
|
||||
|
||||
protected function is_processing( array $args = array() ): bool {
|
||||
return $this->action_scheduler->has_scheduled_action( $this->get_process_item_hook(), array( $args ) );
|
||||
}
|
||||
|
||||
protected function handle_complete( int $final_batch_number, array $args ): void {
|
||||
return;
|
||||
}
|
||||
|
||||
abstract protected function get_batch( int $batch_number, array $args ): array;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
declare( strict_types=1 );
|
||||
|
||||
namespace Hostinger\Admin\Jobs;
|
||||
|
||||
use Exception;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
abstract class AbstractJob implements JobInterface {
|
||||
|
||||
protected ActionScheduler $action_scheduler;
|
||||
|
||||
public function __construct( ActionScheduler $action_scheduler ) {
|
||||
$this->action_scheduler = $action_scheduler;
|
||||
}
|
||||
|
||||
public function init(): void {
|
||||
add_action( $this->get_process_item_hook(), array( $this, 'handle_process_items_action' ) );
|
||||
add_action(
|
||||
$this->get_start_hook(),
|
||||
function ( $args ) {
|
||||
$this->schedule( $args );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public function can_schedule( $args = array() ): bool {
|
||||
return ! $this->is_running( $args );
|
||||
}
|
||||
|
||||
public function handle_process_items_action( array $args = array() ): void {
|
||||
$this->process_items( $args );
|
||||
}
|
||||
|
||||
public function get_process_item_hook(): string {
|
||||
return "{$this->get_hook_base_name()}process_item";
|
||||
}
|
||||
|
||||
public function get_start_hook(): string {
|
||||
return $this->get_name();
|
||||
}
|
||||
|
||||
protected function is_running( ?array $args = array() ): bool {
|
||||
return $this->action_scheduler->has_scheduled_action( $this->get_process_item_hook(), array( $args ) );
|
||||
}
|
||||
|
||||
protected function get_hook_base_name(): string {
|
||||
return "{$this->action_scheduler->get_group()}/jobs/{$this->get_name()}/";
|
||||
}
|
||||
|
||||
abstract public function get_name(): string;
|
||||
abstract protected function process_items( array $args );
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger\Admin\Jobs;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class ActionScheduler {
|
||||
|
||||
public const STATUS_PENDING = 'pending';
|
||||
public const STATUS_COMPLETE = 'complete';
|
||||
public const STATUS_FAILED = 'failed';
|
||||
|
||||
public function get_group(): string {
|
||||
return defined( 'HOSTINGER_PLUGIN_SETTINGS_OPTION' ) ? HOSTINGER_PLUGIN_SETTINGS_OPTION : 'hostinger_tools';
|
||||
}
|
||||
|
||||
public function schedule_single( int $timestamp, string $hook, $args = array() ): int {
|
||||
if ( ! function_exists( 'as_schedule_single_action' ) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return as_schedule_single_action( $timestamp, $hook, $args, $this->get_group() );
|
||||
}
|
||||
|
||||
public function schedule_immediate( string $hook, $args = array() ): int {
|
||||
if ( ! function_exists( 'as_schedule_single_action' ) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return as_schedule_single_action( gmdate( 'U' ) - 1, $hook, $args, $this->get_group() );
|
||||
}
|
||||
|
||||
public function has_scheduled_action( string $hook, $args = array() ): bool {
|
||||
if ( ! function_exists( 'as_next_scheduled_action' ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return as_next_scheduled_action( $hook, $args, $this->get_group() ) !== false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger\Admin\Jobs;
|
||||
|
||||
use Hostinger\Admin\PluginSettings;
|
||||
use Hostinger\Admin\Proxy;
|
||||
use Hostinger\LlmsTxtGenerator\LlmsTxtFileHelper;
|
||||
use Hostinger\LlmsTxtGenerator\LlmsTxtParser;
|
||||
use Hostinger\Mcp\EventHandlerFactory;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class JobInitializer {
|
||||
|
||||
public function __construct( Proxy $proxy ) {
|
||||
$jobs = array();
|
||||
$jobs[] = new NotifyMcpJob( new ActionScheduler(), new EventHandlerFactory( $proxy ) );
|
||||
$jobs[] = new LlmsTxtInjectContentJob( new ActionScheduler(), new LlmsTxtParser(), new LlmsTxtFileHelper(), new PluginSettings() );
|
||||
|
||||
foreach ( $jobs as $job ) {
|
||||
$job->init();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger\Admin\Jobs;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
interface JobInterface {
|
||||
public function get_name(): string;
|
||||
public function get_process_item_hook(): string;
|
||||
public function get_start_hook(): string;
|
||||
public function can_schedule( array $args = array() ): bool;
|
||||
public function schedule( array $args = array() );
|
||||
public function init();
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger\Admin\Jobs;
|
||||
|
||||
use Hostinger\Admin\PluginSettings;
|
||||
use Hostinger\LlmsTxtGenerator\LlmsTxtFileHelper;
|
||||
use Hostinger\LlmsTxtGenerator\LlmsTxtGenerator;
|
||||
use Hostinger\LlmsTxtGenerator\LlmsTxtParser;
|
||||
|
||||
class LlmsTxtInjectContentJob extends AbstractBatchedJob {
|
||||
|
||||
public const JOB_NAME = 'generate_llmstxt';
|
||||
|
||||
protected LlmsTxtParser $llms_txt_parser;
|
||||
protected LlmsTxtFileHelper $llms_txt_file_helper;
|
||||
protected PluginSettings $plugin_settings;
|
||||
|
||||
public function __construct( ActionScheduler $action_scheduler, LlmsTxtParser $llms_txt_parser, LlmsTxtFileHelper $llms_txt_file_helper, PluginSettings $plugin_settings ) {
|
||||
parent::__construct( $action_scheduler );
|
||||
$this->llms_txt_parser = $llms_txt_parser;
|
||||
$this->llms_txt_file_helper = $llms_txt_file_helper;
|
||||
$this->plugin_settings = $plugin_settings;
|
||||
}
|
||||
|
||||
protected function get_batch( int $batch_number, $args ): array {
|
||||
if ( ! isset( $args['post_type'] ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$offset = $this->get_query_offset( $batch_number );
|
||||
$limit = $this->get_batch_size();
|
||||
|
||||
return $this->llms_txt_parser->get_by_post_type( $args['post_type'], $limit, $offset );
|
||||
}
|
||||
|
||||
public function get_name(): string {
|
||||
return self::JOB_NAME;
|
||||
}
|
||||
|
||||
protected function process_items( array $args = array() ): void {
|
||||
if ( ! $this->is_llms_txt_enabled() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$items = $args['items'] ?? array();
|
||||
$job_args = $args['args'] ?? array();
|
||||
|
||||
if ( ! isset( $job_args['post_type'] ) || empty( $items ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$content = $this->llms_txt_parser->get_items( $items );
|
||||
$this->inject_content( $job_args['post_type'], $content );
|
||||
}
|
||||
|
||||
public function schedule( array $args = array() ): void {
|
||||
// Initiate as 2, as the first batch will be created when the user toggles on the option.
|
||||
$this->schedule_create_batch_action( 2, $args );
|
||||
}
|
||||
|
||||
public function can_schedule( $args = array() ): bool {
|
||||
return parent::can_schedule( $args ) && $this->is_llms_txt_enabled();
|
||||
}
|
||||
|
||||
public function is_llms_txt_enabled(): bool {
|
||||
$settings = $this->plugin_settings->get_plugin_settings();
|
||||
return $settings->get_enable_llms_txt();
|
||||
}
|
||||
|
||||
public function inject_content( $post_type, $new_content ): void {
|
||||
$content = $this->llms_txt_file_helper->get_content();
|
||||
$section = LlmsTxtGenerator::HOSTINGER_LLMSTXT_SUPPORTED_POST_TYPES[ $post_type ];
|
||||
$header = "## $section\n\n";
|
||||
$header_position = strpos( $content, $header );
|
||||
$header_length = strlen( $header );
|
||||
if ( $header_position === false ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$before_injection_slot = substr( $content, 0, $header_position + $header_length );
|
||||
$after_injection_slot = substr( $content, $header_position + $header_length );
|
||||
|
||||
$final_content = $before_injection_slot . $new_content . $after_injection_slot;
|
||||
$this->llms_txt_file_helper->create( $final_content );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger\Admin\Jobs;
|
||||
use Hostinger\Mcp\EventHandlerFactory;
|
||||
use Hostinger\Mcp\Handlers\EventHandler;
|
||||
use PHPUnit\Exception;
|
||||
|
||||
class NotifyMcpJob extends AbstractJob implements JobInterface {
|
||||
|
||||
public const JOB_NAME = 'notify_mcp';
|
||||
|
||||
private EventHandlerFactory $event_handler_factory;
|
||||
|
||||
public function __construct( ActionScheduler $action_scheduler, EventHandlerFactory $event_handler_factory ) {
|
||||
$this->event_handler_factory = $event_handler_factory;
|
||||
parent::__construct( $action_scheduler );
|
||||
}
|
||||
|
||||
public function get_name(): string {
|
||||
return self::JOB_NAME;
|
||||
}
|
||||
|
||||
public function event_handler( string $event ): EventHandler {
|
||||
return $this->event_handler_factory->get_handler( $event );
|
||||
}
|
||||
|
||||
public function process_items( array $args = array() ): void {
|
||||
$handler = $this->event_handler( $args['event'] );
|
||||
$handler->send( $args );
|
||||
}
|
||||
|
||||
public function schedule( array $args = array() ): void {
|
||||
if ( $this->can_schedule( $args ) ) {
|
||||
$this->action_scheduler->schedule_immediate( $this->get_process_item_hook(), array( $args ) );
|
||||
}
|
||||
}
|
||||
|
||||
public function can_schedule( $args = array() ): bool {
|
||||
if ( ! parent::can_schedule( $args ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$handler = $this->event_handler( $args['event'] );
|
||||
return $handler->can_send( $args );
|
||||
} catch ( Exception $e ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger\Admin;
|
||||
|
||||
use Hostinger\Admin\PluginSettings;
|
||||
use Hostinger\WpHelper\Utils;
|
||||
use Hostinger\WpMenuManager\Menus;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class Menu {
|
||||
public const MENU_SLUG = 'hostinger-tools';
|
||||
|
||||
public function __construct() {
|
||||
add_filter( 'hostinger_admin_menu_bar_items', array( $this, 'add_admin_bar_items' ) );
|
||||
add_filter( 'hostinger_menu_subpages', array( $this, 'sub_menu' ) );
|
||||
}
|
||||
|
||||
public function add_admin_bar_items( $menu_items ): array {
|
||||
$menu_items[] = array(
|
||||
'id' => 'hostinger-tools-admin-bar',
|
||||
'title' => __( 'Tools', 'hostinger' ),
|
||||
'href' => admin_url( 'admin.php?page=' . self::MENU_SLUG ),
|
||||
);
|
||||
|
||||
return $menu_items;
|
||||
}
|
||||
|
||||
public function sub_menu( $submenus ): array {
|
||||
$tools_submenu = array(
|
||||
'page_title' => __( 'Tools', 'hostinger' ),
|
||||
'menu_title' => __( 'Tools', 'hostinger' ),
|
||||
'capability' => 'manage_options',
|
||||
'menu_slug' => self::MENU_SLUG,
|
||||
'callback' => array( $this, 'render_tools_menu_page' ),
|
||||
'menu_order' => 10,
|
||||
);
|
||||
|
||||
$submenus[] = $tools_submenu;
|
||||
|
||||
return $submenus;
|
||||
}
|
||||
|
||||
public function render_tools_menu_page(): void {
|
||||
echo wp_kses( Menus::renderMenuNavigation(), 'post' );
|
||||
?>
|
||||
<div id="hostinger-tools-vue-app"/>
|
||||
|
||||
|
||||
<?php
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
<?php
|
||||
namespace Hostinger\Admin\Options;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
die;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for handling plugin options
|
||||
*/
|
||||
class PluginOptions {
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private bool $maintenance_mode = false;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private string $bypass_code = '';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private bool $disable_xml_rpc = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private bool $force_https = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private bool $force_www = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private bool $disable_authentication_password = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private bool $enable_llms_txt = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private bool $optin_mcp = false;
|
||||
|
||||
/**
|
||||
* @param array $settings plugin settings array.
|
||||
*/
|
||||
public function __construct( array $settings = array() ) {
|
||||
$this->maintenance_mode = ! empty( $settings['maintenance_mode'] );
|
||||
$this->bypass_code = ! empty( $settings['bypass_code'] ) ? $settings['bypass_code'] : '';
|
||||
$this->disable_xml_rpc = ! empty( $settings['disable_xml_rpc'] );
|
||||
$this->force_https = ! empty( $settings['force_https'] );
|
||||
$this->force_www = ! empty( $settings['force_www'] );
|
||||
$this->disable_authentication_password = ! empty( $settings['disable_authentication_password'] );
|
||||
$this->enable_llms_txt = ! empty( $settings['enable_llms_txt'] );
|
||||
$this->optin_mcp = ! empty( $settings['optin_mcp'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function get_maintenance_mode(): bool {
|
||||
return $this->maintenance_mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $maintenance_mode
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set_maintenance_mode( bool $maintenance_mode ): void {
|
||||
$this->maintenance_mode = $maintenance_mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_bypass_code(): string {
|
||||
return $this->bypass_code;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $bypass_code
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set_bypass_code( string $bypass_code ): void {
|
||||
$this->bypass_code = $bypass_code;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function get_disable_xml_rpc(): bool {
|
||||
return $this->disable_xml_rpc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $disable_xml_rpc
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set_disable_xml_rpc( bool $disable_xml_rpc ): void {
|
||||
$this->disable_xml_rpc = $disable_xml_rpc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function get_force_https(): bool {
|
||||
return $this->force_https;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $force_https
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set_force_https( bool $force_https ): void {
|
||||
$this->force_https = $force_https;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function get_force_www(): bool {
|
||||
return $this->force_www;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $force_www
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set_force_www( bool $force_www ): void {
|
||||
$this->force_www = $force_www;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function get_disable_authentication_password(): bool {
|
||||
return $this->disable_authentication_password;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $authentication_password
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set_disable_authentication_password( bool $authentication_password ): void {
|
||||
$this->disable_authentication_password = $authentication_password;
|
||||
}
|
||||
|
||||
public function get_enable_llms_txt(): bool {
|
||||
return $this->enable_llms_txt;
|
||||
}
|
||||
|
||||
public function set_enable_llms_txt( bool $llmstxt_enabled ): void {
|
||||
$this->enable_llms_txt = $llmstxt_enabled;
|
||||
}
|
||||
|
||||
public function get_optin_mcp(): bool {
|
||||
return $this->optin_mcp;
|
||||
}
|
||||
|
||||
public function set_optin_mcp( bool $optin_mcp ): void {
|
||||
$this->optin_mcp = $optin_mcp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function to_array(): array {
|
||||
return array(
|
||||
'maintenance_mode' => $this->get_maintenance_mode(),
|
||||
'bypass_code' => $this->get_bypass_code(),
|
||||
'disable_xml_rpc' => $this->get_disable_xml_rpc(),
|
||||
'force_https' => $this->get_force_https(),
|
||||
'force_www' => $this->get_force_www(),
|
||||
'disable_authentication_password' => $this->get_disable_authentication_password(),
|
||||
'enable_llms_txt' => $this->get_enable_llms_txt(),
|
||||
'optin_mcp' => $this->get_optin_mcp(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/**
|
||||
* Avoid possibility to get file accessed directly
|
||||
*/
|
||||
|
||||
namespace Hostinger\Admin;
|
||||
|
||||
use Hostinger\Admin\Options\PluginOptions;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
die;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for handling Settings
|
||||
*/
|
||||
class PluginSettings {
|
||||
|
||||
/**
|
||||
* @var PluginOptions|null
|
||||
*/
|
||||
private ?PluginOptions $plugin_options = null;
|
||||
|
||||
/**
|
||||
* @param PluginOptions|null $plugin_options
|
||||
*/
|
||||
public function __construct( ?PluginOptions $plugin_options = null ) {
|
||||
if ( ! is_null( $plugin_options ) ) {
|
||||
$this->plugin_options = $plugin_options;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return plugin settings
|
||||
*
|
||||
* @return PluginOptions
|
||||
*/
|
||||
public function get_plugin_settings(): PluginOptions {
|
||||
|
||||
if ( ! empty( $this->plugin_options ) ) {
|
||||
$settings = $this->plugin_options;
|
||||
} else {
|
||||
$settings = get_option(
|
||||
HOSTINGER_PLUGIN_SETTINGS_OPTION,
|
||||
array()
|
||||
);
|
||||
|
||||
$settings = new PluginOptions( $settings );
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PluginOptions $plugin_options plugin settings.
|
||||
*
|
||||
* @return PluginOptions
|
||||
*/
|
||||
public function save_plugin_settings( PluginOptions $plugin_options ): PluginOptions {
|
||||
$existing_settings = $this->get_plugin_settings();
|
||||
|
||||
$update = update_option( HOSTINGER_PLUGIN_SETTINGS_OPTION, $plugin_options->to_array(), false );
|
||||
|
||||
return ! empty( $update ) ? $plugin_options : $existing_settings;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger\Admin;
|
||||
|
||||
use Hostinger\Mcp\EventHandlerFactory;
|
||||
use Hostinger\WpHelper\Requests\Client;
|
||||
use Hostinger\WpHelper\Utils;
|
||||
use WP_Error;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class Proxy {
|
||||
|
||||
public const HOSTINGER_FREE_SUBDOMAIN_URL = 'hostingersite.com';
|
||||
public const HOSTINGER_DEV_FREE_SUBDOMAIN_URL = 'hostingersite.dev';
|
||||
|
||||
|
||||
private Client $client;
|
||||
private Utils $utils;
|
||||
private string $rest_base;
|
||||
|
||||
public function __construct( Client $client, Utils $utils ) {
|
||||
$this->client = $client;
|
||||
$this->utils = $utils;
|
||||
$this->rest_base = '/api/v1/events/trigger';
|
||||
}
|
||||
|
||||
public function trigger_event( string $event, array $params = array() ): array|WP_Error {
|
||||
if ( $this->is_free_subdomain() || ! $_SERVER['H_PLATFORM'] ) {
|
||||
return new WP_Error( 'domain-not_allowed', 'This domain is not eligible for triggering Hostinger events' );
|
||||
}
|
||||
|
||||
$args = array(
|
||||
'domain' => $this->remove_www( $this->utils->getHostInfo() ),
|
||||
'event' => array(
|
||||
'name' => $event,
|
||||
'params' => $params,
|
||||
),
|
||||
);
|
||||
|
||||
return $this->client->post( $this->rest_base, $args );
|
||||
}
|
||||
|
||||
private function is_free_subdomain(): bool {
|
||||
return str_contains( $this->utils->getHostInfo(), self::HOSTINGER_FREE_SUBDOMAIN_URL ) ||
|
||||
str_contains( $this->utils->getHostInfo(), self::HOSTINGER_DEV_FREE_SUBDOMAIN_URL );
|
||||
}
|
||||
|
||||
private function remove_www( string $url ): string {
|
||||
if ( str_starts_with( $url, 'www.' ) ) {
|
||||
return substr( $url, 4 );
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger\Admin;
|
||||
|
||||
use Hostinger\Settings;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class Redirects {
|
||||
private string $platform;
|
||||
public const PLATFORM_HPANEL = 'hpanel';
|
||||
|
||||
public function __construct() {
|
||||
|
||||
if ( ! Settings::get_setting( 'first_login_at' ) ) {
|
||||
Settings::update_setting( 'first_login_at', gmdate( 'Y-m-d H:i:s' ) );
|
||||
}
|
||||
/** PHPCS:disable WordPress.Security.NonceVerification.Recommended */
|
||||
if ( ! isset( $_GET['platform'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->platform = sanitize_text_field( $_GET['platform'] );
|
||||
$this->login_redirect();
|
||||
/** PHPCS:enable */
|
||||
}
|
||||
|
||||
public function login_redirect(): void {
|
||||
if ( $this->platform === self::PLATFORM_HPANEL ) {
|
||||
add_action(
|
||||
'init',
|
||||
static function () {
|
||||
$redirect_url = admin_url( 'admin.php?page=hostinger' );
|
||||
wp_safe_redirect( $redirect_url );
|
||||
exit;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<div class="hostinger hsr-plugin-rating">
|
||||
<p><?php echo esc_html__( 'Rate this plugin', 'hostinger' ); ?></p>
|
||||
<div class="hsr-rate-stars">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M12 17.27L18.18 21L16.54 13.97L22 9.24L14.81 8.63L12 2L9.19 8.63L2 9.24L7.46 13.97L5.82 21L12 17.27Z"
|
||||
fill="#673DE6"/>
|
||||
</svg>
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M12 17.27L18.18 21L16.54 13.97L22 9.24L14.81 8.63L12 2L9.19 8.63L2 9.24L7.46 13.97L5.82 21L12 17.27Z"
|
||||
fill="#673DE6"/>
|
||||
</svg>
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M12 17.27L18.18 21L16.54 13.97L22 9.24L14.81 8.63L12 2L9.19 8.63L2 9.24L7.46 13.97L5.82 21L12 17.27Z"
|
||||
fill="#673DE6"/>
|
||||
</svg>
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M12 17.27L18.18 21L16.54 13.97L22 9.24L14.81 8.63L12 2L9.19 8.63L2 9.24L7.46 13.97L5.82 21L12 17.27Z"
|
||||
fill="#673DE6"/>
|
||||
</svg>
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M12 17.27L18.18 21L16.54 13.97L22 9.24L14.81 8.63L12 2L9.19 8.63L2 9.24L7.46 13.97L5.82 21L12 17.27Z"
|
||||
fill="#673DE6"/>
|
||||
</svg>
|
||||
</div>
|
||||
<p>
|
||||
<?php
|
||||
echo wp_kses(
|
||||
__(
|
||||
'on <a href="https://wordpress.org/support/plugin/hostinger/reviews/#new-post" target="_blank" rel="noopener noreferrer">WordPress.org</a>',
|
||||
'hostinger'
|
||||
),
|
||||
array(
|
||||
'a' => array(
|
||||
'href' => array(),
|
||||
'target' => array(),
|
||||
'rel' => array(),
|
||||
),
|
||||
),
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
</div>
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger;
|
||||
|
||||
|
||||
use Hostinger\Admin\PluginSettings;
|
||||
use Hostinger\Admin\Jobs\JobInitializer;
|
||||
use Hostinger\Admin\Proxy;
|
||||
use Hostinger\LlmsTxtGenerator\LlmsTxtFileHelper;
|
||||
use Hostinger\LlmsTxtGenerator\LlmsTxtParser;
|
||||
use Hostinger\Rest\Routes;
|
||||
use Hostinger\Rest\SettingsRoutes;
|
||||
use Hostinger\Admin\Assets as AdminAssets;
|
||||
use Hostinger\Admin\Hooks as AdminHooks;
|
||||
use Hostinger\Admin\Menu as AdminMenu;
|
||||
use Hostinger\Admin\Redirects as AdminRedirects;
|
||||
use Hostinger\WpHelper\Config;
|
||||
use Hostinger\WpHelper\Requests\Client;
|
||||
use Hostinger\WpHelper\Utils;
|
||||
use Hostinger\LlmsTxtGenerator\LlmsTxtGenerator;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class Bootstrap {
|
||||
|
||||
protected Loader $loader;
|
||||
protected Utils $utils;
|
||||
protected Config $config;
|
||||
|
||||
public function __construct() {
|
||||
$this->loader = new Loader();
|
||||
$this->utils = new Utils();
|
||||
$this->config = new Config();
|
||||
}
|
||||
|
||||
public function run(): void {
|
||||
$this->load_dependencies();
|
||||
$this->set_locale();
|
||||
$this->loader->run();
|
||||
}
|
||||
|
||||
private function load_dependencies(): void {
|
||||
$this->load_public_dependencies();
|
||||
|
||||
if ( is_admin() ) {
|
||||
$this->load_admin_dependencies();
|
||||
}
|
||||
|
||||
if ( defined( 'WP_CLI' ) && WP_CLI ) {
|
||||
new Cli();
|
||||
}
|
||||
|
||||
$plugin_settings = new PluginSettings();
|
||||
$plugin_options = $plugin_settings->get_plugin_settings();
|
||||
|
||||
if ( $plugin_options->get_maintenance_mode() ) {
|
||||
require_once HOSTINGER_ABSPATH . 'includes/ComingSoon.php';
|
||||
}
|
||||
}
|
||||
|
||||
private function set_locale() {
|
||||
$plugin_i18n = new I18n();
|
||||
$this->loader->add_action( 'plugins_loaded', $plugin_i18n, 'load_plugin_textdomain' );
|
||||
}
|
||||
|
||||
private function load_admin_dependencies(): void {
|
||||
new AdminAssets();
|
||||
new AdminHooks( $this->utils );
|
||||
new AdminMenu();
|
||||
new AdminRedirects();
|
||||
new AdminRedirects();
|
||||
}
|
||||
|
||||
private function load_public_dependencies(): void {
|
||||
|
||||
$client = new Client(
|
||||
'https://wh-wordpress-proxy-api.hostinger.io',
|
||||
array(
|
||||
Config::TOKEN_HEADER => $this->utils->getApiToken(),
|
||||
Config::DOMAIN_HEADER => $this->utils->getHostInfo(),
|
||||
)
|
||||
);
|
||||
|
||||
new JobInitializer( new Proxy( $client, $this->utils ) );
|
||||
new Hooks();
|
||||
|
||||
$plugin_settings = new PluginSettings();
|
||||
|
||||
new LlmsTxtGenerator( $plugin_settings, new LlmsTxtFileHelper(), new LlmsTxtParser() );
|
||||
|
||||
$settings_routes = new SettingsRoutes( $plugin_settings );
|
||||
$routes = new Routes( $settings_routes );
|
||||
$routes->init();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger;
|
||||
|
||||
use WP_CLI;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class Cli {
|
||||
/**
|
||||
* Load required files and hooks to make the CLI work.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->hooks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up and hooks WP CLI to our CLI code.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function hooks(): void {
|
||||
|
||||
if ( ! class_exists( '\WP_CLI' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( class_exists( '\Hostinger\Cli\Commands\Maintenance' ) ) {
|
||||
WP_CLI::add_hook( 'after_wp_load', array( 'Hostinger\Cli\Commands\Maintenance', 'define_command' ) );
|
||||
}
|
||||
|
||||
if ( class_exists( '\Hostinger\Cli\Commands\AI' ) ) {
|
||||
WP_CLI::add_hook( 'after_wp_load', array( 'Hostinger\Cli\Commands\AI', 'define_command' ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger\Cli\Commands;
|
||||
|
||||
use Exception;
|
||||
use Hostinger\Admin\PluginSettings;
|
||||
use WP_CLI;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class AI {
|
||||
protected const WEB2AGENT_FEATURE_NAME = 'Web2Agent feature';
|
||||
protected const LLMS_TXT_FEATURE_NAME = 'LLMS.txt file generation feature';
|
||||
|
||||
public static function define_command(): void {
|
||||
if ( ! class_exists( 'WP_CLI' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
WP_CLI::add_command(
|
||||
'hostinger ai',
|
||||
self::class,
|
||||
array(
|
||||
'shortdesc' => 'Check the status of AI Discovery Features',
|
||||
'longdesc' => 'Available Hostinger AI commands:' . "\n\n" .
|
||||
' wp hostinger ai llmstxt <0|1>' . "\n" .
|
||||
' Manage the ' . self::LLMS_TXT_FEATURE_NAME . '. Use 1 to enable and 0 to disable it.' . "\n\n" .
|
||||
' wp hostinger ai web2agent <0|1>' . "\n" .
|
||||
' Manage the ' . self::WEB2AGENT_FEATURE_NAME . '. Use 1 to enable and 0 to disable it.' . "\n\n" .
|
||||
' wp hostinger ai status' . "\n" .
|
||||
' Display the current status for AI Discovery features.' . "\n\n" .
|
||||
'## EXAMPLES' . "\n\n" .
|
||||
' wp hostinger ai status' . "\n" .
|
||||
' Display the current status for AI Discovery features.' . "\n\n" .
|
||||
' wp hostinger ai llmstxt 0' . "\n" .
|
||||
' Disables ' . self::LLMS_TXT_FEATURE_NAME . '.' . "\n\n" .
|
||||
' wp hostinger ai llmstxt 1' . "\n" .
|
||||
' Enables ' . self::LLMS_TXT_FEATURE_NAME . '.' . "\n",
|
||||
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Command allows enable/disable Web2Agent feature.
|
||||
*
|
||||
* @param array $args
|
||||
*
|
||||
* @return bool
|
||||
* @throws Exception
|
||||
*/
|
||||
public function web2agent( array $args ): bool {
|
||||
$plugin_settings = new PluginSettings();
|
||||
|
||||
if ( ! empty( $args ) ) {
|
||||
$this->validate_args( $args );
|
||||
$this->set_setting_status( $plugin_settings, self::WEB2AGENT_FEATURE_NAME, (bool) $args[0] );
|
||||
}
|
||||
|
||||
return $this->get_setting_status( $plugin_settings, self::WEB2AGENT_FEATURE_NAME );
|
||||
}
|
||||
|
||||
/**
|
||||
* Command allows enable/disable LLMS.txt file generation feature.
|
||||
*
|
||||
* @param array $args
|
||||
*
|
||||
* @return bool
|
||||
* @throws Exception
|
||||
*/
|
||||
public function llmstxt( array $args ): bool {
|
||||
$plugin_settings = new PluginSettings();
|
||||
|
||||
if ( ! empty( $args ) ) {
|
||||
$this->validate_args( $args );
|
||||
$this->set_setting_status( $plugin_settings, self::LLMS_TXT_FEATURE_NAME, (bool) $args[0] );
|
||||
}
|
||||
|
||||
return $this->get_setting_status( $plugin_settings, self::LLMS_TXT_FEATURE_NAME );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current status of AI Discovery features.
|
||||
* @return string
|
||||
*/
|
||||
public function status(): string {
|
||||
$plugin_settings = new PluginSettings();
|
||||
$plugin_options = $plugin_settings->get_plugin_settings();
|
||||
|
||||
$data = array(
|
||||
'llmstxt' => $plugin_options->get_enable_llms_txt(),
|
||||
'web2agent' => $plugin_options->get_optin_mcp(),
|
||||
);
|
||||
|
||||
$status = wp_json_encode( $data );
|
||||
WP_CLI::line( $status );
|
||||
return $status;
|
||||
}
|
||||
|
||||
private function set_setting_status( PluginSettings $plugin_settings, string $setting, bool $is_enabled ): void {
|
||||
$plugin_options = $plugin_settings->get_plugin_settings();
|
||||
|
||||
switch ( $setting ) {
|
||||
case self::WEB2AGENT_FEATURE_NAME:
|
||||
$plugin_options->set_optin_mcp( $is_enabled );
|
||||
break;
|
||||
case self::LLMS_TXT_FEATURE_NAME:
|
||||
$plugin_options->set_enable_llms_txt( $is_enabled );
|
||||
break;
|
||||
default:
|
||||
throw new Exception( 'Invalid setting' );
|
||||
}
|
||||
|
||||
$plugin_settings->save_plugin_settings( $plugin_options );
|
||||
$this->clear_lightspeed_cache();
|
||||
}
|
||||
|
||||
private function get_setting_status( PluginSettings $plugin_settings, string $setting ): bool {
|
||||
$plugin_options = $plugin_settings->get_plugin_settings();
|
||||
switch ( $setting ) {
|
||||
case self::WEB2AGENT_FEATURE_NAME:
|
||||
$is_enabled = $plugin_options->get_optin_mcp();
|
||||
break;
|
||||
case self::LLMS_TXT_FEATURE_NAME:
|
||||
$is_enabled = $plugin_options->get_enable_llms_txt();
|
||||
break;
|
||||
default:
|
||||
throw new Exception( 'Invalid setting' );
|
||||
}
|
||||
|
||||
$enabled = $is_enabled ? 'ENABLED' : 'DISABLED';
|
||||
|
||||
WP_CLI::success( $setting . ' ' . $enabled );
|
||||
return $is_enabled;
|
||||
}
|
||||
|
||||
private function validate_args( mixed $args ): void {
|
||||
if ( $args[0] !== '0' && $args[0] !== '1' ) {
|
||||
throw new Exception( 'Invalid argument. Use 0 or 1' );
|
||||
}
|
||||
}
|
||||
|
||||
private function clear_lightspeed_cache(): void {
|
||||
if ( has_action( 'litespeed_purge_all' ) ) {
|
||||
do_action( 'litespeed_purge_all' );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger\Cli\Commands;
|
||||
|
||||
use Hostinger\Admin\PluginSettings;
|
||||
use WP_CLI;
|
||||
use Hostinger\Settings;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class Maintenance {
|
||||
|
||||
public static function define_command(): void {
|
||||
if ( class_exists( '\WP_CLI' ) ) {
|
||||
WP_CLI::add_command(
|
||||
'hostinger',
|
||||
self::class,
|
||||
array(
|
||||
'shortdesc' => 'List of Hostinger commands.',
|
||||
'longdesc' => 'Available Hostinger commands:' . "\n\n" .
|
||||
' wp hostinger maintenance mode <0|1>' . "\n" .
|
||||
' Manage the maintenance mode of the site. Use 1 to enable and 0 to disable maintenance mode.' . "\n\n" .
|
||||
' wp hostinger maintenance status' . "\n" .
|
||||
' Display the current maintenance mode status.' . "\n\n" .
|
||||
'## SUBCOMMANDS' . "\n\n" .
|
||||
'* mode <0|1>' . "\n" .
|
||||
': Enable (1) or disable (0) maintenance mode.' . "\n\n" .
|
||||
'* status' . "\n" .
|
||||
': Display the current maintenance mode status.' . "\n\n" .
|
||||
'## EXAMPLES' . "\n\n" .
|
||||
' wp hostinger maintenance mode 1' . "\n" .
|
||||
' Enables the maintenance mode.' . "\n\n" .
|
||||
' wp hostinger maintenance mode 0' . "\n" .
|
||||
' Disables the maintenance mode.' . "\n\n" .
|
||||
' wp hostinger maintenance status' . "\n" .
|
||||
' Returns whether maintenance mode is enabled or disabled.' . "\n",
|
||||
)
|
||||
);
|
||||
|
||||
WP_CLI::add_command(
|
||||
'hostinger maintenance',
|
||||
self::class,
|
||||
array(
|
||||
'shortdesc' => 'Manage the maintenance mode of the site.',
|
||||
'longdesc' => 'This command allows you to enable or disable maintenance mode for the site.' . "\n\n" .
|
||||
'## OPTIONS' . "\n\n" .
|
||||
'mode <0|1>' . "\n" .
|
||||
': Enable (1) or disable (0) maintenance mode.' . "\n\n" .
|
||||
'## EXAMPLES' . "\n\n" .
|
||||
' wp hostinger maintenance mode 1' . "\n" .
|
||||
' Enables the maintenance mode.' . "\n\n" .
|
||||
' wp hostinger maintenance mode 0' . "\n" .
|
||||
' Disables the maintenance mode.' . "\n\n" .
|
||||
' wp hostinger maintenance status' . "\n" .
|
||||
' Returns whether maintenance mode is enabled or disabled.' . "\n",
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Command allows enable/disable maintenance mode.
|
||||
*
|
||||
* @param array $args
|
||||
*
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function mode( array $args ): void {
|
||||
if ( empty( $args ) ) {
|
||||
WP_CLI::error( 'Arguments cannot be empty. Use 0 or 1' );
|
||||
}
|
||||
|
||||
$plugin_settings = new PluginSettings();
|
||||
$plugin_options = $plugin_settings->get_plugin_settings();
|
||||
|
||||
switch ( $args[0] ) {
|
||||
case '1':
|
||||
$plugin_options->set_maintenance_mode( true );
|
||||
WP_CLI::success( 'Maintenance mode ENABLED' );
|
||||
break;
|
||||
case '0':
|
||||
$plugin_options->set_maintenance_mode( false );
|
||||
WP_CLI::success( 'Maintenance mode DISABLED' );
|
||||
break;
|
||||
default:
|
||||
throw new \Exception( 'Invalid maintenance mode value' );
|
||||
}
|
||||
|
||||
$plugin_settings->save_plugin_settings( $plugin_options );
|
||||
|
||||
if ( has_action( 'litespeed_purge_all' ) ) {
|
||||
do_action( 'litespeed_purge_all' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Command return maintenance mode status.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function status(): bool {
|
||||
$plugin_settings = new PluginSettings();
|
||||
$plugin_options = $plugin_settings->get_plugin_settings();
|
||||
|
||||
if ( $plugin_options->get_maintenance_mode() ) {
|
||||
WP_CLI::success( 'Maintenance mode ENABLED' );
|
||||
} else {
|
||||
WP_CLI::success( 'Maintenance mode DISABLED' );
|
||||
}
|
||||
|
||||
return (bool) $plugin_options->get_maintenance_mode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger;
|
||||
|
||||
use Hostinger\Admin\Options\PluginOptions;
|
||||
use Hostinger\Admin\PluginSettings;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class ComingSoon {
|
||||
/**
|
||||
* @var PluginOptions
|
||||
*/
|
||||
private PluginOptions $plugin_options;
|
||||
|
||||
public function __construct() {
|
||||
$plugin_settings = new PluginSettings();
|
||||
$this->plugin_options = $plugin_settings->get_plugin_settings();
|
||||
|
||||
add_action( 'wp_footer', array( $this, 'register_styles' ) );
|
||||
add_action( 'template_redirect', array( $this, 'coming_soon' ) );
|
||||
add_filter( 'wp_headers', array( $this, 'modify_headers' ) );
|
||||
|
||||
// Fix deprecated warning.
|
||||
if ( has_action( 'wp_footer', 'the_block_template_skip_link' ) ) {
|
||||
remove_action( 'wp_footer', 'the_block_template_skip_link' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function coming_soon(): void {
|
||||
// Do not cache coming soon page.
|
||||
if ( has_action( 'litespeed_purge_all' ) && ! defined( 'DONOTCACHEPAGE' ) ) {
|
||||
define( 'DONOTCACHEPAGE', true );
|
||||
}
|
||||
|
||||
if ( ! $this->can_bypass_coming_soon() ) {
|
||||
include_once HOSTINGER_ABSPATH . 'includes/Views/ComingSoon.php';
|
||||
die;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function register_styles(): void {
|
||||
wp_enqueue_style(
|
||||
'hostinger_main_styles',
|
||||
HOSTINGER_ASSETS_URL . '/css/coming-soon.min.css',
|
||||
array(),
|
||||
HOSTINGER_VERSION
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $headers
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function modify_headers( array $headers ): array {
|
||||
$headers['Cache-Control'] = 'no-cache';
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
/** PHPCS:disable WordPress.Security.NonceVerification.Recommended */
|
||||
private function can_bypass_coming_soon(): bool {
|
||||
$bypass_code = isset( $_COOKIE['hostinger_bypass_code'] ) ? sanitize_text_field( $_COOKIE['hostinger_bypass_code'] ) : '';
|
||||
|
||||
if ( isset( $_GET['bypass_code'] ) && $this->plugin_options->get_bypass_code() === $_GET['bypass_code'] ) {
|
||||
setcookie( 'hostinger_bypass_code', $this->plugin_options->get_bypass_code() );
|
||||
$bypass_code = $this->plugin_options->get_bypass_code();
|
||||
}
|
||||
|
||||
if ( is_admin() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( current_user_can( 'update_plugins' ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( ! empty( $bypass_code ) && $bypass_code === $this->plugin_options->get_bypass_code() ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
/** PHPCS:enable */
|
||||
}
|
||||
|
||||
new ComingSoon();
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class Deactivator {
|
||||
public static function deactivate(): void {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger;
|
||||
|
||||
use Hostinger\Admin\Options\PluginOptions;
|
||||
use Hostinger\Helper;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class DefaultOptions {
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function add_options(): void {
|
||||
$this->configure_security_settings();
|
||||
|
||||
foreach ( $this->options() as $key => $option ) {
|
||||
update_option( $key, $option );
|
||||
}
|
||||
}
|
||||
|
||||
public function configure_security_settings(): void {
|
||||
$hostinger_plugin_settings = get_option( HOSTINGER_PLUGIN_SETTINGS_OPTION, array() );
|
||||
|
||||
if ( empty( $hostinger_plugin_settings['bypass_code'] ) ) {
|
||||
$hostinger_plugin_settings['bypass_code'] = Helper::generate_bypass_code( 16 );
|
||||
$this->update_plugin_settings( $hostinger_plugin_settings );
|
||||
}
|
||||
|
||||
$this->configure_authentication_password();
|
||||
}
|
||||
|
||||
public function configure_authentication_password(): void {
|
||||
global $wpdb;
|
||||
$hostinger_plugin_settings = get_option( HOSTINGER_PLUGIN_SETTINGS_OPTION, array() );
|
||||
|
||||
$existing_passwords = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->usermeta} WHERE meta_key = %s", '_application_passwords' ) );
|
||||
|
||||
if ( $existing_passwords === 0 ) {
|
||||
$hostinger_plugin_settings['disable_authentication_password'] = true;
|
||||
$this->update_plugin_settings( $hostinger_plugin_settings );
|
||||
}
|
||||
}
|
||||
|
||||
private function update_plugin_settings( array $settings ): void {
|
||||
$plugin_options = new PluginOptions( $settings );
|
||||
update_option( HOSTINGER_PLUGIN_SETTINGS_OPTION, $plugin_options->to_array(), false );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function options(): array {
|
||||
$options = array(
|
||||
'optin_monster_api_activation_redirect_disabled' => 'true',
|
||||
'wpforms_activation_redirect' => 'true',
|
||||
'aioseo_activation_redirect' => 'false',
|
||||
);
|
||||
|
||||
if ( Helper::is_plugin_active( 'astra-sites' ) ) {
|
||||
$options = array_merge( $options, $this->get_astra_options() );
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function get_astra_options(): array {
|
||||
return array(
|
||||
'astra_sites_settings' => 'gutenberg',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class Errors {
|
||||
/**
|
||||
* @var array[]
|
||||
*/
|
||||
private array $error_messages = array();
|
||||
|
||||
public function __construct() {
|
||||
$this->error_messages = array(
|
||||
'action_failed' => array(
|
||||
'default' => __( 'Action Failed. Try again or contact support. Apologies.', 'hostinger' ),
|
||||
),
|
||||
'unexpected_error' => array(
|
||||
'default' => __( 'An unexpected error occurred. Please try again or contact support.', 'hostinger' ),
|
||||
),
|
||||
'server_error' => array(
|
||||
'default' => __( 'We apologize for the inconvenience. The AI content generation process encountered a server error. Please try again later, and if the issue persists, kindly contact our support team for assistance.', 'hostinger' ),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $error_code
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_error_message( string $error_code ) {
|
||||
if ( array_key_exists( $error_code, $this->error_messages ) ) {
|
||||
$message_data = $this->error_messages[ $error_code ];
|
||||
|
||||
return $message_data['default'];
|
||||
} else {
|
||||
return __( 'Unknown error code.', 'hostinger' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new Errors();
|
||||
@@ -0,0 +1,180 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class Helper {
|
||||
const HOSTINGER_LOCALES = array(
|
||||
'lt_LT' => 'hostinger.lt',
|
||||
'uk_UA' => 'hostinger.com.ua',
|
||||
'id_ID' => 'hostinger.co.id',
|
||||
'en_US' => 'hostinger.com',
|
||||
'es_ES' => 'hostinger.es',
|
||||
'es_AR' => 'hostinger.com.ar',
|
||||
'es_MX' => 'hostinger.mx',
|
||||
'es_CO' => 'hostinger.co',
|
||||
'pt_BR' => 'hostinger.com.br',
|
||||
'ro_RO' => 'hostinger.ro',
|
||||
'fr_FR' => 'hostinger.fr',
|
||||
'it_IT' => 'hostinger.it',
|
||||
'pl_PL' => 'hostinger.pl',
|
||||
'en_PH' => 'hostinger.ph',
|
||||
'ar_AE' => 'hostinger.ae',
|
||||
'ms_MY' => 'hostinger.my',
|
||||
'ko_KR' => 'hostinger.kr',
|
||||
'vi_VN' => 'hostinger.vn',
|
||||
'th_TH' => 'hostinger.in.th',
|
||||
'tr_TR' => 'hostinger.web.tr',
|
||||
'pt_PT' => 'hostinger.pt',
|
||||
'de_DE' => 'hostinger.de',
|
||||
'en_IN' => 'hostinger.in',
|
||||
'ja_JP' => 'hostinger.jp',
|
||||
'nl_NL' => 'hostinger.nl',
|
||||
'en_GB' => 'hostinger.co.uk',
|
||||
'el_GR' => 'hostinger.gr',
|
||||
'cs_CZ' => 'hostinger.cz',
|
||||
'hu_HU' => 'hostinger.hu',
|
||||
'sv_SE' => 'hostinger.se',
|
||||
'da_DK' => 'hostinger.dk',
|
||||
'fi_FI' => 'hostinger.fi',
|
||||
'sk_SK' => 'hostinger.sk',
|
||||
'no_NO' => 'hostinger.no',
|
||||
'hr_HR' => 'hostinger.hr',
|
||||
'zh_HK' => 'hostinger.com.hk',
|
||||
'he_IL' => 'hostinger.co.il',
|
||||
'lv_LV' => 'hostinger.lv',
|
||||
'et_EE' => 'hostinger.ee',
|
||||
'ur_PK' => 'hostinger.pk',
|
||||
);
|
||||
|
||||
public const HOMEPAGE_DISPLAY = 'page';
|
||||
|
||||
/**
|
||||
*
|
||||
* Check if plugin is active
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access public
|
||||
*/
|
||||
public static function is_plugin_active( $plugin_slug ): bool {
|
||||
$active_plugins = (array) get_option( 'active_plugins', array() );
|
||||
foreach ( $active_plugins as $active_plugin ) {
|
||||
if ( strpos( $active_plugin, $plugin_slug . '.php' ) !== false ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function is_preview_domain( $headers = null ): bool {
|
||||
// @codeCoverageIgnoreStart
|
||||
if ( $headers === null && function_exists( 'getallheaders' ) ) {
|
||||
$headers = getallheaders();
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
if ( isset( $headers['X-Preview-Indicator'] ) && $headers['X-Preview-Indicator'] ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function woocommerce_onboarding_choice(): bool {
|
||||
return (bool) get_option( 'hostinger_woo_onboarding_choice', false );
|
||||
}
|
||||
|
||||
public static function generate_bypass_code( $length ) {
|
||||
$characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
||||
$code = '';
|
||||
$max_index = strlen( $characters ) - 1;
|
||||
|
||||
for ( $i = 0; $i < $length; $i++ ) {
|
||||
$random_index = wp_rand( 0, $max_index );
|
||||
$code .= $characters[ $random_index ];
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function get_hostinger_plugin_url(): string {
|
||||
$website_locale = get_locale() ?? 'en_US';
|
||||
$reseller_locale = get_option( 'hostinger_reseller', null );
|
||||
$base_domain = $reseller_locale ?? ( self::HOSTINGER_LOCALES[ $website_locale ] ?? 'hostinger.com' );
|
||||
|
||||
$plugin_url = rtrim( $base_domain, '/' ) . '/';
|
||||
$plugin_url .= str_replace( ABSPATH, '', HOSTINGER_ABSPATH );
|
||||
|
||||
return $plugin_url;
|
||||
}
|
||||
|
||||
public function get_recommended_php_version(): string {
|
||||
$wp_version = get_bloginfo( 'version' );
|
||||
|
||||
if ( empty( $wp_version ) ) {
|
||||
return '8.2';
|
||||
}
|
||||
|
||||
// Remove any additional version info (like -RC1, -beta1, etc.).
|
||||
$wp_version = preg_replace( '/[-+].*$/', '', $wp_version );
|
||||
|
||||
if ( version_compare( $wp_version, '6.6', '>=' ) ) {
|
||||
return '8.2';
|
||||
}
|
||||
|
||||
if ( version_compare( $wp_version, '6.3', '>=' ) ) {
|
||||
return '8.1';
|
||||
}
|
||||
|
||||
if ( version_compare( $wp_version, '5.3', '>=' ) ) {
|
||||
return '7.4';
|
||||
}
|
||||
|
||||
if ( version_compare( $wp_version, '5.0', '>=' ) ) {
|
||||
return '7.3';
|
||||
}
|
||||
|
||||
if ( version_compare( $wp_version, '4.9', '=' ) ) {
|
||||
return '7.2';
|
||||
}
|
||||
|
||||
if ( version_compare( $wp_version, '4.7', '>=' ) ) {
|
||||
return '7.1';
|
||||
}
|
||||
|
||||
if ( version_compare( $wp_version, '4.4', '>=' ) ) {
|
||||
return '7.0';
|
||||
}
|
||||
|
||||
return '5.6';
|
||||
}
|
||||
|
||||
public function get_edit_site_url(): string {
|
||||
if ( wp_is_block_theme() ) {
|
||||
return add_query_arg(
|
||||
array(
|
||||
'canvas' => 'edit',
|
||||
),
|
||||
admin_url( 'site-editor.php' )
|
||||
);
|
||||
}
|
||||
|
||||
$show_on_front = get_option( 'show_on_front' );
|
||||
$front_page_id = get_option( 'page_on_front' );
|
||||
|
||||
if ( $show_on_front === self::HOMEPAGE_DISPLAY && $front_page_id ) {
|
||||
return add_query_arg(
|
||||
array(
|
||||
'post' => $front_page_id,
|
||||
'action' => 'edit',
|
||||
),
|
||||
admin_url( 'post.php' )
|
||||
);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger;
|
||||
|
||||
use Hostinger\Admin\PluginSettings;
|
||||
use Hostinger\Admin\Jobs\NotifyMcpJob;
|
||||
use Hostinger\Mcp\EventHandlerFactory;
|
||||
use Hostinger\WpHelper\Utils;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class Hooks {
|
||||
public function __construct() {
|
||||
add_filter( 'xmlrpc_enabled', array( $this, 'check_xmlrpc_enabled' ) );
|
||||
add_filter( 'wp_is_application_passwords_available', array( $this, 'check_authentication_password_enabled' ) );
|
||||
add_filter( 'wp_headers', array( $this, 'check_pingback' ) );
|
||||
add_filter( 'plugins_loaded', array( $this, 'plugins_loaded' ) );
|
||||
add_action( 'update_option_woocommerce_coming_soon', array( $this, 'litespeed_flush_cache' ) );
|
||||
add_action( 'update_option_woocommerce_store_pages_only', array( $this, 'litespeed_flush_cache' ) );
|
||||
add_action( 'upgrader_process_complete', array( $this, 'disable_auth_passwords_on_update' ), 10, 2 );
|
||||
add_action( 'transition_post_status', array( $this, 'handle_transition_post_status' ), 10, 3 );
|
||||
add_action( 'updated_option', array( $this, 'handle_updated_option' ), 10, 3 );
|
||||
}
|
||||
|
||||
public function handle_transition_post_status( string $new_status, string $old_status, \WP_Post $post ): void {
|
||||
if ( $new_status === 'publish' || $old_status === 'publish' ) {
|
||||
do_action(
|
||||
NotifyMcpJob::JOB_NAME,
|
||||
array(
|
||||
'event' => EventHandlerFactory::MCP_EVENT_PAGE_UPDATED,
|
||||
'post_id' => $post->ID,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function handle_updated_option( string $option, mixed $old_value, mixed $value ): void {
|
||||
if ( $option === 'cron' || $this->is_transient( $option ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $old_value !== $value ) {
|
||||
do_action( NotifyMcpJob::JOB_NAME, array( 'event' => EventHandlerFactory::MCP_EVENT_UPDATED ) );
|
||||
}
|
||||
|
||||
if ( $option === HOSTINGER_PLUGIN_SETTINGS_OPTION && isset( $value['optin_mcp'] ) && isset( $old_value['optin_mcp'] ) ) {
|
||||
if ( $old_value['optin_mcp'] !== $value['optin_mcp'] ) {
|
||||
do_action(
|
||||
NotifyMcpJob::JOB_NAME,
|
||||
array(
|
||||
'event' => EventHandlerFactory::MCP_EVENT_OPTIN_TOGGLED,
|
||||
'value' => $value['optin_mcp'],
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function disable_auth_passwords_on_update( \WP_Upgrader $upgrader_object, array $options ): void {
|
||||
if ( $options['action'] !== 'update' || $options['type'] !== 'plugin' || empty( $options['plugins'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! in_array( 'hostinger/hostinger.php', $options['plugins'], true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$settings = get_option( HOSTINGER_PLUGIN_SETTINGS_OPTION, array() );
|
||||
|
||||
if ( ! empty( $settings['disable_authentication_password'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$options = new DefaultOptions();
|
||||
$options->configure_authentication_password();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function plugins_loaded() {
|
||||
$utils = new Utils();
|
||||
$plugin_settings = new PluginSettings();
|
||||
$settings = $plugin_settings->get_plugin_settings();
|
||||
|
||||
if ( defined( 'WP_CLI' ) && \WP_CLI ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Xmlrpc.
|
||||
if ( $settings->get_disable_xml_rpc() && $utils->isThisPage( 'xmlrpc.php' ) ) {
|
||||
exit( 'Disabled' );
|
||||
}
|
||||
|
||||
// SSL redirect.
|
||||
if ( $settings->get_force_https() && ! is_ssl() ) {
|
||||
if ( isset( $_SERVER['HTTP_HOST'] ) && isset( $_SERVER['REQUEST_URI'] ) ) {
|
||||
$host = sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) );
|
||||
|
||||
if ( $settings->get_force_www() && strpos( $host, 'www.' ) === false ) {
|
||||
$host = 'www.' . $host;
|
||||
}
|
||||
|
||||
wp_safe_redirect( 'https://' . $host . sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ), 301 );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// Force www.
|
||||
if ( $settings->get_force_www() ) {
|
||||
if ( isset( $_SERVER['HTTP_HOST'] ) && isset( $_SERVER['REQUEST_URI'] ) ) {
|
||||
$host = sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) );
|
||||
|
||||
if ( strpos( $host, 'www.' ) === false ) {
|
||||
wp_safe_redirect( 'https://www.' . $host . sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ), 301 );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $headers
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function check_pingback( $headers ) {
|
||||
$plugin_settings = new PluginSettings();
|
||||
$settings = $plugin_settings->get_plugin_settings();
|
||||
|
||||
if ( $settings->get_disable_xml_rpc() ) {
|
||||
unset( $headers['X-Pingback'] );
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function check_xmlrpc_enabled(): bool {
|
||||
$plugin_settings = new PluginSettings();
|
||||
$settings = $plugin_settings->get_plugin_settings();
|
||||
|
||||
if ( $settings->get_disable_xml_rpc() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function check_authentication_password_enabled(): bool {
|
||||
$plugin_settings = new PluginSettings();
|
||||
$settings = $plugin_settings->get_plugin_settings();
|
||||
|
||||
if ( $settings->get_disable_authentication_password() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function litespeed_flush_cache(): void {
|
||||
if ( has_action( 'litespeed_purge_all' ) ) {
|
||||
do_action( 'litespeed_purge_all' );
|
||||
}
|
||||
}
|
||||
|
||||
private function is_transient( $option ): bool {
|
||||
return str_contains( $option, '_transient' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger;
|
||||
|
||||
use Hostinger\Bootstrap;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class Hostinger {
|
||||
protected string $plugin_name = 'Hostinger';
|
||||
protected string $version;
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function bootstrap(): void {
|
||||
$this->version = $this->get_plugin_version();
|
||||
$bootstrap = new Bootstrap();
|
||||
$bootstrap->run();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function run(): void {
|
||||
$this->bootstrap();
|
||||
}
|
||||
/** PHPCS:disable WordPress.NamingConventions.PrefixAllGlobals.VariableConstantNameFound */
|
||||
/**
|
||||
* Define constant
|
||||
*
|
||||
* @param string $name Constant name.
|
||||
* @param string|bool $value Constant value.
|
||||
*/
|
||||
private function define( string $name, $value ): void {
|
||||
if ( ! defined( $name ) ) {
|
||||
define( $name, $value );
|
||||
}
|
||||
}
|
||||
/** PHPCS:enable */
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function get_plugin_version(): string {
|
||||
if ( defined( 'HOSTINGER_VERSION' ) ) {
|
||||
return HOSTINGER_VERSION;
|
||||
}
|
||||
|
||||
return '1.0.0';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class I18n {
|
||||
/**
|
||||
* Load the plugin text domain for translation.
|
||||
*
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public function load_plugin_textdomain(): void {
|
||||
load_plugin_textdomain(
|
||||
'hostinger',
|
||||
false,
|
||||
dirname( plugin_basename( __FILE__ ), 2 ) . '/languages/'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger\LlmsTxtGenerator;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class LlmsTxtFileHelper {
|
||||
|
||||
public const HOSTINGER_LLMSTXT_FILENAME = 'llms.txt';
|
||||
|
||||
public function is_user_generated_file(): bool {
|
||||
if ( ! $this->llmstxt_file_exists() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$content = $this->get_content();
|
||||
|
||||
if ( $content === false ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ! str_contains( $content, LlmsTxtGenerator::HOSTINGER_LLMSTXT_SIGNATURE );
|
||||
}
|
||||
|
||||
public function get_content(): string {
|
||||
global $wp_filesystem;
|
||||
$this->init_wp_filesystem();
|
||||
return $wp_filesystem->get_contents( $this->get_llmstxt_file_path() );
|
||||
}
|
||||
|
||||
public function create( string $content ): void {
|
||||
global $wp_filesystem;
|
||||
$this->init_wp_filesystem();
|
||||
$wp_filesystem->put_contents( $this->get_llmstxt_file_path(), $content );
|
||||
}
|
||||
|
||||
public function delete(): void {
|
||||
if ( $this->llmstxt_file_exists() && ! $this->is_user_generated_file() ) {
|
||||
global $wp_filesystem;
|
||||
$this->init_wp_filesystem();
|
||||
$wp_filesystem->delete( $this->get_llmstxt_file_path() );
|
||||
}
|
||||
}
|
||||
|
||||
public function get_llmstxt_file_path(): string {
|
||||
return ABSPATH . self::HOSTINGER_LLMSTXT_FILENAME;
|
||||
}
|
||||
|
||||
public function get_llmstxt_file_url(): string {
|
||||
return site_url( LlmsTxtFileHelper::HOSTINGER_LLMSTXT_FILENAME );
|
||||
}
|
||||
|
||||
public function llmstxt_file_exists(): bool {
|
||||
return file_exists( $this->get_llmstxt_file_path() );
|
||||
}
|
||||
|
||||
protected function init_wp_filesystem(): void {
|
||||
require_once ABSPATH . '/wp-admin/includes/file.php';
|
||||
WP_Filesystem();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger\LlmsTxtGenerator;
|
||||
|
||||
use Hostinger\Admin\Jobs\LlmsTxtInjectContentJob;
|
||||
use Hostinger\Admin\PluginSettings;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class LlmsTxtGenerator {
|
||||
|
||||
public const HOSTINGER_LLMSTXT_SIGNATURE = '[comment]: # (Generated by Hostinger Tools Plugin)';
|
||||
protected const UTF8_BOM = "\xEF\xBB\xBF";
|
||||
public const HOSTINGER_LLMSTXT_SUPPORTED_POST_TYPES = array(
|
||||
'post' => 'Posts',
|
||||
'page' => 'Pages',
|
||||
'product' => 'Products',
|
||||
);
|
||||
|
||||
/**
|
||||
* Activation and deactivation hooks appears too early,
|
||||
* so the plugin status is not available yet.
|
||||
*
|
||||
* That's why we use a flag this->woocommerce_status to know the plugin status.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected ?string $woocommerce_status = null;
|
||||
protected PluginSettings $plugin_settings;
|
||||
protected LlmsTxtFileHelper $file_helper;
|
||||
protected LlmsTxtParser $parser;
|
||||
|
||||
public function __construct( PluginSettings $plugin_settings, LlmsTxtFileHelper $llmstxt_file_helper, LlmsTxtParser $llms_txt_parser ) {
|
||||
$this->plugin_settings = $plugin_settings;
|
||||
$this->file_helper = $llmstxt_file_helper;
|
||||
$this->parser = $llms_txt_parser;
|
||||
add_action( 'init', array( $this, 'init' ) );
|
||||
}
|
||||
|
||||
public function on_woocommerce_activation(): void {
|
||||
$this->woocommerce_status = 'active';
|
||||
$this->generate();
|
||||
}
|
||||
|
||||
public function on_woocommerce_deactivation(): void {
|
||||
$this->woocommerce_status = 'inactive';
|
||||
$this->generate();
|
||||
}
|
||||
|
||||
public function init(): void {
|
||||
if ( wp_doing_ajax() || wp_doing_cron() || ! current_user_can( 'manage_options' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$settings = $this->plugin_settings->get_plugin_settings();
|
||||
|
||||
if ( $settings->get_enable_llms_txt() && ! $this->file_helper->llmstxt_file_exists() ) {
|
||||
$this->generate();
|
||||
}
|
||||
|
||||
$this->init_hooks();
|
||||
}
|
||||
|
||||
public function on_settings_update( bool $is_enabled ): void {
|
||||
if ( $is_enabled ) {
|
||||
$this->generate();
|
||||
} else {
|
||||
$this->delete();
|
||||
}
|
||||
}
|
||||
|
||||
public function on_post_status_change( string $new_status, string $old_status, \WP_Post $post ): void {
|
||||
if ( ! $this->is_post_type_supported( $post->post_type ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( $new_status === 'publish' || $old_status === 'publish' ) {
|
||||
$this->generate();
|
||||
}
|
||||
}
|
||||
|
||||
public function on_blog_change( mixed $old_value, mixed $new_value ): void {
|
||||
if ( $old_value !== $new_value ) {
|
||||
$this->generate();
|
||||
}
|
||||
}
|
||||
|
||||
public function get_content(): string {
|
||||
$content = self::UTF8_BOM;
|
||||
$content .= $this->parser->inject_title();
|
||||
$content .= $this->parser->inject_site_description();
|
||||
$content .= $this->parser->inject_items( $this->parser->get_by_post_type( 'post' ), 'Posts' );
|
||||
$content .= $this->parser->inject_items( $this->parser->get_by_post_type( 'page' ), 'Pages' );
|
||||
$content .= $this->maybe_inject_woocommerce_products();
|
||||
$content .= $this->maybe_inject_optional_entries();
|
||||
$content .= $this->parser->inject_signature();
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
public function init_hooks(): void {
|
||||
add_action( 'transition_post_status', array( $this, 'on_post_status_change' ), 10, 3 );
|
||||
add_action( 'hostinger_tools_settings_saved', array( $this, 'on_settings_saved' ) );
|
||||
add_action( 'update_option_blogname', array( $this, 'on_blog_change' ), 10, 2 );
|
||||
add_action( 'update_option_blogdescription', array( $this, 'on_blog_change' ), 10, 2 );
|
||||
add_action( 'activate_woocommerce/woocommerce.php', array( $this, 'on_woocommerce_activation' ) );
|
||||
add_action( 'deactivate_woocommerce/woocommerce.php', array( $this, 'on_woocommerce_deactivation' ) );
|
||||
}
|
||||
|
||||
public function on_settings_saved( array $settings ): void {
|
||||
if ( $settings['enable_llms_txt'] ) {
|
||||
$this->generate();
|
||||
} else {
|
||||
$this->delete();
|
||||
}
|
||||
}
|
||||
|
||||
public function generate(): void {
|
||||
$this->file_helper->create( $this->get_content() );
|
||||
do_action( LlmsTxtInjectContentJob::JOB_NAME, array( 'post_type' => 'post' ) );
|
||||
do_action( LlmsTxtInjectContentJob::JOB_NAME, array( 'post_type' => 'page' ) );
|
||||
|
||||
if ( $this->is_woocommerce_active() ) {
|
||||
do_action( LlmsTxtInjectContentJob::JOB_NAME, array( 'post_type' => 'product' ) );
|
||||
}
|
||||
}
|
||||
|
||||
protected function delete(): void {
|
||||
$this->file_helper->delete();
|
||||
}
|
||||
|
||||
protected function is_post_type_supported( string $post_type ): bool {
|
||||
return array_key_exists( $post_type, self::HOSTINGER_LLMSTXT_SUPPORTED_POST_TYPES );
|
||||
}
|
||||
|
||||
protected function is_woocommerce_active(): bool {
|
||||
return ( is_null( $this->woocommerce_status ) && is_plugin_active( 'woocommerce/woocommerce.php' ) ) || $this->woocommerce_status === 'active';
|
||||
}
|
||||
|
||||
protected function maybe_inject_woocommerce_products(): string {
|
||||
if ( ! $this->is_woocommerce_active() ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->parser->inject_items( $this->parser->get_by_post_type( 'product' ), 'Products' );
|
||||
}
|
||||
|
||||
protected function maybe_inject_optional_entries(): string {
|
||||
$entries = array();
|
||||
$mcp_entry = $this->maybe_inject_mcp_agent_entry();
|
||||
|
||||
if ( $mcp_entry ) {
|
||||
$entries[] = $mcp_entry;
|
||||
}
|
||||
|
||||
return $this->parser->inject_optional_entries( $entries );
|
||||
}
|
||||
|
||||
protected function maybe_inject_mcp_agent_entry(): string {
|
||||
$settings = $this->plugin_settings->get_plugin_settings();
|
||||
if ( ! $settings->get_optin_mcp() ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->parser->inject_mcp_agent_entry();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger\LlmsTxtGenerator;
|
||||
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class LlmsTxtParser {
|
||||
|
||||
public const DEFAULT_LIMIT = 100;
|
||||
public const HOSTINGER_LLMSTXT_SIGNATURE = '[comment]: # (Generated by Hostinger Tools Plugin)';
|
||||
|
||||
public function get_by_post_type( string $post_type, int $limit = self::DEFAULT_LIMIT, int $offset = 0 ): array {
|
||||
$limit = apply_filters( 'hostinger_batch_item_limit', $limit );
|
||||
$args = array(
|
||||
'post_type' => $post_type,
|
||||
'post_status' => 'publish',
|
||||
'fields' => 'ids',
|
||||
'posts_per_page' => $limit,
|
||||
'offset' => $offset,
|
||||
);
|
||||
|
||||
return get_posts( $args );
|
||||
}
|
||||
|
||||
public function get_items( array $items ): string {
|
||||
$content = '';
|
||||
foreach ( $items as $item ) {
|
||||
$post = get_post( $item );
|
||||
$title = $post->post_title;
|
||||
$permalink = get_permalink( $post );
|
||||
$excerpt = $this->prepare_excerpt( $post );
|
||||
|
||||
$content .= "- [$title]($permalink)";
|
||||
if ( $excerpt ) {
|
||||
$content .= ": $excerpt";
|
||||
}
|
||||
|
||||
$content .= "\n";
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
public function inject_site_description(): string {
|
||||
$description = get_bloginfo( 'description' );
|
||||
|
||||
return $description ? "> $description\n\n" : '';
|
||||
}
|
||||
|
||||
public function inject_title(): string {
|
||||
$title = get_bloginfo( 'name' ) ? get_bloginfo( 'name' ) : site_url();
|
||||
|
||||
return "# $title\n\n";
|
||||
}
|
||||
|
||||
public function inject_signature(): string {
|
||||
return "\n\n" . self::HOSTINGER_LLMSTXT_SIGNATURE;
|
||||
}
|
||||
|
||||
public function inject_mcp_agent_entry(): string {
|
||||
$domain = parse_url( site_url(), PHP_URL_HOST );
|
||||
|
||||
return "- [Agent (MCP protocol)](websites-agents.hostinger.com/$domain/mcp)";
|
||||
}
|
||||
|
||||
public function inject_items( array $items, string $title ): string {
|
||||
if ( empty( $items ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$content = "\n## $title\n\n";
|
||||
$content .= $this->get_items( $items );
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
public function inject_optional_entries( array $entries ): string {
|
||||
$output = '';
|
||||
|
||||
if ( ! empty( $entries ) ) {
|
||||
$output = "\n## Optional\n\n";
|
||||
$output .= implode( "\n", $entries );
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
public function prepare_excerpt( \WP_Post $item ): string {
|
||||
return str_replace( array( "\r", "\n" ), ' ', strip_tags( wp_trim_excerpt( $item->post_excerpt, $item ) ) );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class Loader {
|
||||
protected array $actions;
|
||||
protected array $filters;
|
||||
|
||||
public function __construct() {
|
||||
$this->actions = array();
|
||||
$this->filters = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $hook
|
||||
* @param mixed $component
|
||||
* @param string $callback
|
||||
* @param int $priority
|
||||
* @param int $accepted_args
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add_action( string $hook, $component, string $callback, int $priority = 10, int $accepted_args = 1 ) {
|
||||
$this->actions = $this->add( $this->actions, $hook, $component, $callback, $priority, $accepted_args );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $hook
|
||||
* @param mixed $component
|
||||
* @param string $callback
|
||||
* @param int $priority
|
||||
* @param int $accepted_args
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add_filter( string $hook, $component, string $callback, int $priority = 10, int $accepted_args = 1 ) {
|
||||
$this->filters = $this->add( $this->filters, $hook, $component, $callback, $priority, $accepted_args );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $hooks
|
||||
* @param string $hook
|
||||
* @param mixed $component
|
||||
* @param string $callback
|
||||
* @param int $priority
|
||||
* @param int $accepted_args
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function add(
|
||||
array $hooks,
|
||||
string $hook,
|
||||
$component,
|
||||
string $callback,
|
||||
int $priority,
|
||||
int $accepted_args
|
||||
): array {
|
||||
$hooks[] = array(
|
||||
'hook' => $hook,
|
||||
'component' => $component,
|
||||
'callback' => $callback,
|
||||
'priority' => $priority,
|
||||
'accepted_args' => $accepted_args,
|
||||
);
|
||||
|
||||
return $hooks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function run(): void {
|
||||
foreach ( $this->filters as $hook ) {
|
||||
add_filter(
|
||||
$hook['hook'],
|
||||
array( $hook['component'], $hook['callback'] ),
|
||||
$hook['priority'],
|
||||
$hook['accepted_args']
|
||||
);
|
||||
}
|
||||
|
||||
foreach ( $this->actions as $hook ) {
|
||||
add_action(
|
||||
$hook['hook'],
|
||||
array( $hook['component'], $hook['callback'] ),
|
||||
$hook['priority'],
|
||||
$hook['accepted_args']
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger\Mcp;
|
||||
|
||||
use Hostinger\Admin\Proxy;
|
||||
use Hostinger\Mcp\Handlers\WebsiteMcpOptInToggled;
|
||||
use Hostinger\Mcp\Handlers\WebsitePageUpdated;
|
||||
use Hostinger\Mcp\Handlers\WebsiteUpdated;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class EventHandlerFactory {
|
||||
|
||||
public const MCP_EVENT_UPDATED = 'wordpress.website.updated';
|
||||
public const MCP_EVENT_PAGE_UPDATED = 'wordpress.website.page_updated';
|
||||
public const MCP_EVENT_OPTIN_TOGGLED = 'wordpress.website.mcp.opt_in_toggled';
|
||||
|
||||
private array $handlers;
|
||||
private Proxy $proxy;
|
||||
|
||||
public function __construct( Proxy $proxy ) {
|
||||
$this->proxy = $proxy;
|
||||
$this->handlers = array(
|
||||
self::MCP_EVENT_UPDATED => WebsiteUpdated::class,
|
||||
self::MCP_EVENT_PAGE_UPDATED => WebsitePageUpdated::class,
|
||||
self::MCP_EVENT_OPTIN_TOGGLED => WebsiteMcpOptInToggled::class,
|
||||
);
|
||||
}
|
||||
|
||||
public function get_handler( string $event ) {
|
||||
$handler = $this->handlers[ $event ] ?? false;
|
||||
if ( ! $handler ) {
|
||||
throw new \WP_Exception( 'Invalid event' );
|
||||
}
|
||||
|
||||
return new $handler( $this->proxy );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger\Mcp\Handlers;
|
||||
|
||||
use Hostinger\Admin\Proxy;
|
||||
use Hostinger\Mcp\EventHandlerFactory;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
abstract class EventHandler {
|
||||
|
||||
public Proxy $proxy;
|
||||
|
||||
public function __construct( Proxy $proxy ) {
|
||||
$this->proxy = $proxy;
|
||||
}
|
||||
|
||||
protected function send_to_proxy( array $args = array() ): bool {
|
||||
if ( ! $this->can_send( $args ) ) {
|
||||
$this->debug_mcp( 'Event failed: User is not opted in' . ' -- ' . print_r( $args, true ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
$event = $args['event'];
|
||||
$request = $this->proxy->trigger_event( $event, $args );
|
||||
if ( is_wp_error( $request ) ) {
|
||||
$this->debug_mcp( 'Event failed: ' . $event . $request->get_error_message() . ' -- ' . print_r( $args, true ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
$response_code = wp_remote_retrieve_response_code( $request );
|
||||
if ( $response_code < 300 ) {
|
||||
$this->debug_mcp( 'Event sent: ' . $event . ' -- ' . print_r( $args, true ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->debug_mcp( 'Event failed: ' . $event . ' --' . $response_code . ' -- ' . print_r( $args, true ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
public function can_send( array $args = array() ): bool {
|
||||
return isset( $args['event'] ) && ( $this->is_optin_event( $args['event'] ) || $this->is_opted_in() );
|
||||
}
|
||||
|
||||
private function is_opted_in(): bool {
|
||||
$settings = get_option( HOSTINGER_PLUGIN_SETTINGS_OPTION, array() );
|
||||
return $settings['optin_mcp'] ?? false;
|
||||
}
|
||||
|
||||
private function is_optin_event( $event ): bool {
|
||||
return $event === EventHandlerFactory::MCP_EVENT_OPTIN_TOGGLED;
|
||||
}
|
||||
|
||||
private function debug_mcp( string $msg ): void {
|
||||
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
|
||||
error_log( 'Hostinger Tools MCP: ' . $msg . PHP_EOL, 3, WP_CONTENT_DIR . '/mcp.log' );
|
||||
}
|
||||
}
|
||||
|
||||
abstract public function send( array $args = array() ): void;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger\Mcp\Handlers;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
class WebsiteMcpOptInToggled extends EventHandler {
|
||||
|
||||
public function send( array $args = array() ): void {
|
||||
$this->send_to_proxy( $args );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger\Mcp\Handlers;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class WebsitePageUpdated extends EventHandler {
|
||||
|
||||
const ALLOWED_POST_TYPES = array( 'post', 'product', 'page' );
|
||||
|
||||
public function send( array $args = array() ): void {
|
||||
if ( ! $this->can_send( $args ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$post = get_post( $args['post_id'] );
|
||||
$args['url'] = get_permalink( $post );
|
||||
$this->send_to_proxy( $args );
|
||||
}
|
||||
|
||||
public function can_send( array $args = array() ): bool {
|
||||
$post_id = $args['post_id'] ?? false;
|
||||
|
||||
if ( ! $post_id ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$post = get_post( $post_id );
|
||||
if ( ! $post ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return parent::can_send( $args ) && in_array( $post->post_type, self::ALLOWED_POST_TYPES, true );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger\Mcp\Handlers;
|
||||
|
||||
|
||||
use Hostinger\Admin\Jobs\ActionScheduler;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
class WebsiteUpdated extends EventHandler {
|
||||
|
||||
const MCP_SITE_TRANSIENT = 'hostinger_mcp_site_status';
|
||||
const MCP_SITE_TRANSIENT_LIFETIME = 1200; // 20 mins
|
||||
|
||||
public function send( array $args = array() ): void {
|
||||
if ( ! $this->can_send( $args ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$status = ActionScheduler::STATUS_FAILED;
|
||||
|
||||
if ( $this->send_to_proxy( $args ) ) {
|
||||
$status = ActionScheduler::STATUS_COMPLETE;
|
||||
}
|
||||
|
||||
set_transient( self::MCP_SITE_TRANSIENT, $status, self::MCP_SITE_TRANSIENT_LIFETIME );
|
||||
}
|
||||
|
||||
public function can_send( array $args = array() ): bool {
|
||||
return parent::can_send( $args ) && get_transient( self::MCP_SITE_TRANSIENT ) !== ActionScheduler::STATUS_COMPLETE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger\Rest;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
die;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for handling Rest Api Routes
|
||||
*/
|
||||
class Routes {
|
||||
/**
|
||||
* @var Settings
|
||||
*/
|
||||
private SettingsRoutes $settings_routes;
|
||||
|
||||
/**
|
||||
* @param SettingsRoutes $settings_routes Settings route class.
|
||||
*/
|
||||
public function __construct( SettingsRoutes $settings_routes ) {
|
||||
$this->settings_routes = $settings_routes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init rest routes
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init(): void {
|
||||
add_action( 'rest_api_init', array( $this, 'register_routes' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function register_routes() {
|
||||
// Register Settings Rest API Routes.
|
||||
$this->register_settings_routes();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function register_settings_routes(): void {
|
||||
// Return settings.
|
||||
register_rest_route(
|
||||
HOSTINGER_PLUGIN_REST_API_BASE,
|
||||
'get-settings',
|
||||
array(
|
||||
'methods' => 'GET',
|
||||
'callback' => array( $this->settings_routes, 'get_settings' ),
|
||||
'permission_callback' => array( $this, 'permission_check' ),
|
||||
)
|
||||
);
|
||||
|
||||
// Update settings.
|
||||
register_rest_route(
|
||||
HOSTINGER_PLUGIN_REST_API_BASE,
|
||||
'update-settings',
|
||||
array(
|
||||
'methods' => 'POST',
|
||||
'callback' => array( $this->settings_routes, 'update_settings' ),
|
||||
'permission_callback' => array( $this, 'permission_check' ),
|
||||
)
|
||||
);
|
||||
|
||||
// Regenerate bypass link.
|
||||
register_rest_route(
|
||||
HOSTINGER_PLUGIN_REST_API_BASE,
|
||||
'regenerate-bypass-code',
|
||||
array(
|
||||
'methods' => 'GET',
|
||||
'callback' => array( $this->settings_routes, 'regenerate_bypass_code' ),
|
||||
'permission_callback' => array( $this, 'permission_check' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WP_REST_Request $request WordPress rest request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
/** PHPCS:disable Generic.CodeAnalysis.UnusedFunctionParameter.Found */
|
||||
public function permission_check( $request ): bool {
|
||||
// Workaround if Rest Api endpoint cache is enabled.
|
||||
// We don't want to cache these requests.
|
||||
if ( has_action( 'litespeed_control_set_nocache' ) ) {
|
||||
do_action(
|
||||
'litespeed_control_set_nocache',
|
||||
'Custom Rest API endpoint, not cacheable.'
|
||||
);
|
||||
}
|
||||
|
||||
if ( empty( is_user_logged_in() ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Implement custom capabilities when needed.
|
||||
return current_user_can( 'manage_options' );
|
||||
}
|
||||
}
|
||||
/** PHPCS:enable */
|
||||
@@ -0,0 +1,315 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger\Rest;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
die;
|
||||
}
|
||||
|
||||
use Hostinger\Admin\Options\PluginOptions;
|
||||
use Hostinger\Admin\PluginSettings;
|
||||
use Hostinger\Helper;
|
||||
|
||||
/**
|
||||
* Class for handling Settings Rest API
|
||||
*/
|
||||
class SettingsRoutes {
|
||||
|
||||
|
||||
/**
|
||||
* @var PluginSettings plugin settings.
|
||||
*/
|
||||
private PluginSettings $plugin_settings;
|
||||
|
||||
/**
|
||||
* Construct class with dependencies
|
||||
*
|
||||
* @param PluginSettings $plugin_settings instance.
|
||||
*/
|
||||
public function __construct( PluginSettings $plugin_settings ) {
|
||||
$this->plugin_settings = $plugin_settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all stored plugin settings
|
||||
*
|
||||
* @param WP_REST_Request $request WordPress rest request.
|
||||
*
|
||||
* @return \WP_REST_Response
|
||||
*/
|
||||
|
||||
/** PHPCS:disable Generic.CodeAnalysis.UnusedFunctionParameter.Found */
|
||||
public function get_settings( \WP_REST_Request $request ): \WP_REST_Response {
|
||||
global $wp_version;
|
||||
$plugin_settings = $this->plugin_settings->get_plugin_settings();
|
||||
|
||||
$data = array(
|
||||
'newest_wp_version' => $this->get_latest_wordpress_version(),
|
||||
'current_wp_version' => $wp_version,
|
||||
'php_version' => phpversion(),
|
||||
'newest_php_version' => '8.2', // Will be refactored.
|
||||
'is_eligible_www_redirect' => $this->is_eligible_www_redirect(),
|
||||
'optin_mcp' => $plugin_settings->get_optin_mcp(),
|
||||
);
|
||||
|
||||
$hostinger_plugin_settings = get_option( HOSTINGER_PLUGIN_SETTINGS_OPTION, array() );
|
||||
|
||||
if ( empty( $plugin_settings->get_bypass_code() ) ) {
|
||||
if ( empty( $hostinger_plugin_settings['bypass_code'] ) ) {
|
||||
$hostinger_plugin_settings['bypass_code'] = Helper::generate_bypass_code( 16 );
|
||||
}
|
||||
}
|
||||
|
||||
$plugin_settings = $plugin_settings->to_array();
|
||||
|
||||
$response = array(
|
||||
'data' => array_merge( $data, $plugin_settings ),
|
||||
);
|
||||
|
||||
$response = new \WP_REST_Response( $response );
|
||||
|
||||
$response->set_headers( array( 'Cache-Control' => 'no-cache' ) );
|
||||
|
||||
$response->set_status( \WP_Http::OK );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \WP_REST_Request $request
|
||||
*
|
||||
* @return \WP_REST_Response
|
||||
*/
|
||||
public function regenerate_bypass_code( \WP_REST_Request $request ): \WP_REST_Response {
|
||||
$settings = $this->plugin_settings->get_plugin_settings();
|
||||
|
||||
$settings->set_bypass_code( Helper::generate_bypass_code( 16 ) );
|
||||
|
||||
$new_settings = $settings->to_array();
|
||||
|
||||
$new_plugin_options = new PluginOptions( $new_settings );
|
||||
|
||||
$response = new \WP_REST_Response( array( 'data' => $this->plugin_settings->save_plugin_settings( $new_plugin_options )->to_array() ) );
|
||||
|
||||
$response->set_headers( array( 'Cache-Control' => 'no-cache' ) );
|
||||
|
||||
$response->set_status( \WP_Http::OK );
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \WP_REST_Request $request
|
||||
*
|
||||
* @return \WP_REST_Response
|
||||
*/
|
||||
public function update_settings( \WP_REST_Request $request ): \WP_REST_Response {
|
||||
$settings = $this->plugin_settings->get_plugin_settings();
|
||||
|
||||
$new_settings = array();
|
||||
|
||||
$parameters = $request->get_params();
|
||||
|
||||
foreach ( $settings->to_array() as $field_key => $field_value ) {
|
||||
if ( $field_key === 'bypass_code' ) {
|
||||
$new_settings[ $field_key ] = $field_value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( isset( $parameters[ $field_key ] ) ) {
|
||||
$new_settings[ $field_key ] = ! empty( $parameters[ $field_key ] );
|
||||
} else {
|
||||
$new_settings[ $field_key ] = $field_value;
|
||||
}
|
||||
|
||||
if ( $this->has_changed( $field_key, $new_settings[ $field_key ] ) ) {
|
||||
do_action( "hostinger_tools_setting_{$field_key}_update", $new_settings[ $field_key ] );
|
||||
}
|
||||
}
|
||||
|
||||
$new_plugin_options = new PluginOptions( $new_settings );
|
||||
|
||||
$response = new \WP_REST_Response( array( 'data' => $this->plugin_settings->save_plugin_settings( $new_plugin_options )->to_array() ) );
|
||||
|
||||
$this->update_urls( $new_plugin_options );
|
||||
|
||||
$response->set_headers( array( 'Cache-Control' => 'no-cache' ) );
|
||||
|
||||
$response->set_status( \WP_Http::OK );
|
||||
|
||||
if ( has_action( 'litespeed_purge_all' ) ) {
|
||||
do_action( 'litespeed_purge_all' );
|
||||
}
|
||||
|
||||
do_action( 'hostinger_tools_settings_saved', $new_settings );
|
||||
|
||||
return $response;
|
||||
}
|
||||
/** PHPCS:enable */
|
||||
/**
|
||||
* @param PluginOptions $plugin_options
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function update_urls( PluginOptions $plugin_options ): bool {
|
||||
$siteurl = get_option( 'siteurl' );
|
||||
$home = get_option( 'home' );
|
||||
|
||||
if ( empty( $siteurl ) || empty( $home ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $plugin_options->get_force_https() ) {
|
||||
$siteurl = str_replace( 'http://', 'https://', $siteurl );
|
||||
$home = str_replace( 'http://', 'https://', $home );
|
||||
}
|
||||
|
||||
if ( $this->is_eligible_www_redirect() ) {
|
||||
if ( $plugin_options->get_force_www() ) {
|
||||
$siteurl = $this->add_www_to_url( $siteurl );
|
||||
$home = $this->add_www_to_url( $home );
|
||||
} else {
|
||||
$siteurl = str_replace( 'www.', '', $siteurl );
|
||||
$home = str_replace( 'www.', '', $home );
|
||||
}
|
||||
}
|
||||
|
||||
update_option( 'siteurl', $siteurl );
|
||||
update_option( 'home', $home );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private function add_www_to_url( string $url ): string {
|
||||
$parsed_url = wp_parse_url( $url );
|
||||
|
||||
if ( isset( $parsed_url['host'] ) ) {
|
||||
$host = $parsed_url['host'];
|
||||
|
||||
if ( strpos( $host, 'www.' ) !== 0 ) {
|
||||
$host = 'www.' . $host;
|
||||
}
|
||||
|
||||
$parsed_url['host'] = $host;
|
||||
|
||||
return $this->rebuild_url( $parsed_url );
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $params
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function rebuild_url( array $params ): string {
|
||||
$scheme = isset( $params['scheme'] ) ? $params['scheme'] . '://' : '';
|
||||
$host = isset( $params['host'] ) ? $params['host'] : '';
|
||||
$path = isset( $params['path'] ) ? $params['path'] : '';
|
||||
$query = isset( $params['query'] ) ? '?' . $params['query'] : '';
|
||||
$fragment = isset( $params['fragment'] ) ? '#' . $params['fragment'] : '';
|
||||
|
||||
return "$scheme$host$path$query$fragment";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function get_latest_wordpress_version(): string {
|
||||
$newest_wordpress_version = get_transient( 'hostinger_newest_wordpress_version' );
|
||||
|
||||
if ( $newest_wordpress_version !== false ) {
|
||||
return $newest_wordpress_version;
|
||||
}
|
||||
|
||||
$newest_wordpress_version = $this->fetch_wordpress_version();
|
||||
|
||||
if ( ! empty( $newest_wordpress_version ) ) {
|
||||
set_transient( 'hostinger_newest_wordpress_version', $newest_wordpress_version, 86400 );
|
||||
|
||||
return $newest_wordpress_version;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function fetch_wordpress_version(): string {
|
||||
$url = 'https://api.wordpress.org/core/version-check/1.7/';
|
||||
$response = wp_remote_get( $url );
|
||||
|
||||
if ( is_wp_error( $response ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$response_body = wp_remote_retrieve_body( $response );
|
||||
|
||||
if ( $response_body === false ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$data = json_decode( $response_body, true );
|
||||
|
||||
if ( json_last_error() !== JSON_ERROR_NONE || empty( $data['offers'][0]['current'] ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $data['offers'][0]['current'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function is_eligible_www_redirect(): bool {
|
||||
$is_eligible_www_redirect = get_transient( 'hostinger_is_eligible_www_redirect' );
|
||||
|
||||
if ( $is_eligible_www_redirect !== false ) {
|
||||
return $is_eligible_www_redirect;
|
||||
}
|
||||
|
||||
$domain = str_replace( 'www.', '', get_option( 'siteurl' ) );
|
||||
$www_domain = $this->add_www_to_url( $domain );
|
||||
|
||||
$is_eligible_www_redirect = $this->check_domain_records( $domain, $www_domain );
|
||||
|
||||
if ( isset( $is_eligible_www_redirect ) ) {
|
||||
set_transient( 'hostinger_is_eligible_www_redirect', $is_eligible_www_redirect, 120 );
|
||||
|
||||
return $is_eligible_www_redirect;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the field has changed.
|
||||
*/
|
||||
private function has_changed( string $field, mixed $new_value ): bool {
|
||||
$settings = $this->plugin_settings->get_plugin_settings();
|
||||
$settings = $settings->to_array();
|
||||
|
||||
if ( ! array_key_exists( $field, $settings ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $new_value !== $settings[ $field ];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $domain_a
|
||||
* @param string $domain_b
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function check_domain_records( string $domain_a, string $domain_b ): bool {
|
||||
return ( gethostbyname( $domain_a ) === gethostbyname( $domain_b ) );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace Hostinger;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
class Settings {
|
||||
public const MYSELF = 'myself';
|
||||
public const FREELANCER = 'freelancer';
|
||||
public const DEVELOPER = 'developer';
|
||||
public const OTHER = 'other';
|
||||
public const BUSINESS_BEGINNER_SEGMENT = 'business_beginner';
|
||||
public const LEARNER_SEGMENT = 'learner';
|
||||
public const BUSINESS_OWNER_SEGMENT = 'business_owner';
|
||||
public const WEBSITE_TYPE_BUSINESS = 'business';
|
||||
public const WEBSITE_TYPE_PORTFOLIO = 'portfolio';
|
||||
public const WEBSITE_TYPE_BLOG = 'blog';
|
||||
public const SITE_TITLE_OPTION = 'blogname';
|
||||
|
||||
public function __construct() {
|
||||
if ( ! $this->get_setting( 'user_segment' ) ) {
|
||||
$this->set_user_segment();
|
||||
}
|
||||
}
|
||||
|
||||
public function set_user_segment(): void {
|
||||
$created_by = self::get_setting( 'survey.website.created_by' );
|
||||
$created_for = self::get_setting( 'survey.website.for' );
|
||||
$need_help = self::get_setting( 'survey.website.need_help' );
|
||||
$work_at = self::get_setting( 'survey.work_at' );
|
||||
|
||||
if ( $this->is_business_beginner( $created_by, $created_for, $need_help ) ) {
|
||||
self::update_setting( 'user_segment', self::BUSINESS_BEGINNER_SEGMENT );
|
||||
}
|
||||
|
||||
if ( $this->is_learner( $work_at, $need_help ) ) {
|
||||
self::update_setting( 'user_segment', self::LEARNER_SEGMENT );
|
||||
}
|
||||
|
||||
if ( $this->is_bussiness_owner( $created_for, $created_by ) ) {
|
||||
self::update_setting( 'user_segment', self::BUSINESS_OWNER_SEGMENT );
|
||||
}
|
||||
}
|
||||
|
||||
private function is_business_beginner( string $created_by, string $created_for, bool $need_help ): bool {
|
||||
return $created_by === self::MYSELF && $created_for === self::MYSELF && $need_help;
|
||||
}
|
||||
|
||||
private function is_learner( string $work_at, bool $need_help ): bool {
|
||||
return $work_at === self::FREELANCER && $need_help;
|
||||
}
|
||||
|
||||
private function is_bussiness_owner( string $created_for, string $created_by ): bool {
|
||||
return $created_for === self::MYSELF && ( $created_by === self::DEVELOPER || $created_by === self::OTHER );
|
||||
}
|
||||
|
||||
public static function get_setting( string $setting ): string {
|
||||
|
||||
if ( $setting ) {
|
||||
return get_option( 'hostinger_' . $setting, '' );
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
public static function update_setting( string $setting, $value, $autoload = null ): void {
|
||||
|
||||
if ( $setting ) {
|
||||
update_option( 'hostinger_' . $setting, $value, $autoload );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new Settings();
|
||||
@@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<html <?php language_attributes(); ?>>
|
||||
<head>
|
||||
<meta charset="<?php bloginfo( 'charset' ); ?>">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title><?php echo esc_html__( 'Coming Soon', 'hostinger' ); ?></title>
|
||||
<style>
|
||||
html *:not(body):not(.hsr-coming-soon-body > *) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hsr-coming-soon-body {
|
||||
display: flex !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="hostinger">
|
||||
<div class="hsr-coming-soon-body">
|
||||
<img alt="logo" class="hsr-logo"
|
||||
src="<?php echo esc_url( HOSTINGER_PLUGIN_URL . 'assets/images/logo-black.svg' ); ?>">
|
||||
<img alt="illustration" class="hsr-coming-soon-illustration"
|
||||
src="<?php echo esc_url( HOSTINGER_PLUGIN_URL . 'assets/images/illustration.png' ); ?>">
|
||||
<h3>
|
||||
<?php echo esc_html__( 'Coming Soon', 'hostinger' ); ?>
|
||||
</h3>
|
||||
<p>
|
||||
<?php echo esc_html__( 'New WordPress website is being built and will be published soon', 'hostinger' ); ?>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<?php wp_footer(); ?>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user