1*1a97af9eSJohann Duscher<?php 2*1a97af9eSJohann Duscher/** 3*1a97af9eSJohann Duscher * Admin interface for Delete Page Guard pattern validation 4*1a97af9eSJohann Duscher * 5*1a97af9eSJohann Duscher * @license GPL 2 (https://www.gnu.org/licenses/gpl-2.0.html) - see LICENSE.md 6*1a97af9eSJohann Duscher * @author Johann Duscher <jonny.dee@posteo.net> 7*1a97af9eSJohann Duscher * @copyright 2025 Johann Duscher 8*1a97af9eSJohann Duscher */ 9*1a97af9eSJohann Duscher 10*1a97af9eSJohann Duscheruse dokuwiki\Extension\AdminPlugin; 11*1a97af9eSJohann Duscher 12*1a97af9eSJohann Duscher// Protect against direct call 13*1a97af9eSJohann Duscherif (!defined('DOKU_INC')) die(); 14*1a97af9eSJohann Duscher 15*1a97af9eSJohann Duscher/** 16*1a97af9eSJohann Duscher * Class admin_plugin_deletepageguard 17*1a97af9eSJohann Duscher * 18*1a97af9eSJohann Duscher * Provides an admin interface for validating Delete Page Guard patterns 19*1a97af9eSJohann Duscher * and offering configuration guidance to administrators. 20*1a97af9eSJohann Duscher */ 21*1a97af9eSJohann Duscherclass admin_plugin_deletepageguard extends AdminPlugin { 22*1a97af9eSJohann Duscher 23*1a97af9eSJohann Duscher /** 24*1a97af9eSJohann Duscher * Return sort order for position in admin menu 25*1a97af9eSJohann Duscher * @return int 26*1a97af9eSJohann Duscher */ 27*1a97af9eSJohann Duscher public function getMenuSort() { 28*1a97af9eSJohann Duscher return 200; 29*1a97af9eSJohann Duscher } 30*1a97af9eSJohann Duscher 31*1a97af9eSJohann Duscher /** 32*1a97af9eSJohann Duscher * Return true if access to this admin plugin is allowed 33*1a97af9eSJohann Duscher * @return bool 34*1a97af9eSJohann Duscher */ 35*1a97af9eSJohann Duscher public function forAdminOnly() { 36*1a97af9eSJohann Duscher return true; 37*1a97af9eSJohann Duscher } 38*1a97af9eSJohann Duscher 39*1a97af9eSJohann Duscher /** 40*1a97af9eSJohann Duscher * Handle user request 41*1a97af9eSJohann Duscher * @return void 42*1a97af9eSJohann Duscher */ 43*1a97af9eSJohann Duscher public function handle() { 44*1a97af9eSJohann Duscher if (isset($_POST['validate_patterns'])) { 45*1a97af9eSJohann Duscher $this->validatePatternsFromPost(); 46*1a97af9eSJohann Duscher } 47*1a97af9eSJohann Duscher } 48*1a97af9eSJohann Duscher 49*1a97af9eSJohann Duscher /** 50*1a97af9eSJohann Duscher * Render HTML output 51*1a97af9eSJohann Duscher * @return void 52*1a97af9eSJohann Duscher */ 53*1a97af9eSJohann Duscher public function html() { 54*1a97af9eSJohann Duscher echo '<h1>' . $this->getLang('admin_title') . '</h1>'; 55*1a97af9eSJohann Duscher echo '<div class="level1">'; 56*1a97af9eSJohann Duscher 57*1a97af9eSJohann Duscher // Show current patterns and validation results 58*1a97af9eSJohann Duscher $patterns = $this->getConf('patterns'); 59*1a97af9eSJohann Duscher $this->showPatternValidation($patterns); 60*1a97af9eSJohann Duscher 61*1a97af9eSJohann Duscher // Add validation form 62*1a97af9eSJohann Duscher echo '<h2>' . $this->getLang('test_patterns_title') . '</h2>'; 63*1a97af9eSJohann Duscher echo '<form method="post" accept-charset="utf-8">'; 64*1a97af9eSJohann Duscher echo '<p>' . $this->getLang('test_patterns_help') . '</p>'; 65*1a97af9eSJohann Duscher echo '<textarea name="test_patterns" rows="10" cols="80" class="edit">' . hsc($patterns) . '</textarea><br>'; 66*1a97af9eSJohann Duscher echo '<input type="submit" name="validate_patterns" value="' . $this->getLang('validate_button') . '" class="button">'; 67*1a97af9eSJohann Duscher echo '</form>'; 68*1a97af9eSJohann Duscher 69*1a97af9eSJohann Duscher echo '</div>'; 70*1a97af9eSJohann Duscher } 71*1a97af9eSJohann Duscher 72*1a97af9eSJohann Duscher /** 73*1a97af9eSJohann Duscher * Validate patterns from POST data 74*1a97af9eSJohann Duscher * @return void 75*1a97af9eSJohann Duscher */ 76*1a97af9eSJohann Duscher private function validatePatternsFromPost() { 77*1a97af9eSJohann Duscher $patterns = $_POST['test_patterns'] ?? ''; 78*1a97af9eSJohann Duscher echo '<h2>' . $this->getLang('validation_results_title') . '</h2>'; 79*1a97af9eSJohann Duscher $this->showPatternValidation($patterns); 80*1a97af9eSJohann Duscher } 81*1a97af9eSJohann Duscher 82*1a97af9eSJohann Duscher /** 83*1a97af9eSJohann Duscher * Display pattern validation results 84*1a97af9eSJohann Duscher * @param string $patterns The patterns to validate 85*1a97af9eSJohann Duscher * @return void 86*1a97af9eSJohann Duscher */ 87*1a97af9eSJohann Duscher private function showPatternValidation($patterns) { 88*1a97af9eSJohann Duscher if (empty(trim($patterns))) { 89*1a97af9eSJohann Duscher echo '<div class="info">' . $this->getLang('no_patterns') . '</div>'; 90*1a97af9eSJohann Duscher return; 91*1a97af9eSJohann Duscher } 92*1a97af9eSJohann Duscher 93*1a97af9eSJohann Duscher $lines = preg_split('/\R+/', $patterns, -1, PREG_SPLIT_NO_EMPTY); 94*1a97af9eSJohann Duscher $hasErrors = false; 95*1a97af9eSJohann Duscher $validCount = 0; 96*1a97af9eSJohann Duscher 97*1a97af9eSJohann Duscher echo '<div class="level2">'; 98*1a97af9eSJohann Duscher echo '<h3>' . $this->getLang('validation_results') . '</h3>'; 99*1a97af9eSJohann Duscher echo '<ul>'; 100*1a97af9eSJohann Duscher 101*1a97af9eSJohann Duscher foreach ($lines as $i => $line) { 102*1a97af9eSJohann Duscher $pattern = trim($line); 103*1a97af9eSJohann Duscher if ($pattern === '') continue; 104*1a97af9eSJohann Duscher 105*1a97af9eSJohann Duscher $lineNum = $i + 1; 106*1a97af9eSJohann Duscher $status = $this->validateSinglePattern($pattern); 107*1a97af9eSJohann Duscher 108*1a97af9eSJohann Duscher if ($status === true) { 109*1a97af9eSJohann Duscher echo '<li><span style="color: green; font-weight: bold;">✓</span> '; 110*1a97af9eSJohann Duscher echo '<strong>Line ' . $lineNum . ':</strong> <code>' . hsc($pattern) . '</code></li>'; 111*1a97af9eSJohann Duscher $validCount++; 112*1a97af9eSJohann Duscher } else { 113*1a97af9eSJohann Duscher echo '<li><span style="color: red; font-weight: bold;">✗</span> '; 114*1a97af9eSJohann Duscher echo '<strong>Line ' . $lineNum . ':</strong> <code>' . hsc($pattern) . '</code><br>'; 115*1a97af9eSJohann Duscher echo ' <em style="color: red;">' . hsc($status) . '</em></li>'; 116*1a97af9eSJohann Duscher $hasErrors = true; 117*1a97af9eSJohann Duscher } 118*1a97af9eSJohann Duscher } 119*1a97af9eSJohann Duscher 120*1a97af9eSJohann Duscher echo '</ul>'; 121*1a97af9eSJohann Duscher 122*1a97af9eSJohann Duscher if (!$hasErrors && $validCount > 0) { 123*1a97af9eSJohann Duscher echo '<div class="success">' . sprintf($this->getLang('all_patterns_valid'), $validCount) . '</div>'; 124*1a97af9eSJohann Duscher } elseif ($hasErrors) { 125*1a97af9eSJohann Duscher echo '<div class="error">' . $this->getLang('some_patterns_invalid') . '</div>'; 126*1a97af9eSJohann Duscher } 127*1a97af9eSJohann Duscher 128*1a97af9eSJohann Duscher echo '</div>'; 129*1a97af9eSJohann Duscher } 130*1a97af9eSJohann Duscher 131*1a97af9eSJohann Duscher /** 132*1a97af9eSJohann Duscher * Validate a single pattern and return detailed error message 133*1a97af9eSJohann Duscher * @param string $pattern The pattern to validate 134*1a97af9eSJohann Duscher * @return string|true True if valid, error message if invalid 135*1a97af9eSJohann Duscher */ 136*1a97af9eSJohann Duscher private function validateSinglePattern($pattern) { 137*1a97af9eSJohann Duscher // Same validation logic as in action.php but with more detailed messages 138*1a97af9eSJohann Duscher if (strlen($pattern) > 1000) { 139*1a97af9eSJohann Duscher return $this->getLang('error_pattern_too_long'); 140*1a97af9eSJohann Duscher } 141*1a97af9eSJohann Duscher 142*1a97af9eSJohann Duscher if (preg_match('/(\(.*\).*\+.*\(.*\).*\+)|(\(.*\).*\*.*\(.*\).*\*)/', $pattern)) { 143*1a97af9eSJohann Duscher return $this->getLang('error_pattern_redos'); 144*1a97af9eSJohann Duscher } 145*1a97af9eSJohann Duscher 146*1a97af9eSJohann Duscher $escapedPattern = '/' . str_replace('/', '\/', $pattern) . '/u'; 147*1a97af9eSJohann Duscher $test = @preg_match($escapedPattern, ''); 148*1a97af9eSJohann Duscher if ($test === false) { 149*1a97af9eSJohann Duscher $error = error_get_last(); 150*1a97af9eSJohann Duscher $errorMsg = $error && isset($error['message']) ? $error['message'] : 'Unknown error'; 151*1a97af9eSJohann Duscher return $this->getLang('error_pattern_syntax') . ': ' . $errorMsg; 152*1a97af9eSJohann Duscher } 153*1a97af9eSJohann Duscher 154*1a97af9eSJohann Duscher return true; 155*1a97af9eSJohann Duscher } 156*1a97af9eSJohann Duscher}