*/
private array $settings = [];
public static function instance(): Maintenance_Mode
{
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct()
{
$this->settings = $this->get_settings();
add_action('admin_menu', [$this, 'register_admin_menu']);
add_action('admin_init', [$this, 'register_settings']);
add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_assets']);
add_action('template_redirect', [$this, 'maybe_render_maintenance'], 1);
add_action('admin_post_kng_maintenance_export', [$this, 'handle_export']);
add_action('admin_post_kng_maintenance_import', [$this, 'handle_import']);
add_shortcode('kng_maintenance_page', [$this, 'render_shortcode']);
}
public function register_admin_menu(): void
{
add_submenu_page(
'king-addons',
__('Maintenance Mode', 'king-addons'),
__('Maintenance Mode', 'king-addons'),
'manage_options',
'king-addons-maintenance-mode',
[$this, 'render_admin_page']
);
}
public function register_settings(): void
{
register_setting(
'kng_maintenance_settings_group',
self::OPTION_NAME,
[
'type' => 'array',
'sanitize_callback' => [$this, 'sanitize_settings'],
'default' => $this->get_default_settings(),
]
);
}
public function enqueue_admin_assets(string $hook): void
{
if ($hook !== 'king-addons_page_king-addons-maintenance-mode') {
return;
}
$shared_css = KING_ADDONS_URL . 'includes/admin/layouts/shared/admin-v3-styles.css';
$shared_path = KING_ADDONS_PATH . 'includes/admin/layouts/shared/admin-v3-styles.css';
$shared_version = file_exists($shared_path) ? filemtime($shared_path) : KING_ADDONS_VERSION;
wp_enqueue_style('king-addons-admin-v3', $shared_css, [], $shared_version);
$admin_css = KING_ADDONS_URL . 'includes/extensions/Maintenance_Mode/assets/admin.css';
$admin_path = KING_ADDONS_PATH . 'includes/extensions/Maintenance_Mode/assets/admin.css';
$admin_version = file_exists($admin_path) ? filemtime($admin_path) : KING_ADDONS_VERSION;
wp_enqueue_style('king-addons-maintenance-admin', $admin_css, ['king-addons-admin-v3'], $admin_version);
$admin_js = KING_ADDONS_URL . 'includes/extensions/Maintenance_Mode/assets/admin.js';
$admin_path_js = KING_ADDONS_PATH . 'includes/extensions/Maintenance_Mode/assets/admin.js';
$admin_js_version = file_exists($admin_path_js) ? filemtime($admin_path_js) : KING_ADDONS_VERSION;
wp_enqueue_script('king-addons-maintenance-admin', $admin_js, ['jquery'], $admin_js_version, true);
wp_localize_script('king-addons-maintenance-admin', 'KNGMaintenance', [
'ajaxUrl' => admin_url('admin-ajax.php'),
'themeNonce' => wp_create_nonce('king_addons_dashboard_ui'),
]);
}
public function render_admin_page(): void
{
if (!current_user_can('manage_options')) {
return;
}
$view = isset($_GET['view']) ? sanitize_key($_GET['view']) : 'dashboard';
$is_pro = $this->is_pro();
$settings = $this->get_settings();
$templates = $this->get_builtin_templates();
include __DIR__ . '/templates/admin-page.php';
}
public function maybe_render_maintenance(): void
{
if ($this->is_preview_request()) {
$this->render_maintenance_response(true);
exit;
}
if (!$this->is_mode_active()) {
return;
}
if ($this->should_bypass_request()) {
return;
}
$this->render_maintenance_response(false);
exit;
}
private function is_mode_active(): bool
{
if (empty($this->settings['enabled'])) {
return false;
}
if (empty($this->settings['schedule_enabled'])) {
return true;
}
$start = $this->parse_schedule_time($this->settings['schedule_start'] ?? '');
$end = $this->parse_schedule_time($this->settings['schedule_end'] ?? '');
$now = current_time('timestamp', true);
if ($start && $end) {
return $now >= $start && $now <= $end;
}
if ($start && !$end) {
return $now >= $start;
}
if (!$start && $end) {
return $now <= $end;
}
return false;
}
private function should_bypass_request(): bool
{
if (defined('WP_CLI') && WP_CLI) {
return true;
}
if (wp_doing_cron()) {
return true;
}
if (is_admin()) {
return true;
}
if (wp_doing_ajax() && !empty($this->settings['allow_admin_ajax'])) {
return true;
}
if ($this->is_login_request()) {
return true;
}
if (!empty($this->settings['disable_elementor_editor']) && $this->is_elementor_editor()) {
return true;
}
if ($this->is_rest_request()) {
if (is_user_logged_in()) {
return true;
}
return !empty($this->settings['allow_rest']);
}
if ($this->is_user_allowed()) {
return true;
}
if ($this->is_ip_whitelisted()) {
return true;
}
if ($this->is_path_whitelisted()) {
return true;
}
return false;
}
private function is_user_allowed(): bool
{
if (!is_user_logged_in()) {
return false;
}
$user = wp_get_current_user();
if (!$user || !$user->ID) {
return false;
}
if (!empty($this->settings['exclude_admin']) && user_can($user, 'manage_options')) {
return true;
}
if ($this->is_pro()) {
$allowed_roles = $this->settings['allowed_roles'] ?? [];
if (!empty($allowed_roles) && array_intersect((array) $user->roles, $allowed_roles)) {
return true;
}
}
return false;
}
private function is_ip_whitelisted(): bool
{
$whitelist = $this->settings['whitelist_ips'] ?? [];
if (empty($whitelist)) {
return false;
}
$ip = $this->get_client_ip();
if ($ip === '') {
return false;
}
return in_array($ip, $whitelist, true);
}
private function is_path_whitelisted(): bool
{
$paths = $this->settings['whitelist_paths'] ?? [];
if (empty($paths)) {
return false;
}
$request_uri = isset($_SERVER['REQUEST_URI']) ? (string) wp_unslash($_SERVER['REQUEST_URI']) : '';
$path = wp_parse_url($request_uri, PHP_URL_PATH);
$path = $path ? '/' . ltrim($path, '/') : '/';
foreach ($paths as $allowed) {
if ($allowed !== '/' && strpos($path, $allowed) === 0) {
return true;
}
if ($allowed === $path) {
return true;
}
}
return false;
}
private function is_login_request(): bool
{
$request_uri = isset($_SERVER['REQUEST_URI']) ? (string) wp_unslash($_SERVER['REQUEST_URI']) : '';
return strpos($request_uri, 'wp-login.php') !== false || strpos($request_uri, 'wp-register.php') !== false;
}
private function is_rest_request(): bool
{
return defined('REST_REQUEST') && REST_REQUEST;
}
private function is_elementor_editor(): bool
{
if (isset($_GET['elementor-preview'])) {
return true;
}
if (class_exists('\\Elementor\\Plugin')) {
$plugin = \Elementor\Plugin::instance();
if ($plugin->editor && $plugin->editor->is_edit_mode()) {
return true;
}
}
return false;
}
private function render_maintenance_response(bool $is_preview): void
{
$mode = $this->settings['mode'] ?? 'coming_soon';
$status = $mode === 'maintenance' ? 503 : 200;
if ($is_preview) {
$status = 200;
}
status_header($status);
nocache_headers();
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
$retry_after = (int) ($this->settings['retry_after'] ?? 0);
if ($status === 503 && $retry_after > 0) {
header('Retry-After: ' . $retry_after);
}
if (!empty($this->settings['noindex'])) {
header('X-Robots-Tag: noindex, nofollow', true);
}
$title = $mode === 'maintenance' ? __('Maintenance Mode', 'king-addons') : __('Coming Soon', 'king-addons');
$content = $this->get_rendered_page_content();
$this->enqueue_frontend_assets();
echo '';
echo '';
echo '
';
echo '';
echo '';
echo '' . esc_html($title) . '';
if (!empty($this->settings['noindex'])) {
echo '';
}
wp_head();
echo '';
echo '';
echo $content;
wp_footer();
echo '';
}
private function enqueue_frontend_assets(): void
{
wp_enqueue_style(
'king-addons-maintenance-frontend',
KING_ADDONS_URL . 'includes/extensions/Maintenance_Mode/assets/frontend.css',
[],
KING_ADDONS_VERSION
);
wp_enqueue_script(
'king-addons-maintenance-frontend',
KING_ADDONS_URL . 'includes/extensions/Maintenance_Mode/assets/frontend.js',
[],
KING_ADDONS_VERSION,
true
);
}
private function get_rendered_page_content(): string
{
$theme = $this->settings['theme'] ?? 'dark';
$mode = $this->settings['mode'] ?? 'coming_soon';
$template_source = $this->settings['template_source'] ?? 'built_in';
$wrapper_classes = [
'kng-maintenance',
$theme === 'light' ? 'kng-maintenance-theme-light' : 'kng-maintenance-theme-dark',
$mode === 'maintenance' ? 'kng-maintenance-mode-maintenance' : 'kng-maintenance-mode-coming-soon',
];
$content = '';
if ($template_source === 'page' && !empty($this->settings['page_id'])) {
$content = $this->get_page_content((int) $this->settings['page_id']);
} elseif ($template_source === 'elementor' && !empty($this->settings['elementor_id'])) {
$content = $this->get_elementor_content((int) $this->settings['elementor_id']);
} else {
$content = $this->render_builtin_template((string) $this->settings['template_id']);
}
return '' . $content . '';
}
public function render_shortcode(array $atts): string
{
$atts = shortcode_atts([
'id' => '',
'type' => '',
'theme' => '',
'full_height' => 'false',
], $atts, 'kng_maintenance_page');
$theme = $atts['theme'] !== '' ? sanitize_key($atts['theme']) : ($this->settings['theme'] ?? 'dark');
$theme = $theme === 'light' ? 'light' : 'dark';
$template_source = $this->settings['template_source'] ?? 'built_in';
$template_id = $this->settings['template_id'] ?? 'minimal';
$page_id = (int) ($this->settings['page_id'] ?? 0);
$elementor_id = (int) ($this->settings['elementor_id'] ?? 0);
if ($atts['id'] !== '') {
if (is_numeric($atts['id'])) {
$template_source = 'page';
$page_id = absint($atts['id']);
} else {
$template_source = 'built_in';
$template_id = sanitize_key($atts['id']);
}
}
if ($atts['type'] !== '') {
$template_source = sanitize_key($atts['type']);
if ($template_source === 'elementor' && is_numeric($atts['id'])) {
$elementor_id = absint($atts['id']);
}
}
$content = '';
if ($template_source === 'page' && $page_id) {
$content = $this->get_page_content($page_id);
} elseif ($template_source === 'elementor' && $elementor_id) {
$content = $this->get_elementor_content($elementor_id);
} else {
$content = $this->render_builtin_template($this->ensure_template_allowed($template_id));
}
$this->enqueue_frontend_assets();
$classes = [
'kng-maintenance',
$theme === 'light' ? 'kng-maintenance-theme-light' : 'kng-maintenance-theme-dark',
];
if (filter_var($atts['full_height'], FILTER_VALIDATE_BOOLEAN)) {
$classes[] = 'kng-maintenance-full';
}
return '' . $content . '
';
}
private function get_page_content(int $page_id): string
{
$page = get_post($page_id);
if (!$page) {
return $this->render_builtin_template('minimal');
}
$old_post = $GLOBALS['post'] ?? null;
$GLOBALS['post'] = $page;
setup_postdata($page);
$content = apply_filters('the_content', $page->post_content);
wp_reset_postdata();
$GLOBALS['post'] = $old_post;
return $content;
}
private function get_elementor_content(int $template_id): string
{
if (!class_exists('\\Elementor\\Plugin')) {
return $this->render_builtin_template('minimal');
}
$plugin = \Elementor\Plugin::instance();
if (!$plugin->frontend) {
return $this->render_builtin_template('minimal');
}
$plugin->frontend->enqueue_styles();
$plugin->frontend->enqueue_scripts();
return $plugin->frontend->get_builder_content_for_display($template_id, true);
}
private function render_builtin_template(string $template_id): string
{
$templates = $this->get_builtin_templates();
$template_id = isset($templates[$template_id]) ? $template_id : 'minimal';
$template_id = $this->ensure_template_allowed($template_id);
$mode = $this->settings['mode'] ?? 'coming_soon';
$site_name = get_bloginfo('name');
$tagline = get_bloginfo('description');
$schedule_end = $this->settings['schedule_end'] ?? '';
$content = $this->get_template_content($template_id);
$headline = $mode === 'maintenance'
? __('We are upgrading the experience.', 'king-addons')
: __('A new experience is on the horizon.', 'king-addons');
$subhead = $tagline !== '' ? $tagline : __('Our team is preparing something beautiful. Stay tuned.', 'king-addons');
$mode_label = $mode === 'maintenance' ? __('Maintenance Mode', 'king-addons') : __('Coming Soon', 'king-addons');
$badge = $content['badge'] !== '' ? $content['badge'] : $mode_label;
$headline = $content['headline'] !== '' ? $content['headline'] : $headline;
$subhead = $content['subhead'] !== '' ? $content['subhead'] : $subhead;
$launch_label = '';
if ($content['launch_label'] !== '') {
$launch_label = $content['launch_label'];
}
if ($schedule_end !== '') {
$local_end = get_date_from_gmt($schedule_end, 'Y-m-d H:i');
if ($launch_label === '') {
$launch_label = sprintf(__('Estimated return: %s', 'king-addons'), esc_html($local_end));
}
}
$footer_left = $content['footer_left'] !== '' ? $content['footer_left'] : $site_name;
$footer_right = $content['footer_right'] !== '' ? $content['footer_right'] : __('Powered by King Addons', 'king-addons');
$countdown_days = str_pad((string) (int) ($content['countdown_days'] ?? 0), 2, '0', STR_PAD_LEFT);
$countdown_hours = str_pad((string) (int) ($content['countdown_hours'] ?? 0), 2, '0', STR_PAD_LEFT);
$countdown_minutes = str_pad((string) (int) ($content['countdown_minutes'] ?? 0), 2, '0', STR_PAD_LEFT);
$progress_percent = isset($content['progress_percent']) ? min(100, max(0, (int) $content['progress_percent'])) : 0;
$progress_label = $content['progress_label'] ?? '';
if ($progress_label === '') {
$progress_label = sprintf(__('%d%% complete', 'king-addons'), $progress_percent);
}
$form_placeholder = $content['form_placeholder'] ?? '';
if ($form_placeholder === '') {
$form_placeholder = __('Email address', 'king-addons');
}
$form_button = $content['form_button'] ?? '';
if ($form_button === '') {
$form_button = __('Notify me', 'king-addons');
}
ob_start();
?>
__('Minimal', 'king-addons'),
'dark' => __('Dark', 'king-addons'),
'gradient' => __('Gradient', 'king-addons'),
'countdown' => __('Coming Soon Countdown', 'king-addons'),
'progress' => __('Maintenance Progress', 'king-addons'),
'subscribe' => __('Simple Subscribe', 'king-addons'),
'product-launch' => __('Product Launch', 'king-addons'),
'construction' => __('Under Construction', 'king-addons'),
'split' => __('Split Layout', 'king-addons'),
'logo' => __('Centered Logo', 'king-addons'),
];
}
public function get_pro_templates(): array
{
return [
'countdown',
'progress',
'product-launch',
'split',
];
}
public function handle_export(): void
{
if (!current_user_can('manage_options')) {
wp_die(esc_html__('Unauthorized request.', 'king-addons'));
}
check_admin_referer('kng_maintenance_export');
$settings = $this->get_settings();
header('Content-Type: application/json; charset=utf-8');
header('Content-Disposition: attachment; filename=maintenance-mode-settings.json');
echo wp_json_encode($settings);
exit;
}
public function handle_import(): void
{
if (!current_user_can('manage_options')) {
wp_die(esc_html__('Unauthorized request.', 'king-addons'));
}
check_admin_referer('kng_maintenance_import');
if (empty($_FILES['import_file']) || !isset($_FILES['import_file']['tmp_name'])) {
$this->redirect_with_message('import-export', 'error_import');
}
$file = $_FILES['import_file'];
if (!empty($file['error'])) {
$this->redirect_with_message('import-export', 'error_import');
}
$contents = file_get_contents($file['tmp_name']);
if ($contents === false) {
$this->redirect_with_message('import-export', 'error_import');
}
$decoded = json_decode($contents, true);
if (!is_array($decoded)) {
$this->redirect_with_message('import-export', 'error_import');
}
$sanitized = $this->sanitize_settings($decoded);
update_option(self::OPTION_NAME, $sanitized);
$this->redirect_with_message('import-export', 'imported');
}
private function get_default_settings(): array
{
return [
'enabled' => false,
'mode' => 'coming_soon',
'template_source' => 'built_in',
'template_id' => 'minimal',
'template_content' => [],
'page_id' => 0,
'elementor_id' => 0,
'theme' => 'dark',
'noindex' => true,
'retry_after' => 3600,
'whitelist_ips' => [],
'whitelist_paths' => [],
'exclude_admin' => true,
'allowed_roles' => [],
'schedule_enabled' => false,
'schedule_start' => '',
'schedule_end' => '',
'allow_rest' => true,
'allow_admin_ajax' => true,
'disable_elementor_editor' => true,
'custom_css' => '',
'custom_js' => '',
];
}
public function get_settings(): array
{
$defaults = $this->get_default_settings();
$saved = get_option(self::OPTION_NAME, []);
$settings = wp_parse_args($saved, $defaults);
if (!is_array($settings['template_content'])) {
$settings['template_content'] = $defaults['template_content'];
}
if (!is_array($settings['whitelist_ips'])) {
$settings['whitelist_ips'] = $defaults['whitelist_ips'];
}
if (!is_array($settings['whitelist_paths'])) {
$settings['whitelist_paths'] = $defaults['whitelist_paths'];
}
if (!is_array($settings['allowed_roles'])) {
$settings['allowed_roles'] = $defaults['allowed_roles'];
}
return $settings;
}
public function sanitize_settings(array $settings): array
{
$defaults = $this->get_default_settings();
$existing = $this->get_settings();
$settings = wp_parse_args($settings, $existing);
$templates = array_keys($this->get_builtin_templates());
$pro_templates = $this->get_pro_templates();
$source = isset($settings['template_source']) ? sanitize_key($settings['template_source']) : $defaults['template_source'];
if (!in_array($source, ['built_in', 'page', 'elementor'], true)) {
$source = 'built_in';
}
$template_id = isset($settings['template_id']) ? sanitize_key($settings['template_id']) : $defaults['template_id'];
if (!in_array($template_id, $templates, true)) {
$template_id = $defaults['template_id'];
}
if (!$this->is_pro() && in_array($template_id, $pro_templates, true)) {
$template_id = 'minimal';
}
$mode = isset($settings['mode']) ? sanitize_key($settings['mode']) : $defaults['mode'];
if (!in_array($mode, ['coming_soon', 'maintenance'], true)) {
$mode = $defaults['mode'];
}
$theme = isset($settings['theme']) ? sanitize_key($settings['theme']) : $defaults['theme'];
$theme = $theme === 'light' ? 'light' : 'dark';
$whitelist_ips = $this->sanitize_list($settings['whitelist_ips'] ?? []);
$whitelist_paths = $this->sanitize_list($settings['whitelist_paths'] ?? []);
if (!$this->is_pro()) {
$whitelist_ips = array_slice($whitelist_ips, 0, 10);
$whitelist_paths = array_slice($whitelist_paths, 0, 10);
}
$clean = [
'enabled' => !empty($settings['enabled']),
'mode' => $mode,
'template_source' => $source,
'template_id' => $template_id,
'template_content' => $this->sanitize_template_content($settings['template_content'] ?? []),
'page_id' => absint($settings['page_id'] ?? 0),
'elementor_id' => absint($settings['elementor_id'] ?? 0),
'theme' => $theme,
'noindex' => !empty($settings['noindex']),
'retry_after' => max(0, absint($settings['retry_after'] ?? $defaults['retry_after'])),
'whitelist_ips' => $whitelist_ips,
'whitelist_paths' => $this->normalize_paths($whitelist_paths),
'exclude_admin' => !empty($settings['exclude_admin']),
'allowed_roles' => $this->sanitize_list($settings['allowed_roles'] ?? []),
'schedule_enabled' => !empty($settings['schedule_enabled']),
'schedule_start' => $this->sanitize_datetime($settings['schedule_start'] ?? ''),
'schedule_end' => $this->sanitize_datetime($settings['schedule_end'] ?? ''),
'allow_rest' => !empty($settings['allow_rest']),
'allow_admin_ajax' => !empty($settings['allow_admin_ajax']),
'disable_elementor_editor' => !empty($settings['disable_elementor_editor']),
'custom_css' => '',
'custom_js' => '',
];
if (!$this->is_pro()) {
$clean['allowed_roles'] = [];
}
return $clean;
}
public function get_template_content(string $template_id): array
{
$templates = $this->get_builtin_templates();
$template_id = isset($templates[$template_id]) ? $template_id : 'minimal';
$template_id = $this->ensure_template_allowed($template_id);
$defaults = $this->get_template_content_defaults($template_id);
$saved = $this->settings['template_content'][$template_id] ?? [];
if (!is_array($saved)) {
$saved = [];
}
return wp_parse_args($saved, $defaults);
}
private function get_template_content_defaults(string $template_id): array
{
$mode = $this->settings['mode'] ?? 'coming_soon';
$site_name = get_bloginfo('name');
$tagline = get_bloginfo('description');
$headline = $mode === 'maintenance'
? __('We are upgrading the experience.', 'king-addons')
: __('A new experience is on the horizon.', 'king-addons');
$subhead = $tagline !== '' ? $tagline : __('Our team is preparing something beautiful. Stay tuned.', 'king-addons');
$base = [
'badge' => '',
'headline' => $headline,
'subhead' => $subhead,
'launch_label' => '',
'footer_left' => $site_name,
'footer_right' => __('Powered by King Addons', 'king-addons'),
];
if ($template_id === 'countdown') {
$base['countdown_days'] = '14';
$base['countdown_hours'] = '06';
$base['countdown_minutes'] = '42';
}
if ($template_id === 'progress') {
$base['progress_percent'] = 68;
$base['progress_label'] = __('68% complete', 'king-addons');
}
if (in_array($template_id, ['subscribe', 'product-launch'], true)) {
$base['form_placeholder'] = __('Email address', 'king-addons');
$base['form_button'] = __('Notify me', 'king-addons');
}
if ($template_id === 'split') {
$base['split_title_a'] = __('What is happening', 'king-addons');
$base['split_text_a'] = __('We are refining the experience with faster performance and new visuals.', 'king-addons');
$base['split_title_b'] = __('Stay connected', 'king-addons');
$base['split_text_b'] = __('Follow our updates while we prepare the launch.', 'king-addons');
}
return $base;
}
private function sanitize_template_content(array $content): array
{
$fields = $this->get_template_content_fields();
$clean = [];
foreach ($fields as $template_id => $template_fields) {
$values = isset($content[$template_id]) && is_array($content[$template_id]) ? $content[$template_id] : [];
$template_clean = [];
foreach ($template_fields as $field => $type) {
$value = $values[$field] ?? '';
if ($type === 'int') {
$int = absint($value);
if ($field === 'progress_percent') {
$int = min(100, max(0, $int));
}
if ($field === 'countdown_hours') {
$int = min(23, max(0, $int));
}
if ($field === 'countdown_minutes') {
$int = min(59, max(0, $int));
}
$template_clean[$field] = $int;
} else {
$template_clean[$field] = sanitize_text_field((string) $value);
}
}
$clean[$template_id] = $template_clean;
}
return $clean;
}
private function get_template_content_fields(): array
{
$base = [
'badge' => 'text',
'headline' => 'text',
'subhead' => 'text',
'launch_label' => 'text',
'footer_left' => 'text',
'footer_right' => 'text',
];
return [
'minimal' => $base,
'dark' => $base,
'gradient' => $base,
'construction' => $base,
'logo' => $base,
'countdown' => $base + [
'countdown_days' => 'int',
'countdown_hours' => 'int',
'countdown_minutes' => 'int',
],
'progress' => $base + [
'progress_percent' => 'int',
'progress_label' => 'text',
],
'subscribe' => $base + [
'form_placeholder' => 'text',
'form_button' => 'text',
],
'product-launch' => $base + [
'form_placeholder' => 'text',
'form_button' => 'text',
],
'split' => $base + [
'split_title_a' => 'text',
'split_text_a' => 'text',
'split_title_b' => 'text',
'split_text_b' => 'text',
],
];
}
private function ensure_template_allowed(string $template_id): string
{
if (!$this->is_pro() && in_array($template_id, $this->get_pro_templates(), true)) {
return 'minimal';
}
return $template_id;
}
private function sanitize_list($value): array
{
if (is_array($value)) {
$items = $value;
} else {
$items = preg_split('/\\r\\n|\\r|\\n|,/', (string) $value);
}
$items = array_map('trim', $items);
$items = array_filter($items, static function ($item) {
return $item !== '';
});
$items = array_map('sanitize_text_field', $items);
return array_values(array_unique($items));
}
private function normalize_paths(array $paths): array
{
$normalized = [];
foreach ($paths as $path) {
$path = wp_parse_url($path, PHP_URL_PATH) ?: $path;
$path = '/' . ltrim((string) $path, '/');
$normalized[] = $path === '' ? '/' : $path;
}
return array_values(array_unique($normalized));
}
private function sanitize_datetime(string $value): string
{
$value = sanitize_text_field($value);
if ($value === '') {
return '';
}
$value = str_replace('T', ' ', $value);
if (strlen($value) === 16) {
$value .= ':00';
}
$timestamp = strtotime($value);
if (!$timestamp) {
return '';
}
return get_gmt_from_date($value, 'Y-m-d H:i:s');
}
private function parse_schedule_time(string $value): int
{
if ($value === '') {
return 0;
}
return (int) strtotime($value . ' UTC');
}
private function get_client_ip(): string
{
if (!empty($_SERVER['REMOTE_ADDR']) && filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP)) {
return (string) $_SERVER['REMOTE_ADDR'];
}
return '';
}
private function is_preview_request(): bool
{
if (empty($_GET['kng_maintenance_preview'])) {
return false;
}
if (!current_user_can('manage_options')) {
return false;
}
$nonce = isset($_GET['_kng_preview_nonce']) ? sanitize_text_field(wp_unslash($_GET['_kng_preview_nonce'])) : '';
if ($nonce === '' || !wp_verify_nonce($nonce, 'kng_maintenance_preview')) {
return false;
}
return true;
}
private function is_pro(): bool
{
return function_exists('king_addons_freemius') && king_addons_freemius()->can_use_premium_code__premium_only();
}
private function redirect_with_message(string $view, string $message): void
{
$args = [
'page' => 'king-addons-maintenance-mode',
'view' => $view,
'message' => $message,
];
wp_safe_redirect(add_query_arg($args, admin_url('admin.php')));
exit;
}
}