Version: 1.0.1-hotfix - Pages excluded

This commit is contained in:
MOH 2025-09-07 03:35:24 +02:00
parent 3f07806aaf
commit 25702721c1
4 changed files with 260 additions and 106 deletions

View File

@ -1,39 +1,8 @@
<?php <?php
/************************************************************************************
* Pausera Access control shim (disabled)
* This file intentionally does nothing. The redirect logic lives in the Pausera class.
************************************************************************************/
if (!defined('ABSPATH')) exit; if (!defined('ABSPATH')) exit;
// Intentionally left blank. The previous dynamic "add_method" logic was removed.
if (!class_exists('Pausera')) return;
if (!method_exists('Pausera', 'handle_redirect')) {
Pausera::add_method('handle_redirect', function () {
$options = $this->options;
if (empty($options['enable_maintenance'])) return;
if (is_user_logged_in()) {
$user = wp_get_current_user();
foreach ($user->roles as $role) {
if (in_array($role, $options['excluded_roles'])) return;
}
}
if (in_array($_SERVER['REMOTE_ADDR'], $options['excluded_ips'])) return;
global $wp_query;
if (!$options['enable_maintenance'] && $wp_query->is_404()) return;
$current_path = trim(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), '/');
$redirect_path = trim($options['redirect_url'], '/');
if ($current_path === $redirect_path) return;
foreach ($options['excluded_slugs'] as $slug) {
if (stripos($current_path, $slug) !== false) return;
}
if (!is_admin()) {
wp_redirect(home_url($options['redirect_url']));
exit;
}
});
}

View File

