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