Initial commit: Atomaste website

This commit is contained in:
2025-12-10 12:17:30 -05:00
commit 0b9e5d1605
19260 changed files with 5206382 additions and 0 deletions

View File

@@ -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 cant 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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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 );
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}

View File

@@ -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 );
}
}

View File

@@ -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;
}
}
}

View File

@@ -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
}
}

View File

@@ -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(),
);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
);
}
}
}

View File

@@ -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>