├── .gitignore ├── vendor ├── adminer.php └── UPGRADING.md ├── package.yml ├── functions └── function_adminer.php ├── install.php ├── boot.php ├── uninstall.php ├── .github └── workflows │ └── publish-to-redaxo.yml ├── LICENSE ├── README.md ├── pages └── index.php ├── CHANGELOG.md └── lib └── adminer.php /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /vendor/adminer.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FriendsOfREDAXO/adminer/HEAD/vendor/adminer.php -------------------------------------------------------------------------------- /vendor/UPGRADING.md: -------------------------------------------------------------------------------- 1 | * Gehe zu https://github.com/vrana/adminer/releases und suche dort die aktuelle Datei `adminer-X.Y.Z-mysql.php`. 2 | * Kopiere diese nach `redaxo/src/addons/adminer/vendor/`. 3 | * Bennene die neue Datei um in `adminer.php`. Zuvor muss die bestehende Datei gelöscht werden. 4 | -------------------------------------------------------------------------------- /package.yml: -------------------------------------------------------------------------------- 1 | package: adminer 2 | version: '3.5.1' 3 | vendor: '5.4.1 adminer' 4 | author: Friends Of REDAXO 5 | supportpage: https://github.com/FriendsOfREDAXO/adminer 6 | 7 | page: 8 | title: Adminer 9 | perm: admin 10 | live_mode: false 11 | hasLayout: false 12 | icon: rex-icon fa-database 13 | linkAttr: { target: _blank } 14 | 15 | requires: 16 | php: '>=5.5' 17 | redaxo: '^5.0' 18 | -------------------------------------------------------------------------------- /functions/function_adminer.php: -------------------------------------------------------------------------------- 1 | isAdmin() && isset($_GET['username']) && isset($_GET['db'])) { 8 | $page = rex_be_controller::getCurrentPage(); 9 | if (!$page || $page === (string) (int) $page || $page === 'last') { 10 | rex_be_controller::setCurrentPage('adminer'); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /uninstall.php: -------------------------------------------------------------------------------- 1 | $db) { 12 | if (empty($db['host']) || empty($db['name'])) { 13 | continue; 14 | } 15 | 16 | // multiple databases with same name are not supported 17 | if (isset($databases[$db['name']])) { 18 | continue; 19 | } 20 | 21 | $db['id'] = $id; 22 | $databases[$db['name']] = $db; 23 | } 24 | 25 | $database = rex_get('db', 'string'); 26 | $database = isset($databases[$database]) ? $databases[$database] : reset($databases); 27 | 28 | $this->setProperty('databases', $databases); 29 | $this->setProperty('database', $database); 30 | 31 | // auto login and db selection 32 | $_GET['username'] = ''; 33 | $_GET['db'] = $database['name']; 34 | 35 | // adminer uses `page` parameter for pagination (int), but redaxo initially sets `page=adminer` 36 | // so we remove the page parameter if it is not a numeric string 37 | if (isset($_GET['page']) && $_GET['page'] !== (string) (int) $_GET['page']) { 38 | if ($_GET['page'] !== 'last') { 39 | unset($_GET['page']); 40 | } 41 | } 42 | 43 | // workaround against https://github.com/vrana/adminer/commit/15900301eeef0cf22e51f57ed0b7d55b3e822feb 44 | $_SESSION['pwds']['server'][''][$_GET["username"]] = ''; 45 | 46 | // deactive `throw_always_exception` debug option, because adminer is throwing some notices 47 | if (method_exists(rex::class, 'getDebugFlags')) { 48 | $debug = rex::getDebugFlags(); 49 | $debug['throw_always_exception'] = false; 50 | rex::setProperty('debug', $debug); 51 | } 52 | 53 | // CSP für die Adminer-Seite anpassen, um inline-scripts zu erlauben 54 | if (method_exists('rex_response', 'setHeader')) { 55 | rex_response::setHeader('Content-Security-Policy', "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';"); 56 | } 57 | 58 | rex_response::cleanOutputBuffers(); 59 | 60 | // add page param to all adminer urls 61 | ob_start(function ($output) { 62 | return preg_replace('#(?<==(?:"|\'))index\.php\?(?=username=&db=|file=[^&]*&version=)#', 'index.php?page=adminer&', $output); 63 | }); 64 | 65 | include __DIR__ .'/../vendor/adminer.php'; 66 | 67 | // make sure the output buffer callback is called 68 | while (ob_get_level()) { 69 | ob_end_flush(); 70 | } 71 | exit; 72 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | Version 3.5.0 5 | ----------------------- 6 | 7 | Vendor-Update to 5.4.0 8 | 9 | Version 3.4.0 10 | ----------------------- 11 | 12 | * Implemented Namespace "FriendsOfRedaxo\Adminer". 13 | - class `rex_adminer` -> 'FriendsOfRedaxo\Adminer' 14 | - This change is expected to have no side effect as long as the class `rex_adminer` is not used autside this addon. 15 | 16 | Version: 3.3.0 17 | ----------------------- 18 | Adminer Vendor 5.3 19 | 20 | Version: 3.0.0 - 3.1.0 21 | ----------------------- 22 | Adminer Vendor 5.1 23 | 24 | 25 | Version: 2.1.0 - 2.1.1 26 | -------------------------- 27 | Back to adminerer by vrana 4.17.1 28 | 29 | Version 2.0.2 – 20.02.2024 30 | -------------------------- 31 | 32 | * Update adminer auf adminerevo 4.8.4: https://github.com/adminerevo/adminerevo/blob/v4.8.4/changes.txt 33 | 34 | Version 2.0.0 – 20.02.2024 35 | -------------------------- 36 | 37 | * Update adminer auf adminerevo 4.8.3: https://github.com/adminerevo/adminerevo/blob/v4.8.3/changes.txt 38 | 39 | 40 | Version 1.9.1 – 20.08.2021 41 | -------------------------- 42 | 43 | * Update adminer auf 4.8.1: https://github.com/vrana/adminer/blob/v4.8.1/changes.txt 44 | 45 | 46 | Version 1.9.0 – 03.03.2021 47 | -------------------------- 48 | 49 | * Update adminer auf 4.8.0: https://github.com/vrana/adminer/blob/v4.8.0/changes.txt 50 | 51 | 52 | Version 1.8.3 – 07.02.2021 53 | -------------------------- 54 | 55 | * Update adminer auf 4.7.9: https://github.com/vrana/adminer/blob/v4.7.9/changes.txt 56 | * PHP 8: In der Datenansicht kam es zu einem `TypeError` 57 | 58 | 59 | Version 1.8.2 – 06.12.2020 60 | -------------------------- 61 | 62 | * Update adminer auf 4.7.8: https://github.com/vrana/adminer/blob/v4.7.8/changes.txt 63 | 64 | 65 | Version 1.8.1 – 02.02.2020 66 | -------------------------- 67 | 68 | * Update adminer auf 4.7.6: https://github.com/vrana/adminer/blob/v4.7.6/changes.txt 69 | 70 | 71 | Version 1.8.0 – 18.12.2019 72 | -------------------------- 73 | 74 | * Update adminer auf 4.7.5: https://github.com/vrana/adminer/blob/v4.7.5/changes.txt 75 | * Fremdschlüssel konnten nicht angelegt werden 76 | 77 | 78 | Version 1.7.0 – 02.09.2019 79 | -------------------------- 80 | 81 | * Update adminer auf 4.7.3: https://github.com/vrana/adminer/blob/v4.7.3/changes.txt 82 | 83 | 84 | Version 1.6.0 – 15.07.2019 85 | -------------------------- 86 | 87 | * Update adminer auf 4.7.1: https://github.com/vrana/adminer/blob/v4.7.1/changes.txt 88 | * Kompatibilität zu REDAXO < 5.6 wieder hergestellt 89 | 90 | 91 | Version 1.5.0 – 10.12.2018 92 | -------------------------- 93 | 94 | Dieses Update aktualisiert adminer um eine kritische Sicherheitslücke zu schließen. 95 | Ein Update wird dringend Empfohlen - siehe auch https://gwillem.gitlab.io/2019/01/17/adminer-4.6.2-file-disclosure-vulnerability/ 96 | 97 | * Update adminer auf 4.7.0: https://github.com/vrana/adminer/blob/v4.7.0/changes.txt 98 | * Wenn die REDAXO-Debug-Option `throw_always_exception` aktiviert ist, kam es zu Whoops aufgrund von Notices in Adminer 99 | 100 | 101 | Version 1.4.0 – 29.06.2018 102 | -------------------------- 103 | 104 | * Update adminer auf 4.6.3: https://github.com/vrana/adminer/blob/v4.6.3/changes.txt 105 | 106 | 107 | Version 1.3.0 – 14.01.2018 108 | -------------------------- 109 | 110 | * Auch die weiteren Datenbanken aus der config.yml können verwaltet werden 111 | * In der Tabellen-Struktur-Ansicht kann der PHP-Code für rex_sql_table angezeigt werden (praktisch für die install.php von Addons) 112 | 113 | 114 | Version 1.2.0 – 23.04.2017 115 | -------------------------- 116 | 117 | * Update adminer auf 4.3.1: https://github.com/vrana/adminer/blob/v4.3.1/changes.txt 118 | 119 | 120 | Version 1.1.0 – 18.03.2017 121 | -------------------------- 122 | 123 | * Update adminer auf 4.3.0: https://github.com/vrana/adminer/blob/v4.3.0/changes.txt 124 | 125 | 126 | Version 1.0.2 – 28.02.2017 127 | -------------------------- 128 | 129 | * Unter PHP 7 erzeugt adminer Warnings, diese werden daher nun unterdrückt 130 | 131 | 132 | Version 1.0.1 – 09.06.2016 133 | -------------------------- 134 | 135 | * Update adminer auf 4.2.5 136 | * Paginierungslinks führten zurück ins Backend 137 | 138 | 139 | Version 1.0.0 – 02.06.2016 140 | -------------------------- 141 | 142 | * Erstes Release 143 | -------------------------------------------------------------------------------- /lib/adminer.php: -------------------------------------------------------------------------------- 1 | getProperty('database'); 15 | 16 | return [$db['host'], $db['login'], $db['password']]; 17 | } 18 | 19 | public function login($login, $password) 20 | { 21 | return true; 22 | } 23 | 24 | public function databases($flush = true) 25 | { 26 | $databases = []; 27 | 28 | foreach (rex_addon::get('adminer')->getProperty('databases') as $db) { 29 | $databases[$db['name']] = $db['name'] . ' (' . $db['host'] . ')'; 30 | } 31 | 32 | return $databases; 33 | } 34 | 35 | public function databasesPrint($missing) 36 | { 37 | if (\count(rex_addon::get('adminer')->getProperty('databases')) <= 1) { 38 | return; 39 | } 40 | 41 | parent::databasesPrint($missing); 42 | } 43 | 44 | // <<< FIX: Corrected method signature >>> 45 | public function tableStructurePrint($p, $ih = null) 46 | { 47 | // Your custom logic to display rex_sql_table code 48 | if (class_exists(rex_sql_schema_dumper::class) && isset($_GET['table'])) { // Added isset check for safety 49 | $table = rex_sql_table::get($_GET['table']); 50 | if ($table) { // Check if table object was successfully retrieved 51 | $schema = (new rex_sql_schema_dumper())->dumpTable($table); 52 | 53 | // the hightlight() function needs
', '', $code); 58 | 59 | echo ' 60 |
61 | rex_sql_table code 62 | 63 | 151 | 152 | 159 | 160 | ' . \Adminer\script(' 161 | document.getElementById("rex-sql-table-code-link").addEventListener("click", function () { 162 | toggle("rex-sql-table-code"); 163 | return false; 164 | }); 165 | 166 | var code = document.getElementById("rex-sql-table-code"); 167 | 168 | // Verhindern von Bearbeitung 169 | code.addEventListener("cut", function (event) { 170 | event.preventDefault(); 171 | }); 172 | code.addEventListener("paste", function (event) { 173 | event.preventDefault(); 174 | }); 175 | code.addEventListener("keydown", function (event) { 176 | if (!event.metaKey) { 177 | event.preventDefault(); 178 | } 179 | }); 180 | 181 | // Dark Mode Toggle 182 | var themeToggle = document.getElementById("rex-sql-table-theme-toggle"); 183 | if (themeToggle) { 184 | themeToggle.addEventListener("click", function (event) { 185 | event.preventDefault(); 186 | event.stopPropagation(); 187 | code.classList.toggle("dark-mode"); 188 | 189 | // Button Text und Icon anpassen 190 | if (code.classList.contains("dark-mode")) { 191 | themeToggle.innerHTML = "☀️ Light"; 192 | localStorage.setItem("rex_sql_table_theme", "dark"); 193 | } else { 194 | themeToggle.innerHTML = "🌙 Dark"; 195 | localStorage.setItem("rex_sql_table_theme", "light"); 196 | } 197 | }); 198 | 199 | // Überprüfen gespeicherter Präferenzen 200 | if (localStorage.getItem("rex_sql_table_theme") === "dark") { 201 | code.classList.add("dark-mode"); 202 | themeToggle.innerHTML = "☀️ Light"; 203 | } else { 204 | themeToggle.innerHTML = "🌙 Dark"; 205 | } 206 | } 207 | 208 | // Copy Button 209 | var copyButton = document.getElementById("rex-sql-table-copy-button"); 210 | if (copyButton) { 211 | copyButton.addEventListener("click", function (event) { 212 | event.preventDefault(); 213 | event.stopPropagation(); 214 | 215 | try { 216 | // Text aus dem Code-Bereich extrahieren (ohne HTML) 217 | var codeElement = code.querySelector("pre"); 218 | if (!codeElement) { 219 | // Fallback: gesamten Text-Inhalt verwenden 220 | var tempDiv = document.createElement("div"); 221 | tempDiv.innerHTML = code.innerHTML; 222 | // Buttons entfernen 223 | var buttons = tempDiv.querySelector(".rex-sql-table-buttons"); 224 | if (buttons) { 225 | buttons.remove(); 226 | } 227 | var textContent = tempDiv.textContent || tempDiv.innerText; 228 | } else { 229 | var textContent = codeElement.textContent || codeElement.innerText; 230 | } 231 | 232 | // In Zwischenablage kopieren 233 | if (navigator.clipboard && window.isSecureContext) { 234 | navigator.clipboard.writeText(textContent).then(function() { 235 | // Erfolgsmeldung 236 | var originalText = copyButton.innerHTML; 237 | copyButton.innerHTML = "✅ Kopiert!"; 238 | copyButton.style.background = "#10b981"; 239 | setTimeout(function() { 240 | copyButton.innerHTML = originalText; 241 | copyButton.style.background = ""; 242 | }, 2000); 243 | }).catch(function(err) { 244 | console.error("Fehler beim Kopieren: ", err); 245 | fallbackCopy(textContent); 246 | }); 247 | } else { 248 | // Fallback für ältere Browser 249 | fallbackCopy(textContent); 250 | } 251 | 252 | function fallbackCopy(text) { 253 | var textArea = document.createElement("textarea"); 254 | textArea.value = text; 255 | textArea.style.position = "fixed"; 256 | textArea.style.left = "-999999px"; 257 | textArea.style.top = "-999999px"; 258 | document.body.appendChild(textArea); 259 | textArea.focus(); 260 | textArea.select(); 261 | 262 | try { 263 | document.execCommand("copy"); 264 | var originalText = copyButton.innerHTML; 265 | copyButton.innerHTML = "✅ Kopiert!"; 266 | copyButton.style.background = "#10b981"; 267 | setTimeout(function() { 268 | copyButton.innerHTML = originalText; 269 | copyButton.style.background = ""; 270 | }, 2000); 271 | } catch (err) { 272 | console.error("Fallback Kopieren fehlgeschlagen: ", err); 273 | alert("Kopieren fehlgeschlagen. Bitte manuell markieren und kopieren."); 274 | } finally { 275 | document.body.removeChild(textArea); 276 | } 277 | } 278 | 279 | } catch (error) { 280 | console.error("Fehler beim Kopieren: ", error); 281 | alert("Fehler beim Kopieren. Bitte manuell markieren und kopieren."); 282 | } 283 | }); 284 | } 285 | ') . ' 286 |
'; 287 | } 288 | } 289 | 290 | // <<< FIX: Call the parent method with the correct arguments >>> 291 | // This ensures the default Adminer structure is still printed after your custom code. 292 | parent::tableStructurePrint($p, $ih); 293 | } 294 | 295 | // New dark mode functions 296 | public function head($dark = null) 297 | { 298 | ?> 299 | 342 | 366 | 376 | ☀️ 377 | 🌙 378 | ' 379 | . \Adminer\script(" 380 | if (adminerDark != null) { 381 | adminerDarkSet(); 382 | } 383 | 384 | qs('#dark-mode-toggle').onclick = function() { 385 | adminerDarkSwitch(); 386 | }; 387 | ") . "\n" 388 | ; 389 | 390 | // Call parent method to ensure default navigation is still displayed 391 | parent::navigation($missing); 392 | } 393 | } 394 | --------------------------------------------------------------------------------