@ -1,15 +1,20 @@
<?php <?php
/************************************************************************************
* Pausera Admin UI renderer (hook: pausera_render_ui)
************************************************************************************/
if (!defined('ABSPATH')) exit; if (!defined('ABSPATH')) exit;
add_action('pausera_render_ui', function () { add_action('pausera_render_ui', function () {
$settings = get_option('pausera_settings', []); $settings = get_option('pausera_settings', []);
$enabled = !empty($settings['enable_maintenance']); $enabled = !empty($settings['enable_maintenance']);
$excluded_ips = isset($settings['excluded_ips']) ? implode(',', $settings['excluded_ips']) : ''; $excluded_ips = isset($settings['excluded_ips']) ? implode(',', (array) $settings['excluded_ips']) : '';
$redirect_url = $settings['redirect_url'] ?? '/maintenance'; $redirect_url = $settings['redirect_url'] ?? '/maintenance';
$excluded_roles = $settings['excluded_roles'] ?? []; $excluded_roles = (array)($settings['excluded_roles'] ?? []);
$allowed_users = $settings['allowed_users'] ?? []; $allowed_users = (array)($settings['allowed_users'] ?? []);
$plugin_data = get_plugin_data(PAUSERA_PATH . 'pausera.php'); $excluded_slugs = (array)($settings['excluded_slugs'] ?? []);
$plugin_data = function_exists('get_plugin_data') ? get_plugin_data(PAUSERA_PATH . 'pausera.php') : ['Version' => '—'];
global $wp_roles; global $wp_roles;
$all_users = get_users(['fields' => ['user_login']]); $all_users = get_users(['fields' => ['user_login']]);
@ -17,7 +22,6 @@ add_action('pausera_render_ui', function () {
return $u->user_login; return $u->user_login;
}, $all_users); }, $all_users);
?> ?>
<div class="pausera-status-bar"> <div class="pausera-status-bar">
Aktueller Status: <?php echo $enabled ? 'Wartungsmodus AKTIV' : 'Deaktiviert'; ?> Aktueller Status: <?php echo $enabled ? 'Wartungsmodus AKTIV' : 'Deaktiviert'; ?>
</div> </div>
@ -37,11 +41,6 @@ add_action('pausera_render_ui', function () {
<form method="post" action="options.php" id="pausera-form"> <form method="post" action="options.php" id="pausera-form">
<?php settings_fields('pausera_group'); ?> <?php settings_fields('pausera_group'); ?>
<div class="pausera-card toggle-box <?php echo $enabled ? 'active' : ''; ?>" id="maintenance-toggle"> <div class="pausera-card toggle-box <?php echo $enabled ? 'active' : ''; ?>" id="maintenance-toggle">
<input type="checkbox" id="maintenance_toggle_input" name="pausera_settings[enable_maintenance]" value="1" <?php checked($enabled); ?> /> <input type="checkbox" id="maintenance_toggle_input" name="pausera_settings[enable_maintenance]" value="1" <?php checked($enabled); ?> />
<label for="maintenance_toggle_input"> <label for="maintenance_toggle_input">
@ -51,11 +50,6 @@ add_action('pausera_render_ui', function () {
</label> </label>
</div> </div>
<div class="pausera-card"> <div class="pausera-card">
<h3>Weiterleitungsseite</h3> <h3>Weiterleitungsseite</h3>
<label>Seite auswählen</label> <label>Seite auswählen</label>
@ -63,29 +57,18 @@ add_action('pausera_render_ui', function () {
<?php foreach (get_pages() as $page) : <?php foreach (get_pages() as $page) :
$slug = '/' . basename(get_permalink($page->ID)); $slug = '/' . basename(get_permalink($page->ID));
$selected = selected($redirect_url, $slug, false); $selected = selected($redirect_url, $slug, false);
echo "<option value='{$slug}' {$selected}>{$page->post_title}</option>"; echo "<option value='{$slug}' {$selected}>" . esc_html($page->post_title) . "</option>";
endforeach; ?> endforeach; ?>
</select> </select>
</div> </div>
<div class="pausera-card"> <div class="pausera-card">
<h3>Erlaubte Benutzer</h3> <h3>Erlaubte Benutzer</h3>
<label>Mehrere Benutzernamen mit Komma trennen</label> <label>Mehrere Benutzernamen mit Komma trennen</label>
<select name="pausera_settings[allowed_users][]" multiple class="acf-style-select"> <select name="pausera_settings[allowed_users][]" multiple class="acf-style-select">
<?php foreach ($usernames as $username) : <?php foreach ($usernames as $username) :
$selected = in_array($username, $allowed_users) ? 'selected' : ''; $selected = in_array($username, $allowed_users, true) ? 'selected' : '';
echo "<option value='{$username}' {$selected}>{$username}</option>"; echo "<option value='" . esc_attr($username) . "' {$selected}>" . esc_html($username) . "</option>";
endforeach; ?> endforeach; ?>
</select> </select>
</div> </div>
@ -95,12 +78,31 @@ add_action('pausera_render_ui', function () {
<label>Mehrfachauswahl möglich</label> <label>Mehrfachauswahl möglich</label>
<select name="pausera_settings[excluded_roles][]" multiple class="acf-style-select"> <select name="pausera_settings[excluded_roles][]" multiple class="acf-style-select">
<?php foreach ($wp_roles->roles as $key => $role) : <?php foreach ($wp_roles->roles as $key => $role) :
$selected = in_array($key, $excluded_roles) ? 'selected' : ''; $selected = in_array($key, $excluded_roles, true) ? 'selected' : '';
echo "<option value='{$key}' {$selected}>{$role['name']}</option>"; echo "<option value='" . esc_attr($key) . "' {$selected}>" . esc_html($role['name']) . "</option>";
endforeach; ?> endforeach; ?>
</select> </select>
</div> </div>
<div class="pausera-card">
<h3>Ausgeschlossene Seiten (Slugs/Paths)</h3>
<label>Diese Seiten bleiben im Wartungsmodus erreichbar.</label>
<select name="pausera_settings[excluded_slugs][]" multiple class="acf-style-select">
<?php
$pages = get_pages();
foreach ($pages as $page) {
$slug = '/' . basename(get_permalink($page->ID));
$slug = trim($slug, '/');
$selected = in_array($slug, $excluded_slugs, true) ? 'selected' : '';
echo "<option value='" . esc_attr($slug) . "' {$selected}>" . esc_html($page->post_title) . " ({$slug})</option>";
}
?>
</select>
</div>
<div class="pausera-card"> <div class="pausera-card">
<h3>Ausgeschlossene IP-Adressen</h3> <h3>Ausgeschlossene IP-Adressen</h3>
<label>Mehrere IPs mit Komma trennen</label> <label>Mehrere IPs mit Komma trennen</label>
@ -111,7 +113,6 @@ add_action('pausera_render_ui', function () {
<button type="submit" class="pausera-save">Speichern</button> <button type="submit" class="pausera-save">Speichern</button>
</div> </div>
<div class="pausera-confirm-overlay" id="pausera-confirm-box"> <div class="pausera-confirm-overlay" id="pausera-confirm-box">
<div class="pausera-confirm-dialog"> <div class="pausera-confirm-dialog">
<h3>Einstellungen speichern?</h3> <h3>Einstellungen speichern?</h3>
@ -123,9 +124,6 @@ add_action('pausera_render_ui', function () {
</div> </div>
</div> </div>
</form> </form>
</div> </div>
@ -134,12 +132,12 @@ add_action('pausera_render_ui', function () {
<ul> <ul>
<li><strong>Version:</strong> <?php echo esc_html($plugin_data['Version']); ?></li> <li><strong>Version:</strong> <?php echo esc_html($plugin_data['Version']); ?></li>
<li><strong>Autor:</strong> Ihr Name</li> <li><strong>Autor:</strong> Ihr Name</li>
<li><strong>Website:</strong> <a href="https://example.com" target="_blank">example.com</a></li> <li><strong>Website:</strong> <a href="https://example.com" target="_blank" rel="noopener">example.com</a></li>
</ul> </ul>
<h4>Plugin-Links</h4> <h4>Plugin-Links</h4>
<ul> <ul>
<li><a href="https://example.com/docs" target="_blank">📄 Dokumentation</a></li> <li><a href="https://example.com/docs" target="_blank" rel="noopener">📄 Dokumentation</a></li>
<li><a href="mailto:support@example.com">Kontaktieren Sie uns</a></li> <li><a href="mailto:support@example.com">Kontaktieren Sie uns</a></li>
</ul> </ul>
</div> </div>

View File

@ -1,7 +1,12 @@
<?php <?php
/************************************************************************************
* Pausera Admin assets enqueue
************************************************************************************/
if (!defined('ABSPATH')) exit; if (!defined('ABSPATH')) exit;
function pausera_enqueue_assets() { function pausera_enqueue_assets()
{
if (!isset($_GET['page']) || $_GET['page'] !== 'pausera') { if (!isset($_GET['page']) || $_GET['page'] !== 'pausera') {
return; return;
} }
@ -16,7 +21,7 @@ function pausera_enqueue_assets() {
wp_enqueue_script( wp_enqueue_script(
'pausera-js', 'pausera-js',
PAUSERA_URL . 'assets/admin.js', PAUSERA_URL . 'assets/admin.js',
array(), array('jquery'),
filemtime(PAUSERA_PATH . 'assets/admin.js'), filemtime(PAUSERA_PATH . 'assets/admin.js'),
true true
); );
@ -39,13 +44,26 @@ function pausera_enqueue_assets() {
add_action('admin_footer', 'pausera_footer_script'); add_action('admin_footer', 'pausera_footer_script');
} }
function pausera_footer_script() { function pausera_footer_script()
{
echo '<script> echo '<script>
jQuery(document).ready(function($){ jQuery(function($){
$(".acf-style-select").select2(); $(".acf-style-select").select2();
$(".js-tags").select2({
tags: true,
tokenSeparators: [",", " "],
width: "100%",
placeholder: "Slug hinzufügen…",
createTag: function(params) {
var term = $.trim(params.term);
if (term === "") return null;
return { id: term, text: term, newTag: true };
}
});
$(".nav-tab").on("mousedown", function(e){ e.preventDefault(); }); $(".nav-tab").on("mousedown", function(e){ e.preventDefault(); });
}); });
</script>'; </script>';
} }
add_action('admin_enqueue_scripts', 'pausera_enqueue_assets'); add_action('admin_enqueue_scripts', 'pausera_enqueue_assets');

View File

@ -1,25 +1,194 @@
<?php <?php
/**
* Plugin Name: Pausera /************************************************************************************
* Description: Elegant maintenance mode with full control over access, roles, pages and redirects. * Pausera Lightweight maintenance mode with role/user/IP/page exclusions
* Version: 1.0.0 ************************************************************************************/
* Author: Moh Farawati /*
* Text Domain: pausera Plugin Name: Pausera
Description: Maintenance mode with exclusions (roles, users, IPs, specific pages) and a simple admin UI.
Version: 1.0.1-hotfix
Author: Your Name
*/ */
if (!defined('ABSPATH')) exit; if (!defined('ABSPATH')) exit;
define('PAUSERA_PATH', plugin_dir_path(__FILE__)); if (!defined('PAUSERA_PATH')) define('PAUSERA_PATH', plugin_dir_path(__FILE__));
define('PAUSERA_URL', plugin_dir_url(__FILE__)); if (!defined('PAUSERA_URL')) define('PAUSERA_URL', plugin_dir_url(__FILE__));
// Include class only once
if (!class_exists('Pausera')) {
require_once PAUSERA_PATH . 'includes/settings-handler.php';
}
require_once PAUSERA_PATH . 'includes/access-control.php';
require_once PAUSERA_PATH . 'includes/assets.php'; require_once PAUSERA_PATH . 'includes/assets.php';
require_once PAUSERA_PATH . 'includes/admin-ui.php'; require_once PAUSERA_PATH . 'includes/admin-ui.php';
// Initialize plugin class Pausera
new Pausera(); {
private $options;
/************************************************************************************
* constructor
************************************************************************************/
public function __construct()
{
$defaults = [
'enable_maintenance' => false,
'excluded_slugs' => [], // e.g. ["qr", "coming-soon", "shop/qr"]
'excluded_ips' => [],
'excluded_roles' => [],
'allowed_users' => [],
'redirect_url' => '/maintenance',
];
$saved_options = get_option('pausera_settings', []);
$this->options = wp_parse_args($saved_options, $defaults);
add_action('admin_menu', [$this, 'admin_menu']);
add_action('admin_init', [$this, 'register_settings']);
add_action('template_redirect', [$this, 'handle_redirect']);
add_action('admin_bar_menu', [$this, 'add_admin_bar_status'], 100);
}
/************************************************************************************
* settings registration + sanitization
************************************************************************************/
public function register_settings()
{
register_setting('pausera_group', 'pausera_settings', [
'sanitize_callback' => [$this, 'sanitize_settings']
]);
}
public function sanitize_settings($input)
{
add_option('pausera_settings_saved', 1);
// Keep nested paths like "shop/qr" clean
$sanitize_path = function ($val) {
$val = is_string($val) ? trim($val) : '';
$val = trim($val, "/ \t\n\r\0\x0B");
if ($val === '') return '';
$parts = array_filter(explode('/', $val), 'strlen');
$parts = array_map('sanitize_title', $parts);
return implode('/', $parts);
};
$excluded_slugs = [];
if (isset($input['excluded_slugs'])) {
$raw = is_array($input['excluded_slugs']) ? $input['excluded_slugs'] : [$input['excluded_slugs']];
foreach ($raw as $r) {
$s = $sanitize_path($r);
if ($s !== '') $excluded_slugs[] = $s;
}
$excluded_slugs = array_values(array_unique($excluded_slugs));
}
return [
'enable_maintenance' => !empty($input['enable_maintenance']),
'excluded_slugs' => $excluded_slugs,
'excluded_ips' => array_filter(array_map('trim', explode(',', sanitize_text_field($input['excluded_ips'] ?? '')))),
'excluded_roles' => isset($input['excluded_roles']) ? array_filter(array_map('sanitize_text_field', (array) $input['excluded_roles'])) : [],
'redirect_url' => esc_url_raw($input['redirect_url'] ?? '/maintenance'),
'allowed_users' => isset($input['allowed_users']) ? array_filter(array_map('sanitize_user', (array) $input['allowed_users'])) : [],
];
}
/************************************************************************************
* admin UI + notices
************************************************************************************/
public function admin_menu()
{
add_menu_page(
'Pausera',
'Pausera',
'manage_options',
'pausera',
[$this, 'settings_page'],
'dashicons-shield-alt',
80
);
}
public function settings_page()
{
do_action('pausera_render_ui');
}
public function add_admin_bar_status($wp_admin_bar)
{
if (!current_user_can('manage_options')) return;
$enabled = !empty($this->options['enable_maintenance']);
$label = 'Pausera: ' . ($enabled ? 'ON' : 'OFF');
$color = $enabled ? 'orange' : '#999';
$wp_admin_bar->add_node([
'id' => 'pausera_status',
'title' => '<span style="color:' . esc_attr($color) . '; font-weight: bold;">' . esc_html($label) . '</span>',
'href' => admin_url('admin.php?page=pausera'),
'meta' => ['title' => 'Pausera Status']
]);
}
/************************************************************************************
* redirect logic
************************************************************************************/
private function path_matches($current_path, $slug)
{
$slug = trim($slug, "/ \t\n\r\0\x0B");
if ($slug === '') return false;
if ($current_path === $slug) return true; // exact
if (strpos($current_path, $slug . '/') === 0) return true; // prefix segment
return false;
}
private function is_public_system_request($current_path)
{
if (function_exists('wp_doing_ajax') && wp_doing_ajax()) return true;
if (defined('REST_REQUEST') && REST_REQUEST) return true;
if (strpos($current_path, 'wp-json') === 0) return true;
$script = $_SERVER['SCRIPT_NAME'] ?? '';
if (strpos($script, 'wp-login.php') !== false) return true;
if (strpos($script, 'wp-cron.php') !== false) return true;
if (function_exists('is_customize_preview') && is_customize_preview()) return true;
if (is_feed()) return true;
return false;
}
public function handle_redirect()
{
if (empty($this->options['enable_maintenance'])) return;
if (is_user_logged_in()) {
$user = wp_get_current_user();
if (in_array($user->user_login, $this->options['allowed_users'] ?? [], true)) return;
foreach ((array) $user->roles as $role) {
if (in_array($role, (array) $this->options['excluded_roles'], true)) return;
}
}
$remote_ip = $_SERVER['REMOTE_ADDR'] ?? '';
if ($remote_ip && in_array($remote_ip, (array) $this->options['excluded_ips'], true)) return;
$current_path = trim(parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?? '', '/');
$redirect_path = trim($this->options['redirect_url'] ?? '/maintenance', '/');
if ($this->is_public_system_request($current_path)) return;
if ($current_path === $redirect_path) return;
foreach ((array) $this->options['excluded_slugs'] as $slug) {
if ($this->path_matches($current_path, $slug)) return;
}
if (!is_admin()) {
wp_safe_redirect(home_url('/' . ltrim($redirect_path, '/')));
exit;
}
}
}
/************************************************************************************
* bootstrap
************************************************************************************/
add_action('plugins_loaded', function () {
$GLOBALS['pausera_instance'] = new Pausera();
});