diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..116880d
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,12 @@
+* text=auto
+*.php text eol=lf
+*.md text eol=lf
+*.txt text eol=lf
+*.json text eol=lf
+*.png binary
+*.mo binary
+/tools export-ignore
+/tests export-ignore
+/.github export-ignore
+
+/AGENTS.md export-ignore
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000..3b181e0
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,25 @@
+---
+name: Bug report / Fehler melden
+about: Report a reproducible QuickNav problem / Reproduzierbaren Fehler melden
+title: ''
+labels: ''
+assignees: ''
+---
+
+**Problem / Fehler**
+Describe actual and expected behavior. / Tatsächliches und erwartetes Verhalten beschreiben.
+
+**Steps to reproduce / Schritte zum Nachstellen**
+1.
+2.
+
+**Environment / Umgebung**
+- QuickNav version / Version:
+- Advanced Scripts version / Version:
+- WordPress version / Version:
+- PHP version / Version:
+- Plugin or snippet / Plugin oder Snippet:
+- Multisite: yes/no / ja/nein:
+
+**Details / Details**
+Screenshots or sanitized errors help. Never include credentials or premium source code. / Screenshots oder bereinigte Fehlermeldungen helfen. Keine Zugangsdaten oder Premium-Quelltexte veröffentlichen.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000..b5bf5ea
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,13 @@
+---
+name: Feature request / Funktionswunsch
+about: Suggest a focused improvement / Gezielte Verbesserung vorschlagen
+title: ''
+labels: ''
+assignees: ''
+---
+
+**Use case / Anwendungsfall**
+What task would this make faster? / Welche Aufgabe würde damit schneller gehen?
+
+**Suggestion / Vorschlag**
+Describe the desired behavior. / Gewünschtes Verhalten beschreiben.
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..229a4d8
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,11 @@
+## Change / Änderung
+
+Describe the problem and resulting behavior. / Problem und resultierendes Verhalten beschreiben.
+
+## Validation / Prüfung
+
+- [ ] Relevant focused checks passed / Relevante gezielte Prüfungen bestanden
+- [ ] English/German docs and categorized changelogs synchronized / Zweisprachige Dokumentation und kategorisierte Changelogs abgestimmt
+- [ ] No Advanced Scripts premium code included / Kein Premium-Code enthalten
+
+Document remaining validation limits. / Verbleibende Prüfgrenzen dokumentieren.
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..ec8ab1f
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,23 @@
+name: Validate and build package
+on:
+ workflow_dispatch:
+ push:
+ branches: [main]
+ pull_request:
+permissions:
+ contents: read
+jobs:
+ package:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: Check PHP syntax
+ run: find . -name '*.php' -not -path './.git/*' -print0 | xargs -0 -n1 php -l
+ - name: Focused release checks
+ run: php tests/smoke.php
+ - name: Build plugin and snippet
+ run: python3 tools/build.py
+ - uses: actions/upload-artifact@v4
+ with:
+ name: advanced-scripts-quicknav-package
+ path: dist/
diff --git a/.gitignore b/.gitignore
index c704ae1..c593b0e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,20 +1,8 @@
-# Composer
-vendor/**/.git
-vendor/
-composer.lock
-composer.phar
-
-# Grunt
-node_modules/
-
-# macOS
-.idea/
.DS_Store
-
-# GitHub
+.idea/
.nova/
-.github/
-.git/
-.gitattributes
-#.gitignore
-#_config.yml
\ No newline at end of file
+node_modules/
+vendor/
+dist/
+__pycache__/
+*.pyc
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000..3860abf
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,14 @@
+# deckerweb standards for Advanced Scripts QuickNav
+
+- Keep English and German README.md/readme.txt editions, FAQ and Wiki sources synchronized.
+- Categorize every changelog entry as New:, Improved:, Fixed:, Misc:. Translate German prefixes to Neu:, Verbessert:, Behoben:, Sonstiges: across all bundled history and dialogs.
+- Use the shared deckerweb GitHub Updater V2 and embedded deckerweb Library; keep host-specific adaptation outside shared components.
+- Keep the settings header/footer, localized documentation link, local changelog dialog and copyright © 2022–2026 David Decker – DECKERWEB.
+- Preserve existing ASQN_* constants, public filters and legacy status-view IDs where practical. Distinguish a script's own active flag from ancestor blocking and actual execution.
+- Never bundle the Advanced Scripts premium source. Read through the adapter; do not implement a second execution engine.
+- Build the plugin ZIP and the standalone Advanced Scripts JSON snippet from one source using tools/build.py. Plugin root slug: advanced-scripts-quicknav.
+- 1.2.0 is the author-approved stable release based on successfully tested rc3. The user explicitly deferred the broad WordPress integration matrix; focused syntax, packaging and new-function checks are still expected.
+- Artwork A (Code Compass) was selected by the user and is included.
+
+- Footer changelogs use the shared deckerweb HTML renderer: version/date headings, categorized list items and subtle badges; escaped source only, no raw readme or pre blocks. See includes/deckerweb-changelog-v1.php.
+- QuickNav has no fullscreen block-editor adjustments, preference or WordPress-logo removal as of rc2.
diff --git a/README-de.md b/README-de.md
new file mode 100644
index 0000000..276ef53
--- /dev/null
+++ b/README-de.md
@@ -0,0 +1,136 @@
+> Stabile Version 1.2.0 – auf Basis der erfolgreich getesteten rc3.
+
+# Advanced Scripts QuickNav
+
+
+
+**Deine Scripts. Einen Klick entfernt.** Greife direkt aus der WordPress-Toolbar auf persönliche Favoriten, Statuslisten und Scripts im Ordnerbaum zu. Ein schlankes Add-on für Advanced Scripts.
+
+**Version:** 1.2.0 · **WordPress:** 6.7+ · **PHP:** 8.0+ · **GPL-2.0-or-later**
+
+[English](README.md) · [Anleitung und vollständige FAQ](docs/wiki/Deutsch.md) · [GitHub](https://github.com/deckerweb/advanced-scripts-quicknav)
+
+[Plugin-ZIP herunterladen](https://github.com/deckerweb/advanced-scripts-quicknav/releases/latest/download/advanced-scripts-quicknav.zip) · [Releases](https://github.com/deckerweb/advanced-scripts-quicknav/releases)
+
+## Inhaltsverzeichnis
+
+- [Auf einen Blick](#section-0)
+- [Installation](#section-1)
+- [Updates und Library](#section-2)
+- [Konfiguration](#section-3)
+- [Häufige Fragen](#section-4)
+- [Changelog](#section-5)
+- [Über das Plugin](#section-6)
+
+
+
+## Auf einen Blick
+
+- Persönliche Favoriten je Benutzer und Website.
+- Script-Links im Ordnerbaum und Script hier anlegen.
+- Eigener Aktivstatus plus Hinweise auf deaktivierte Elternordner.
+- Typ, Ordnerpfad, Ausführungsort und Hook als Zusatzinfos.
+- Visuelle Favoritenkarten, Snippet-Statistik, Anzeigeoptionen, Safe-Mode-Hinweise, SCRIPT_DEBUG und optionale Entwicklerlinks.
+- Bestehende Ressourcenlinks und Unterstützung für DevKit Pro, System Dashboard, Variable Inspector und Debug Log Manager bleiben enthalten.
+
+
+
+## Installation
+
+1. Lade das Release-ZIP hoch: Plugins → Plugin hinzufügen → Plugin hochladen.
+2. Aktiviere Advanced Scripts und QuickNav.
+3. Öffne Einstellungen → Advanced Scripts QuickNav und wähle Favoriten.
+4. Speichere deine Einstellungen und öffne Scripts in der Toolbar.
+
+Alternativ: Importiere ddw-advanced-scripts-quicknav.as.json in Advanced Scripts. Verwende nur eine Installationsart. Der PHP-Safe-Mode kann das Snippet im Admin blockieren; Updater und deckerweb Library sind nur im Plugin enthalten.
+
+Der Code wurde mit Advanced Scripts 2.6.2 abgeglichen und rc3 vom Plugin-Autor erfolgreich getestet. Die umfassende WordPress-Integrationsmatrix bleibt zurückgestellt. PHP 7.4 wird wegen der gemeinsamen Library nicht mehr unterstützt.
+
+
+
+## Updates und Library
+
+Der deckerweb GitHub Updater V2 zeigt öffentliche stabile Releases im regulären WordPress-Updatesystem an. Für die Erstinstallation das Release-ZIP hochladen. Die deckerweb Library ergänzt den deckerweb-Tab im Plugin-Installer und eigene gemeinsame Einstellungen. Sie ist unabhängig von Snippets finden.
+
+Header und Footer der Einstellungsseite zeigen Icon, Version, sprachabhängige Dokumentation, den als HTML formatierten lokalen Changelog, Plugin-Website und © 2022–2026 David Decker – DECKERWEB.
+
+
+
+## Konfiguration
+
+Konstanten haben Vorrang vor persönlichen Anzeigeoptionen. Benutzer benötigen manage_options und die konfigurierte QuickNav-Berechtigung. Der Ordnerbaum enthält maximal acht Ebenen und standardmäßig 40 Script-/Ordnereinträge insgesamt. Favoriten und jede Statusliste sind ebenfalls auf je 40 Einträge begrenzt. Alle anzeigen öffnet Advanced Scripts.
+
+Beispiele, bei Bedarf einzeln verwenden:
+
+```php
+define( 'ASQN_VIEW_CAPABILITY', 'activate_plugins' );
+define( 'ASQN_ENABLED_USERS', [ 1, 500 ] );
+define( 'ASQN_NAME_IN_ADMINBAR', 'Scripts' );
+define( 'ASQN_COUNTER', 'yes' );
+define( 'ASQN_ICON', 'remix' );
+define( 'ASQN_DISABLE_LIBRARY', 'yes' );
+define( 'ASQN_DISABLE_FOOTER', 'yes' );
+define( 'ASQN_EXPERT_MODE', false );
+define( 'ASQN_MENU_LIMIT', 40 );
+```
+
+ASQN_ICON akzeptiert blue oder remix; ohne Konstante erscheint das QuickNav-Symbol. ASQN_DISABLE_LIBRARY steuert nur Ressourcenlinks. Die deckerweb Library hat eigene Einstellungen.
+
+
+
+## Häufige Fragen
+
+### Für wen ist QuickNav gedacht?
+
+Für Administratoren, die häufig mit Advanced Scripts arbeiten und aus der Backend- oder Frontend-Toolbar direkt auf Scripts zugreifen möchten.
+
+### Wo wähle ich Favoriten aus?
+
+Öffne Einstellungen → Advanced Scripts QuickNav. Sieh klickbare Favoritenkarten und die Snippet-Statistik, filtere nach Titel oder Ordner, wähle Scripts und speichere deine Einstellungen.
+
+### Was bedeutet Durch Ordner deaktiviert?
+
+Mindestens ein übergeordneter Ordner ist inaktiv. Advanced Scripts überspringt diesen Zweig auch dann, wenn das Script selbst aktiv ist.
+
+### Bedeutet Aktiv, dass ein Script gerade ausgeführt wird?
+
+Nein. Gemeint ist der gespeicherte Script-Status. Ordnerstatus, Ausführungsort, Bedingungen, Hooks und PHP-Safe-Mode können die Ausführung beeinflussen.
+
+### Wie erkennt QuickNav den Safe Mode?
+
+Eine definierte Konstante AS_SAFE_MODE hat Vorrang, auch mit false. Andernfalls liest QuickNav die Option advanced-scripts-safemode. Es verändert den Safe Mode nicht.
+
+### Wie funktionieren Updates?
+
+Das Plugin integriert deckerweb GitHub Updater V2 in das reguläre WordPress-Updatesystem. Er prüft öffentliche stabile GitHub-Releases und stellt das passende Plugin-ZIP bereit. Automatische Updates werden nicht eingeschaltet.
+
+### Kann ich stattdessen das Snippet installieren?
+
+Ja. Importiere das erzeugte JSON in Advanced Scripts und aktiviere es mit dem Hook plugins_loaded. Es enthält Navigation und persönliche Einstellungen, aber keinen Plugin-Updater und keine deckerweb Library. Verwende entweder Plugin oder Snippet.
+
+
+
+## Changelog
+
+### 1.2.0 · 2026-10-01
+
+- **Neu:** Persönliche Favoriten mit klickbaren Karten, direkter Auswahlvorschau und Filter nach Titel oder Ordner.
+- **Neu:** Snippet-Statistik, Script-Links im Ordnerbaum und die Aktion Script hier hinzufügen.
+- **Verbessert:** Modulare Metadaten-Anbindung an Advanced Scripts, Script-Details, Hinweise auf Ordnersperren und begrenzte Menüs.
+- **Verbessert:** Gemeinsamer deckerweb GitHub Updater V2, deckerweb Library, Einstellungs-Header/-Footer und zugänglicher HTML-Changelog-Dialog.
+- **Verbessert:** Code-Compass-Grafiken sowie abgestimmte deutsche/englische Dokumentation, FAQs und Übersetzungen.
+- **Behoben:** Safe-Mode-Erkennung, Verarbeitung der Link-Filter, Menü-IDs, Sichtbarkeitsoptionen und der veraltete Toolbar-Callback, der in rc2 einen Fatal Error verursachte.
+- **Sonstiges:** Benötigt WordPress 6.7+ und PHP 8.0+. Entfernt Vollbild-Blockeditor-Anpassungen. Plugin und eigenständiges Snippet entstehen aus derselben Quelle.
+- **Sonstiges:** Veröffentlicht nach erfolgreichem Benutzertest von rc3 und gezielten Release-Prüfungen. Die umfassende WordPress-Integrationsmatrix bleibt zurückgestellt.
+
+Der vollständige [deutsche Änderungsverlauf](docs/CHANGELOG-de.md) und die [englische Fassung](docs/CHANGELOG.md) enthalten die gesamte Versionshistorie.
+
+
+
+## Über das Plugin
+
+Ein unabhängiges Add-on von David Decker – DECKERWEB. Advanced Scripts wird von Clean Plugins entwickelt. QuickNav enthält keinen Premium-Quellcode von Advanced Scripts.
+
+[Projekt unterstützen](https://ko-fi.com/deckerweb) · [Newsletter](https://eepurl.com/gbAUUn) · [Advanced Scripts kaufen (Affiliate-Link)](https://r.freemius.com/6334/142255/)
+
+GPL v2 oder höher. Neue QuickNav-Grafiken: © 2026 David Decker – DECKERWEB. Vorhandene Drittanbieter-Icons behalten ihre Urheberzuordnung.
diff --git a/README.md b/README.md
index ca8d7b9..2c398be 100644
--- a/README.md
+++ b/README.md
@@ -1,240 +1,136 @@
-# Advanced Scripts QuickNav (for the _Advanced Scripts_ Plugin)
+> Stable release 1.2.0 — based on the successfully tested rc3.
-
+# Advanced Scripts QuickNav
-The **Advanced Scripts QuickNav** plugin adds a quick-access navigator (aka QuickNav) to the WordPress Admin Bar (Toolbar). It allows easy access to your Scripts & Code Snippets listed by Active, Inactive, Snippet Type or Tag. Safe Mode is supported. Comes with inspiring links to snippet libraries.
+
-An awesome free add-on for [_Advanced Scripts_](https://r.freemius.com/6334/142255/) (premium) plugin!
+**Your scripts. One click away.** Reach personal favorites, status lists and scripts inside folders directly from the WordPress toolbar. A focused add-on for Advanced Scripts.
-#### Video Overview - Short Plugin Demo:
-[](https://www.youtube.com/watch?v=PGtDhwAeTVY)
+**Version:** 1.2.0 · **WordPress:** 6.7+ · **PHP:** 8.0+ · **GPL-2.0-or-later**
-* Contributors: [David Decker](https://github.com/deckerweb), [contributors](https://github.com/deckerweb/advanced-scripts-quicknav/graphs/contributors)
-* Tags: advanced scripts, quicknav, admin bar, toolbar, site builder, administrators, snippets, code snippets
-* Requires at least: 6.7
-* Requires PHP: 7.4
-* Stable tag: [main](https://github.com/deckerweb/advanced-scripts-quicknav/releases/latest)
-* Donate link: https://paypal.me/deckerweb
-* License: GPL v2 or later
+[Deutsch](README-de.md) · [Guide and complete FAQ](docs/wiki/English.md) · [GitHub](https://github.com/deckerweb/advanced-scripts-quicknav)
----
+[Download plugin ZIP](https://github.com/deckerweb/advanced-scripts-quicknav/releases/latest/download/advanced-scripts-quicknav.zip) · [Releases](https://github.com/deckerweb/advanced-scripts-quicknav/releases)
-[Support Project](#support-the-project) | [Installation](#installation) | [Updates](#updates) | [Description](#description) | [FAQ](#frequently-asked-questions) | [Custom Tweaks](#custom-tweaks-via-constants) | [Changelog](#changelog) | [Plugin's Backstory](#plugins-backstory) | [Plugin Scope / Disclaimer](#plugin-scope--disclaimer)
+## Contents
----
+- [At a glance](#section-0)
+- [Installation](#section-1)
+- [Updates and Library](#section-2)
+- [Configuration](#section-3)
+- [FAQ](#section-4)
+- [Changelog](#section-5)
+- [About](#section-6)
-## Support the Project
+
-If you find this project helpful, consider showing your support by buying me a coffee! Your contribution helps me keep developing and improving this plugin.
+## At a glance
-Enjoying the plugin? Feel free to treat me to a cup of coffee ☕🙂 through the following options:
+- Personal favorites per user and website.
+- Script links in the folder tree and Add script here.
+- Own activation status plus inactive-ancestor hints.
+- Type, folder path, execution location and hook details.
+- Visual favorite cards, snippet statistics, display preferences, Safe Mode notices, SCRIPT_DEBUG and optional developer links.
+- Existing resource links and integrations with DevKit Pro, System Dashboard, Variable Inspector and Debug Log Manager remain included.
-- [](https://ko-fi.com/W7W81BNTZE)
-- [Buy me a coffee](https://buymeacoffee.com/daveshine)
-- [PayPal donation](https://paypal.me/deckerweb)
-- [Join my **newsletter** for DECKERWEB WordPress Plugins](https://eepurl.com/gbAUUn)
+
-Buy a copy of [_Advanced Scripts_](https://r.freemius.com/6334/142255/) (premium plugin) so I earn a small affiliate commission.
+## Installation
----
+1. Upload the release ZIP through Plugins → Add New → Upload Plugin.
+2. Activate Advanced Scripts and QuickNav.
+3. Open Settings → Advanced Scripts QuickNav and select favorites.
+4. Save preferences and open Scripts in the toolbar.
-## Installation
+Alternatively, import ddw-advanced-scripts-quicknav.as.json into Advanced Scripts. Use one installation mode only. PHP Safe Mode can block the snippet in the admin; updater and deckerweb Library are plugin-only.
-#### **Quick Install – as Plugin**
-1. **Download ZIP:** [**advanced-scripts-quicknav.zip**](https://github.com/deckerweb/advanced-scripts-quicknav/releases/latest/download/advanced-scripts-quicknav.zip)
-2. Upload via WordPress Plugins > Add New > Upload Plugin
-3. Once activated, you’ll see the **Scripts** menu item in the Admin Bar.
+Source was reviewed against Advanced Scripts 2.6.2 and rc3 was successfully tested by the plugin author. The broader WordPress integration matrix remains deferred. PHP 7.4 is no longer supported because of the shared Library.
-Note: You need [_Advanced Scripts_](https://r.freemius.com/6334/142255/) to be installed and activated to the new Admin Bar items!
+
-#### **Alternative: Use as Script (Code Snippet)**
-1. Below, download the appropriate snippet version
-2. activate or deactivate in your snippets plugin
+## Updates and Library
-[**Download .json**](https://github.com/deckerweb/advanced-scripts-quicknav/releases/latest/download/ddw-advanced-scripts-quicknav.as.json) version for _Advanced Scripts_ just use the "Import" feature.
+deckerweb GitHub Updater V2 offers public stable releases through regular WordPress updates. Install the release ZIP for the first installation. deckerweb Library adds a deckerweb tab in the plugin installer and shared settings. It is separate from Find Snippets.
---> Please decide for one of both alternatives!
+The settings header/footer includes the icon, version, localized documentation, local changelog, plugin website and © 2022–2026 David Decker – DECKERWEB.
-#### Minimum Requirements
-* WordPress version 6.7 or higher
-* PHP version 7.4 or higher (better 8.3+)
-* MySQL version 8.0 or higher / OR MariaDB 10.1 or higher
-* Administrator user with capability `manage_options` and `activate_plugins`
+
-### Tested Compatibility
-- **Latest Advanced Scripts**: 2.5.2
-- **WordPress**: 6.7.2 / 6.8 Beta
-- **PHP**: 8.0 – 8.3
+## Configuration
----
+Constants override personal display preferences. Users need manage_options and the configured QuickNav capability. The folder tree allows eight levels and defaults to 40 script/folder entries overall. Favorites and each status list also default to 40 entries. View all opens Advanced Scripts.
-## Updates
+Examples; apply individually as needed:
-#### For Plugin Version:
-
-1) Alternative 1: Just download a new [ZIP file](https://github.com/deckerweb/advanced-scripts-quicknav/releases/latest/download/advanced-scripts-quicknav.zip) (see above), upload and override existing version. Done.
-
-2) Alternative 2: Use the (free) [**_Git Updater_ plugin**](https://git-updater.com/) and get updates automatically.
-
-3) Alternative 3: Upcoming! – In future I will built-in our own deckerweb updater. This is currently being worked on for my plugins. Stay tuned!
-
-#### For Code Snippet Version:
-
-Just manually: Download the latest Snippet version (see above) and import it in _Advanced Scripts_. – You can delete the old snippet; then just activate the new one. Done.
-
----
-
-## Description
-
-### How this Plugin Works
-
-1. **Your Scripts/ Code Snippets in the Admin Bar**: various listings – Active scripts, Inactive scripts, by Folder (including Subfolder)
-2. **Additional Links**:
- - _Snippets_: Code snippet libraries for WordPress by various authors, including the official Code Snippets Cloud
- - _Plugin ecosystem_: Links to resources like the Code Snippets website, Docs, Learning, Emergency fixes etc., plus Facebook group.
- - _About_: Includes links to the plugin author.
-3. Support for Advanced Scripts own "Safe Mode" – extra notice in Admin Bar
-4. Support for WordPress own "Script Debug" constant - extra notice in Admin Bar
-5. Third-party plugin support/integration (currently: _DevKitPro_ by DPlugins / _System Dashboard_ by Bowo / _Variable Inspector_ by Bowo / _Debug Log Manager_ by Bowo)
-6. Plugin installation mode:
- - a) As regular plugin (support translations then)
- - b) As a script/ code snippet - directly in _Advanced Scripts_ itself! 👏
-7. Custom tweaks via constants: enable or disable various additional features or tweaks – just as simple code snippets, see below --- this keeps the plugin/snippet simple and lightweight (you can check the config in your WP install via: _Tools > Site Health > Info_ – there look for the row: _Advanced Scripts QuickNav (Plugin)_)
-8. Show the Admin Bar also in Block Editor full screen mode.
-
----
-
-## Frequently Asked Questions
-
-### How can I change / tweak things?
-Please see here under [**Custom Tweaks via Constants**](#custom-tweaks-via-constants) what is possible!
-
-### Why is this functionality not baked into _Advanced Scripts_ itself?
-I don't know. Not everything needs to be built-in. That's what plugins are for: those who _need_ this functionality can install and use them. Or better, [just use it as code snippet](#installation) in _Advanced Scripts_ itself. Done :-)
-
-### Why did you create this plugin?
-Because I needed (and wanted!) it myself for the sites I maintain. [Read the backstory here ...](#plugins-backstory)
-
-### Why is this plugin not on wordpress.org plugin repository?
-Because the restrictions there for plugin authors are becoming more and more. It would be possible but I don't want that anymore. The same for limited support forums for plugin authors on .org. I have decided to leave this whole thing behind me.
-
----
-
-## Custom Tweaks via Constants
-
-### Default capability (aka permission)
-The intended usage of this plugin is for Administrator users only. Therefore the default capability to see the new Admin Bar node is set to `activate_plugins`. You can change this via the constant `ASQN_VIEW_CAPABILITY` – define that via `wp-config.php` or via Advanced Scripts plugin:
-```
+```php
define( 'ASQN_VIEW_CAPABILITY', 'activate_plugins' );
-```
-
-### Restrict to defined user IDs only (since v1.1.0)
-You can define an array of user IDs (can also be only _one_ ID) and that way restrict showing the Snippets Admin Bar item only for those users. Define that via `wp-config.php` or via Advanced Scripts plugin:
-```
-define( 'ASQN_ENABLED_USERS', [ 1, 500, 867 ] );
-```
-This would enable only for the users with the IDs 1, 500 and 867. Note the square brackets around, and no single quotes, just the ID numbers.
-
-For example you are one of many admin users (role `administrator`) but _only you_ want to show it _for yourself_. Given you have user ID 1:
-```
-define( 'ASQN_ENABLED_USERS', [ 1 ] );
-```
-That way only you can see it, the other admins can't!
-
-### Name of main menu item
-The default is just "Snippets" – catchy and short. However, if you don't enjoy "Snippets" you can tweak that also via the constant `ASQN_NAME_IN_ADMINBAR` – define that also via `wp-config.php` or via Advanced Scripts plugin:
-```
-define( 'ASQN_NAME_IN_ADMINBAR', 'Snippets' );
-```
-
-### Snippets count – addition to main menu item:
-```
+define( 'ASQN_ENABLED_USERS', [ 1, 500 ] );
+define( 'ASQN_NAME_IN_ADMINBAR', 'Scripts' );
define( 'ASQN_COUNTER', 'yes' );
+define( 'ASQN_ICON', 'remix' );
+define( 'ASQN_DISABLE_LIBRARY', 'yes' );
+define( 'ASQN_DISABLE_FOOTER', 'yes' );
+define( 'ASQN_EXPERT_MODE', false );
+define( 'ASQN_MENU_LIMIT', 40 );
```
-### Default icon of main menu item
-
-Since the official plugin/company logo is a bit too complex for the Admin Bar, I created an icon myself. However, you can use two other alternatives: 1) Of course, the Advanced Scripts company logo if you really want that or 2) a more neutral "code" logo from Remix Icon (free and open source licensed!). You can also tweak that via a constant in `wp-config.php` or via Advanced Scripts plugin:
-```
-define( 'ASQN_ICON', 'blue' ); // Advanced Scripts company logo
-```
-```
-define( 'ASQN_ICON', 'remix' ); // code icon by Remix Icon
-```
+ASQN_ICON accepts blue or remix; without a constant the QuickNav symbol is used. ASQN_DISABLE_LIBRARY controls resource links only. deckerweb Library has its own settings.
-### Disable code snippets library items
-Removes the "Find Snippets" section
-```
-define( 'ASQN_DISABLE_LIBRARY', 'yes' );
-```
+
-### Disable footer items (Links & About)
-Removes the "Links" & "About" sections
-```
-define( 'ASQN_DISABLE_FOOTER', 'yes' );
-```
+## FAQ
-### "Expert Mode"
-This is enabled by default, hence the original plugin name _Advanced_ Scripts. It just adds some additional links for coders:
-- _Site Health Info_ (WP Core)
-- Plugin: _DevKit Pro_ by DPlugins
-- Plugin: _System Dashboard_ by Bowo
-- Plugin: _Variable Inspector_ by Bowo
-- Plugin: _Debug Log Manager_ by Bowo
+### Who is QuickNav for?
-If you **don't want** that just **disable** it via constant:
-```
-define( 'ASQN_EXPERT_MODE', FALSE );
-```
-Note: Support for _some_ additional stuff in that mode may come in future.
+Administrators who frequently use Advanced Scripts and want direct access from the backend and frontend toolbar.
+
+### Where do I select favorites?
----
+Open Settings → Advanced Scripts QuickNav. See clickable favorite cards and snippet statistics, filter by title or folder, select scripts and save your preferences.
-## Changelog
+### What does Disabled by folder mean?
-**The Releases**
+The script has at least one inactive ancestor folder. Advanced Scripts skips that branch even if the script itself is active.
-### 🎉 v1.1.0 – 2025-04-05
-* New: Optionally only enable for defined user IDs _(new custom tweak)_
-* New: Installable and updateable via [Git Updater plugin](https://git-updater.com/)
-* Improved: Admin Bar CSS for Block / Site Editor fullscreen mode
-* Fix: PHP warning on frontend
-* Fix: Minor styling issues for top-level item
-* Update: `.pot` file, plus packaged German translations, now including new `l10n.php` files!
+### Does Active mean currently running?
-### 🎉 v1.0.0 – 2025-03-24
-* Initial release
-* Includes some plugin support
-* Includes `.pot` file, plus packaged German translations
+No. It is the saved script flag. Folder status, execution location, conditions, hooks and PHP Safe Mode can affect execution.
----
+### How does Safe Mode detection work?
-## Plugin's Backstory
+A defined AS_SAFE_MODE takes precedence, including false. Otherwise QuickNav reads the advanced-scripts-safemode option. It does not change Safe Mode.
-_I needed (and wanted) this plugin (Advanced Scripts QuickNav) myself so I developed it. Since Advanced Scripts was first released in summer of 2020 I am using it and loving it. On some sites I have up to 20 or 30 snippets, small stuff mostly, but sometimes bigger also. For a long time, I have wanted a way to get faster to specific snippets to maintain those (for whatever reason). Since I have long history of Admin Bar (Toolbar) plugins I thought that would be another one I could make. In the last few weeks I felt the need to finally code something. So I came up with this little helper plugin / "snippet". And, scratching my own itch is also always something enjoyable. My hope is, that you will enjoy it as well (the finished plugin)._
+### How do updates work?
-–– David Decker, plugin developer, in March of 2025
+The plugin integrates deckerweb GitHub Updater V2 with the regular WordPress update system. It checks public stable GitHub releases and provides the matching plugin ZIP. It does not enable automatic updates.
----
+### Can I install the snippet instead?
-## Plugin Scope / Disclaimer
+Yes. Import the generated JSON into Advanced Scripts and enable it with the plugins_loaded hook. It contains navigation and personal preferences, but no plugin updater or deckerweb Library. Use either the plugin or the snippet.
-This plugin comes as is.
+
-_Disclaimer 1:_ So far I will support the plugin for breaking errors to keep it working. Otherwise support will be very limited. Also, it will NEVER be released to WordPress.org Plugin Repository for a lot of reasons (ah, thanks, Matt!).
+## Changelog
-_Disclaimer 2:_ All of the above might change. I do all this stuff only in my spare time.
+### 1.2.0 · 2026-10-01
-_Most of all:_ Blessed (snippet) coding, and have fun building great sites!!! 😉
+- **New:** Personal favorites with clickable cards, a live selection preview and filtering by title or folder.
+- **New:** Snippet statistics, folder-tree script links and an Add script here shortcut.
+- **Improved:** Modular metadata-only integration with Advanced Scripts, script details, folder-blocking hints and bounded menus.
+- **Improved:** Shared deckerweb GitHub Updater V2, deckerweb Library, settings header/footer and an accessible HTML changelog dialog.
+- **Improved:** Code Compass artwork and synchronized English/German documentation, FAQs and translations.
+- **Fixed:** Safe Mode detection, link-filter handling, menu IDs, visibility controls and the obsolete toolbar callback that caused a fatal error in rc2.
+- **Misc:** Requires WordPress 6.7+ and PHP 8.0+. Removes fullscreen block-editor adjustments. Plugin and standalone snippet are built from one source.
+- **Misc:** Released after successful user testing of rc3 and focused release checks. The broader WordPress integration matrix remains deferred.
----
+The complete [English changelog](docs/CHANGELOG.md) and [German edition](docs/CHANGELOG-de.md) include the full version history.
-Links to [_Advanced Scripts_](https://r.freemius.com/6334/142255/) may be affiliate links.
+
-Official _Advanced Scripts_ plugin/company logo icon: © Clean Plugins by Abdelouahed Errouaguy
+## About
-Icons used in Admin Bar items: [© Remix Icon](https://remixicon.com/)
+An independent add-on by David Decker – DECKERWEB. Advanced Scripts is developed by Clean Plugins. QuickNav does not include Advanced Scripts premium source.
-Icons used in promo graphics: [© Remix Icon](https://remixicon.com/)
+[Support the project](https://ko-fi.com/deckerweb) · [Newsletter](https://eepurl.com/gbAUUn) · [Buy Advanced Scripts (affiliate link)](https://r.freemius.com/6334/142255/)
-Readme & Plugin Copyright: © 2025, David Decker – DECKERWEB.de
\ No newline at end of file
+GPL v2 or later. New QuickNav artwork: © 2026 David Decker – DECKERWEB. Existing third-party icons retain their attribution.
diff --git a/advanced-scripts-quicknav.php b/advanced-scripts-quicknav.php
index 1b8daba..ac538ff 100644
--- a/advanced-scripts-quicknav.php
+++ b/advanced-scripts-quicknav.php
@@ -1,1284 +1,32 @@
array(
- 'bg' => '#1d2327',
- 'base' => 'rgba(240,246,252,.6)',
- 'hover' => '#72aee6',
- ),
- 'light' => array(
- 'bg' => '#e5e5e5',
- 'base' => '#999',
- 'hover' => '#04a4cc',
- ),
- 'modern' => array(
- 'bg' => '#1e1e1e',
- 'base' => '#f3f1f1',
- 'hover' => '#33f078',
- ),
- 'blue' => array(
- 'bg' => '#52accc',
- 'base' => '#e5f8ff',
- 'hover' => '#fff',
- ),
- 'coffee' => array(
- 'bg' => '#59524c',
- 'base' => 'hsl(27.6923076923,7%,95%)',
- 'hover' => '#c7a589',
- ),
- 'ectoplasm' => array(
- 'bg' => '#523f6d',
- 'base' => '#ece6f6',
- 'hover' => '#a3b745',
- ),
- 'midnight' => array(
- 'bg' => '#363b3f',
- 'base' => 'hsl(206.6666666667,7%,95%)',
- 'hover' => '#e14d43',
- ),
- 'ocean' => array(
- 'bg' => '#738e96',
- 'base' => '#f2fcff',
- 'hover' => '#9ebaa0',
- ),
- 'sunrise' => array(
- 'bg' => '#cf4944',
- 'base' => 'hsl(2.1582733813,7%,95%)',
- 'hover' => 'rgb(247.3869565217,227.0108695652,211.1130434783)',
- ),
- );
-
- /** No filter currently b/c of sanitizing issues with the above CSS values */
- //$scheme_colors = (array) apply_filters( 'ddw/quicknav/asqn_scheme_colors', $scheme_colors );
-
- return $scheme_colors;
- }
-
- /**
- * Enqueue custom styles for the Admin Bar.
- * NOTE: Used within Admin and on the front-end (if Toolbar enabled).
- */
- public function enqueue_admin_bar_styles() {
-
- /**
- * Depending on user color scheme get proper base and hover color values for the main item (svg) icon.
- */
- $user_color_scheme = get_user_option( 'admin_color' );
- $user_color_scheme = ( is_admin() || is_network_admin() ) ? $user_color_scheme : 'fresh'; // b/c in frontend there is no 'admin_color'
- $admin_scheme = $this->get_scheme_colors();
-
- $base_color = $admin_scheme[ $user_color_scheme ][ 'base' ];
- $hover_color = $admin_scheme[ $user_color_scheme ][ 'hover' ];
-
- /**
- * Build the inline CSS
- * NOTE: We need to use 'sprintf()' because of the percentage values and similar!
- */
- $inline_css = sprintf(
- '
- #wpadminbar .asqn-scripts-list .ab-sub-wrapper ul li span.location,
- #wpadminbar .asqn-scripts-list .ab-sub-wrapper ul li span.status {
- font-family: monospace;
- font-size: %1$s;
- vertical-align: super;
- }
-
- #wpadminbar .asqn-scripts-list .ab-sub-wrapper ul li span.location,
- #wpadminbar .asqn-scripts-list .ab-sub-wrapper ul li span.status.inactive {
- /* filter: brightness(%2$s); */
- color: hsl(0, %3$s, %4$s);
- }
-
- #wpadminbar .asqn-scripts-list .ab-sub-wrapper ul li span.status.active {
- /* filter: brightness(%2$s); */
- color: hsl(120, %3$s, %7$s);
- }
-
- #wpadminbar .asqn-safemode {
- background-color: #9C1005;
- }
- #wpadminbar .asqn-safemode:hover,
- #wpadminbar ul li.asqn-safemode:hover {
- background: #BD3126;
- }
- #wpadminbar .asqn-safemode a {
- color: #FBE4C6;
- font-weight: 700;
- }
-
- #wpadminbar .has-icon .icon-svg svg {
- display: inline-block;
- margin-bottom: 3px;
- vertical-align: middle;
- width: 16px;
- height: 16px;
- }
-
- .icon-svg.ab-icon svg {
- width: 15px;
- height: 15px;
- }
-
- .asqn-scripts-list .ab-item .icon-svg.ab-icon svg {
- color: %5$s;
- }
-
- .asqn-scripts-list .ab-item:hover .icon-svg.ab-icon svg {
- color: %6$s;
- }
- ',
- '80%', // 1
- '120%', // 2
- '100%', // 3
- '70%', // 4
- $base_color, // 5
- $hover_color, // 6
- '40%' // 7
- );
-
- /** Only add the styles if Admin Bar is showing */
- if ( is_admin_bar_showing() ) {
- wp_add_inline_style( 'admin-bar', $inline_css );
- }
- }
-
- /**
- * Check for active SCRIPT_DEBUG constant. (helper function)
- *
- * @link https://developer.wordpress.org/advanced-administration/debug/debug-wordpress/#script_debug
- */
- private function is_wp_dev_mode_active() {
-
- return defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG;
- }
-
- /**
- * Check for active Safe Mode constant. (helper function)
- *
- * @link https://www.cleanplugins.com/blog/advanced-scripts-2-4-0-release-overview/
- */
- private function is_safe_mode_active() {
-
- if ( defined( 'AS_SAFE_MODE' ) && AS_SAFE_MODE ) return TRUE;
-
- return FALSE;
- }
-
- /**
- * Get type of Script/ Snippet. (helper function)
- *
- * @return string Type/Language of script to execute.
- */
- private function get_type( $scope ) {
-
- $type = '';
-
- switch ( $scope ) {
- case 'folder':
- $type = 'Folder';
- break;
-
- case 'application/x-httpd-php':
- $type = 'PHP';
- break;
-
- case 'url/javascript':
- case 'text/javascript':
- $type = 'JS';
- break;
-
- case 'url/css':
- case 'text/css':
- $type = 'CSS';
- break;
-
- case 'text/x-scss':
- case 'text/x-scss-partial':
- $type = 'SASS';
- break;
-
- case 'text/x-less':
- $type = 'Less';
- break;
-
- case 'text/html':
- $type = 'HTML';
- break;
- }
-
- return $type;
- }
-
- /**
- * Get type of Script/ Snippet. (helper function)
- *
- * @return string SVG icon of script type (built-in versions).
- */
- private function get_icon( $type ) {
-
- $type = $this->get_type( $type );
-
- switch ( strtolower( $type ) ) {
- case 'php':
- $icon = ' ';
- break;
-
- case 'css':
- $icon = ' ';
- break;
-
- case 'js':
- $icon = ' ';
- break;
-
- case 'html':
- $icon = ' ';
- break;
-
- case 'sass':
- $icon = ' ';
- break;
-
- case 'less':
- $icon = ' ';
- break;
- }
-
- return '' . $icon . ' ';
- }
-
- /**
- * Get location of Script/ Snippet. (helper function)
- *
- * @return string Location of script execution (lowercase).
- */
- private function get_location( $where ) {
-
- switch ( $where ) {
- case 'all':
- $location = _x( 'everywhere', 'Script/ Snippet scope: everywhere (global)', 'advanced-scripts-quicknav' );
- break;
-
- case 'front':
- $location = _x( 'front-end', 'Script/ Snippet scope: front-end', 'advanced-scripts-quicknav' );
- break;
-
- default:
- $location = $where;
- }
-
- return esc_html( strtolower( $location ) );
- }
-
- /**
- * Get number of all Scripts (without folders). (helper function)
- *
- * @return int Number of scripts.
- */
- private function script_counter() {
-
- if ( ! function_exists( 'cpas_scripts_manager' ) ) return 0;
-
- /** Get all Scripts from the DB (official operator function) */
- $scripts = cpas_scripts_manager();
- $scripts = $scripts->scripts;
-
- $count_all = 0;
-
- /** First, iterate through all Scripts, use only Active ones! */
- if ( $scripts ) {
-
- foreach ( $scripts as $script ) {
- /** Get only scripts but no folders */
- if ( 'folder' === $script[ 'type' ] ) {
- continue;
- }
- $count_all++;
- } // end foreach
- } // end if
-
- /** Set active counter for class */
- self::$scripts_all = $count_all;
-
- return $count_all;
- }
-
- /**
- * Adds the main Scripts (Code Snippets) menu and its submenus to the Admin Bar.
- *
- * @param WP_Admin_Bar $wp_admin_bar The WP_Admin_Bar instance.
- */
- public function add_admin_bar_menu( $wp_admin_bar ) {
-
- $enabled_users = defined( 'ASQN_ENABLED_USERS' ) ? (array) ASQN_ENABLED_USERS : [];
-
- /** Optional: let only defined user IDs access the plugin */
- if ( defined( 'ASQN_ENABLED_USERS' ) && ! in_array( get_current_user_id(), $enabled_users ) ) {
- return;
- }
-
- /** Don't do anything if Advanced Scripts plugin is NOT active */
- if ( ! defined( 'EPXADVSC_VER' ) || ! function_exists( 'cpas_scripts_manager' ) ) {
- return;
- }
-
- /** Also, don't do anything if the current user has no permission */
- $asqn_permission = ( defined( 'ASQN_VIEW_CAPABILITY' ) && ! empty( ASQN_VIEW_CAPABILITY ) ) ? ASQN_VIEW_CAPABILITY : 'activate_plugins';
-
- if ( ! current_user_can( sanitize_key( $asqn_permission ) ) ) {
- return;
- }
-
- /** Build the main item title, optional scripts count value */
- $all_scripts = $this->script_counter();
- $counter = ( defined( 'ASQN_COUNTER' ) && 'yes' === sanitize_key( ASQN_COUNTER ) ) ? ' (' . intval( $all_scripts ) . ')' : '';
- $asqn_name = ( defined( 'ASQN_NAME_IN_ADMINBAR' ) ) ? esc_html( ASQN_NAME_IN_ADMINBAR ) : esc_html__( 'Scripts', 'advanced-scripts-quicknav' );
- $asqn_name = $asqn_name . $counter;
-
- /** Default "script icon" */
- $code_icon = ' ';
-
- /** Optional code icon by Remix Icon */
- $remix_icon = ' ';
-
- /** Original blue icon (svg-ed) by Clean Plugins */
- $blue_icon = ' ';
-
- /** "Fire" Emoji for Safe Mode */
- $safemode = ( $this->is_safe_mode_active() ) ? ' 🔥' : '';
-
- $title_html = $code_icon . '' . $asqn_name . ' ' . $safemode;
-
- if ( defined( 'ASQN_ICON' ) && 'blue' === sanitize_key( ASQN_ICON ) ) {
- $title_html = $blue_icon . '' . $asqn_name . ' ' . $safemode;
- } elseif ( defined( 'ASQN_ICON' ) && 'remix' === sanitize_key( ASQN_ICON ) ) {
- $title_html = $remix_icon . '' . $asqn_name . ' ' . $safemode;
- }
-
- /** Add the parent menu item with an icon (main node) */
- $wp_admin_bar->add_node( array(
- 'id' => 'ddw-advscripts-quicknav',
- 'title' => $title_html,
- 'href' => esc_url( admin_url( 'tools.php?page=advanced-scripts' ) ),
- 'meta' => array( 'class' => 'asqn-scripts-list has-icon' ),
- ) );
-
- /** Add submenus */
- $this->add_safemode_submenu( $wp_admin_bar ); // group node
- $this->add_scripts_by_status_group( $wp_admin_bar ); // group node
- $this->add_scripts_by_folder_group( $wp_admin_bar ); // group node
- $this->add_scripts_new_group( $wp_admin_bar ); // group node
- $this->add_settings_group( $wp_admin_bar ); // group node
- $this->add_library_group( $wp_admin_bar ); // group node
- $this->add_libraries_submenu( $wp_admin_bar );
- $this->add_footer_group( $wp_admin_bar ); // group node
- $this->add_links_submenu( $wp_admin_bar );
- $this->add_about_submenu( $wp_admin_bar );
- }
-
- /**
- * Add group node for Safe Mode, and Script Debug
- */
- private function add_safemode_submenu( $wp_admin_bar ) {
-
- $wp_admin_bar->add_group( array(
- 'id' => 'asqn-group-safemode',
- 'parent' => 'ddw-advscripts-quicknav',
- ) );
-
- /** Warning for Advanced Scripts' own Safe Mode */
- if ( $this->is_safe_mode_active() ) {
- $wp_admin_bar->add_node( array(
- 'id' => 'asqn-safemode-active',
- 'title' => esc_html__( 'SAFE MODE is active 🔥', 'advanced-scripts-quicknav' ),
- 'href' => esc_url( admin_url( 'tools.php?page=advanced-scripts' ) ),
- 'parent' => 'asqn-group-safemode',
- 'meta' => array( 'class' => 'asqn-safemode' ),
- ) );
-
- $wp_admin_bar->add_node( array(
- 'id' => 'asqn-safemode-active-topright',
- 'title' => strtoupper( esc_html__( 'Advanced Scripts Safe Mode active 🔥', 'advanced-scripts-quicknav' ) ),
- 'href' => 'https://www.cleanplugins.com/blog/advanced-scripts-2-4-0-release-overview/',
- 'parent' => 'top-secondary', /** Puts the text on the right side of the Toolbar! */
- 'meta' => array( 'class' => 'asqn-safemode', 'target' => '_blank', 'rel' => 'nofollow noopener noreferrer' ),
- ) );
- } // end if
-
- /** Warning for WordPress' SCRIPT_DEBUG constant */
- if ( $this->is_wp_dev_mode_active() ) {
- $wp_admin_bar->add_node( array(
- 'id' => 'asqn-devmode-active',
- 'title' => esc_html__( 'SCRIPT_DEBUG is on ⚠', 'advanced-scripts-quicknav' ),
- 'href' => 'https://developer.wordpress.org/advanced-administration/debug/debug-wordpress/#script_debug',
- 'parent' => 'asqn-group-safemode',
- 'meta' => array( 'class' => 'asqn-safemode', 'rel' => 'nofollow noopener noreferrer' ),
- ) );
-
- $wp_admin_bar->add_node( array(
- 'id' => 'asqn-devmode-active-topright',
- 'title' => strtoupper( esc_html__( 'SCRIPT_DEBUG is on ⚠', 'advanced-scripts-quicknav' ) ),
- 'href' => 'https://developer.wordpress.org/advanced-administration/debug/debug-wordpress/#script_debug',
- 'parent' => 'top-secondary', /** Puts the text on the right side of the Toolbar! */
- 'meta' => array( 'class' => 'asqn-safemode', 'target' => '_blank', 'rel' => 'nofollow noopener noreferrer' ),
- ) );
- } // end if
- }
-
- /**
- * Add group node for Active & Inactive Scripts
- */
- private function add_scripts_by_status_group( $wp_admin_bar ) {
-
- $wp_admin_bar->add_group( array(
- 'id' => 'asqn-group-status',
- 'parent' => 'ddw-advscripts-quicknav',
- ) );
-
- $this->add_scripts_to_admin_bar( $wp_admin_bar );
- }
-
- /**
- * Status Group: Add Active & Inactive Scripts
- */
- private function add_scripts_to_admin_bar( $wp_admin_bar ) {
-
- if ( ! function_exists( 'cpas_scripts_manager' ) ) return $wp_admin_bar;
-
- /** Get all Scripts from the DB (official operator function) */
- $scripts = cpas_scripts_manager();
- $scripts = $scripts->scripts;
-
- $count_active = 0;
- $count_inactive = 0;
-
- $parent = '';
-
- /** First, iterate through all Scripts, use only Active ones! */
- if ( $scripts ) {
-
- $wp_admin_bar->add_node( array(
- 'id' => 'asqn-active',
- //'title' => esc_html__( 'Active Scripts', 'advanced-scripts-quicknav' ) . ' (' . $count_active . ')',
- 'href' => esc_url( admin_url( 'tools.php?page=advanced-scripts' ) ),
- 'parent' => 'asqn-group-status',
- ) );
-
- foreach ( $scripts as $script ) {
-
- /** List only active scripts for now */
- if ( ! $script[ 'status' ] || 'folder' === $script[ 'type' ] ) {
- continue;
- }
-
- $count_active++;
-
- /** Consider parent folders */
- $parent = ( 0 !== $script[ 'parent' ] ) ? intval( $script[ 'parent' ] ) : '';
- $parent = ( ! empty( $parent ) ) ? '&parent=' . $parent : '';
-
- $edit_link = admin_url( 'tools.php?page=advanced-scripts' . $parent . '&edit=' . intval( $script[ 'term_id' ] ) );
-
- $icon = $this->get_icon( $script[ 'type' ] );
-
- $location = sprintf(
- ' %s ',
- esc_html( $this->get_location( $script[ 'location' ] ) )
- );
-
- $wp_admin_bar->add_node( array(
- 'id' => 'asqn-script-' . intval( $script[ 'term_id' ] ),
- 'title' => $icon . esc_html( $script[ 'title' ] ) . $location,
- 'href' => esc_url( $edit_link ),
- 'parent' => 'asqn-active',
- ) );
-
- } // end foreach
- } // end if - active check
-
- /** Populate the "Active" title string with counter result after foreach iteration */
- $title_active_node = $wp_admin_bar->get_node( 'asqn-active' );
- if ( $scripts ) {
- $title_active_node->title = esc_html__( 'Active Scripts', 'advanced-scripts-quicknav' ) . ' (' . $count_active . ')';
- }
- $wp_admin_bar->add_node( $title_active_node );
-
- /** Set active counter for class */
- self::$scripts_active = $count_active;
-
- /** Second, iterate through all Scripts, use only In-Active ones! */
- if ( $scripts ) {
-
- $wp_admin_bar->add_node( array(
- 'id' => 'asqn-inactive',
- //'title' => esc_html__( 'Inactive Scripts', 'advanced-scripts-quicknav' ) . ' (' . $count_inactive . ')',
- 'href' => esc_url( admin_url( 'tools.php?page=advanced-scripts' ) ),
- 'parent' => 'asqn-group-status',
- ) );
-
- foreach ( $scripts as $script ) {
-
- /** List only In-active scripts for now */
- if ( $script[ 'status' ] || 'folder' === $script[ 'type' ] ) {
- continue;
- }
-
- $count_inactive++;
-
- /** Consider parent folders */
- $parent = ( 0 !== $script[ 'parent' ] ) ? intval( $script[ 'parent' ] ) : '';
- $parent = ( ! empty( $parent ) ) ? '&parent=' . $parent : '';
-
- $edit_link = admin_url( 'tools.php?page=advanced-scripts' . $parent . '&edit=' . intval( $script[ 'term_id' ] ) );
-
- $icon = $this->get_icon( $script[ 'type' ] );
-
- $location = sprintf(
- ' %s ',
- esc_html( $this->get_location( $script[ 'location' ] ) )
- );
-
- $wp_admin_bar->add_node( array(
- 'id' => 'asqn-script-' . intval( $script[ 'term_id' ] ),
- 'title' => $icon . esc_html( $script[ 'title' ] ) . $location,
- 'href' => esc_url( $edit_link ),
- 'parent' => 'asqn-inactive',
- ) );
- } // end foreach
- } // end if - in-active check
-
- /** Populate the "Inactive" title string with counter result after foreach iteration */
- $title_inactive_node = $wp_admin_bar->get_node( 'asqn-inactive' );
- if ( $scripts ) {
- $title_inactive_node->title = esc_html__( 'Inactive Scripts', 'advanced-scripts-quicknav' ) . ' (' . $count_inactive . ')';
- }
- $wp_admin_bar->add_node( $title_inactive_node );
-
- /** Set inactive counter for class */
- self::$scripts_inactive = $count_inactive;
- }
-
- /**
- * Add group node for Scripts by folder
- */
- private function add_scripts_by_folder_group( $wp_admin_bar ) {
-
- $wp_admin_bar->add_group( array(
- 'id' => 'asqn-group-folders',
- 'parent' => 'ddw-advscripts-quicknav',
- ) );
-
- $this->add_scripts_listings_submenu( $wp_admin_bar );
- //$this->add_snippets_type_submenu( $wp_admin_bar );
- }
-
- /**
- * Types Group: Add Scripts listings submenu (by status)
- */
- private function add_scripts_listings_submenu( $wp_admin_bar ) {
-
- if ( ! function_exists( 'cpas_scripts_manager' ) ) return $wp_admin_bar;
-
- /** Get all Scripts from the DB (official operator function) */
- $scripts = cpas_scripts_manager();
- $scripts = $scripts->scripts;
-
- $icon = ' ';
-
- $icon_parent = ' ';
-
- $status = '';
-
- /** Plugin: Variable Inspector by bowo */
- do_action( 'inspect', [ 'scripts', $scripts ] );
-
- if ( $scripts ) {
- $wp_admin_bar->add_node( array(
- 'id' => 'asqn-scripts-folders',
- 'title' => esc_html__( 'Scripts by Folder', 'advanced-scripts-quicknav' ),
- 'href' => esc_url( admin_url( 'tools.php?page=advanced-scripts' ) ),
- 'parent' => 'asqn-group-folders',
- ) );
-
- foreach ( $scripts as $script ) {
-
- /** List only folders for now */
- if ( 'folder' !== $script[ 'type' ] ) {
- continue;
- }
-
- /** Plugin: Variable Inspector by bowo */
- do_action( 'inspect', [ 'script', $script ] );
-
- $status = ( $script[ 'status' ] ) ? esc_html__( 'active', 'advanced-scripts-quicknav' ) : esc_html__( 'inactive', 'advanced-scripts-quicknav' );
- $status = sprintf(
- ' %s ',
- ( $script[ 'status' ] ) ? 'active' : 'inactive',
- $status
- );
-
- $parent_node = ( 0 !== $script[ 'parent' ] ) ? 'asqn-folder-' . intval( $script[ 'parent' ] ) : 'asqn-scripts-folders';
-
- $wp_admin_bar->add_node( array(
- 'id' => 'asqn-folder-' . intval( $script[ 'term_id' ] ),
- 'title' => $icon . esc_html( $script[ 'title' ] ) . $status,
- 'href' => esc_url( admin_url( 'tools.php?page=advanced-scripts&parent=' . intval( $script[ 'term_id' ] ) ) ),
- 'parent' => $parent_node,
- 'meta' => array( 'class' => 'has-icon' ),
- ) );
- } // end foreach
- } // end if
- }
-
- /**
- * Add group node for New Scripts
- */
- private function add_scripts_new_group( $wp_admin_bar ) {
-
- $wp_admin_bar->add_group( array(
- 'id' => 'asqn-group-new',
- 'parent' => 'ddw-advscripts-quicknav',
- ) );
-
- $this->add_scripts_new_submenu( $wp_admin_bar );
- }
-
- /**
- * New Scripts Group: Add Code Scripts - New submenu
- */
- private function add_scripts_new_submenu( $wp_admin_bar ) {
-
- $icon_add = ' ';
-
- /** Add New Snippet – also by type */
- $wp_admin_bar->add_node( array(
- 'id' => 'asqn-add-script',
- 'title' => $icon_add . esc_html__( 'Add New', 'advanced-scripts-quicknav' ),
- 'href' => esc_url( admin_url( 'tools.php?page=advanced-scripts&parent=0&edit=0' ) ),
- 'parent' => 'asqn-group-new',
- 'meta' => array( 'class' => 'has-icon' ),
- ) );
-
- /** WP's own "New Content" section: Add New Script */
- $wp_admin_bar->add_node( array(
- 'id' => 'asqn-wpnewcontent-add-script',
- 'title' => esc_html__( 'Code Script', 'advanced-scripts-quicknav' ),
- 'href' => esc_url( admin_url( 'tools.php?page=advanced-scripts&parent=0&edit=0' ) ),
- 'parent' => 'new-content',
- ) );
- }
-
- /**
- * Add group node for Advanced Scripts "settings".
- */
- private function add_settings_group( $wp_admin_bar ) {
-
- $wp_admin_bar->add_group( array(
- 'id' => 'asqn-group-settings',
- 'parent' => 'ddw-advscripts-quicknav',
- ) );
-
- //$this->add_settings_submenu( $wp_admin_bar );
- $this->add_expert_submenu( $wp_admin_bar );
- }
-
- /**
- * Add expert mode submenu.
- */
- private function add_expert_submenu( $wp_admin_bar ) {
-
- if ( ! $this->is_expert_mode() ) return $wp_admin_bar;
-
- $icon_dash = ' ';
-
- if ( defined( 'SYSTEM_DASHBOARD_VERSION' ) ) {
- $wp_admin_bar->add_node( array(
- 'id' => 'asqn-system-dashboard',
- 'title' => $icon_dash . esc_html__( 'System Dashboard', 'advanced-scripts-quicknav' ),
- 'href' => esc_url( admin_url( 'index.php?page=system-dashboard' ) ),
- 'parent' => 'asqn-group-settings',
- 'meta' => array( 'class' => 'has-icon' ),
- ) );
- }
-
- $icon_info = ' ';
-
- $wp_admin_bar->add_node( array(
- 'id' => 'asqn-sitehealth-info',
- 'title' => $icon_info . esc_html__( 'Site Health Info', 'advanced-scripts-quicknav' ),
- 'href' => esc_url( admin_url( 'site-health.php?tab=debug' ) ),
- 'parent' => 'asqn-group-settings',
- 'meta' => array( 'class' => 'has-icon' ),
- ) );
-
- $icon_code = ' ';
-
- if ( defined( 'VARIABLE_INSPECTOR_VERSION' ) ) {
- $wp_admin_bar->add_node( array(
- 'id' => 'asqn-variable-inspector',
- 'title' => $icon_code . esc_html__( 'Variable Inspector', 'advanced-scripts-quicknav' ),
- 'href' => esc_url( admin_url( 'tools.php?page=variable-inspector' ) ),
- 'parent' => 'asqn-group-settings',
- 'meta' => array( 'class' => 'has-icon' ),
- ) );
- }
-
- $icon_bug = ' ';
-
- /**
- * We need double check here as there is the "Downdload Manager" plugin
- * with the same 'DLM' prefix & constant.
- */
- if ( defined( 'DLM_SLUG' ) && 'debug-log-manager' === DLM_SLUG ) {
- $wp_admin_bar->add_node( array(
- 'id' => 'asqn-debuglog-manager',
- 'title' => $icon_bug . esc_html__( 'Debug Log Manager', 'advanced-scripts-quicknav' ),
- 'href' => esc_url( admin_url( 'tools.php?page=debug-log-manager' ) ),
- 'parent' => 'asqn-group-settings',
- 'meta' => array( 'class' => 'has-icon' ),
- ) );
- }
-
- $icon_codebox = ' ';
-
- if ( defined( 'DPDEVKIT_URL' ) && current_user_can( 'manage_options' ) ) {
- $wp_admin_bar->add_node( array(
- 'id' => 'asqn-devkitpro',
- 'title' => $icon_codebox . 'DevKit Pro',
- 'href' => esc_url( admin_url( 'admin.php?page=devkit' ) ),
- 'parent' => 'asqn-group-settings',
- 'meta' => array( 'class' => 'has-icon' ),
- ) );
-
- $wp_admin_bar->add_node( array(
- 'id' => 'asqn-devkitpro-filemanager',
- 'title' => esc_html__( 'File Manager', 'advanced-scripts-quicknav' ),
- 'href' => esc_url( admin_url( 'admin.php?page=devkit&tab=file-manager' ) ),
- 'parent' => 'asqn-devkitpro',
- 'meta' => array( 'target' => '_blank' ),
- ) );
-
- $wp_admin_bar->add_node( array(
- 'id' => 'asqn-devkitpro-errorlogs',
- 'title' => esc_html__( 'Error Logs', 'advanced-scripts-quicknav' ),
- 'href' => esc_url( admin_url( 'admin.php?page=devkit&tab=error-log' ) ),
- 'parent' => 'asqn-devkitpro',
- 'meta' => array( 'target' => '_blank' ),
- ) );
-
- if ( 'yes' === get_option( 'dpdevkit_adminer' ) ) {
- $wp_admin_bar->add_node( array(
- 'id' => 'asqn-devkitpro-adminer',
- 'title' => esc_html__( 'Adminer: Manage DB', 'advanced-scripts-quicknav' ),
- 'href' => esc_url( site_url() . '/devkit-adminer' ),
- 'parent' => 'asqn-devkitpro',
- 'meta' => array( 'target' => '_blank' ),
- ) );
- }
-
- $wp_admin_bar->add_node( array(
- 'id' => 'asqn-devkitpro-tools',
- 'title' => esc_html__( 'Tools', 'advanced-scripts-quicknav' ),
- 'href' => esc_url( admin_url( 'admin.php?page=devkit&tab=tools' ) ),
- 'parent' => 'asqn-devkitpro',
- 'meta' => array( 'target' => '_blank' ),
- ) );
- }
-
- add_action( 'admin_bar_menu', array( $this, 'remove_adminbar_nodes' ), 9999 );
- }
-
- /**
- * Add group node for Script/ Snippet Library items (external links)
- */
- private function add_library_group( $wp_admin_bar ) {
-
- if ( defined( 'ASQN_DISABLE_LIBRARY' ) && 'yes' === sanitize_key( ASQN_DISABLE_LIBRARY ) ) {
- return $wp_admin_bar;
- }
-
- $wp_admin_bar->add_group( array(
- 'id' => 'asqn-library',
- 'parent' => 'ddw-advscripts-quicknav',
- 'meta' => array( 'class' => 'ab-sub-secondary' ),
- ) );
- }
-
- /**
- * Libraries Group: Add linked Libraries submenu
- */
- private function add_libraries_submenu( $wp_admin_bar ) {
-
- $icon = ' ';
-
- $wp_admin_bar->add_node( array(
- 'id' => 'asqn-libraries',
- 'title' => $icon . esc_html__( 'Find Snippets', 'advanced-scripts-quicknav' ),
- 'href' => '#',
- 'parent' => 'asqn-footer',
- 'meta' => array( 'class' => 'has-icon' ),
- ) );
-
- $codelibs = array(
- 'codesnippets-cloud' => array(
- 'title' => __( 'Code Snippets Cloud', 'advanced-scripts-quicknav' ),
- 'url' => 'https://codesnippets.cloud/search',
- ),
- 'wpsnippets-library' => array(
- 'title' => __( 'WP Snippets Library', 'advanced-scripts-quicknav' ),
- 'url' => 'https://wpsnippets.org/library/',
- ),
- 'websquadron-codes' => array(
- 'title' => __( 'Codes by Web Squadron', 'advanced-scripts-quicknav' ),
- 'url' => 'https://learn.websquadron.co.uk/codes/',
- ),
- 'wpsnippetclub-archive' => array(
- 'title' => __( 'WP SnippetClub Archive', 'advanced-scripts-quicknav' ),
- 'url' => 'https://wpsnippet.club/snippet/',
- ),
- 'dplugins-code' => array(
- 'title' => __( 'Snippets Library by dPlugins', 'advanced-scripts-quicknav' ),
- 'url' => 'https://code.dplugins.com/',
- ),
- 'wpcodebin' => array(
- 'title' => __( 'WPCodeBin by WPCodeBox', 'advanced-scripts-quicknav' ),
- 'url' => 'https://wpcodebin.com/',
- ),
- 'wpcode-library' => array(
- 'title' => __( 'Snippets Library by WPCode', 'advanced-scripts-quicknav' ),
- 'url' => 'https://library.wpcode.com/',
- ),
- );
-
- /** Make code libs array filterable */
- apply_filters( 'ddw/quicknav/csn_codelibs', $codelibs );
-
- foreach ( $codelibs as $id => $info ) {
- $wp_admin_bar->add_node( array(
- 'id' => 'asqn-link-' . sanitize_key( $id ),
- 'title' => esc_html( $info[ 'title' ] ),
- 'href' => esc_url( $info[ 'url' ] ),
- 'parent' => 'asqn-libraries',
- 'meta' => array( 'target' => '_blank', 'rel' => 'nofollow noopener noreferrer' ),
- ) );
- } // end foreach
- }
-
- /**
- * Add group node for footer items (Links & About)
- */
- private function add_footer_group( $wp_admin_bar ) {
-
- if ( defined( 'ASQN_DISABLE_FOOTER' ) && 'yes' === sanitize_key( ASQN_DISABLE_FOOTER ) ) {
- return $wp_admin_bar;
- }
-
- $wp_admin_bar->add_group( array(
- 'id' => 'asqn-footer',
- 'parent' => 'ddw-advscripts-quicknav',
- 'meta' => array( 'class' => 'ab-sub-secondary' ),
- ) );
- }
-
- /**
- * Footer Group: Add Links submenu
- */
- private function add_links_submenu( $wp_admin_bar ) {
-
- $icon = ' ';
-
- $wp_admin_bar->add_node( array(
- 'id' => 'asqn-links',
- 'title' => $icon . esc_html__( 'Links', 'advanced-scripts-quicknav' ),
- 'href' => '#',
- 'parent' => 'asqn-footer',
- 'meta' => array( 'class' => 'has-icon' ),
- ) );
-
- $links = array(
- 'as-cleanplugins' => array(
- 'title' => __( 'Advanced Scripts', 'advanced-scripts-quicknav' ),
- 'url' => 'https://r.freemius.com/6334/142255/',
- ),
- 'as-emergency' => array(
- 'title' => __( 'Emergency Fixes 🔥', 'advanced-scripts-quicknav' ),
- 'url' => 'https://www.cleanplugins.com/blog/advanced-scripts-2-4-0-release-overview/',
- ),
- 'cp-blog' => array(
- 'title' => __( 'Clean Plugins Blog', 'advanced-scripts-quicknav' ),
- 'url' => 'https://www.cleanplugins.com/blog/',
- ),
- 'cp-youtube' => array(
- 'title' => __( 'CP YouTube Channel', 'advanced-scripts-quicknav' ),
- 'url' => 'https://www.youtube.com/c/cleanplugins',
- ),
- 'cp-fb-group' => array(
- 'title' => __( 'CP Facbook Group (official)', 'advanced-scripts-quicknav' ),
- 'url' => 'https://www.facebook.com/groups/cleanplugins',
- ),
- );
-
- /** Make links array filterable */
- apply_filters( 'ddw/quicknav/as_links', $links );
-
- foreach ( $links as $id => $info ) {
- $wp_admin_bar->add_node( array(
- 'id' => 'asqn-link-' . sanitize_key( $id ),
- 'title' => esc_html( $info[ 'title' ] ),
- 'href' => esc_url( $info[ 'url' ] ),
- 'parent' => 'asqn-links',
- 'meta' => array( 'target' => '_blank', 'rel' => 'nofollow noopener noreferrer' ),
- ) );
- } // end foreach
- }
-
- /**
- * Footer Group: Add About submenu
- */
- private function add_about_submenu( $wp_admin_bar ) {
-
- $icon = ' ';
-
- $wp_admin_bar->add_node( array(
- 'id' => 'asqn-about',
- 'title' => $icon . esc_html__( 'About', 'advanced-scripts-quicknav' ),
- 'href' => '#',
- 'parent' => 'asqn-footer',
- 'meta' => array( 'class' => 'has-icon' ),
- ) );
-
- $about_links = array(
- 'author' => array(
- 'title' => __( 'Author: David Decker', 'advanced-scripts-quicknav' ),
- 'url' => 'https://deckerweb.de/',
- ),
- 'github' => array(
- 'title' => __( 'Plugin on GitHub', 'advanced-scripts-quicknav' ),
- 'url' => 'https://github.com/deckerweb/advanced-scripts-quicknav',
- ),
- 'kofi' => array(
- 'title' => __( 'Buy Me a Coffee', 'advanced-scripts-quicknav' ),
- 'url' => 'https://ko-fi.com/deckerweb',
- ),
- );
-
- foreach ( $about_links as $id => $info ) {
- $wp_admin_bar->add_node( array(
- 'id' => 'asqn-about-' . sanitize_key( $id ),
- 'title' => esc_html( $info[ 'title' ] ),
- 'href' => esc_url( $info[ 'url' ] ),
- 'parent' => 'asqn-about',
- 'meta' => array( 'target' => '_blank', 'rel' => 'nofollow noopener noreferrer' ),
- ) );
- } // end foreach
- }
-
- /**
- * Show the Admin Bar also in Block Editor full screen mode.
- */
- public function adminbar_block_editor_fullscreen() {
-
- if ( ! is_admin_bar_showing() ) return;
-
- /**
- * Depending on user color scheme get proper bg color value for admin bar.
- */
- $user_color_scheme = get_user_option( 'admin_color' );
- $admin_scheme = $this->get_scheme_colors();
-
- $bg_color = $admin_scheme[ $user_color_scheme ][ 'bg' ];
-
- $inline_css_block_editor = sprintf(
- '
- @media (min-width: 600px) {
- body.is-fullscreen-mode .block-editor__container {
- top: var(--wp-admin--admin-bar--height);
- }
- }
-
- @media (min-width: 782px) {
- body.js.is-fullscreen-mode #wpadminbar {
- display: block;
- }
-
- body.is-fullscreen-mode .block-editor__container {
- min-height: calc(100vh - var(--wp-admin--admin-bar--height));
- }
-
- body.is-fullscreen-mode .edit-post-layout .editor-post-publish-panel {
- top: var(--wp-admin--admin-bar--height);
- }
-
- .edit-post-fullscreen-mode-close.components-button {
- background: %s;
- }
-
- .edit-post-fullscreen-mode-close.components-button::before {
- box-shadow: none;
- }
- }
-
- @media (min-width: 783px) {
- .is-fullscreen-mode .interface-interface-skeleton {
- top: var(--wp-admin--admin-bar--height);
- }
- }
- ',
- sanitize_hex_color( $bg_color )
- );
-
- wp_add_inline_style( 'wp-block-editor', $inline_css_block_editor );
-
- $inline_css_edit_site = sprintf(
- '
- body.is-fullscreen-mode .edit-site {
- top: var(--wp-admin--admin-bar--height);
- }
-
- body.is-fullscreen-mode .edit-site-layout__canvas-container {
- top: calc( var(--wp-admin--admin-bar--height) * -1 );
- }
-
- .edit-site-editor__view-mode-toggle .edit-site-editor__view-mode-toggle-icon img,
- .edit-site-editor__view-mode-toggle .edit-site-editor__view-mode-toggle-icon svg {
- background: %s;
- }
- ',
- sanitize_hex_color( $bg_color )
- );
-
- wp_add_inline_style( 'wp-edit-site', $inline_css_edit_site );
-
- add_action( 'admin_bar_menu', array( $this, 'remove_adminbar_nodes' ), 999 );
- }
-
- /**
- * Remove Admin Bar nodes.
- */
- public function remove_adminbar_nodes( $wp_admin_bar ) {
- $wp_admin_bar->remove_node( 'wp-logo' );
- }
-
- /**
- * Add additional plugin related info to the Site Health Debug Info section.
- *
- * @link https://make.wordpress.org/core/2019/04/25/site-health-check-in-5-2/
- *
- * @param array $debug_info Array holding all Debug Info items.
- * @return array Modified array of Debug Info.
- */
- public function site_health_debug_info( $debug_info ) {
-
- $string_undefined = esc_html_x( 'Undefined', 'Site Health Debug info', 'advanced-scripts-quicknav' );
- $string_enabled = esc_html_x( 'Enabled', 'Site Health Debug info', 'advanced-scripts-quicknav' );
- $string_disabled = esc_html_x( 'Disabled', 'Site Health Debug info', 'advanced-scripts-quicknav' );
- $string_value = ' – ' . esc_html_x( 'value', 'Site Health Debug info', 'advanced-scripts-quicknav' ) . ': ';
- $string_version = defined( 'EPXADVSC_VER' ) ? EPXADVSC_VER : '';
-
- /** Add our Debug info */
- $debug_info[ 'advanced-scripts-quicknav' ] = array(
- 'label' => esc_html__( 'Advanced Scripts QuickNav', 'advanced-scripts-quicknav' ) . ' (' . esc_html__( 'Plugin', 'advanced-scripts-quicknav' ) . ')',
- 'fields' => array(
-
- /** Various values */
- 'asqn_plugin_version' => array(
- 'label' => esc_html__( 'Plugin version', 'advanced-scripts-quicknav' ),
- 'value' => self::VERSION,
- ),
- 'asqn_install_type' => array(
- 'label' => esc_html__( 'WordPress Install Type', 'advanced-scripts-quicknav' ),
- 'value' => ( is_multisite() ? esc_html__( 'Multisite install', 'advanced-scripts-quicknav' ) : esc_html__( 'Single Site install', 'advanced-scripts-quicknav' ) ),
- ),
-
- /** Advanced Scripts QuickNav constants */
- 'ASQN_VIEW_CAPABILITY' => array(
- 'label' => 'ASQN_VIEW_CAPABILITY',
- 'value' => ( ! defined( 'ASQN_VIEW_CAPABILITY' ) ? $string_undefined : ( ASQN_VIEW_CAPABILITY ? $string_enabled : $string_disabled ) ),
- ),
- 'ASQN_ENABLED_USERS' => array(
- 'label' => 'ASQN_ENABLED_USERS',
- 'value' => ( ! defined( 'ASQN_ENABLED_USERS' ) ? $string_undefined : ( ASQN_ENABLED_USERS ? $string_enabled . $string_value . implode( ', ', array_map( 'absint', ASQN_ENABLED_USERS ) ) : $string_disabled ) ),
- ),
- 'ASQN_NAME_IN_ADMINBAR' => array(
- 'label' => 'ASQN_NAME_IN_ADMINBAR',
- 'value' => ( ! defined( 'ASQN_NAME_IN_ADMINBAR' ) ? $string_undefined : ( ASQN_NAME_IN_ADMINBAR ? $string_enabled . $string_value . esc_html( ASQN_NAME_IN_ADMINBAR ) : $string_disabled ) ),
- ),
- 'ASQN_COUNTER' => array(
- 'label' => 'ASQN_COUNTER',
- 'value' => ( ! defined( 'ASQN_COUNTER' ) ? $string_undefined : ( ASQN_COUNTER ? $string_enabled : $string_disabled ) ),
- ),
- 'ASQN_ICON' => array(
- 'label' => 'ASQN_ICON',
- 'value' => ( ! defined( 'ASQN_ICON' ) ? $string_undefined : ( ASQN_ICON ? $string_enabled . $string_value . sanitize_key( ASQN_ICON ) : $string_disabled ) ),
- ),
- 'ASQN_DISABLE_LIBRARY' => array(
- 'label' => 'ASQN_DISABLE_LIBRARY',
- 'value' => ( ! defined( 'ASQN_DISABLE_LIBRARY' ) ? $string_undefined : ( ASQN_DISABLE_LIBRARY ? $string_enabled : $string_disabled ) ),
- ),
- 'ASQN_DISABLE_FOOTER' => array(
- 'label' => 'ASQN_DISABLE_FOOTER',
- 'value' => ( ! defined( 'ASQN_DISABLE_FOOTER' ) ? $string_undefined : ( ASQN_DISABLE_FOOTER ? $string_enabled : $string_disabled ) ),
- ),
- 'ASQN_EXPERT_MODE' => array(
- 'label' => 'ASQN_EXPERT_MODE',
- 'value' => ( ! defined( 'ASQN_EXPERT_MODE' ) ? $string_undefined : ( ASQN_EXPERT_MODE ? $string_enabled : $string_disabled ) ),
- ),
- 'ASQN_AS_SAFE_MODE' => array(
- 'label' => 'AS_SAFE_MODE',
- 'value' => ( ! defined( 'AS_SAFE_MODE' ) ? $string_undefined : ( AS_SAFE_MODE ? $string_enabled : $string_disabled ) ),
- ),
- 'ASQN_SCRIPT_DEBUG' => array(
- 'label' => 'SCRIPT_DEBUG',
- 'value' => ( ! defined( 'SCRIPT_DEBUG' ) ? $string_undefined : ( SCRIPT_DEBUG ? $string_enabled : $string_disabled ) ),
- ),
- 'asqn_as_version' => array(
- 'label' => esc_html( 'Advanced Scripts Version', 'advanced-scripts-quicknav' ),
- 'value' => ( ! defined( 'EPXADVSC_VER' ) ? esc_html__( 'Plugin not installed', 'advanced-scripts-quicknav' ) : $string_version ),
- ),
- ), // end array
- );
-
- /** Return modified Debug Info array */
- return $debug_info;
- }
-
-} // end of class
-
-/** Start instance of Class */
-new DDW_Advanced_Scripts_QuickNav();
-
-endif;
-
-
-if ( ! function_exists( 'ddw_asqn_pluginrow_meta' ) ) :
-
-add_filter( 'plugin_row_meta', 'ddw_asqn_pluginrow_meta', 10, 2 );
/**
- * Add plugin related links to plugin page.
- *
- * @param array $ddwp_meta (Default) Array of plugin meta links.
- * @param string $ddwp_file File location of plugin.
- * @return array $ddwp_meta (Modified) Array of plugin links/ meta.
+ * Plugin Name: Advanced Scripts QuickNav
+ * Plugin URI: https://github.com/deckerweb/advanced-scripts-quicknav
+ * Description: Reach your Advanced Scripts through personal favorites, status lists and folders in the WordPress toolbar.
+ * Version: 1.2.0
+ * Author: David Decker – DECKERWEB
+ * Author URI: https://deckerweb.de/
+ * Text Domain: advanced-scripts-quicknav
+ * Domain Path: /languages/
+ * License: GPL-2.0-or-later
+ * License URI: https://www.gnu.org/licenses/gpl-2.0.html
+ * Requires at least: 6.7
+ * Requires PHP: 8.0
+ * Update URI: https://github.com/deckerweb/advanced-scripts-quicknav
+ * GitHub Plugin URI: https://github.com/deckerweb/advanced-scripts-quicknav
+ * Primary Branch: main
+ * Copyright: © 2022–2026 David Decker – DECKERWEB
*/
-function ddw_asqn_pluginrow_meta( $ddwp_meta, $ddwp_file ) {
-
- if ( ! current_user_can( 'install_plugins' ) ) return $ddwp_meta;
-
- /** Get current user */
- $user = wp_get_current_user();
-
- /** Build Newsletter URL */
- $url_nl = sprintf(
- 'https://deckerweb.us2.list-manage.com/subscribe?u=e09bef034abf80704e5ff9809&id=380976af88&MERGE0=%1$s&MERGE1=%2$s',
- esc_attr( $user->user_email ),
- esc_attr( $user->user_firstname )
- );
-
- /** List additional links only for this plugin */
- if ( $ddwp_file === trailingslashit( dirname( plugin_basename( __FILE__ ) ) ) . basename( __FILE__ ) ) {
- $ddwp_meta[] = sprintf(
- '❤ %1$s ',
- esc_html_x( 'Donate', 'Plugins page listing', 'advanced-scripts-quicknav' )
- );
-
- $ddwp_meta[] = sprintf(
- '⚡ %2$s ',
- $url_nl,
- esc_html_x( 'Join our Newsletter', 'Plugins page listing', 'advanced-scripts-quicknav' )
- );
- } // end if
-
- return apply_filters( 'ddw/admin_extras/pluginrow_meta', $ddwp_meta );
-
- } // end function
-
- endif;
\ No newline at end of file
+defined( 'ABSPATH' ) || exit;
+if ( version_compare( PHP_VERSION, '8.0', '<' ) ) {
+ add_action( 'admin_notices', static function() { echo '
Advanced Scripts QuickNav requires PHP 8.0 or newer.
'; } );
+ return;
+}
+// The generated snippet uses the same class guard; never register the toolbar twice.
+if ( class_exists( 'DDW_Advanced_Scripts_QuickNav', false ) ) { return; }
+define( 'ASQN_PLUGIN_FILE', __FILE__ );
+foreach ( [ 'config', 'adapter', 'favorites', 'navigation', 'renderer', 'settings', 'plugin' ] as $component ) { require_once __DIR__ . '/includes/class-asqn-' . $component . '.php'; }
+require_once __DIR__ . '/includes/deckerweb-changelog-v1.php';
+require_once __DIR__ . '/includes/deckerweb-plugin-library/bootstrap.php';
+deckerweb_library_register( __FILE__, [], __DIR__ . '/includes/deckerweb-plugin-library' );
+new DDW_Advanced_Scripts_QuickNav();
diff --git a/assets-github/Advanced Scripts QuickNav GitHub Banner.png b/assets-github/Advanced Scripts QuickNav GitHub Banner.png
deleted file mode 100644
index c27bc2b..0000000
Binary files a/assets-github/Advanced Scripts QuickNav GitHub Banner.png and /dev/null differ
diff --git a/assets-github/advanced-scripts-quicknav-screenshot.png b/assets-github/advanced-scripts-quicknav-screenshot.png
deleted file mode 100644
index 7eca364..0000000
Binary files a/assets-github/advanced-scripts-quicknav-screenshot.png and /dev/null differ
diff --git a/assets-github/banner-1544x500.png b/assets-github/banner-1544x500.png
new file mode 100644
index 0000000..9ff0a8d
Binary files /dev/null and b/assets-github/banner-1544x500.png differ
diff --git a/assets-github/banner-772x250.png b/assets-github/banner-772x250.png
new file mode 100644
index 0000000..dd04fb7
Binary files /dev/null and b/assets-github/banner-772x250.png differ
diff --git a/assets-github/banner-de-1544x500.png b/assets-github/banner-de-1544x500.png
new file mode 100644
index 0000000..cf1bace
Binary files /dev/null and b/assets-github/banner-de-1544x500.png differ
diff --git a/assets-github/banner-de-772x250.png b/assets-github/banner-de-772x250.png
new file mode 100644
index 0000000..5937305
Binary files /dev/null and b/assets-github/banner-de-772x250.png differ
diff --git a/assets-github/banner-de.svg b/assets-github/banner-de.svg
new file mode 100644
index 0000000..4590328
--- /dev/null
+++ b/assets-github/banner-de.svg
@@ -0,0 +1 @@
+DECKERWEB · WORDPRESS PLUGINS Advanced Scripts QuickNav Deine Scripts. Einen Klick entfernt. Favoriten · Ordner · Klare Statushinweise
\ No newline at end of file
diff --git a/assets-github/banner.svg b/assets-github/banner.svg
new file mode 100644
index 0000000..abeb425
--- /dev/null
+++ b/assets-github/banner.svg
@@ -0,0 +1 @@
+DECKERWEB · WORDPRESS PLUGINS Advanced Scripts QuickNav Your scripts. One click away. Favorites · Folders · Clear status hints
\ No newline at end of file
diff --git a/assets-github/icon-128x128.png b/assets-github/icon-128x128.png
new file mode 100644
index 0000000..c6124a1
Binary files /dev/null and b/assets-github/icon-128x128.png differ
diff --git a/assets-github/icon-256x256.png b/assets-github/icon-256x256.png
new file mode 100644
index 0000000..7638eaa
Binary files /dev/null and b/assets-github/icon-256x256.png differ
diff --git a/assets-github/icon-alternatives.png b/assets-github/icon-alternatives.png
deleted file mode 100644
index 5a8d37d..0000000
Binary files a/assets-github/icon-alternatives.png and /dev/null differ
diff --git a/assets-github/icon.svg b/assets-github/icon.svg
new file mode 100644
index 0000000..0a75a49
--- /dev/null
+++ b/assets-github/icon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/banner-1544x500.png b/assets/banner-1544x500.png
index 0d4e2a4..9ff0a8d 100644
Binary files a/assets/banner-1544x500.png and b/assets/banner-1544x500.png differ
diff --git a/assets/banner-772x250.png b/assets/banner-772x250.png
index 85189af..dd04fb7 100644
Binary files a/assets/banner-772x250.png and b/assets/banner-772x250.png differ
diff --git a/assets/css/style.scss b/assets/css/style.scss
deleted file mode 100644
index df1ab16..0000000
--- a/assets/css/style.scss
+++ /dev/null
@@ -1,19 +0,0 @@
----
----
-
-@import "{{ site.theme }}";
-
-.page-header {
- background-color: #C86DD7;
- background-image: linear-gradient(120deg, #3023AE, #C86DD7);
-}
-
-.main-content h1,
-.main-content h2,
-.main-content h3,
-.main-content h4,
-.main-content h5,
-.main-content h6 {
- font-weight: 700;
- color: #C86DD7;
-}
\ No newline at end of file
diff --git a/assets/icon-128x128.png b/assets/icon-128x128.png
index 1436de3..c6124a1 100644
Binary files a/assets/icon-128x128.png and b/assets/icon-128x128.png differ
diff --git a/assets/icon-256x256.png b/assets/icon-256x256.png
index 31b82e3..7638eaa 100644
Binary files a/assets/icon-256x256.png and b/assets/icon-256x256.png differ
diff --git a/assets/icon.svg b/assets/icon.svg
index a619f56..0a75a49 100644
--- a/assets/icon.svg
+++ b/assets/icon.svg
@@ -1,43 +1 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/assets/settings.css b/assets/settings.css
new file mode 100644
index 0000000..9116eeb
--- /dev/null
+++ b/assets/settings.css
@@ -0,0 +1,3 @@
+.asqn-settings{max-width:1040px}.asqn-page-heading{display:flex;gap:16px;align-items:center;margin:24px 0}.asqn-page-heading h1{padding:0}.asqn-page-heading p{margin:6px 0;color:#50575e}.asqn-card{background:#fff;border:1px solid #dcdcde;border-radius:8px;padding:20px;margin:20px 0}.asqn-card h2{margin-top:0}.asqn-option{display:block;margin:12px 0}.asqn-favorite-list{max-height:480px;overflow:auto;margin-top:16px}.asqn-favorite{display:flex;gap:12px;padding:12px;border-bottom:1px solid #f0f0f1;align-items:flex-start}.asqn-favorite[hidden]{display:none}.asqn-favorite small{display:block;color:#50575e;margin-top:4px}.asqn-footer{display:flex;gap:24px;justify-content:space-between;border-top:1px solid #dcdcde;margin-top:32px;padding:20px 0;color:#50575e}.asqn-footer>div:last-child{display:flex;flex-direction:column;gap:8px}.asqn-settings dialog{max-width:min(840px,85vw);max-height:80vh;padding:24px;border:1px solid #8c8f94;border-radius:8px}.asqn-settings dialog::backdrop{background:#0008}.asqn-settings dialog>header{display:flex;gap:20px;align-items:center;justify-content:space-between}#wpadminbar .asqn-type,#wpadminbar .asqn-muted{font-size:11px;color:#b8c2cc}#wpadminbar .asqn-blocked{font-size:11px;color:#ffd181}#wpadminbar .asqn-scripts-list .ab-sub-wrapper{max-width:min(600px,95vw)}@media(max-width:600px){.asqn-footer{flex-direction:column}.asqn-card{padding:14px}}
+
+.asqn-stats{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px;margin:18px 0}.asqn-stat{display:flex;flex-direction:column-reverse;gap:6px;padding:18px;border-radius:10px;background:#f5f7fa;border:1px solid #e5eaf0}.asqn-stat dt{color:#50575e;font-size:13px}.asqn-stat dd{margin:0;font-size:32px;line-height:1.1;font-weight:650;font-variant-numeric:tabular-nums;color:#18263e}.asqn-stat-active{background:#edf8f2}.asqn-stat-active dd{color:#17623f}.asqn-stat-blocked{background:#fff7e9}.asqn-stat-blocked dd{color:#805400}.asqn-stat-favorites{background:#edf4ff}.asqn-stat-favorites dd{color:#1a60b0}.asqn-status-bar{height:6px;border-radius:8px;overflow:hidden;background:#dce3eb}.asqn-status-bar span{display:block;height:100%;background:#26845b}.asqn-type-summary{display:flex;flex-wrap:wrap;gap:8px;margin-top:16px}.asqn-type-summary>span{padding:6px 10px;background:#f5f7fa;border:1px solid #e5eaf0;border-radius:20px;color:#50575e}.asqn-type-summary strong{margin-left:8px;color:#1d2327}.asqn-selected-heading{display:flex;align-items:center;gap:10px}.asqn-selected-heading h3{margin:12px 0}.asqn-selected-heading>span{background:#eaf2ff;color:#1a60b0;border-radius:20px;padding:3px 10px;font-weight:600}.asqn-selected-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin:14px 0 24px}.asqn-selected-card{display:grid;grid-template-columns:24px minmax(0,1fr) 16px;gap:10px;align-items:center;text-decoration:none;background:#f8faff;border:1px solid #dce6f5;border-radius:8px;padding:14px;transition:background-color .15s,border-color .15s,box-shadow .15s}.asqn-selected-card strong{overflow-wrap:anywhere;display:block;font-size:14px}.asqn-selected-card small{display:block;color:#50575e;margin-top:5px;line-height:1.5;overflow-wrap:anywhere}.asqn-selected-icon{color:#c28a17;font-size:21px}.asqn-selected-card:hover{background:#eef5ff;border-color:#92b8ec;box-shadow:0 2px 8px #172d5010}.asqn-selected-card:focus-visible{outline:2px solid #2271b1;outline-offset:2px}.asqn-selected-card[hidden],.asqn-empty[hidden]{display:none}.asqn-empty{padding:18px;border:1px dashed #b7c8df;border-radius:8px;background:#f8faff;color:#50575e}.asqn-settings dialog{box-sizing:border-box;overflow:hidden;padding:0;width:840px}.asqn-settings dialog>header{padding:16px 24px;border-bottom:1px solid #dcdcde;background:#fff}.asqn-settings dialog>header h2{margin:0;font-size:18px}.ddw-changelog-content{padding:8px 24px 24px;max-height:calc(80vh - 100px);overflow:auto;overscroll-behavior:contain}.ddw-changelog-release{padding:20px 0;border-bottom:1px solid #e5eaf0}.ddw-changelog-release:last-child{border-bottom:0}.ddw-changelog-version{display:flex;align-items:baseline;flex-wrap:wrap;gap:12px;margin-bottom:16px}.ddw-changelog-version h3{font-size:22px;margin:0;color:#18263e}.ddw-changelog-version>span{color:#646970;font-size:13px}.ddw-changelog-entries{list-style:none;margin:0;padding:0}.ddw-changelog-entries li{display:grid;grid-template-columns:90px minmax(0,1fr);gap:12px;align-items:start;line-height:1.65;margin:12px 0}.ddw-changelog-badge{display:inline-block;text-align:center;border-radius:5px;padding:3px 7px;font-size:11px;font-weight:650;line-height:1.5}.ddw-changelog-new{background:#e6f5ec;color:#17623f}.ddw-changelog-improved{background:#eaf2ff;color:#1a60b0}.ddw-changelog-fixed{background:#fff3df;color:#805400}.ddw-changelog-misc{background:#f0f0f1;color:#50575e}.ddw-changelog-entries code{font-size:12px;overflow-wrap:anywhere}@media(max-width:600px){.asqn-stats{grid-template-columns:repeat(2,minmax(0,1fr))}.asqn-stat{padding:14px}.asqn-stat dd{font-size:28px}.asqn-selected-grid{grid-template-columns:1fr}.asqn-settings dialog>header{padding:14px}.ddw-changelog-content{padding:8px 14px 18px}.ddw-changelog-entries li{grid-template-columns:1fr;gap:5px}.ddw-changelog-badge{justify-self:start}.asqn-page-heading img{flex-shrink:0}}@media(prefers-reduced-motion:reduce){.asqn-selected-card{transition:none}}
diff --git a/assets/settings.js b/assets/settings.js
new file mode 100644
index 0000000..94fd0a4
--- /dev/null
+++ b/assets/settings.js
@@ -0,0 +1,40 @@
+(() => {
+ 'use strict';
+ const input = document.getElementById('asqn-favorite-filter');
+ const rows = Array.from(document.querySelectorAll('[data-asqn-search]'));
+ const normalize = value => value.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLocaleLowerCase();
+ if (input) input.addEventListener('input', () => {
+ const query = normalize(input.value.trim());
+ rows.forEach(row => { row.hidden = !normalize(row.dataset.asqnSearch).includes(query); });
+ const status = document.getElementById('asqn-filter-status');
+ if (status) status.textContent = `${rows.filter(row => !row.hidden).length} / ${rows.length}`;
+ });
+ const cards = new Map(Array.from(document.querySelectorAll('[data-asqn-favorite-card]')).map(card => [card.dataset.asqnFavoriteCard, card]));
+ const synchronizeFavorites = () => {
+ let count = 0;
+ rows.forEach(row => {
+ const selected = row.querySelector('input[type="checkbox"]').checked;
+ const card = cards.get(row.dataset.asqnId);
+ if (card) card.hidden = !selected;
+ if (selected) count++;
+ });
+ const counter = document.getElementById('asqn-selected-count');
+ const empty = document.getElementById('asqn-selected-empty');
+ if (counter) counter.textContent = count;
+ if (empty) empty.hidden = count > 0;
+ };
+ rows.forEach(row => row.querySelector('input[type="checkbox"]').addEventListener('change', synchronizeFavorites));
+ synchronizeFavorites();
+ const dialog = document.getElementById('asqn-changelog');
+ const trigger = document.querySelector('[data-asqn-changelog]');
+ if (dialog && trigger && typeof dialog.showModal === 'function') {
+ trigger.addEventListener('click', event => { event.preventDefault(); dialog.showModal(); });
+ dialog.querySelector('[data-asqn-close]').addEventListener('click', () => dialog.close());
+ dialog.addEventListener('close', () => trigger.focus());
+ dialog.addEventListener('click', event => {
+ if (event.target !== dialog) return;
+ const box = dialog.getBoundingClientRect();
+ if (event.clientX < box.left || event.clientX > box.right || event.clientY < box.top || event.clientY > box.bottom) dialog.close();
+ });
+ }
+})();
diff --git a/assets/toolbar.css b/assets/toolbar.css
new file mode 100644
index 0000000..ac955b5
--- /dev/null
+++ b/assets/toolbar.css
@@ -0,0 +1 @@
+#wpadminbar .asqn-scripts-list .asqn-type,#wpadminbar .asqn-scripts-list .asqn-muted{font-size:11px;color:#b8c2cc}#wpadminbar .asqn-scripts-list .asqn-blocked{font-size:11px;color:#ffd181}#wpadminbar .asqn-scripts-list .ab-sub-wrapper{max-width:min(600px,95vw)}#wpadminbar .asqn-scripts-list .ab-item{overflow:hidden;text-overflow:ellipsis}#wpadminbar .asqn-scripts-list .ab-item:focus{outline:2px solid currentColor;outline-offset:-2px}
diff --git a/docs/CHANGELOG-de.md b/docs/CHANGELOG-de.md
new file mode 100644
index 0000000..b7f0f73
--- /dev/null
+++ b/docs/CHANGELOG-de.md
@@ -0,0 +1,64 @@
+# Änderungsverlauf
+
+[English](CHANGELOG.md)
+
+## 1.2.0 · 2026-10-01
+
+- **Neu:** Persönliche Favoriten mit klickbaren Karten, direkter Auswahlvorschau und Filter nach Titel oder Ordner.
+- **Neu:** Snippet-Statistik, Script-Links im Ordnerbaum und die Aktion Script hier hinzufügen.
+- **Verbessert:** Modulare Metadaten-Anbindung an Advanced Scripts, Script-Details, Hinweise auf Ordnersperren und begrenzte Menüs.
+- **Verbessert:** Gemeinsamer deckerweb GitHub Updater V2, deckerweb Library, Einstellungs-Header/-Footer und zugänglicher HTML-Changelog-Dialog.
+- **Verbessert:** Code-Compass-Grafiken sowie abgestimmte deutsche/englische Dokumentation, FAQs und Übersetzungen.
+- **Behoben:** Safe-Mode-Erkennung, Verarbeitung der Link-Filter, Menü-IDs, Sichtbarkeitsoptionen und der veraltete Toolbar-Callback, der in rc2 einen Fatal Error verursachte.
+- **Sonstiges:** Benötigt WordPress 6.7+ und PHP 8.0+. Entfernt Vollbild-Blockeditor-Anpassungen. Plugin und eigenständiges Snippet entstehen aus derselben Quelle.
+- **Sonstiges:** Veröffentlicht nach erfolgreichem Benutzertest von rc3 und gezielten Release-Prüfungen. Die umfassende WordPress-Integrationsmatrix bleibt zurückgestellt.
+
+## 1.2.0-rc3 · 2026-10-01 (Testkandidat)
+
+- **Behoben:** Verbliebene admin_bar_menu-Registrierung der entfernten Methode remove_adminbar_nodes gelöscht; verhindert einen Fatal Error bei aktivierten Expertenlinks.
+- **Verbessert:** Regressionstests erkennen jetzt ungültige Action-Callbacks.
+
+## 1.2.0-rc2 · 2026-10-01 (Testkandidat)
+
+- **Neu:** Visuelle Favoritenkarten mit direkten Editor-Links und sofortiger Vorschau der aktuellen Auswahl.
+- **Neu:** Kompakte Snippet-Statistik mit Gesamtzahl, Aktiv-/Inaktiv-Zahlen, Ordnern, gespeicherten Favoriten, durch Ordner blockierten aktiven Snippets und Typverteilung.
+- **Verbessert:** Formatiert den lokalen Changelog-Dialog als maskiertes HTML mit Versions- und Datumsüberschriften, lesbaren Listen und dezenten Kategorie-Badges nach dem gemeinsamen deckerweb-Footer-Standard.
+- **Verbessert:** Behält die gewählte Gestaltung Code Compass (Variante A) bei und aktualisiert die abgestimmten deutschen und englischen Dokumentationen und Übersetzungen.
+- **Sonstiges:** Entfernt die Vollbild-Blockeditor-Option, zugehörige Hooks, CSS-Anpassungen und das Entfernen des WordPress-Logos.
+- **Sonstiges:** Vorhandene Favoriten bleiben erhalten. Die umfassende WordPress-Integrationsmatrix bleibt auf später verschoben.
+
+## 1.2.0-rc1 · 2026-10-01 (Testkandidat)
+
+- **Neu:** Persönliche Favoriten getrennt nach Benutzer und Website, mit Filter nach Titel oder Ordner auf der Einstellungsseite.
+- **Neu:** Direkte Script-Links im Ordnerbaum und die Aktion Script hier anlegen für jeden Ordner.
+- **Neu:** Persönliche Einstellungen für Script-Zähler, Entwicklerlinks, Vollbild-Toolbar und begrenzte Menülisten.
+- **Verbessert:** Zeigt Durch Ordner deaktiviert an, ohne den eigenen Aktivstatus umzudeuten; ergänzt Typ, Ordnerpfad, Ausführungsort und Hook.
+- **Verbessert:** Trennt Konfiguration, Advanced-Scripts-Adapter, Navigation, Darstellung, persönliche Einstellungen und Hook-Registrierung.
+- **Verbessert:** Integriert den gemeinsamen deckerweb GitHub Updater V2, sprachabhängige Update-Grafiken und die deckerweb Library 0.2.0.
+- **Verbessert:** Ergänzt den deckerweb-Header und -Footer der Einstellungsseite sowie einen zugänglichen lokalen Changelog-Dialog.
+- **Behoben:** Erkennt den über die Advanced-Scripts-Oberfläche aktivierten Safe Mode und berücksichtigt den Vorrang von AS_SAFE_MODE.
+- **Behoben:** Übernimmt Link-Filter-Rückgaben, trennt die Sichtbarkeit von Bibliothek und Footer und berücksichtigt leere Sammlungen und eigene Admin-Farbschemata.
+- **Behoben:** Verwendet gemeinsame Berechtigungsprüfungen für Toolbar und Vollbildintegration; schützt Einstellungen durch POST, Berechtigungen und Nonce.
+- **Sonstiges:** Benötigt PHP 8.0+, behält WordPress 6.7+ bei und liefert abgestimmte deutsche und englische Readmes, FAQ, Wiki-Quellen und vollständige Changelogs.
+- **Sonstiges:** Enthält vorläufig Design A; drei Icon- und Banner-Alternativen werden separat zur Auswahl bereitgestellt.
+- **Sonstiges:** Erzeugt das eigenständige Advanced-Scripts-JSON-Snippet aus derselben Quelle. Updater und deckerweb Library sind der Plugin-Installation vorbehalten.
+- **Sonstiges:** Dies ist ein lokaler Testkandidat. Die umfassende WordPress-Integrationsprüfung und die öffentliche Veröffentlichung stehen noch aus.
+
+## 1.1.0 · 2025-04-05
+
+- **Neu:** Beschränkt QuickNav optional mit ASQN_ENABLED_USERS auf festgelegte Benutzer-IDs.
+- **Verbessert:** Unterstützt Installation und Aktualisierung mit Git Updater.
+- **Behoben:** Behebt eine PHP-Warnung im Frontend.
+
+## 1.0.0 · 2025-03-24
+
+- **Neu:** Erste öffentliche Version mit Statuslisten, Ordnerlinks, Safe-Mode-Hinweisen, Ressourcenlinks, Entwickleranbindungen und Konstanten.
+
+## 0.5.0 · 2025-03-23
+
+- **Sonstiges:** Interne Testversion.
+
+## 0.0.0 · 2025-03-23
+
+- **Sonstiges:** Entwicklungsbeginn.
+
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
new file mode 100644
index 0000000..d34344b
--- /dev/null
+++ b/docs/CHANGELOG.md
@@ -0,0 +1,64 @@
+# Changelog
+
+[Deutsch](CHANGELOG-de.md)
+
+## 1.2.0 · 2026-10-01
+
+- **New:** Personal favorites with clickable cards, a live selection preview and filtering by title or folder.
+- **New:** Snippet statistics, folder-tree script links and an Add script here shortcut.
+- **Improved:** Modular metadata-only integration with Advanced Scripts, script details, folder-blocking hints and bounded menus.
+- **Improved:** Shared deckerweb GitHub Updater V2, deckerweb Library, settings header/footer and an accessible HTML changelog dialog.
+- **Improved:** Code Compass artwork and synchronized English/German documentation, FAQs and translations.
+- **Fixed:** Safe Mode detection, link-filter handling, menu IDs, visibility controls and the obsolete toolbar callback that caused a fatal error in rc2.
+- **Misc:** Requires WordPress 6.7+ and PHP 8.0+. Removes fullscreen block-editor adjustments. Plugin and standalone snippet are built from one source.
+- **Misc:** Released after successful user testing of rc3 and focused release checks. The broader WordPress integration matrix remains deferred.
+
+## 1.2.0-rc3 · 2026-10-01 (test candidate)
+
+- **Fixed:** Removed the remaining admin_bar_menu registration for the deleted remove_adminbar_nodes method, preventing a fatal error when expert links are enabled.
+- **Improved:** Regression checks now reject invalid action callbacks.
+
+## 1.2.0-rc2 · 2026-10-01 (test candidate)
+
+- **New:** Visual favorites cards with direct editor links and a live preview of the current selection.
+- **New:** A compact snippet overview with totals, active/inactive counts, folders, saved favorites, active snippets blocked by folders and a type breakdown.
+- **Improved:** Formats the local changelog dialog as escaped HTML with version/date headings, readable lists and subtle category badges, following the shared deckerweb footer standard.
+- **Improved:** Keeps the selected Code Compass artwork (variant A) and updates synchronized English/German documentation and translations.
+- **Misc:** Removes the fullscreen block-editor preference, hooks, CSS adjustments and WordPress-logo removal.
+- **Misc:** Existing favorites remain unchanged. The broad WordPress integration matrix remains deferred.
+
+## 1.2.0-rc1 · 2026-10-01 (test candidate)
+
+- **New:** Personal favorites stored separately for each user and website, with a filter by title or folder on the preferences page.
+- **New:** Direct script links in the folder tree and an Add script here shortcut for each folder.
+- **New:** Personal preferences for script counts, developer links, fullscreen toolbar and bounded menu lists.
+- **Improved:** Shows Disabled by folder without changing the script’s own active/inactive status; includes type, folder path, location and hook details.
+- **Improved:** Separates configuration, the Advanced Scripts adapter, navigation, rendering, personal settings and hook registration.
+- **Improved:** Adds the shared deckerweb GitHub Updater V2, localized update artwork and embedded deckerweb Library 0.2.0.
+- **Improved:** Adds the deckerweb settings header/footer and an accessible local changelog dialog.
+- **Fixed:** Recognizes Safe Mode enabled through the Advanced Scripts interface, respecting AS_SAFE_MODE precedence.
+- **Fixed:** Applies link-filter return values, separates library/footer visibility and handles empty collections and custom admin color schemes.
+- **Fixed:** Uses shared permission checks for the toolbar and fullscreen integration; protects preferences with POST, capabilities and a nonce.
+- **Misc:** Requires PHP 8.0+, preserves WordPress 6.7+, and provides synchronized English/German readmes, FAQs, Wiki sources and complete changelogs.
+- **Misc:** Includes provisional design A; three icon/banner alternatives are supplied separately for selection.
+- **Misc:** Generates the standalone Advanced Scripts JSON snippet from the same source. Updater and deckerweb Library are plugin-only.
+- **Misc:** This is a local test candidate. Full WordPress integration validation and public release publication are pending.
+
+## 1.1.0 · 2025-04-05
+
+- **New:** Optionally restrict QuickNav to defined user IDs with ASQN_ENABLED_USERS.
+- **Improved:** Supports installation and updates with Git Updater.
+- **Fixed:** Addresses a PHP warning on the frontend.
+
+## 1.0.0 · 2025-03-24
+
+- **New:** Initial public release with status lists, folder links, Safe Mode notices, resource links, developer integrations and constants.
+
+## 0.5.0 · 2025-03-23
+
+- **Misc:** Internal test version.
+
+## 0.0.0 · 2025-03-23
+
+- **Misc:** Development started.
+
diff --git a/docs/DEVELOPMENT-de.md b/docs/DEVELOPMENT-de.md
new file mode 100644
index 0000000..ec14705
--- /dev/null
+++ b/docs/DEVELOPMENT-de.md
@@ -0,0 +1,24 @@
+# Entwicklung
+
+[English](DEVELOPMENT.md)
+
+PHP 8.0+ und Python 3 genügen für den Paketbau. JavaScript-Bundler und Composer sind nicht erforderlich.
+
+```sh
+php tests/smoke.php
+python3 tools/build.py
+```
+
+`dist/advanced-scripts-quicknav.zip` ist das Plugin-Archiv mit stabilem Verzeichnisnamen. Das versionierte ZIP enthält denselben Inhalt. Die erzeugte `.as.json` ist ein Advanced-Scripts-Import, kein WordPress-Plugin-ZIP. Inaktiv importieren und anschließend mit `plugins_loaded`, Priorität 20 aktivieren. Die erzeugte `.php` ist die lesbare Ein-Datei-Fassung.
+
+Im Plugin-Betrieb werden die Scripts erst nach dem Laden aller aktiven Plugins gelesen. Der Adapter verwendet `get_scripts()` und reicht ausschließlich Metadaten weiter. Die Navigation erhält die Reihenfolge des Hauptplugins und erzeugt daraus begrenzte Ansichten. Sie ruft keine Ausführungsmethoden von Advanced Scripts auf. Favoriten gehören zum aktuellen Benutzer und zur aktuellen Website-ID.
+
+Die gemeinsame Library wählt bei `plugins_loaded` eine Laufzeit mit der höchsten Version. Sie wird nur vom eigentlichen Plugin registriert. Der Updater startet bei `init` und erhält pluginspezifische Paket-, Versions- und Anforderungsprüfungen. Beide Komponenten werden nicht in das eigenständige Snippet eingebaut.
+
+Der GitHub-Workflow baut Artefakte, veröffentlicht aber kein Release. Testkandidaten gehören in Entwürfe oder Vorabversionen. Eine stabile Version folgt nach Freigabe durch den Autor und gezielten Release-Prüfungen; die zurückgestellte Integrationsmatrix dokumentieren. Bei Versionsänderungen alle Sprachfassungen und Versionsangaben prüfen. Der Updater überspringt Entwürfe und Vorabversionen.
+
+`tests/smoke.php` verwendet WordPress-Testdoubles und prüft Navigation, Ordnerstatus, Metadatenumfang, Berechtigungen und Menügrenzen. Der Test belegt keine WordPress-Laufzeitkompatibilität. Durchgeführte Prüfungen und Grenzen stehen in VALIDATION-de.md.
+
+SVG-Quellen und PNG-Exporte liegen in `assets-github/`. Deutsche und englische Banner sind getrennte Dateien. Historische Grafikpfade in `assets/` verwenden die gewählte Gestaltung Code Compass (A).
+
+Übersetzungen werden als PO-, MO- und PHP-Kataloge mitgeliefert. Beide deutschen Sprachvarianten müssen zum POT passen. Fehlende Übersetzungen dürfen nicht als geprüfte Kompatibilität ausgegeben werden.
diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md
new file mode 100644
index 0000000..d1c6e6f
--- /dev/null
+++ b/docs/DEVELOPMENT.md
@@ -0,0 +1,24 @@
+# Development
+
+[Deutsch](DEVELOPMENT-de.md)
+
+PHP 8.0+ and Python 3 are sufficient to build the source checkout. No JavaScript bundler or Composer installation is needed.
+
+```sh
+php tests/smoke.php
+python3 tools/build.py
+```
+
+`dist/advanced-scripts-quicknav.zip` is the stable-slug plugin archive. The versioned ZIP has identical content. The generated `.as.json` is an Advanced Scripts import, not a WordPress plugin ZIP. Import it inactive, then enable it with `plugins_loaded`, priority 20. The generated `.php` is the readable single-file equivalent.
+
+The plugin-mode bootstrap resolves scripts after all active plugins have loaded. The adapter uses `get_scripts()` and keeps only metadata. Navigation builds an indexed, guarded projection while preserving upstream order; it never calls Advanced Scripts execution methods. Favorite metadata is scoped to the current user and blog ID.
+
+The shared Library elects one highest-version runtime at `plugins_loaded`. Register it only from the actual plugin. The shared updater is initialized at `init`, with host-specific package identity, version and requirements checks. Neither component is included in the standalone snippet.
+
+GitHub workflow builds artifacts; it does not publish a release. Use a draft/prerelease for candidates and publish a stable package after author approval and focused release checks; document the deferred integration matrix. Review bilingual changelogs and version strings when bumping the release. The updater skips drafts and prereleases.
+
+`tests/smoke.php` uses WordPress doubles. It checks meaningful navigation, ancestor status, metadata scope, permission gates and menu limits, but cannot establish WordPress runtime compatibility. See VALIDATION.md for performed checks and limits.
+
+SVG source artwork and PNG exports are bundled in `assets-github/`. English and German banners are separate assets. Historical asset URLs in `assets/` use the selected Code Compass artwork (A).
+
+Translations ship as PO, MO and PHP catalogs. Keep both German informal and formal catalogs in sync with the POT source template. A missing translation must not be marked as verified compatibility.
diff --git a/docs/FOOTER-STANDARD-de.md b/docs/FOOTER-STANDARD-de.md
new file mode 100644
index 0000000..bcf7aae
--- /dev/null
+++ b/docs/FOOTER-STANDARD-de.md
@@ -0,0 +1,11 @@
+# Gemeinsamer deckerweb-Footer-Standard
+
+[English](FOOTER-STANDARD.md)
+
+Der Benutzer hat den formatierten HTML-Änderungsverlauf als Standard für alle Plugin-Footer festgelegt. Die dauerhafte Vorgabe steht zusätzlich in Documents/Codex/AGENTS.md. Bereits bestehende andere Plugins werden damit bei ihrer nächsten Bearbeitung aktualisiert.
+
+Referenz: `includes/deckerweb-changelog-v1.php`, Klasse `Deckerweb_Changelog_Renderer_V1`. Die gemeinsame Klasse ist gegen doppelte Deklarationen beim gleichzeitigen Einsatz mehrerer Plugins geschützt. Im QuickNav-Snippet wird sie aus derselben Quelle eingebettet.
+
+Die Datenquelle bleibt der lokale, übersetzte Changelog. Die Darstellung enthält Versionsüberschrift, Datum, kategorisierte Listeneinträge und dezente Badges (Neu, Verbessert, Behoben, Sonstiges beziehungsweise New, Improved, Fixed, Misc). Readme-Marker und rohe Textblöcke erscheinen nicht im Modal. Quell-HTML wird immer maskiert; nur feste Struktur und maskierte Inline-Code-Spannen werden als HTML ausgegeben.
+
+Modal: zugänglicher Titel, sichtbare Schließen-Schaltfläche, Escape, Fokusrückgabe, scrollbarer Inhalt, mobile Darstellung und gewöhnlicher Changelog-Link als Fallback ohne JavaScript.
diff --git a/docs/FOOTER-STANDARD.md b/docs/FOOTER-STANDARD.md
new file mode 100644
index 0000000..d75310a
--- /dev/null
+++ b/docs/FOOTER-STANDARD.md
@@ -0,0 +1,11 @@
+# Shared deckerweb footer standard
+
+[Deutsch](FOOTER-STANDARD-de.md)
+
+The user selected a formatted HTML changelog as the standard for all plugin footers. The persistent rule is also stored in Documents/Codex/AGENTS.md. Existing other plugins will adopt it when next edited.
+
+Reference: `includes/deckerweb-changelog-v1.php`, `Deckerweb_Changelog_Renderer_V1`. The shared class guards against duplicate declarations across plugins; the QuickNav snippet embeds the same source.
+
+Source remains the local translated changelog. Presentation uses version/date headings, categorized list entries and subtle New/Improved/Fixed/Misc badges (translated in German). No readme markers or raw text blocks appear in the modal. Source HTML is always escaped; only fixed structure and escaped inline code spans become HTML.
+
+Modal: accessible title, visible close action, Escape, focus restoration, scrollable content, mobile layout and an ordinary changelog link as a no-JavaScript fallback.
diff --git a/docs/RELEASE-1.2.0-de.md b/docs/RELEASE-1.2.0-de.md
new file mode 100644
index 0000000..c4953c7
--- /dev/null
+++ b/docs/RELEASE-1.2.0-de.md
@@ -0,0 +1,12 @@
+# Advanced Scripts QuickNav 1.2.0
+
+* Neu: Persönliche Favoriten mit klickbaren Karten, direkter Auswahlvorschau und Filter nach Titel oder Ordner.
+* Neu: Snippet-Statistik, Script-Links im Ordnerbaum und die Aktion Script hier hinzufügen.
+* Verbessert: Modulare Metadaten-Anbindung an Advanced Scripts, Script-Details, Hinweise auf Ordnersperren und begrenzte Menüs.
+* Verbessert: Gemeinsamer deckerweb GitHub Updater V2, deckerweb Library, Einstellungs-Header/-Footer und zugänglicher HTML-Changelog-Dialog.
+* Verbessert: Code-Compass-Grafiken sowie abgestimmte deutsche/englische Dokumentation, FAQs und Übersetzungen.
+* Behoben: Safe-Mode-Erkennung, Verarbeitung der Link-Filter, Menü-IDs, Sichtbarkeitsoptionen und der veraltete Toolbar-Callback, der in rc2 einen Fatal Error verursachte.
+* Sonstiges: Benötigt WordPress 6.7+ und PHP 8.0+. Entfernt Vollbild-Blockeditor-Anpassungen. Plugin und eigenständiges Snippet entstehen aus derselben Quelle.
+* Sonstiges: Veröffentlicht nach erfolgreichem Benutzertest von rc3 und gezielten Release-Prüfungen. Die umfassende WordPress-Integrationsmatrix bleibt zurückgestellt.
+
+[English](RELEASE-1.2.0.md)
diff --git a/docs/RELEASE-1.2.0-rc2-de.md b/docs/RELEASE-1.2.0-rc2-de.md
new file mode 100644
index 0000000..4b6151b
--- /dev/null
+++ b/docs/RELEASE-1.2.0-rc2-de.md
@@ -0,0 +1,22 @@
+# Advanced Scripts QuickNav 1.2.0-rc2
+
+Lokaler Testkandidat vom 1. Oktober 2026.
+
+Installation: Das Plugin-ZIP über Plugins → Plugin hinzufügen → Plugin hochladen installieren und eine vorhandene Version ersetzen. Advanced Scripts muss aktiv sein; WordPress 6.7+ und PHP 8.0+ werden benötigt.
+
+Einstellungen → Advanced Scripts QuickNav: Favoriten wählen und speichern. In der Toolbar aktive/inaktive Scripts und Ordner öffnen. Ein deaktivierter Elternordner erzeugt den Hinweis Durch Ordner deaktiviert. Script hier anlegen öffnet den nativen Erstellungsdialog.
+
+Die vom Benutzer gewählte Variante A (Code Compass) ist enthalten. Alle Pakete enthalten ausschließlich QuickNav und die gemeinsamen deckerweb-Komponenten, keinen Premium-Quellcode.
+
+Die umfassende WordPress-Integrationsmatrix ist ausdrücklich verschoben. Das Paket wurde nicht als öffentliche stabile Version veröffentlicht. Favoriten bleiben bei Deaktivierung erhalten.
+
+[FAQ](wiki/Deutsch.md) · [Changelog](CHANGELOG-de.md)
+
+## Was sich gegenüber rc1 geändert hat
+
+- Die Vollbildeditor-Option und sämtliche zugehörigen Anpassungen sind entfernt.
+- Favoriten werden als Karten mit direkten Editor-Links dargestellt. Die Karten aktualisieren sich beim An- und Abwählen; die Toolbar nach dem Speichern.
+- Die Statistik zeigt Snippets insgesamt, aktiv, inaktiv, Ordner, gespeicherte Favoriten und durch Ordner blockierte aktive Snippets sowie die Typverteilung.
+- Das Changelog-Modal zeigt formatiertes HTML mit Versionsüberschriften, Datum, Listen und Kategorie-Badges. Diese Gestaltung ist als gemeinsamer Plugin-Footer-Standard hinterlegt.
+
+Vorhandene Favoriten bleiben beim Ersetzen des Plugins erhalten. Für den Test das bisherige Plugin mit dem neuen ZIP ersetzen; nicht parallel installieren.
diff --git a/docs/RELEASE-1.2.0-rc2.md b/docs/RELEASE-1.2.0-rc2.md
new file mode 100644
index 0000000..89da97d
--- /dev/null
+++ b/docs/RELEASE-1.2.0-rc2.md
@@ -0,0 +1,18 @@
+# Advanced Scripts QuickNav 1.2.0-rc2
+
+Local test candidate, October 1, 2026.
+
+Install the plugin ZIP via Plugins → Add New → Upload Plugin, replacing the previous installation. Requires active Advanced Scripts, WordPress 6.7+ and PHP 8.0+. Select favorites under Settings → Advanced Scripts QuickNav.
+
+The user-selected variant A (Code Compass) is included. Packages include no Advanced Scripts premium source. Full WordPress integration validation is explicitly deferred; this is not a public stable release. Deactivation retains favorites.
+
+[FAQ](wiki/English.md) · [Changelog](CHANGELOG.md)
+
+## Changes from rc1
+
+- Removed fullscreen-editor preferences and adjustments.
+- Added directly linked favorite cards and immediate selection previews. Save applies the selection to the toolbar.
+- Added total, active/inactive, folder, saved-favorite and active-but-folder-blocked counts, plus type distribution.
+- Formatted the modal as HTML with version/date headings, lists and category badges; recorded it as the shared plugin-footer standard.
+
+Replace the existing plugin with this ZIP. Saved favorites remain intact. Do not install a second copy.
diff --git a/docs/RELEASE-1.2.0-rc3-de.md b/docs/RELEASE-1.2.0-rc3-de.md
new file mode 100644
index 0000000..d8bba9b
--- /dev/null
+++ b/docs/RELEASE-1.2.0-rc3-de.md
@@ -0,0 +1,6 @@
+# Prüfstand 1.2.0-rc3
+
+* Behoben: Verbliebene admin_bar_menu-Registrierung der entfernten Methode remove_adminbar_nodes gelöscht; verhindert einen Fatal Error bei aktivierten Expertenlinks.
+* Verbessert: Regressionstests erkennen jetzt ungültige Action-Callbacks.
+
+Der Fehler wurde vor der Korrektur im Test reproduziert. Die umfassende WordPress-Integrationsmatrix bleibt zurückgestellt.
diff --git a/docs/RELEASE-1.2.0-rc3.md b/docs/RELEASE-1.2.0-rc3.md
new file mode 100644
index 0000000..78f7352
--- /dev/null
+++ b/docs/RELEASE-1.2.0-rc3.md
@@ -0,0 +1,6 @@
+# Validation status 1.2.0-rc3
+
+* Fixed: Removed the remaining admin_bar_menu registration for the deleted remove_adminbar_nodes method, preventing a fatal error when expert links are enabled.
+* Improved: Regression checks now reject invalid action callbacks.
+
+The regression reproduced the error before the fix. The full WordPress integration matrix remains deferred.
diff --git a/docs/RELEASE-1.2.0.md b/docs/RELEASE-1.2.0.md
new file mode 100644
index 0000000..1560f6b
--- /dev/null
+++ b/docs/RELEASE-1.2.0.md
@@ -0,0 +1,12 @@
+# Advanced Scripts QuickNav 1.2.0
+
+* New: Personal favorites with clickable cards, a live selection preview and filtering by title or folder.
+* New: Snippet statistics, folder-tree script links and an Add script here shortcut.
+* Improved: Modular metadata-only integration with Advanced Scripts, script details, folder-blocking hints and bounded menus.
+* Improved: Shared deckerweb GitHub Updater V2, deckerweb Library, settings header/footer and an accessible HTML changelog dialog.
+* Improved: Code Compass artwork and synchronized English/German documentation, FAQs and translations.
+* Fixed: Safe Mode detection, link-filter handling, menu IDs, visibility controls and the obsolete toolbar callback that caused a fatal error in rc2.
+* Misc: Requires WordPress 6.7+ and PHP 8.0+. Removes fullscreen block-editor adjustments. Plugin and standalone snippet are built from one source.
+* Misc: Released after successful user testing of rc3 and focused release checks. The broader WordPress integration matrix remains deferred.
+
+[Deutsch](RELEASE-1.2.0-de.md)
diff --git a/docs/ROADMAP-de.md b/docs/ROADMAP-de.md
new file mode 100644
index 0000000..886078b
--- /dev/null
+++ b/docs/ROADMAP-de.md
@@ -0,0 +1,7 @@
+# Ausblick
+
+[English](ROADMAP.md)
+
+Für 1.2.0 umgesetzt: Integrationskorrekturen, modulares Refactoring, visuelle Favoriten mit direkten Links, Snippet-Statistik, HTML-Changelog, Scripts im Ordnerbaum, Statushinweise zu Elternordnern, begrenzte Menüs und ordnerspezifische Erstellungslinks.
+
+Später: tastaturbedienbare Toolbar-Suche, zuletzt geöffnete Scripts, umfassende WordPress-Integrationsmatrix und öffentliches stabiles Release. Weitere Features sind für diesen Testkandidaten nicht zugesagt.
diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md
new file mode 100644
index 0000000..072e251
--- /dev/null
+++ b/docs/ROADMAP.md
@@ -0,0 +1,7 @@
+# Roadmap
+
+[Deutsch](ROADMAP-de.md)
+
+Implemented for 1.2.0: integration corrections, modular refactoring, visual favorites with direct links, snippet statistics, HTML changelog, folder scripts, ancestor-status hints, bounded menus and folder-specific creation links.
+
+Later: keyboard-accessible toolbar search, recently opened scripts, full WordPress integration matrix and public stable release. No additional features are promised for this candidate.
diff --git a/docs/VALIDATION-de.md b/docs/VALIDATION-de.md
new file mode 100644
index 0000000..86981fa
--- /dev/null
+++ b/docs/VALIDATION-de.md
@@ -0,0 +1,3 @@
+# Prüfstand 1.2.0
+
+rc3 wurde vom Plugin-Autor erfolgreich getestet. Syntaxprüfungen aller PHP-Dateien, Hook-Callback-Regression, Navigation/Favoriten/Statistik, zweisprachige Dokumentation, ZIP-Struktur und Snippet-Bootstrap werden vor Veröffentlichung geprüft. Die rc2-UI wurde mit Beispieldaten im Browser geprüft. Die umfassende WordPress-Integrationsmatrix bleibt auf Benutzerwunsch zurückgestellt.
diff --git a/docs/VALIDATION.md b/docs/VALIDATION.md
new file mode 100644
index 0000000..6ceec7c
--- /dev/null
+++ b/docs/VALIDATION.md
@@ -0,0 +1,3 @@
+# Validation status 1.2.0
+
+rc3 was successfully tested by the plugin author. PHP syntax, hook-callback regression, navigation/favorites/statistics, bilingual documentation, archive layout and standalone snippet bootstrap are checked before publication. The rc2 UI was checked in a browser with fixture data. The broader WordPress integration matrix remains deferred at the user’s request.
diff --git a/docs/changelog-de.txt b/docs/changelog-de.txt
new file mode 100644
index 0000000..caa4336
--- /dev/null
+++ b/docs/changelog-de.txt
@@ -0,0 +1,62 @@
+== Changelog ==
+
+= 1.2.0 · 2026-10-01 =
+
+* Neu: Persönliche Favoriten mit klickbaren Karten, direkter Auswahlvorschau und Filter nach Titel oder Ordner.
+* Neu: Snippet-Statistik, Script-Links im Ordnerbaum und die Aktion Script hier hinzufügen.
+* Verbessert: Modulare Metadaten-Anbindung an Advanced Scripts, Script-Details, Hinweise auf Ordnersperren und begrenzte Menüs.
+* Verbessert: Gemeinsamer deckerweb GitHub Updater V2, deckerweb Library, Einstellungs-Header/-Footer und zugänglicher HTML-Changelog-Dialog.
+* Verbessert: Code-Compass-Grafiken sowie abgestimmte deutsche/englische Dokumentation, FAQs und Übersetzungen.
+* Behoben: Safe-Mode-Erkennung, Verarbeitung der Link-Filter, Menü-IDs, Sichtbarkeitsoptionen und der veraltete Toolbar-Callback, der in rc2 einen Fatal Error verursachte.
+* Sonstiges: Benötigt WordPress 6.7+ und PHP 8.0+. Entfernt Vollbild-Blockeditor-Anpassungen. Plugin und eigenständiges Snippet entstehen aus derselben Quelle.
+* Sonstiges: Veröffentlicht nach erfolgreichem Benutzertest von rc3 und gezielten Release-Prüfungen. Die umfassende WordPress-Integrationsmatrix bleibt zurückgestellt.
+
+= 1.2.0-rc3 · 2026-10-01 (Testkandidat) =
+
+* Behoben: Verbliebene admin_bar_menu-Registrierung der entfernten Methode remove_adminbar_nodes gelöscht; verhindert einen Fatal Error bei aktivierten Expertenlinks.
+* Verbessert: Regressionstests erkennen jetzt ungültige Action-Callbacks.
+
+= 1.2.0-rc2 · 2026-10-01 (Testkandidat) =
+
+* Neu: Visuelle Favoritenkarten mit direkten Editor-Links und sofortiger Vorschau der aktuellen Auswahl.
+* Neu: Kompakte Snippet-Statistik mit Gesamtzahl, Aktiv-/Inaktiv-Zahlen, Ordnern, gespeicherten Favoriten, durch Ordner blockierten aktiven Snippets und Typverteilung.
+* Verbessert: Formatiert den lokalen Changelog-Dialog als maskiertes HTML mit Versions- und Datumsüberschriften, lesbaren Listen und dezenten Kategorie-Badges nach dem gemeinsamen deckerweb-Footer-Standard.
+* Verbessert: Behält die gewählte Gestaltung Code Compass (Variante A) bei und aktualisiert die abgestimmten deutschen und englischen Dokumentationen und Übersetzungen.
+* Sonstiges: Entfernt die Vollbild-Blockeditor-Option, zugehörige Hooks, CSS-Anpassungen und das Entfernen des WordPress-Logos.
+* Sonstiges: Vorhandene Favoriten bleiben erhalten. Die umfassende WordPress-Integrationsmatrix bleibt auf später verschoben.
+
+= 1.2.0-rc1 · 2026-10-01 (Testkandidat) =
+
+* Neu: Persönliche Favoriten getrennt nach Benutzer und Website, mit Filter nach Titel oder Ordner auf der Einstellungsseite.
+* Neu: Direkte Script-Links im Ordnerbaum und die Aktion Script hier anlegen für jeden Ordner.
+* Neu: Persönliche Einstellungen für Script-Zähler, Entwicklerlinks, Vollbild-Toolbar und begrenzte Menülisten.
+* Verbessert: Zeigt Durch Ordner deaktiviert an, ohne den eigenen Aktivstatus umzudeuten; ergänzt Typ, Ordnerpfad, Ausführungsort und Hook.
+* Verbessert: Trennt Konfiguration, Advanced-Scripts-Adapter, Navigation, Darstellung, persönliche Einstellungen und Hook-Registrierung.
+* Verbessert: Integriert den gemeinsamen deckerweb GitHub Updater V2, sprachabhängige Update-Grafiken und die deckerweb Library 0.2.0.
+* Verbessert: Ergänzt den deckerweb-Header und -Footer der Einstellungsseite sowie einen zugänglichen lokalen Changelog-Dialog.
+* Behoben: Erkennt den über die Advanced-Scripts-Oberfläche aktivierten Safe Mode und berücksichtigt den Vorrang von AS_SAFE_MODE.
+* Behoben: Übernimmt Link-Filter-Rückgaben, trennt die Sichtbarkeit von Bibliothek und Footer und berücksichtigt leere Sammlungen und eigene Admin-Farbschemata.
+* Behoben: Verwendet gemeinsame Berechtigungsprüfungen für Toolbar und Vollbildintegration; schützt Einstellungen durch POST, Berechtigungen und Nonce.
+* Sonstiges: Benötigt PHP 8.0+, behält WordPress 6.7+ bei und liefert abgestimmte deutsche und englische Readmes, FAQ, Wiki-Quellen und vollständige Changelogs.
+* Sonstiges: Enthält vorläufig Design A; drei Icon- und Banner-Alternativen werden separat zur Auswahl bereitgestellt.
+* Sonstiges: Erzeugt das eigenständige Advanced-Scripts-JSON-Snippet aus derselben Quelle. Updater und deckerweb Library sind der Plugin-Installation vorbehalten.
+* Sonstiges: Dies ist ein lokaler Testkandidat. Die umfassende WordPress-Integrationsprüfung und die öffentliche Veröffentlichung stehen noch aus.
+
+= 1.1.0 · 2025-04-05 =
+
+* Neu: Beschränkt QuickNav optional mit ASQN_ENABLED_USERS auf festgelegte Benutzer-IDs.
+* Verbessert: Unterstützt Installation und Aktualisierung mit Git Updater.
+* Behoben: Behebt eine PHP-Warnung im Frontend.
+
+= 1.0.0 · 2025-03-24 =
+
+* Neu: Erste öffentliche Version mit Statuslisten, Ordnerlinks, Safe-Mode-Hinweisen, Ressourcenlinks, Entwickleranbindungen und Konstanten.
+
+= 0.5.0 · 2025-03-23 =
+
+* Sonstiges: Interne Testversion.
+
+= 0.0.0 · 2025-03-23 =
+
+* Sonstiges: Entwicklungsbeginn.
+
diff --git a/docs/changelog.txt b/docs/changelog.txt
new file mode 100644
index 0000000..7c5c0ed
--- /dev/null
+++ b/docs/changelog.txt
@@ -0,0 +1,62 @@
+== Changelog ==
+
+= 1.2.0 · 2026-10-01 =
+
+* New: Personal favorites with clickable cards, a live selection preview and filtering by title or folder.
+* New: Snippet statistics, folder-tree script links and an Add script here shortcut.
+* Improved: Modular metadata-only integration with Advanced Scripts, script details, folder-blocking hints and bounded menus.
+* Improved: Shared deckerweb GitHub Updater V2, deckerweb Library, settings header/footer and an accessible HTML changelog dialog.
+* Improved: Code Compass artwork and synchronized English/German documentation, FAQs and translations.
+* Fixed: Safe Mode detection, link-filter handling, menu IDs, visibility controls and the obsolete toolbar callback that caused a fatal error in rc2.
+* Misc: Requires WordPress 6.7+ and PHP 8.0+. Removes fullscreen block-editor adjustments. Plugin and standalone snippet are built from one source.
+* Misc: Released after successful user testing of rc3 and focused release checks. The broader WordPress integration matrix remains deferred.
+
+= 1.2.0-rc3 · 2026-10-01 (test candidate) =
+
+* Fixed: Removed the remaining admin_bar_menu registration for the deleted remove_adminbar_nodes method, preventing a fatal error when expert links are enabled.
+* Improved: Regression checks now reject invalid action callbacks.
+
+= 1.2.0-rc2 · 2026-10-01 (test candidate) =
+
+* New: Visual favorites cards with direct editor links and a live preview of the current selection.
+* New: A compact snippet overview with totals, active/inactive counts, folders, saved favorites, active snippets blocked by folders and a type breakdown.
+* Improved: Formats the local changelog dialog as escaped HTML with version/date headings, readable lists and subtle category badges, following the shared deckerweb footer standard.
+* Improved: Keeps the selected Code Compass artwork (variant A) and updates synchronized English/German documentation and translations.
+* Misc: Removes the fullscreen block-editor preference, hooks, CSS adjustments and WordPress-logo removal.
+* Misc: Existing favorites remain unchanged. The broad WordPress integration matrix remains deferred.
+
+= 1.2.0-rc1 · 2026-10-01 (test candidate) =
+
+* New: Personal favorites stored separately for each user and website, with a filter by title or folder on the preferences page.
+* New: Direct script links in the folder tree and an Add script here shortcut for each folder.
+* New: Personal preferences for script counts, developer links, fullscreen toolbar and bounded menu lists.
+* Improved: Shows Disabled by folder without changing the script’s own active/inactive status; includes type, folder path, location and hook details.
+* Improved: Separates configuration, the Advanced Scripts adapter, navigation, rendering, personal settings and hook registration.
+* Improved: Adds the shared deckerweb GitHub Updater V2, localized update artwork and embedded deckerweb Library 0.2.0.
+* Improved: Adds the deckerweb settings header/footer and an accessible local changelog dialog.
+* Fixed: Recognizes Safe Mode enabled through the Advanced Scripts interface, respecting AS_SAFE_MODE precedence.
+* Fixed: Applies link-filter return values, separates library/footer visibility and handles empty collections and custom admin color schemes.
+* Fixed: Uses shared permission checks for the toolbar and fullscreen integration; protects preferences with POST, capabilities and a nonce.
+* Misc: Requires PHP 8.0+, preserves WordPress 6.7+, and provides synchronized English/German readmes, FAQs, Wiki sources and complete changelogs.
+* Misc: Includes provisional design A; three icon/banner alternatives are supplied separately for selection.
+* Misc: Generates the standalone Advanced Scripts JSON snippet from the same source. Updater and deckerweb Library are plugin-only.
+* Misc: This is a local test candidate. Full WordPress integration validation and public release publication are pending.
+
+= 1.1.0 · 2025-04-05 =
+
+* New: Optionally restrict QuickNav to defined user IDs with ASQN_ENABLED_USERS.
+* Improved: Supports installation and updates with Git Updater.
+* Fixed: Addresses a PHP warning on the frontend.
+
+= 1.0.0 · 2025-03-24 =
+
+* New: Initial public release with status lists, folder links, Safe Mode notices, resource links, developer integrations and constants.
+
+= 0.5.0 · 2025-03-23 =
+
+* Misc: Internal test version.
+
+= 0.0.0 · 2025-03-23 =
+
+* Misc: Development started.
+
diff --git a/docs/wiki/Changelog-Deutsch.md b/docs/wiki/Changelog-Deutsch.md
new file mode 100644
index 0000000..fc8c33f
--- /dev/null
+++ b/docs/wiki/Changelog-Deutsch.md
@@ -0,0 +1,64 @@
+# Änderungsverlauf
+
+[English](https://github.com/deckerweb/advanced-scripts-quicknav/wiki/Changelog%E2%80%90English)
+
+## 1.2.0 · 2026-10-01
+
+- **Neu:** Persönliche Favoriten mit klickbaren Karten, direkter Auswahlvorschau und Filter nach Titel oder Ordner.
+- **Neu:** Snippet-Statistik, Script-Links im Ordnerbaum und die Aktion Script hier hinzufügen.
+- **Verbessert:** Modulare Metadaten-Anbindung an Advanced Scripts, Script-Details, Hinweise auf Ordnersperren und begrenzte Menüs.
+- **Verbessert:** Gemeinsamer deckerweb GitHub Updater V2, deckerweb Library, Einstellungs-Header/-Footer und zugänglicher HTML-Changelog-Dialog.
+- **Verbessert:** Code-Compass-Grafiken sowie abgestimmte deutsche/englische Dokumentation, FAQs und Übersetzungen.
+- **Behoben:** Safe-Mode-Erkennung, Verarbeitung der Link-Filter, Menü-IDs, Sichtbarkeitsoptionen und der veraltete Toolbar-Callback, der in rc2 einen Fatal Error verursachte.
+- **Sonstiges:** Benötigt WordPress 6.7+ und PHP 8.0+. Entfernt Vollbild-Blockeditor-Anpassungen. Plugin und eigenständiges Snippet entstehen aus derselben Quelle.
+- **Sonstiges:** Veröffentlicht nach erfolgreichem Benutzertest von rc3 und gezielten Release-Prüfungen. Die umfassende WordPress-Integrationsmatrix bleibt zurückgestellt.
+
+## 1.2.0-rc3 · 2026-10-01 (Testkandidat)
+
+- **Behoben:** Verbliebene admin_bar_menu-Registrierung der entfernten Methode remove_adminbar_nodes gelöscht; verhindert einen Fatal Error bei aktivierten Expertenlinks.
+- **Verbessert:** Regressionstests erkennen jetzt ungültige Action-Callbacks.
+
+## 1.2.0-rc2 · 2026-10-01 (Testkandidat)
+
+- **Neu:** Visuelle Favoritenkarten mit direkten Editor-Links und sofortiger Vorschau der aktuellen Auswahl.
+- **Neu:** Kompakte Snippet-Statistik mit Gesamtzahl, Aktiv-/Inaktiv-Zahlen, Ordnern, gespeicherten Favoriten, durch Ordner blockierten aktiven Snippets und Typverteilung.
+- **Verbessert:** Formatiert den lokalen Changelog-Dialog als maskiertes HTML mit Versions- und Datumsüberschriften, lesbaren Listen und dezenten Kategorie-Badges nach dem gemeinsamen deckerweb-Footer-Standard.
+- **Verbessert:** Behält die gewählte Gestaltung Code Compass (Variante A) bei und aktualisiert die abgestimmten deutschen und englischen Dokumentationen und Übersetzungen.
+- **Sonstiges:** Entfernt die Vollbild-Blockeditor-Option, zugehörige Hooks, CSS-Anpassungen und das Entfernen des WordPress-Logos.
+- **Sonstiges:** Vorhandene Favoriten bleiben erhalten. Die umfassende WordPress-Integrationsmatrix bleibt auf später verschoben.
+
+## 1.2.0-rc1 · 2026-10-01 (Testkandidat)
+
+- **Neu:** Persönliche Favoriten getrennt nach Benutzer und Website, mit Filter nach Titel oder Ordner auf der Einstellungsseite.
+- **Neu:** Direkte Script-Links im Ordnerbaum und die Aktion Script hier anlegen für jeden Ordner.
+- **Neu:** Persönliche Einstellungen für Script-Zähler, Entwicklerlinks, Vollbild-Toolbar und begrenzte Menülisten.
+- **Verbessert:** Zeigt Durch Ordner deaktiviert an, ohne den eigenen Aktivstatus umzudeuten; ergänzt Typ, Ordnerpfad, Ausführungsort und Hook.
+- **Verbessert:** Trennt Konfiguration, Advanced-Scripts-Adapter, Navigation, Darstellung, persönliche Einstellungen und Hook-Registrierung.
+- **Verbessert:** Integriert den gemeinsamen deckerweb GitHub Updater V2, sprachabhängige Update-Grafiken und die deckerweb Library 0.2.0.
+- **Verbessert:** Ergänzt den deckerweb-Header und -Footer der Einstellungsseite sowie einen zugänglichen lokalen Changelog-Dialog.
+- **Behoben:** Erkennt den über die Advanced-Scripts-Oberfläche aktivierten Safe Mode und berücksichtigt den Vorrang von AS_SAFE_MODE.
+- **Behoben:** Übernimmt Link-Filter-Rückgaben, trennt die Sichtbarkeit von Bibliothek und Footer und berücksichtigt leere Sammlungen und eigene Admin-Farbschemata.
+- **Behoben:** Verwendet gemeinsame Berechtigungsprüfungen für Toolbar und Vollbildintegration; schützt Einstellungen durch POST, Berechtigungen und Nonce.
+- **Sonstiges:** Benötigt PHP 8.0+, behält WordPress 6.7+ bei und liefert abgestimmte deutsche und englische Readmes, FAQ, Wiki-Quellen und vollständige Changelogs.
+- **Sonstiges:** Enthält vorläufig Design A; drei Icon- und Banner-Alternativen werden separat zur Auswahl bereitgestellt.
+- **Sonstiges:** Erzeugt das eigenständige Advanced-Scripts-JSON-Snippet aus derselben Quelle. Updater und deckerweb Library sind der Plugin-Installation vorbehalten.
+- **Sonstiges:** Dies ist ein lokaler Testkandidat. Die umfassende WordPress-Integrationsprüfung und die öffentliche Veröffentlichung stehen noch aus.
+
+## 1.1.0 · 2025-04-05
+
+- **Neu:** Beschränkt QuickNav optional mit ASQN_ENABLED_USERS auf festgelegte Benutzer-IDs.
+- **Verbessert:** Unterstützt Installation und Aktualisierung mit Git Updater.
+- **Behoben:** Behebt eine PHP-Warnung im Frontend.
+
+## 1.0.0 · 2025-03-24
+
+- **Neu:** Erste öffentliche Version mit Statuslisten, Ordnerlinks, Safe-Mode-Hinweisen, Ressourcenlinks, Entwickleranbindungen und Konstanten.
+
+## 0.5.0 · 2025-03-23
+
+- **Sonstiges:** Interne Testversion.
+
+## 0.0.0 · 2025-03-23
+
+- **Sonstiges:** Entwicklungsbeginn.
+
diff --git a/docs/wiki/Changelog-English.md b/docs/wiki/Changelog-English.md
new file mode 100644
index 0000000..3f405a3
--- /dev/null
+++ b/docs/wiki/Changelog-English.md
@@ -0,0 +1,64 @@
+# Changelog
+
+[Deutsch](https://github.com/deckerweb/advanced-scripts-quicknav/wiki/Changelog%E2%80%90Deutsch)
+
+## 1.2.0 · 2026-10-01
+
+- **New:** Personal favorites with clickable cards, a live selection preview and filtering by title or folder.
+- **New:** Snippet statistics, folder-tree script links and an Add script here shortcut.
+- **Improved:** Modular metadata-only integration with Advanced Scripts, script details, folder-blocking hints and bounded menus.
+- **Improved:** Shared deckerweb GitHub Updater V2, deckerweb Library, settings header/footer and an accessible HTML changelog dialog.
+- **Improved:** Code Compass artwork and synchronized English/German documentation, FAQs and translations.
+- **Fixed:** Safe Mode detection, link-filter handling, menu IDs, visibility controls and the obsolete toolbar callback that caused a fatal error in rc2.
+- **Misc:** Requires WordPress 6.7+ and PHP 8.0+. Removes fullscreen block-editor adjustments. Plugin and standalone snippet are built from one source.
+- **Misc:** Released after successful user testing of rc3 and focused release checks. The broader WordPress integration matrix remains deferred.
+
+## 1.2.0-rc3 · 2026-10-01 (test candidate)
+
+- **Fixed:** Removed the remaining admin_bar_menu registration for the deleted remove_adminbar_nodes method, preventing a fatal error when expert links are enabled.
+- **Improved:** Regression checks now reject invalid action callbacks.
+
+## 1.2.0-rc2 · 2026-10-01 (test candidate)
+
+- **New:** Visual favorites cards with direct editor links and a live preview of the current selection.
+- **New:** A compact snippet overview with totals, active/inactive counts, folders, saved favorites, active snippets blocked by folders and a type breakdown.
+- **Improved:** Formats the local changelog dialog as escaped HTML with version/date headings, readable lists and subtle category badges, following the shared deckerweb footer standard.
+- **Improved:** Keeps the selected Code Compass artwork (variant A) and updates synchronized English/German documentation and translations.
+- **Misc:** Removes the fullscreen block-editor preference, hooks, CSS adjustments and WordPress-logo removal.
+- **Misc:** Existing favorites remain unchanged. The broad WordPress integration matrix remains deferred.
+
+## 1.2.0-rc1 · 2026-10-01 (test candidate)
+
+- **New:** Personal favorites stored separately for each user and website, with a filter by title or folder on the preferences page.
+- **New:** Direct script links in the folder tree and an Add script here shortcut for each folder.
+- **New:** Personal preferences for script counts, developer links, fullscreen toolbar and bounded menu lists.
+- **Improved:** Shows Disabled by folder without changing the script’s own active/inactive status; includes type, folder path, location and hook details.
+- **Improved:** Separates configuration, the Advanced Scripts adapter, navigation, rendering, personal settings and hook registration.
+- **Improved:** Adds the shared deckerweb GitHub Updater V2, localized update artwork and embedded deckerweb Library 0.2.0.
+- **Improved:** Adds the deckerweb settings header/footer and an accessible local changelog dialog.
+- **Fixed:** Recognizes Safe Mode enabled through the Advanced Scripts interface, respecting AS_SAFE_MODE precedence.
+- **Fixed:** Applies link-filter return values, separates library/footer visibility and handles empty collections and custom admin color schemes.
+- **Fixed:** Uses shared permission checks for the toolbar and fullscreen integration; protects preferences with POST, capabilities and a nonce.
+- **Misc:** Requires PHP 8.0+, preserves WordPress 6.7+, and provides synchronized English/German readmes, FAQs, Wiki sources and complete changelogs.
+- **Misc:** Includes provisional design A; three icon/banner alternatives are supplied separately for selection.
+- **Misc:** Generates the standalone Advanced Scripts JSON snippet from the same source. Updater and deckerweb Library are plugin-only.
+- **Misc:** This is a local test candidate. Full WordPress integration validation and public release publication are pending.
+
+## 1.1.0 · 2025-04-05
+
+- **New:** Optionally restrict QuickNav to defined user IDs with ASQN_ENABLED_USERS.
+- **Improved:** Supports installation and updates with Git Updater.
+- **Fixed:** Addresses a PHP warning on the frontend.
+
+## 1.0.0 · 2025-03-24
+
+- **New:** Initial public release with status lists, folder links, Safe Mode notices, resource links, developer integrations and constants.
+
+## 0.5.0 · 2025-03-23
+
+- **Misc:** Internal test version.
+
+## 0.0.0 · 2025-03-23
+
+- **Misc:** Development started.
+
diff --git a/docs/wiki/Deutsch.md b/docs/wiki/Deutsch.md
new file mode 100644
index 0000000..9f9849e
--- /dev/null
+++ b/docs/wiki/Deutsch.md
@@ -0,0 +1,154 @@
+# Advanced Scripts QuickNav · Deutsch
+
+[English](https://github.com/deckerweb/advanced-scripts-quicknav/wiki/English) · [README](../../README-de.md)
+
+## Schnellstart
+
+1. Lade das Release-ZIP hoch: Plugins → Plugin hinzufügen → Plugin hochladen.
+2. Aktiviere Advanced Scripts und QuickNav.
+3. Öffne Einstellungen → Advanced Scripts QuickNav und wähle Favoriten.
+4. Speichere deine Einstellungen und öffne Scripts in der Toolbar.
+
+Alternativ: Importiere ddw-advanced-scripts-quicknav.as.json in Advanced Scripts. Verwende nur eine Installationsart. Der PHP-Safe-Mode kann das Snippet im Admin blockieren; Updater und deckerweb Library sind nur im Plugin enthalten.
+
+Der Code wurde mit Advanced Scripts 2.6.2 abgeglichen und rc3 vom Plugin-Autor erfolgreich getestet. Die umfassende WordPress-Integrationsmatrix bleibt zurückgestellt. PHP 7.4 wird wegen der gemeinsamen Library nicht mehr unterstützt.
+
+## Anzeige und Konfiguration
+
+Konstanten haben Vorrang vor persönlichen Anzeigeoptionen. Benutzer benötigen manage_options und die konfigurierte QuickNav-Berechtigung. Der Ordnerbaum enthält maximal acht Ebenen und standardmäßig 40 Script-/Ordnereinträge insgesamt. Favoriten und jede Statusliste sind ebenfalls auf je 40 Einträge begrenzt. Alle anzeigen öffnet Advanced Scripts.
+
+Beispiele, bei Bedarf einzeln verwenden:
+
+```php
+define( 'ASQN_VIEW_CAPABILITY', 'activate_plugins' );
+define( 'ASQN_ENABLED_USERS', [ 1, 500 ] );
+define( 'ASQN_NAME_IN_ADMINBAR', 'Scripts' );
+define( 'ASQN_COUNTER', 'yes' );
+define( 'ASQN_ICON', 'remix' );
+define( 'ASQN_DISABLE_LIBRARY', 'yes' );
+define( 'ASQN_DISABLE_FOOTER', 'yes' );
+define( 'ASQN_EXPERT_MODE', false );
+define( 'ASQN_MENU_LIMIT', 40 );
+```
+
+ASQN_ICON akzeptiert blue oder remix; ohne Konstante erscheint das QuickNav-Symbol. ASQN_DISABLE_LIBRARY steuert nur Ressourcenlinks. Die deckerweb Library hat eigene Einstellungen.
+
+## FAQ
+
+### Für wen ist QuickNav gedacht?
+
+Für Administratoren, die häufig mit Advanced Scripts arbeiten und aus der Backend- oder Frontend-Toolbar direkt auf Scripts zugreifen möchten.
+
+### Ersetzt QuickNav Advanced Scripts?
+
+Nein. Advanced Scripts verwaltet weiterhin Code, Ausführung, Bedingungen, Import und Export. QuickNav ergänzt die Navigation.
+
+### Welche Voraussetzungen gelten?
+
+WordPress 6.7+, PHP 8.0+ und eine aktive Advanced-Scripts-Installation. Der Quellcode wurde mit der bereitgestellten Version 2.6.2 abgeglichen; die Laufzeitkompatibilität wurde noch nicht umfassend geprüft.
+
+### Wo wähle ich Favoriten aus?
+
+Öffne Einstellungen → Advanced Scripts QuickNav. Sieh klickbare Favoritenkarten und die Snippet-Statistik, filtere nach Titel oder Ordner, wähle Scripts und speichere deine Einstellungen.
+
+### Werden Favoriten geteilt?
+
+Nein. Favoriten und Anzeigeoptionen gehören zu deinem Benutzerkonto auf der aktuellen Website. Andere Benutzer und Multisite-Unterwebsites haben eigene Auswahlen.
+
+### Was bedeutet Durch Ordner deaktiviert?
+
+Mindestens ein übergeordneter Ordner ist inaktiv. Advanced Scripts überspringt diesen Zweig auch dann, wenn das Script selbst aktiv ist.
+
+### Bedeutet Aktiv, dass ein Script gerade ausgeführt wird?
+
+Nein. Gemeint ist der gespeicherte Script-Status. Ordnerstatus, Ausführungsort, Bedingungen, Hooks und PHP-Safe-Mode können die Ausführung beeinflussen.
+
+### Wie erkennt QuickNav den Safe Mode?
+
+Eine definierte Konstante AS_SAFE_MODE hat Vorrang, auch mit false. Andernfalls liest QuickNav die Option advanced-scripts-safemode. Es verändert den Safe Mode nicht.
+
+### Stoppt Safe Mode alle Frontend-Scripts?
+
+Nein. Im bereitgestellten Quellcode von Advanced Scripts 2.6.2 schützt der PHP-Safe-Mode Admin und Login; er ist kein allgemeiner Ausschalter für Frontend-Scripts.
+
+### Kann ich ein Script direkt in einem Ordner anlegen?
+
+Ja. Script hier anlegen öffnet den Advanced-Scripts-Editor mit dem gewählten übergeordneten Ordner. Erst das Speichern im Editor legt das Script an.
+
+### Warum fehlen manche Einträge?
+
+Standardmäßig erscheinen bis zu 40 Einträge je Status- und Favoritenliste sowie insgesamt 40 Script- und Ordnereinträge im Ordnerbaum. Wähle 10–200 in den Einstellungen. Alle anzeigen öffnet Advanced Scripts. Der Baum ist auf acht Ebenen begrenzt.
+
+### Kann ich die Toolbar durchsuchen?
+
+In dieser Version noch nicht. Auf der Einstellungsseite lässt sich die Favoritenauswahl nach Titel oder Ordner filtern. Eine tastaturbedienbare Toolbar-Suche ist separat geplant.
+
+### Funktionieren vorhandene Konstanten weiterhin?
+
+Ja. ASQN_VIEW_CAPABILITY, ASQN_ENABLED_USERS, ASQN_NAME_IN_ADMINBAR, ASQN_COUNTER, ASQN_ICON, ASQN_DISABLE_LIBRARY, ASQN_DISABLE_FOOTER und ASQN_EXPERT_MODE bleiben unterstützt. Anzeige-Konstanten haben Vorrang vor persönlichen Einstellungen.
+
+### Welche Berechtigungen werden benötigt?
+
+Der Benutzer benötigt manage_options und die QuickNav-Berechtigung, standardmäßig activate_plugins. ASQN_ENABLED_USERS kann die Sichtbarkeit weiter beschränken. Eine niedrigere QuickNav-Berechtigung gewährt keinen zusätzlichen Zugriff auf Advanced Scripts.
+
+### Wie funktionieren Updates?
+
+Das Plugin integriert deckerweb GitHub Updater V2 in das reguläre WordPress-Updatesystem. Er prüft öffentliche stabile GitHub-Releases und stellt das passende Plugin-ZIP bereit. Automatische Updates werden nicht eingeschaltet.
+
+### Was ist die deckerweb Library?
+
+Ein eingebetteter Katalog unter Plugins → Plugin hinzufügen → deckerweb mit gemeinsamen Einstellungen. Er ist unabhängig von den Ressourcenlinks unter Snippets finden. Mehrere kompatible deckerweb-Plugins teilen sich eine Laufzeit.
+
+### Sendet QuickNav meine Scripts an andere Dienste?
+
+QuickNav-Navigation und Favoriten senden keine Script-Daten an andere Dienste. Der Updater ruft Release-Metadaten von GitHub ab. Library-Aktionen können freigegebene GitHub-Downloads abrufen; ihr optionaler Online-Katalog hat eigene Einstellungen.
+
+### Kann ich stattdessen das Snippet installieren?
+
+Ja. Importiere das erzeugte JSON in Advanced Scripts und aktiviere es mit dem Hook plugins_loaded. Es enthält Navigation und persönliche Einstellungen, aber keinen Plugin-Updater und keine deckerweb Library. Verwende entweder Plugin oder Snippet.
+
+### Was passiert mit dem Snippet im Safe Mode?
+
+Advanced Scripts kann die Ausführung des QuickNav-PHP-Snippets im Admin verhindern. Dann kann es auch keinen eigenen Safe-Mode-Hinweis anzeigen. Für zuverlässige Admin-Diagnose empfiehlt sich die Plugin-Installation.
+
+### Was passiert mit gelöschten Favoriten?
+
+Sie verschwinden automatisch aus der angezeigten Auswahl. Beim Speichern werden veraltete IDs entfernt. Gleichnamige Scripts bleiben durch ihre Term-IDs unterscheidbar.
+
+### Kann ich Scripts in QuickNav ein- und ausschalten?
+
+Nein. Die Links öffnen den nativen Editor. Aktivierung, Löschung und Ausführung bleiben bei Advanced Scripts.
+
+### Wo werden meine Einstellungen gespeichert?
+
+In Benutzermetadaten mit der ID der aktuellen Website. Eine Deaktivierung erhält sie. Diese Version enthält keine automatische Bereinigung bei Deinstallation.
+
+### Sind Übersetzungen enthalten?
+
+Deutsche informelle und formelle Kataloge werden mitgeliefert. Das Plugin verwendet die WordPress-Benutzer- oder Websitesprache. Das eigenständige Snippet enthält seinen deutschen Katalog.
+
+### Was zählt die Statistik?
+
+Alle Snippets zählt ohne Ordner. Aktiv und inaktiv beziehen sich auf den gespeicherten Status. Durch einen inaktiven Elternordner blockierte aktive Snippets sind eine zusätzliche Teilmenge der aktiven Snippets. Favoriten zählt die gespeicherte Auswahl unabhängig vom Toolbar-Limit. Typzahlen enthalten aktive und inaktive Snippets.
+
+### Wie funktionieren die Favoritenkarten?
+
+Die Karten öffnen direkt den nativen Editor. Beim An- und Abwählen aktualisiert sich die Kartenvorschau sofort; Meine Einstellungen speichern übernimmt die Auswahl in die Toolbar. Ohne JavaScript bleiben gespeicherte Favoritenkarten nutzbar.
+
+### Was wurde aus der Vollbild-Toolbar-Option?
+
+QuickNav passt den Vollbild-Blockeditor nicht mehr an, ergänzt kein Vollbild-CSS und entfernt das WordPress-Logo nicht mehr. Alte ASQN_FULLSCREEN-Definitionen und gespeicherte Vollbildoptionen haben keine Wirkung mehr auf QuickNav.
+
+### Ist diese Version für den Produktiveinsatz fertig?
+
+Version 1.2.0 basiert auf der erfolgreich getesteten rc3. Syntax und gezielte Release-Prüfungen sind bestanden; die umfassende WordPress-Integrationsmatrix folgt wie vereinbart später.
+
+## Updates und Library
+
+Der deckerweb GitHub Updater V2 zeigt öffentliche stabile Releases im regulären WordPress-Updatesystem an. Für die Erstinstallation das Release-ZIP hochladen. Die deckerweb Library ergänzt den deckerweb-Tab im Plugin-Installer und eigene gemeinsame Einstellungen. Sie ist unabhängig von Snippets finden.
+
+Header und Footer der Einstellungsseite zeigen Icon, Version, sprachabhängige Dokumentation, den als HTML formatierten lokalen Changelog, Plugin-Website und © 2022–2026 David Decker – DECKERWEB.
+
+## Changelog
+
+[Vollständiger Änderungsverlauf](../CHANGELOG-de.md)
diff --git a/docs/wiki/English.md b/docs/wiki/English.md
new file mode 100644
index 0000000..c8d2e28
--- /dev/null
+++ b/docs/wiki/English.md
@@ -0,0 +1,154 @@
+# Advanced Scripts QuickNav · English
+
+[Deutsch](https://github.com/deckerweb/advanced-scripts-quicknav/wiki/Deutsch) · [README](../../README.md)
+
+## Quick start
+
+1. Upload the release ZIP through Plugins → Add New → Upload Plugin.
+2. Activate Advanced Scripts and QuickNav.
+3. Open Settings → Advanced Scripts QuickNav and select favorites.
+4. Save preferences and open Scripts in the toolbar.
+
+Alternatively, import ddw-advanced-scripts-quicknav.as.json into Advanced Scripts. Use one installation mode only. PHP Safe Mode can block the snippet in the admin; updater and deckerweb Library are plugin-only.
+
+Source was reviewed against Advanced Scripts 2.6.2 and rc3 was successfully tested by the plugin author. The broader WordPress integration matrix remains deferred. PHP 7.4 is no longer supported because of the shared Library.
+
+## Display and configuration
+
+Constants override personal display preferences. Users need manage_options and the configured QuickNav capability. The folder tree allows eight levels and defaults to 40 script/folder entries overall. Favorites and each status list also default to 40 entries. View all opens Advanced Scripts.
+
+Examples; apply individually as needed:
+
+```php
+define( 'ASQN_VIEW_CAPABILITY', 'activate_plugins' );
+define( 'ASQN_ENABLED_USERS', [ 1, 500 ] );
+define( 'ASQN_NAME_IN_ADMINBAR', 'Scripts' );
+define( 'ASQN_COUNTER', 'yes' );
+define( 'ASQN_ICON', 'remix' );
+define( 'ASQN_DISABLE_LIBRARY', 'yes' );
+define( 'ASQN_DISABLE_FOOTER', 'yes' );
+define( 'ASQN_EXPERT_MODE', false );
+define( 'ASQN_MENU_LIMIT', 40 );
+```
+
+ASQN_ICON accepts blue or remix; without a constant the QuickNav symbol is used. ASQN_DISABLE_LIBRARY controls resource links only. deckerweb Library has its own settings.
+
+## FAQ
+
+### Who is QuickNav for?
+
+Administrators who frequently use Advanced Scripts and want direct access from the backend and frontend toolbar.
+
+### Does QuickNav replace Advanced Scripts?
+
+No. Advanced Scripts remains responsible for code, execution, conditions, imports and exports. QuickNav provides navigation.
+
+### What are the requirements?
+
+WordPress 6.7+, PHP 8.0+ and an active Advanced Scripts installation. The source was reviewed against Advanced Scripts 2.6.2; rc3 was successfully user-tested. Broader runtime compatibility checks remain deferred.
+
+### Where do I select favorites?
+
+Open Settings → Advanced Scripts QuickNav. See clickable favorite cards and snippet statistics, filter by title or folder, select scripts and save your preferences.
+
+### Are favorites shared?
+
+No. Favorites and display preferences belong to your account on the current website. Other users and multisite subsites have separate selections.
+
+### What does Disabled by folder mean?
+
+The script has at least one inactive ancestor folder. Advanced Scripts skips that branch even if the script itself is active.
+
+### Does Active mean currently running?
+
+No. It is the saved script flag. Folder status, execution location, conditions, hooks and PHP Safe Mode can affect execution.
+
+### How does Safe Mode detection work?
+
+A defined AS_SAFE_MODE takes precedence, including false. Otherwise QuickNav reads the advanced-scripts-safemode option. It does not change Safe Mode.
+
+### Does Safe Mode stop every frontend script?
+
+No. In the supplied Advanced Scripts 2.6.2 source, PHP Safe Mode protects the admin and login contexts; it is not a general off switch for frontend scripts.
+
+### Can I create a script inside a folder?
+
+Yes. Add script here opens the native Advanced Scripts editor with the selected parent folder. Nothing is created until you save in that editor.
+
+### Why are some entries omitted?
+
+The default limit is 40 entries per status list and favorites list, and 40 script/folder entries across the folder tree. Choose 10–200 in preferences. View all opens Advanced Scripts. Tree depth is limited to eight levels.
+
+### Can I search the toolbar?
+
+Not in this release. The preferences page filters the favorites selection by title or folder. A keyboard-accessible toolbar search is planned separately.
+
+### Do existing constants still work?
+
+Yes. ASQN_VIEW_CAPABILITY, ASQN_ENABLED_USERS, ASQN_NAME_IN_ADMINBAR, ASQN_COUNTER, ASQN_ICON, ASQN_DISABLE_LIBRARY, ASQN_DISABLE_FOOTER and ASQN_EXPERT_MODE remain supported. Display constants override personal preferences.
+
+### Which permissions are required?
+
+The current user needs manage_options and the QuickNav capability (activate_plugins by default). ASQN_ENABLED_USERS can restrict visibility further. Lowering the QuickNav capability does not grant access to Advanced Scripts.
+
+### How do updates work?
+
+The plugin integrates deckerweb GitHub Updater V2 with the regular WordPress update system. It checks public stable GitHub releases and provides the matching plugin ZIP. It does not enable automatic updates.
+
+### What is the deckerweb Library?
+
+An embedded catalog under Plugins → Add New → deckerweb, with shared settings. It is separate from the Find Snippets resource links. Multiple compatible deckerweb plugins share one runtime.
+
+### Does QuickNav send my scripts elsewhere?
+
+QuickNav navigation and favorites do not send script data anywhere. The updater contacts GitHub for release metadata. Library actions can contact approved GitHub downloads; its optional online catalog has separate settings.
+
+### Can I install the snippet instead?
+
+Yes. Import the generated JSON into Advanced Scripts and enable it with the plugins_loaded hook. It contains navigation and personal preferences, but no plugin updater or deckerweb Library. Use either the plugin or the snippet.
+
+### What happens to the snippet in Safe Mode?
+
+Advanced Scripts can prevent the QuickNav PHP snippet from running in the admin. It then cannot show its own Safe Mode notice. Use the plugin installation for reliable admin diagnostics.
+
+### What happens if a favorite is deleted?
+
+It is excluded from the displayed selection automatically. Saving preferences removes obsolete IDs. Scripts with the same title remain distinct because favorites use term IDs.
+
+### Can I switch scripts on and off in QuickNav?
+
+No. Links open the native editor. Activation, deletion and execution stay with Advanced Scripts.
+
+### Where are my preferences stored?
+
+In user metadata keyed by the current site ID. Deactivation keeps them. There is no automatic uninstall cleanup in this version.
+
+### Are translations included?
+
+German informal and formal catalogs are bundled. Plugin mode uses the WordPress user/site language. The standalone snippet includes its German catalog.
+
+### What do the statistics count?
+
+All snippets excludes folders. Active and inactive use saved status. Active snippets blocked by an inactive ancestor folder are an additional subset of active snippets. Favorites counts the saved selection, independent of toolbar limits. Type totals include active and inactive snippets.
+
+### How do favorite cards work?
+
+Cards open the native editor directly. Selecting or clearing a checkbox updates the card preview immediately; Save my preferences applies it to the toolbar. Without JavaScript, saved favorite cards remain usable.
+
+### What happened to fullscreen toolbar settings?
+
+QuickNav no longer adjusts the fullscreen block editor, adds fullscreen CSS or removes the WordPress logo. Old ASQN_FULLSCREEN definitions and stored fullscreen preferences no longer affect QuickNav.
+
+### Is this ready for production?
+
+Version 1.2.0 is based on successfully user-tested rc3. Syntax and focused release checks passed; the broader WordPress integration matrix remains deferred.
+
+## Updates and Library
+
+deckerweb GitHub Updater V2 offers public stable releases through regular WordPress updates. Install the release ZIP for the first installation. deckerweb Library adds a deckerweb tab in the plugin installer and shared settings. It is separate from Find Snippets.
+
+The settings header/footer includes the icon, version, localized documentation, local changelog, plugin website and © 2022–2026 David Decker – DECKERWEB.
+
+## Changelog
+
+[Complete changelog](../CHANGELOG.md)
diff --git a/docs/wiki/Home.md b/docs/wiki/Home.md
new file mode 100644
index 0000000..d37921d
--- /dev/null
+++ b/docs/wiki/Home.md
@@ -0,0 +1,7 @@
+# Advanced Scripts QuickNav
+
+Version **1.2.0** · WordPress **6.7+** · PHP **8.0+**
+
+[Deutsch – Anleitung und FAQ](https://github.com/deckerweb/advanced-scripts-quicknav/wiki/Deutsch) · [English – guide and FAQ](https://github.com/deckerweb/advanced-scripts-quicknav/wiki/English)
+
+[Download](https://github.com/deckerweb/advanced-scripts-quicknav/releases/latest) · [Changelog Deutsch](https://github.com/deckerweb/advanced-scripts-quicknav/wiki/Changelog%E2%80%90Deutsch) · [Changelog English](https://github.com/deckerweb/advanced-scripts-quicknav/wiki/Changelog%E2%80%90English)
diff --git a/docs/wiki/_Sidebar.md b/docs/wiki/_Sidebar.md
new file mode 100644
index 0000000..02c6cb0
--- /dev/null
+++ b/docs/wiki/_Sidebar.md
@@ -0,0 +1,9 @@
+## QuickNav 1.2.0
+
+[Home](https://github.com/deckerweb/advanced-scripts-quicknav/wiki/Home)
+
+- [Deutsch – Anleitung & FAQ](https://github.com/deckerweb/advanced-scripts-quicknav/wiki/Deutsch)
+- [English – guide & FAQ](https://github.com/deckerweb/advanced-scripts-quicknav/wiki/English)
+- [Changelog Deutsch](https://github.com/deckerweb/advanced-scripts-quicknav/wiki/Changelog%E2%80%90Deutsch)
+- [Changelog English](https://github.com/deckerweb/advanced-scripts-quicknav/wiki/Changelog%E2%80%90English)
+- [Download](https://github.com/deckerweb/advanced-scripts-quicknav/releases/latest)
diff --git a/includes/class-asqn-adapter.php b/includes/class-asqn-adapter.php
new file mode 100644
index 0000000..901955e
--- /dev/null
+++ b/includes/class-asqn-adapter.php
@@ -0,0 +1,32 @@
+ 'advanced-scripts', 'parent' => $parent ];
+ if ( null !== $edit ) { $args['edit'] = $edit; }
+ return add_query_arg( $args, admin_url( 'tools.php' ) );
+ }
+ /** Keep upstream order and expose metadata only, with safe defaults. */
+ public function scripts(): array {
+ if ( ! self::available() ) { return []; }
+ $manager = cpas_scripts_manager();
+ if ( ! is_object( $manager ) || ! method_exists( $manager, 'get_scripts' ) ) { return []; }
+ $raw = $manager->get_scripts();
+ $result = [];
+ foreach ( is_array( $raw ) ? $raw : [] as $item ) {
+ if ( ! is_array( $item ) || empty( $item['term_id'] ) ) { continue; }
+ $id = absint( $item['term_id'] );
+ if ( ! $id ) { continue; }
+ $result[$id] = [ 'term_id' => $id, 'parent' => absint( $item['parent'] ?? 0 ), 'status' => ! empty( $item['status'] ) ];
+ foreach ( [ 'title', 'type', 'location', 'hook' ] as $key ) {
+ $result[$id][$key] = isset( $item[$key] ) && is_scalar( $item[$key] ) ? (string) $item[$key] : '';
+ }
+ }
+ return $result;
+ }
+}
diff --git a/includes/class-asqn-config.php b/includes/class-asqn-config.php
new file mode 100644
index 0000000..9da1820
--- /dev/null
+++ b/includes/class-asqn-config.php
@@ -0,0 +1,31 @@
+ false, 'expert' => true, 'limit' => 40 ], is_array( $value ) ? array_intersect_key( $value, [ 'counter' => true, 'expert' => true, 'limit' => true ] ) : [] );
+ }
+ public static function flag( string $constant, bool $default ): bool {
+ if ( ! defined( $constant ) ) { return $default; }
+ $value = constant( $constant );
+ return true === $value || 1 === $value || in_array( strtolower( (string) $value ), [ 'yes', 'true', '1', 'on' ], true );
+ }
+ public static function capability(): string {
+ return defined( 'ASQN_VIEW_CAPABILITY' ) && is_string( ASQN_VIEW_CAPABILITY ) && '' !== ASQN_VIEW_CAPABILITY ? sanitize_key( ASQN_VIEW_CAPABILITY ) : 'activate_plugins';
+ }
+ public static function allowed(): bool {
+ if ( ! current_user_can( 'manage_options' ) || ! current_user_can( self::capability() ) ) { return false; }
+ if ( ! defined( 'ASQN_ENABLED_USERS' ) ) { return true; }
+ return in_array( get_current_user_id(), array_map( 'absint', (array) ASQN_ENABLED_USERS ), true );
+ }
+ public static function visible(): bool { return self::allowed() && ASQN_Advanced_Scripts_Adapter::available() && is_admin_bar_showing(); }
+ public static function counter(): bool { return self::flag( 'ASQN_COUNTER', (bool) self::preferences()['counter'] ); }
+ public static function expert(): bool { return self::flag( 'ASQN_EXPERT_MODE', (bool) self::preferences()['expert'] ); }
+ public static function limit(): int {
+ $value = defined( 'ASQN_MENU_LIMIT' ) ? ASQN_MENU_LIMIT : self::preferences()['limit'];
+ return max( 10, min( 200, absint( $value ) ) );
+ }
+}
diff --git a/includes/class-asqn-favorites.php b/includes/class-asqn-favorites.php
new file mode 100644
index 0000000..eb2ee0a
--- /dev/null
+++ b/includes/class-asqn-favorites.php
@@ -0,0 +1,18 @@
+artwork()
+ );
+ } catch ( \InvalidArgumentException $error ) {
+ // An unsupported install directory must not break navigation.
+ return;
+ }
+ $updater->register();
+ add_filter( 'http_request_args', array( $this, 'request_limits' ), 20, 2 );
+ add_filter( 'upgrader_source_selection', array( $this, 'validate_source' ), 30, 4 );
+ }
+
+ /**
+ * Provide bundled artwork matching the current administrator's language.
+ *
+ * @return array> WordPress icon and banner maps.
+ */
+ public function artwork(): array {
+ $language = 1 === preg_match( '/^de(?:_|$)/i', determine_locale() ) ? 'de-' : '';
+ return array(
+ 'icons' => array(
+ 'svg' => plugins_url( 'assets-github/icon.svg', ASQN_PLUGIN_FILE ),
+ '1x' => plugins_url( 'assets-github/icon-128x128.png', ASQN_PLUGIN_FILE ),
+ '2x' => plugins_url( 'assets-github/icon-256x256.png', ASQN_PLUGIN_FILE ),
+ ),
+ 'banners' => array(
+ 'low' => plugins_url( 'assets-github/banner-' . $language . '772x250.png', ASQN_PLUGIN_FILE ),
+ 'high' => plugins_url( 'assets-github/banner-' . $language . '1544x500.png', ASQN_PLUGIN_FILE ),
+ ),
+ );
+ }
+
+ /**
+ * Bound only this repository's metadata requests; do not change package downloads.
+ *
+ * @param array $args WordPress HTTP arguments.
+ * @param string $url Requested URL.
+ * @return array
+ */
+ public function request_limits( array $args, string $url ): array {
+ if ( 'https://api.github.com/repos/deckerweb/advanced-scripts-quicknav/releases/latest' === $url ) {
+ $args['limit_response_size'] = 512 * 1024;
+ $args['timeout'] = 6;
+ $args['redirection'] = 0;
+ $args['sslverify'] = true;
+ $args['reject_unsafe_urls'] = true;
+ }
+ return $args;
+ }
+
+ /**
+ * Validate the actual candidate before WordPress removes the installed plugin.
+ * Requirements in installed headers cannot describe a future release reliably.
+ *
+ * @param mixed $source Normalized directory or WP_Error from the shared updater.
+ * @param mixed $remote_source Unused extraction root.
+ * @param mixed $upgrader Unused WordPress upgrader instance.
+ * @param array $hook_extra Upgrade context.
+ * @return mixed Original source or localized WP_Error.
+ */
+ public function validate_source( $source, $remote_source, $upgrader, array $hook_extra ) {
+ if ( ( $hook_extra['plugin'] ?? '' ) !== plugin_basename( ASQN_PLUGIN_FILE ) || ( $hook_extra['type'] ?? '' ) !== 'plugin' || ( $hook_extra['action'] ?? '' ) !== 'update' ) {
+ return $source;
+ }
+ if ( is_wp_error( $source ) ) {
+ $messages = array(
+ 'ddw_ghru_filesystem' => __( 'The update filesystem is unavailable. Please try again.', 'advanced-scripts-quicknav' ),
+ 'ddw_ghru_archive' => __( 'The GitHub package does not contain the Advanced Scripts QuickNav plugin file.', 'advanced-scripts-quicknav' ),
+ 'ddw_ghru_rename' => __( 'The GitHub package could not be prepared. The installed version has been kept.', 'advanced-scripts-quicknav' ),
+ );
+ $code = $source->get_error_code();
+ return isset( $messages[ $code ] ) ? new \WP_Error( $code, $messages[ $code ], $source->get_error_data( $code ) ) : $source;
+ }
+ global $wp_filesystem;
+ if ( ! is_string( $source ) || ! $wp_filesystem ) {
+ return new \WP_Error( 'asqn_update_source', __( 'The update package could not be checked.', 'advanced-scripts-quicknav' ) );
+ }
+ $main = trailingslashit( $source ) . basename( ASQN_PLUGIN_FILE );
+ if ( ! $wp_filesystem->is_file( $main ) || $wp_filesystem->size( $main ) > 1024 * 1024 ) {
+ return new \WP_Error( 'asqn_update_source', __( 'The update package could not be checked.', 'advanced-scripts-quicknav' ) );
+ }
+ $text = $wp_filesystem->get_contents( $main );
+ if ( ! is_string( $text ) ) {
+ return new \WP_Error( 'asqn_update_source', __( 'The update package could not be checked.', 'advanced-scripts-quicknav' ) );
+ }
+ $headers = array();
+ foreach ( array( 'Plugin Name', 'Version', 'Update URI', 'Requires PHP', 'Requires at least' ) as $header ) {
+ $headers[ $header ] = preg_match( '/^[ \t\/*#@]*' . preg_quote( $header, '/' ) . ':(.*)$/mi', str_replace( "\r", "\n", substr( $text, 0, 8192 ) ), $match ) ? trim( $match[1] ) : '';
+ }
+ if ( 'Advanced Scripts QuickNav' !== $headers['Plugin Name'] || self::REPOSITORY !== $headers['Update URI'] || ! preg_match( '/^\d+\.\d+\.\d+$/D', $headers['Version'] ) || version_compare( $headers['Version'], \ASQN_Config::VERSION, '<=' ) ) {
+ return new \WP_Error( 'asqn_update_identity', __( 'The package identity or version does not match a newer Advanced Scripts QuickNav release.', 'advanced-scripts-quicknav' ) );
+ }
+ $current = get_site_transient( 'update_plugins' );
+ $expected = is_object( $current ) ? ( $current->response[ plugin_basename( ASQN_PLUGIN_FILE ) ]->new_version ?? '' ) : '';
+ if ( ! is_string( $expected ) || '' === $expected || $headers['Version'] !== $expected ) {
+ return new \WP_Error( 'asqn_update_version', __( 'The package version differs from the offered update. Please check for updates again.', 'advanced-scripts-quicknav' ) );
+ }
+ foreach ( array( 'Requires PHP', 'Requires at least' ) as $header ) {
+ if ( ! preg_match( '/^\d+\.\d+(?:\.\d+)?$/D', $headers[ $header ] ) ) {
+ return new \WP_Error( 'asqn_update_requirements', __( 'The update package has missing or invalid WordPress/PHP requirements.', 'advanced-scripts-quicknav' ) );
+ }
+ }
+ if ( ! is_php_version_compatible( $headers['Requires PHP'] ) || ! is_wp_version_compatible( $headers['Requires at least'] ) ) {
+ return new \WP_Error( 'asqn_update_compatibility', __( 'This release requires a newer WordPress or PHP version. The installed version has been kept.', 'advanced-scripts-quicknav' ) );
+ }
+ return $source;
+ }
+}
diff --git a/includes/class-asqn-navigation.php b/includes/class-asqn-navigation.php
new file mode 100644
index 0000000..0e67f2b
--- /dev/null
+++ b/includes/class-asqn-navigation.php
@@ -0,0 +1,112 @@
+scripts = $scripts;
+ foreach ( $scripts as $item ) { $this->children[$item['parent']][] = $item['term_id']; }
+ }
+ public function scripts(): array { return $this->scripts; }
+ public function count(): int { return count( array_filter( $this->scripts, static function( $item ) { return 'folder' !== $item['type']; } ) ); }
+ /** Saved status counts, with ancestor blocking reported as an additional dimension. */
+ public function statistics(): array {
+ $stats = [ 'total' => 0, 'active' => 0, 'inactive' => 0, 'blocked' => 0, 'folders' => 0, 'types' => [] ];
+ foreach ( $this->scripts as $item ) {
+ if ( 'folder' === $item['type'] ) { $stats['folders']++; continue; }
+ $stats['total']++;
+ $stats[$item['status'] ? 'active' : 'inactive']++;
+ if ( $item['status'] && $this->blocked( $item ) ) { $stats['blocked']++; }
+ $type = self::type( $item['type'] );
+ $stats['types'][$type] = ( $stats['types'][$type] ?? 0 ) + 1;
+ }
+ arsort( $stats['types'] );
+ return $stats;
+ }
+ /** Own enabled flag is distinct from ancestor blocking and actual execution. */
+ public function blocked( array $item ): bool {
+ $seen = [];
+ $parent = $item['parent'];
+ while ( $parent && isset( $this->scripts[$parent] ) && ! isset( $seen[$parent] ) ) {
+ $seen[$parent] = true;
+ $folder = $this->scripts[$parent];
+ if ( 'folder' === $folder['type'] && ! $folder['status'] ) { return true; }
+ $parent = $folder['parent'];
+ }
+ return false;
+ }
+ public function path( array $item ): string {
+ $parts = []; $seen = []; $parent = $item['parent'];
+ while ( $parent && isset( $this->scripts[$parent] ) && ! isset( $seen[$parent] ) ) {
+ $seen[$parent] = true; $folder = $this->scripts[$parent];
+ array_unshift( $parts, $folder['title'] ); $parent = $folder['parent'];
+ }
+ return implode( ' / ', $parts );
+ }
+ public static function type( string $type ): string {
+ $types = [ 'application/x-httpd-php' => 'PHP', 'url/javascript' => 'JS', 'text/javascript' => 'JS', 'url/css' => 'CSS', 'text/css' => 'CSS', 'text/x-scss' => 'SCSS', 'text/x-scss-partial' => __( 'SCSS partial', 'advanced-scripts-quicknav' ), 'text/x-less' => 'LESS', 'text/html' => 'HTML', 'folder' => __( 'Folder', 'advanced-scripts-quicknav' ) ];
+ return $types[$type] ?? __( 'Code', 'advanced-scripts-quicknav' );
+ }
+ public function description( array $item ): string {
+ $locations = [ 'all' => __( 'everywhere', 'advanced-scripts-quicknav' ), 'front' => __( 'front-end', 'advanced-scripts-quicknav' ), 'admin' => __( 'administration', 'advanced-scripts-quicknav' ), 'shortcode' => __( 'shortcode', 'advanced-scripts-quicknav' ) ];
+ $parts = [ self::type( $item['type'] ), $item['status'] ? __( 'active', 'advanced-scripts-quicknav' ) : __( 'inactive', 'advanced-scripts-quicknav' ), $this->path( $item ), $locations[$item['location']] ?? $item['location'], $item['hook'] ];
+ if ( $this->blocked( $item ) ) { $parts[] = __( 'Disabled by folder', 'advanced-scripts-quicknav' ); }
+ return implode( ' · ', array_filter( $parts ) );
+ }
+ private function script_node( $bar, array $item, string $parent, string $context ): void {
+ $blocked = $this->blocked( $item );
+ $label = esc_html( $item['title'] ?: __( 'Untitled script', 'advanced-scripts-quicknav' ) );
+ $label .= ' ' . esc_html( self::type( $item['type'] ) ) . ' ';
+ if ( $blocked ) { $label .= ' ' . esc_html__( 'Disabled by folder', 'advanced-scripts-quicknav' ) . ' '; }
+ elseif ( ! $item['status'] ) { $label .= ' ' . esc_html__( 'inactive', 'advanced-scripts-quicknav' ) . ' '; }
+ // Preserve legacy status-view IDs; each additional projection gets its own ID.
+ $id = 'status' === $context ? 'asqn-script-' . $item['term_id'] : 'asqn-' . $context . '-script-' . $item['term_id'];
+ $bar->add_node( [ 'id' => $id, 'parent' => $parent, 'title' => $label, 'href' => esc_url( ASQN_Advanced_Scripts_Adapter::url( $item['parent'], $item['term_id'] ) ), 'meta' => [ 'title' => $this->description( $item ) ] ] );
+ }
+ public function statuses( $bar ): void {
+ foreach ( [ 'active' => true, 'inactive' => false ] as $name => $status ) {
+ $items = array_filter( $this->scripts, static function( $item ) use ( $status ) { return 'folder' !== $item['type'] && $status === $item['status']; } );
+ $title = $status ? __( 'Active Scripts', 'advanced-scripts-quicknav' ) : __( 'Inactive Scripts', 'advanced-scripts-quicknav' );
+ $bar->add_node( [ 'id' => 'asqn-' . $name, 'parent' => 'asqn-group-status', 'title' => esc_html( $title . ' (' . count( $items ) . ')' ), 'href' => esc_url( ASQN_Advanced_Scripts_Adapter::url() ) ] );
+ $i = 0;
+ foreach ( $items as $item ) { if ( $i++ >= ASQN_Config::limit() ) { break; } $this->script_node( $bar, $item, 'asqn-' . $name, 'status' ); }
+ if ( count( $items ) > ASQN_Config::limit() ) { $this->more( $bar, 'asqn-' . $name, 0 ); }
+ }
+ }
+ private function more( $bar, string $parent, int $folder ): void {
+ $bar->add_node( [ 'id' => $parent . '-more', 'parent' => $parent, 'title' => esc_html__( 'View all in Advanced Scripts…', 'advanced-scripts-quicknav' ), 'href' => esc_url( ASQN_Advanced_Scripts_Adapter::url( $folder ) ) ] );
+ }
+ public function favorites( $bar ): void {
+ $ids = ASQN_Favorites::ids( $this->scripts );
+ $bar->add_group( [ 'id' => 'asqn-group-favorites', 'parent' => 'ddw-advscripts-quicknav' ] );
+ $bar->add_node( [ 'id' => 'asqn-favorites', 'parent' => 'asqn-group-favorites', 'title' => esc_html__( '★ Favorites', 'advanced-scripts-quicknav' ), 'href' => esc_url( admin_url( 'options-general.php?page=advanced-scripts-quicknav' ) ) ] );
+ foreach ( array_slice( $ids, 0, ASQN_Config::limit() ) as $id ) { $this->script_node( $bar, $this->scripts[$id], 'asqn-favorites', 'favorite' ); }
+ $bar->add_node( [ 'id' => 'asqn-favorites-manage', 'parent' => 'asqn-favorites', 'title' => esc_html__( 'Manage favorites…', 'advanced-scripts-quicknav' ), 'href' => esc_url( admin_url( 'options-general.php?page=advanced-scripts-quicknav' ) ) ] );
+ }
+ public function folders( $bar ): void {
+ $bar->add_node( [ 'id' => 'asqn-scripts-folders', 'parent' => 'asqn-group-folders', 'title' => esc_html__( 'Scripts by Folder', 'advanced-scripts-quicknav' ), 'href' => esc_url( ASQN_Advanced_Scripts_Adapter::url() ) ] );
+ $visited = []; $budget = ASQN_Config::limit();
+ $this->branch( $bar, 0, 'asqn-scripts-folders', $visited, $budget, 0 );
+ // Keep orphaned items reachable without inventing an upstream folder hierarchy.
+ foreach ( $this->scripts as $item ) {
+ if ( isset( $visited[$item['term_id']] ) || $budget <= 0 ) { continue; }
+ if ( 'folder' !== $item['type'] ) { $this->script_node( $bar, $item, 'asqn-scripts-folders', 'folder' ); $visited[$item['term_id']] = true; $budget--; }
+ }
+ if ( count( $visited ) < count( $this->scripts ) ) { $this->more( $bar, 'asqn-scripts-folders', 0 ); }
+ }
+ private function branch( $bar, int $folder, string $parent, array &$visited, int &$budget, int $depth ): void {
+ if ( $depth >= 8 ) { $this->more( $bar, $parent, $folder ); return; }
+ foreach ( $this->children[$folder] ?? [] as $id ) {
+ if ( isset( $visited[$id] ) ) { continue; }
+ if ( $budget <= 0 ) { $this->more( $bar, $parent, $folder ); break; }
+ $visited[$id] = true; $budget--; $item = $this->scripts[$id];
+ if ( 'folder' !== $item['type'] ) { $this->script_node( $bar, $item, $parent, 'folder' ); continue; }
+ $node = 'asqn-folder-' . $id;
+ $label = '▸ ' . $item['title'] . ( $item['status'] ? '' : ' · ' . __( 'inactive', 'advanced-scripts-quicknav' ) );
+ $bar->add_node( [ 'id' => $node, 'parent' => $parent, 'title' => esc_html( $label ), 'href' => esc_url( ASQN_Advanced_Scripts_Adapter::url( $id ) ), 'meta' => [ 'title' => $this->description( $item ) ] ] );
+ $bar->add_node( [ 'id' => $node . '-new', 'parent' => $node, 'title' => esc_html__( 'Add script here…', 'advanced-scripts-quicknav' ), 'href' => esc_url( ASQN_Advanced_Scripts_Adapter::url( $id, 0 ) ) ] );
+ $this->branch( $bar, $id, $node, $visited, $budget, $depth + 1 );
+ }
+ }
+}
diff --git a/includes/class-asqn-plugin.php b/includes/class-asqn-plugin.php
new file mode 100644
index 0000000..c1b6414
--- /dev/null
+++ b/includes/class-asqn-plugin.php
@@ -0,0 +1,47 @@
+register();
+ if ( ! defined( 'ASQN_SNIPPET_MODE' ) ) {
+ add_action( 'init', [ $this, 'updates' ] );
+ add_action( 'init', [ $this, 'translations' ], 0 );
+ add_filter( 'plugin_action_links_' . plugin_basename( ASQN_PLUGIN_FILE ), [ $this, 'links' ] );
+ add_filter( 'plugin_row_meta', [ $this, 'meta' ], 10, 2 );
+ }
+ }
+ private function renderer(): ASQN_Admin_Bar_Renderer {
+ if ( null === $this->renderer ) { $this->renderer = new ASQN_Admin_Bar_Renderer( new ASQN_Navigation_Builder( ( new ASQN_Advanced_Scripts_Adapter() )->scripts() ) ); }
+ return $this->renderer;
+ }
+ public function translations(): void { load_plugin_textdomain( 'advanced-scripts-quicknav', false, dirname( plugin_basename( ASQN_PLUGIN_FILE ) ) . '/languages' ); }
+ public function menu( $bar ): void { $this->renderer()->add_admin_bar_menu( $bar ); }
+ public function assets(): void {
+ if ( ! ASQN_Config::visible() ) { return; }
+ $this->renderer()->enqueue_admin_bar_styles();
+ if ( defined( 'ASQN_SNIPPET_MODE' ) ) { wp_add_inline_style( 'admin-bar', ASQN_SNIPPET_CSS ); }
+ else { wp_enqueue_style( 'asqn-toolbar', plugins_url( 'assets/toolbar.css', ASQN_PLUGIN_FILE ), [ 'admin-bar' ], ASQN_Config::VERSION ); }
+ }
+ public function diagnostics( array $info ): array { return $this->renderer()->site_health_debug_info( $info ); }
+ public function updates(): void {
+ require_once dirname( ASQN_PLUGIN_FILE ) . '/includes/class-asqn-github-updates.php';
+ ( new \Deckerweb\AdvancedScriptsQuickNav\GitHubUpdates() )->boot();
+ }
+ public function links( array $links ): array {
+ if ( ASQN_Config::allowed() ) { array_unshift( $links, '' . esc_html__( 'Preferences', 'advanced-scripts-quicknav' ) . ' ' ); }
+ return $links;
+ }
+ public function meta( array $links, string $file ): array {
+ if ( $file !== plugin_basename( ASQN_PLUGIN_FILE ) ) { return $links; }
+ $links[] = '' . esc_html__( 'Documentation', 'advanced-scripts-quicknav' ) . ' ';
+ $links[] = '' . esc_html__( 'Support the project', 'advanced-scripts-quicknav' ) . ' ';
+ $links[] = '' . esc_html__( 'Newsletter', 'advanced-scripts-quicknav' ) . ' ';
+ return apply_filters( 'ddw/admin_extras/pluginrow_meta', $links );
+ }
+}
diff --git a/includes/class-asqn-renderer.php b/includes/class-asqn-renderer.php
new file mode 100644
index 0000000..6b7a816
--- /dev/null
+++ b/includes/class-asqn-renderer.php
@@ -0,0 +1,714 @@
+navigation = $navigation; }
+
+ /**
+ * Is expert mode active?
+ * Gives some more stuff that is more focused at (plugin/snippet) developers
+ * and mostly not needed for fast site building.
+ *
+ * @return bool
+ */
+ private function is_expert_mode(): bool { return ASQN_Config::expert(); }
+
+ /**
+ * Get specific Admin Color scheme colors we need. Covers all 9 default
+ * color schemes coming with a default WordPress install.
+ * (helper function)
+ */
+ private function get_scheme_colors() {
+
+ $scheme_colors = array(
+ 'fresh' => array(
+ 'bg' => '#1d2327',
+ 'base' => 'rgba(240,246,252,.6)',
+ 'hover' => '#72aee6',
+ ),
+ 'light' => array(
+ 'bg' => '#e5e5e5',
+ 'base' => '#999',
+ 'hover' => '#04a4cc',
+ ),
+ 'modern' => array(
+ 'bg' => '#1e1e1e',
+ 'base' => '#f3f1f1',
+ 'hover' => '#33f078',
+ ),
+ 'blue' => array(
+ 'bg' => '#52accc',
+ 'base' => '#e5f8ff',
+ 'hover' => '#fff',
+ ),
+ 'coffee' => array(
+ 'bg' => '#59524c',
+ 'base' => 'hsl(27.6923076923,7%,95%)',
+ 'hover' => '#c7a589',
+ ),
+ 'ectoplasm' => array(
+ 'bg' => '#523f6d',
+ 'base' => '#ece6f6',
+ 'hover' => '#a3b745',
+ ),
+ 'midnight' => array(
+ 'bg' => '#363b3f',
+ 'base' => 'hsl(206.6666666667,7%,95%)',
+ 'hover' => '#e14d43',
+ ),
+ 'ocean' => array(
+ 'bg' => '#738e96',
+ 'base' => '#f2fcff',
+ 'hover' => '#9ebaa0',
+ ),
+ 'sunrise' => array(
+ 'bg' => '#cf4944',
+ 'base' => 'hsl(2.1582733813,7%,95%)',
+ 'hover' => 'rgb(247.3869565217,227.0108695652,211.1130434783)',
+ ),
+ );
+
+ /** No filter currently b/c of sanitizing issues with the above CSS values */
+ //$scheme_colors = (array) apply_filters( 'ddw/quicknav/asqn_scheme_colors', $scheme_colors );
+
+ return $scheme_colors;
+ }
+
+ /**
+ * Enqueue custom styles for the Admin Bar.
+ * NOTE: Used within Admin and on the front-end (if Toolbar enabled).
+ */
+ public function enqueue_admin_bar_styles() {
+ if ( ! ASQN_Config::visible() ) { return; }
+
+ /**
+ * Depending on user color scheme get proper base and hover color values for the main item (svg) icon.
+ */
+ $user_color_scheme = get_user_option( 'admin_color' );
+ $user_color_scheme = ( is_admin() || is_network_admin() ) ? $user_color_scheme : 'fresh'; // b/c in frontend there is no 'admin_color'
+ $admin_scheme = $this->get_scheme_colors();
+ $user_color_scheme = isset( $admin_scheme[$user_color_scheme] ) ? $user_color_scheme : 'fresh';
+
+ $base_color = $admin_scheme[ $user_color_scheme ][ 'base' ];
+ $hover_color = $admin_scheme[ $user_color_scheme ][ 'hover' ];
+
+ /**
+ * Build the inline CSS
+ * NOTE: We need to use 'sprintf()' because of the percentage values and similar!
+ */
+ $inline_css = sprintf(
+ '
+ #wpadminbar .asqn-scripts-list .ab-sub-wrapper ul li span.location,
+ #wpadminbar .asqn-scripts-list .ab-sub-wrapper ul li span.status {
+ font-family: monospace;
+ font-size: %1$s;
+ vertical-align: super;
+ }
+
+ #wpadminbar .asqn-scripts-list .ab-sub-wrapper ul li span.location,
+ #wpadminbar .asqn-scripts-list .ab-sub-wrapper ul li span.status.inactive {
+ /* filter: brightness(%2$s); */
+ color: hsl(0, %3$s, %4$s);
+ }
+
+ #wpadminbar .asqn-scripts-list .ab-sub-wrapper ul li span.status.active {
+ /* filter: brightness(%2$s); */
+ color: hsl(120, %3$s, %7$s);
+ }
+
+ #wpadminbar .asqn-safemode {
+ background-color: #9C1005;
+ }
+ #wpadminbar .asqn-safemode:hover,
+ #wpadminbar ul li.asqn-safemode:hover {
+ background: #BD3126;
+ }
+ #wpadminbar .asqn-safemode a {
+ color: #FBE4C6;
+ font-weight: 700;
+ }
+
+ #wpadminbar .asqn-scripts-list .has-icon .icon-svg svg {
+ display: inline-block;
+ margin-bottom: 3px;
+ vertical-align: middle;
+ width: 16px;
+ height: 16px;
+ }
+
+ #wpadminbar .asqn-scripts-list .icon-svg.ab-icon svg {
+ width: 15px;
+ height: 15px;
+ }
+
+ .asqn-scripts-list .ab-item .icon-svg.ab-icon svg {
+ color: %5$s;
+ }
+
+ .asqn-scripts-list .ab-item:hover .icon-svg.ab-icon svg {
+ color: %6$s;
+ }
+ ',
+ '80%', // 1
+ '120%', // 2
+ '100%', // 3
+ '70%', // 4
+ $base_color, // 5
+ $hover_color, // 6
+ '40%' // 7
+ );
+
+ /** Only add the styles if Admin Bar is showing */
+ if ( is_admin_bar_showing() ) {
+ wp_add_inline_style( 'admin-bar', $inline_css );
+ }
+ }
+
+ /**
+ * Check for active SCRIPT_DEBUG constant. (helper function)
+ *
+ * @link https://developer.wordpress.org/advanced-administration/debug/debug-wordpress/#script_debug
+ */
+ private function is_wp_dev_mode_active() {
+
+ return defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG;
+ }
+
+ /**
+ * Check for active Safe Mode constant. (helper function)
+ *
+ * @link https://www.cleanplugins.com/blog/advanced-scripts-2-4-0-release-overview/
+ */
+ private function is_safe_mode_active(): bool { return ASQN_Advanced_Scripts_Adapter::safe_mode(); }
+
+ /**
+ * Get number of all Scripts (without folders). (helper function)
+ *
+ * @return int Number of scripts.
+ */
+ private function script_counter(): int { return $this->navigation->count(); }
+
+ /**
+ * Adds the main Scripts (Code Snippets) menu and its submenus to the Admin Bar.
+ *
+ * @param WP_Admin_Bar $wp_admin_bar The WP_Admin_Bar instance.
+ */
+ public function add_admin_bar_menu( $wp_admin_bar ) {
+
+ if ( ! ASQN_Config::visible() ) { return; }
+
+ /** Build the main item title, optional scripts count value */
+ $all_scripts = $this->script_counter();
+ $counter = ASQN_Config::counter() ? ' (' . intval( $all_scripts ) . ')' : '';
+ $asqn_name = ( defined( 'ASQN_NAME_IN_ADMINBAR' ) ) ? esc_html( ASQN_NAME_IN_ADMINBAR ) : esc_html__( 'Scripts', 'advanced-scripts-quicknav' );
+ $asqn_name = $asqn_name . $counter;
+
+ /** Default "script icon" */
+ $code_icon = ' ';
+
+ /** Optional code icon by Remix Icon */
+ $remix_icon = ' ';
+
+ /** Original blue icon (svg-ed) by Clean Plugins */
+ $blue_icon = ' ';
+
+ /** "Fire" Emoji for Safe Mode */
+ $safemode = ( $this->is_safe_mode_active() ) ? ' 🔥' : '';
+
+ $title_html = $code_icon . '' . $asqn_name . ' ' . $safemode;
+
+ if ( defined( 'ASQN_ICON' ) && 'blue' === sanitize_key( ASQN_ICON ) ) {
+ $title_html = $blue_icon . '' . $asqn_name . ' ' . $safemode;
+ } elseif ( defined( 'ASQN_ICON' ) && 'remix' === sanitize_key( ASQN_ICON ) ) {
+ $title_html = $remix_icon . '' . $asqn_name . ' ' . $safemode;
+ }
+
+ /** Add the parent menu item with an icon (main node) */
+ $wp_admin_bar->add_node( array(
+ 'id' => 'ddw-advscripts-quicknav',
+ 'title' => $title_html,
+ 'href' => esc_url( admin_url( 'tools.php?page=advanced-scripts' ) ),
+ 'meta' => array( 'class' => 'asqn-scripts-list has-icon' ),
+ ) );
+
+ $wp_admin_bar->add_node( [ 'id' => 'asqn-preferences', 'parent' => 'ddw-advscripts-quicknav', 'title' => esc_html__( 'QuickNav preferences', 'advanced-scripts-quicknav' ), 'href' => esc_url( admin_url( 'options-general.php?page=advanced-scripts-quicknav' ) ) ] );
+ /** Add submenus */
+ $this->add_safemode_submenu( $wp_admin_bar ); // group node
+ $this->navigation->favorites( $wp_admin_bar );
+ $this->add_scripts_by_status_group( $wp_admin_bar ); // group node
+ $this->add_scripts_by_folder_group( $wp_admin_bar ); // group node
+ $this->add_scripts_new_group( $wp_admin_bar ); // group node
+ $this->add_settings_group( $wp_admin_bar ); // group node
+ $this->add_library_group( $wp_admin_bar ); // group node
+ if ( ! ASQN_Config::flag( 'ASQN_DISABLE_LIBRARY', false ) ) { $this->add_libraries_submenu( $wp_admin_bar ); }
+ $this->add_footer_group( $wp_admin_bar ); // group node
+ if ( ! ASQN_Config::flag( 'ASQN_DISABLE_FOOTER', false ) ) {
+ $this->add_links_submenu( $wp_admin_bar );
+ $this->add_about_submenu( $wp_admin_bar );
+ }
+ }
+
+ /**
+ * Add group node for Safe Mode, and Script Debug
+ */
+ private function add_safemode_submenu( $wp_admin_bar ) {
+
+ $wp_admin_bar->add_group( array(
+ 'id' => 'asqn-group-safemode',
+ 'parent' => 'ddw-advscripts-quicknav',
+ ) );
+
+ /** Warning for Advanced Scripts' own Safe Mode */
+ if ( $this->is_safe_mode_active() ) {
+ $wp_admin_bar->add_node( array(
+ 'id' => 'asqn-safemode-active',
+ 'title' => esc_html__( 'SAFE MODE is active 🔥', 'advanced-scripts-quicknav' ),
+ 'href' => esc_url( admin_url( 'tools.php?page=advanced-scripts' ) ),
+ 'parent' => 'asqn-group-safemode',
+ 'meta' => array( 'class' => 'asqn-safemode' ),
+ ) );
+
+ $wp_admin_bar->add_node( array(
+ 'id' => 'asqn-safemode-active-topright',
+ 'title' => strtoupper( esc_html__( 'Advanced Scripts Safe Mode active 🔥', 'advanced-scripts-quicknav' ) ),
+ 'href' => 'https://www.cleanplugins.com/blog/advanced-scripts-2-4-0-release-overview/',
+ 'parent' => 'top-secondary', /** Puts the text on the right side of the Toolbar! */
+ 'meta' => array( 'class' => 'asqn-safemode', 'target' => '_blank', 'rel' => 'nofollow noopener noreferrer' ),
+ ) );
+ } // end if
+
+ /** Warning for WordPress' SCRIPT_DEBUG constant */
+ if ( $this->is_wp_dev_mode_active() ) {
+ $wp_admin_bar->add_node( array(
+ 'id' => 'asqn-devmode-active',
+ 'title' => esc_html__( 'SCRIPT_DEBUG is on ⚠', 'advanced-scripts-quicknav' ),
+ 'href' => 'https://developer.wordpress.org/advanced-administration/debug/debug-wordpress/#script_debug',
+ 'parent' => 'asqn-group-safemode',
+ 'meta' => array( 'class' => 'asqn-safemode', 'rel' => 'nofollow noopener noreferrer' ),
+ ) );
+
+ $wp_admin_bar->add_node( array(
+ 'id' => 'asqn-devmode-active-topright',
+ 'title' => strtoupper( esc_html__( 'SCRIPT_DEBUG is on ⚠', 'advanced-scripts-quicknav' ) ),
+ 'href' => 'https://developer.wordpress.org/advanced-administration/debug/debug-wordpress/#script_debug',
+ 'parent' => 'top-secondary', /** Puts the text on the right side of the Toolbar! */
+ 'meta' => array( 'class' => 'asqn-safemode', 'target' => '_blank', 'rel' => 'nofollow noopener noreferrer' ),
+ ) );
+ } // end if
+ }
+
+ /**
+ * Add group node for Active & Inactive Scripts
+ */
+ private function add_scripts_by_status_group( $wp_admin_bar ) {
+
+ $wp_admin_bar->add_group( array(
+ 'id' => 'asqn-group-status',
+ 'parent' => 'ddw-advscripts-quicknav',
+ ) );
+
+ $this->add_scripts_to_admin_bar( $wp_admin_bar );
+ }
+
+ /**
+ * Status Group: Add Active & Inactive Scripts
+ */
+ private function add_scripts_to_admin_bar( $wp_admin_bar ): void { $this->navigation->statuses( $wp_admin_bar ); }
+
+ /**
+ * Add group node for Scripts by folder
+ */
+ private function add_scripts_by_folder_group( $wp_admin_bar ) {
+
+ $wp_admin_bar->add_group( array(
+ 'id' => 'asqn-group-folders',
+ 'parent' => 'ddw-advscripts-quicknav',
+ ) );
+
+ $this->add_scripts_listings_submenu( $wp_admin_bar );
+ //$this->add_snippets_type_submenu( $wp_admin_bar );
+ }
+
+ /**
+ * Types Group: Add Scripts listings submenu (by status)
+ */
+ private function add_scripts_listings_submenu( $wp_admin_bar ): void { $this->navigation->folders( $wp_admin_bar ); }
+
+ /**
+ * Add group node for New Scripts
+ */
+ private function add_scripts_new_group( $wp_admin_bar ) {
+
+ $wp_admin_bar->add_group( array(
+ 'id' => 'asqn-group-new',
+ 'parent' => 'ddw-advscripts-quicknav',
+ ) );
+
+ $this->add_scripts_new_submenu( $wp_admin_bar );
+ }
+
+ /**
+ * New Scripts Group: Add Code Scripts - New submenu
+ */
+ private function add_scripts_new_submenu( $wp_admin_bar ) {
+
+ $icon_add = ' ';
+
+ /** Add New Snippet – also by type */
+ $wp_admin_bar->add_node( array(
+ 'id' => 'asqn-add-script',
+ 'title' => $icon_add . esc_html__( 'Add New', 'advanced-scripts-quicknav' ),
+ 'href' => esc_url( admin_url( 'tools.php?page=advanced-scripts&parent=0&edit=0' ) ),
+ 'parent' => 'asqn-group-new',
+ 'meta' => array( 'class' => 'has-icon' ),
+ ) );
+
+ /** WP's own "New Content" section: Add New Script */
+ $wp_admin_bar->add_node( array(
+ 'id' => 'asqn-wpnewcontent-add-script',
+ 'title' => esc_html__( 'Code Script', 'advanced-scripts-quicknav' ),
+ 'href' => esc_url( admin_url( 'tools.php?page=advanced-scripts&parent=0&edit=0' ) ),
+ 'parent' => 'new-content',
+ ) );
+ }
+
+ /**
+ * Add group node for Advanced Scripts "settings".
+ */
+ private function add_settings_group( $wp_admin_bar ) {
+
+ $wp_admin_bar->add_group( array(
+ 'id' => 'asqn-group-settings',
+ 'parent' => 'ddw-advscripts-quicknav',
+ ) );
+
+ //$this->add_settings_submenu( $wp_admin_bar );
+ $this->add_expert_submenu( $wp_admin_bar );
+ }
+
+ /**
+ * Add expert mode submenu.
+ */
+ private function add_expert_submenu( $wp_admin_bar ) {
+
+ if ( ! $this->is_expert_mode() ) return $wp_admin_bar;
+
+ $icon_dash = ' ';
+
+ if ( defined( 'SYSTEM_DASHBOARD_VERSION' ) ) {
+ $wp_admin_bar->add_node( array(
+ 'id' => 'asqn-system-dashboard',
+ 'title' => $icon_dash . esc_html__( 'System Dashboard', 'advanced-scripts-quicknav' ),
+ 'href' => esc_url( admin_url( 'index.php?page=system-dashboard' ) ),
+ 'parent' => 'asqn-group-settings',
+ 'meta' => array( 'class' => 'has-icon' ),
+ ) );
+ }
+
+ $icon_info = ' ';
+
+ $wp_admin_bar->add_node( array(
+ 'id' => 'asqn-sitehealth-info',
+ 'title' => $icon_info . esc_html__( 'Site Health Info', 'advanced-scripts-quicknav' ),
+ 'href' => esc_url( admin_url( 'site-health.php?tab=debug' ) ),
+ 'parent' => 'asqn-group-settings',
+ 'meta' => array( 'class' => 'has-icon' ),
+ ) );
+
+ $icon_code = ' ';
+
+ if ( defined( 'VARIABLE_INSPECTOR_VERSION' ) ) {
+ $wp_admin_bar->add_node( array(
+ 'id' => 'asqn-variable-inspector',
+ 'title' => $icon_code . esc_html__( 'Variable Inspector', 'advanced-scripts-quicknav' ),
+ 'href' => esc_url( admin_url( 'tools.php?page=variable-inspector' ) ),
+ 'parent' => 'asqn-group-settings',
+ 'meta' => array( 'class' => 'has-icon' ),
+ ) );
+ }
+
+ $icon_bug = ' ';
+
+ /**
+ * We need double check here as there is the "Downdload Manager" plugin
+ * with the same 'DLM' prefix & constant.
+ */
+ if ( defined( 'DLM_SLUG' ) && 'debug-log-manager' === DLM_SLUG ) {
+ $wp_admin_bar->add_node( array(
+ 'id' => 'asqn-debuglog-manager',
+ 'title' => $icon_bug . esc_html__( 'Debug Log Manager', 'advanced-scripts-quicknav' ),
+ 'href' => esc_url( admin_url( 'tools.php?page=debug-log-manager' ) ),
+ 'parent' => 'asqn-group-settings',
+ 'meta' => array( 'class' => 'has-icon' ),
+ ) );
+ }
+
+ $icon_codebox = ' ';
+
+ if ( defined( 'DPDEVKIT_URL' ) && current_user_can( 'manage_options' ) ) {
+ $wp_admin_bar->add_node( array(
+ 'id' => 'asqn-devkitpro',
+ 'title' => $icon_codebox . 'DevKit Pro',
+ 'href' => esc_url( admin_url( 'admin.php?page=devkit' ) ),
+ 'parent' => 'asqn-group-settings',
+ 'meta' => array( 'class' => 'has-icon' ),
+ ) );
+
+ $wp_admin_bar->add_node( array(
+ 'id' => 'asqn-devkitpro-filemanager',
+ 'title' => esc_html__( 'File Manager', 'advanced-scripts-quicknav' ),
+ 'href' => esc_url( admin_url( 'admin.php?page=devkit&tab=file-manager' ) ),
+ 'parent' => 'asqn-devkitpro',
+ 'meta' => array( 'target' => '_blank' ),
+ ) );
+
+ $wp_admin_bar->add_node( array(
+ 'id' => 'asqn-devkitpro-errorlogs',
+ 'title' => esc_html__( 'Error Logs', 'advanced-scripts-quicknav' ),
+ 'href' => esc_url( admin_url( 'admin.php?page=devkit&tab=error-log' ) ),
+ 'parent' => 'asqn-devkitpro',
+ 'meta' => array( 'target' => '_blank' ),
+ ) );
+
+ if ( 'yes' === get_option( 'dpdevkit_adminer' ) ) {
+ $wp_admin_bar->add_node( array(
+ 'id' => 'asqn-devkitpro-adminer',
+ 'title' => esc_html__( 'Adminer: Manage DB', 'advanced-scripts-quicknav' ),
+ 'href' => esc_url( site_url() . '/devkit-adminer' ),
+ 'parent' => 'asqn-devkitpro',
+ 'meta' => array( 'target' => '_blank' ),
+ ) );
+ }
+
+ $wp_admin_bar->add_node( array(
+ 'id' => 'asqn-devkitpro-tools',
+ 'title' => esc_html__( 'Tools', 'advanced-scripts-quicknav' ),
+ 'href' => esc_url( admin_url( 'admin.php?page=devkit&tab=tools' ) ),
+ 'parent' => 'asqn-devkitpro',
+ 'meta' => array( 'target' => '_blank' ),
+ ) );
+ }
+
+ }
+
+ /**
+ * Add group node for Script/ Snippet Library items (external links)
+ */
+ private function add_library_group( $wp_admin_bar ) {
+
+ if ( ASQN_Config::flag( 'ASQN_DISABLE_LIBRARY', false ) ) {
+ return $wp_admin_bar;
+ }
+
+ $wp_admin_bar->add_group( array(
+ 'id' => 'asqn-library',
+ 'parent' => 'ddw-advscripts-quicknav',
+ 'meta' => array( 'class' => 'ab-sub-secondary' ),
+ ) );
+ }
+
+ /**
+ * Libraries Group: Add linked Libraries submenu
+ */
+ private function add_libraries_submenu( $wp_admin_bar ) {
+
+ $icon = ' ';
+
+ $wp_admin_bar->add_node( array(
+ 'id' => 'asqn-libraries',
+ 'title' => $icon . esc_html__( 'Find Snippets', 'advanced-scripts-quicknav' ),
+ 'href' => '#',
+ 'parent' => 'asqn-library',
+ 'meta' => array( 'class' => 'has-icon' ),
+ ) );
+
+ $codelibs = array(
+ 'codesnippets-cloud' => array(
+ 'title' => __( 'Code Snippets Cloud', 'advanced-scripts-quicknav' ),
+ 'url' => 'https://codesnippets.cloud/search',
+ ),
+ 'wpsnippets-library' => array(
+ 'title' => __( 'WP Snippets Library', 'advanced-scripts-quicknav' ),
+ 'url' => 'https://wpsnippets.org/library/',
+ ),
+ 'websquadron-codes' => array(
+ 'title' => __( 'Codes by Web Squadron', 'advanced-scripts-quicknav' ),
+ 'url' => 'https://learn.websquadron.co.uk/codes/',
+ ),
+ 'wpsnippetclub-archive' => array(
+ 'title' => __( 'WP SnippetClub Archive', 'advanced-scripts-quicknav' ),
+ 'url' => 'https://wpsnippet.club/snippet/',
+ ),
+ 'dplugins-code' => array(
+ 'title' => __( 'Snippets Library by dPlugins', 'advanced-scripts-quicknav' ),
+ 'url' => 'https://code.dplugins.com/',
+ ),
+ 'wpcodebin' => array(
+ 'title' => __( 'WPCodeBin by WPCodeBox', 'advanced-scripts-quicknav' ),
+ 'url' => 'https://wpcodebin.com/',
+ ),
+ 'wpcode-library' => array(
+ 'title' => __( 'Snippets Library by WPCode', 'advanced-scripts-quicknav' ),
+ 'url' => 'https://library.wpcode.com/',
+ ),
+ );
+
+ /** Make code libs array filterable */
+ $codelibs = apply_filters( 'ddw/quicknav/csn_codelibs', $codelibs );
+ $codelibs = is_array( $codelibs ) ? $codelibs : [];
+
+ foreach ( $codelibs as $id => $info ) {
+ if ( ! is_array( $info ) || ! is_string( $info['title'] ?? null ) || ! is_string( $info['url'] ?? null ) ) { continue; }
+ $wp_admin_bar->add_node( array(
+ 'id' => 'asqn-library-link-' . sanitize_key( $id ),
+ 'title' => esc_html( $info[ 'title' ] ),
+ 'href' => esc_url( $info[ 'url' ] ),
+ 'parent' => 'asqn-libraries',
+ 'meta' => array( 'target' => '_blank', 'rel' => 'nofollow noopener noreferrer' ),
+ ) );
+ } // end foreach
+ }
+
+ /**
+ * Add group node for footer items (Links & About)
+ */
+ private function add_footer_group( $wp_admin_bar ) {
+
+ if ( ASQN_Config::flag( 'ASQN_DISABLE_FOOTER', false ) ) {
+ return $wp_admin_bar;
+ }
+
+ $wp_admin_bar->add_group( array(
+ 'id' => 'asqn-footer',
+ 'parent' => 'ddw-advscripts-quicknav',
+ 'meta' => array( 'class' => 'ab-sub-secondary' ),
+ ) );
+ }
+
+ /**
+ * Footer Group: Add Links submenu
+ */
+ private function add_links_submenu( $wp_admin_bar ) {
+
+ $icon = ' ';
+
+ $wp_admin_bar->add_node( array(
+ 'id' => 'asqn-links',
+ 'title' => $icon . esc_html__( 'Links', 'advanced-scripts-quicknav' ),
+ 'href' => '#',
+ 'parent' => 'asqn-footer',
+ 'meta' => array( 'class' => 'has-icon' ),
+ ) );
+
+ $links = array(
+ 'as-cleanplugins' => array(
+ 'title' => __( 'Advanced Scripts', 'advanced-scripts-quicknav' ),
+ 'url' => 'https://r.freemius.com/6334/142255/',
+ ),
+ 'as-emergency' => array(
+ 'title' => __( 'Emergency Fixes 🔥', 'advanced-scripts-quicknav' ),
+ 'url' => 'https://www.cleanplugins.com/blog/advanced-scripts-2-4-0-release-overview/',
+ ),
+ 'cp-blog' => array(
+ 'title' => __( 'Clean Plugins Blog', 'advanced-scripts-quicknav' ),
+ 'url' => 'https://www.cleanplugins.com/blog/',
+ ),
+ 'cp-youtube' => array(
+ 'title' => __( 'CP YouTube Channel', 'advanced-scripts-quicknav' ),
+ 'url' => 'https://www.youtube.com/c/cleanplugins',
+ ),
+ 'cp-fb-group' => array(
+ 'title' => __( 'CP Facebook Group (official)', 'advanced-scripts-quicknav' ),
+ 'url' => 'https://www.facebook.com/groups/cleanplugins',
+ ),
+ );
+
+ /** Make links array filterable */
+ $links = apply_filters( 'ddw/quicknav/as_links', $links );
+ $links = is_array( $links ) ? $links : [];
+
+ foreach ( $links as $id => $info ) {
+ if ( ! is_array( $info ) || ! is_string( $info['title'] ?? null ) || ! is_string( $info['url'] ?? null ) ) { continue; }
+ $wp_admin_bar->add_node( array(
+ 'id' => 'asqn-link-' . sanitize_key( $id ),
+ 'title' => esc_html( $info[ 'title' ] ),
+ 'href' => esc_url( $info[ 'url' ] ),
+ 'parent' => 'asqn-links',
+ 'meta' => array( 'target' => '_blank', 'rel' => 'nofollow noopener noreferrer' ),
+ ) );
+ } // end foreach
+ }
+
+ /**
+ * Footer Group: Add About submenu
+ */
+ private function add_about_submenu( $wp_admin_bar ) {
+
+ $icon = ' ';
+
+ $wp_admin_bar->add_node( array(
+ 'id' => 'asqn-about',
+ 'title' => $icon . esc_html__( 'About', 'advanced-scripts-quicknav' ),
+ 'href' => '#',
+ 'parent' => 'asqn-footer',
+ 'meta' => array( 'class' => 'has-icon' ),
+ ) );
+
+ $about_links = array(
+ 'author' => array(
+ 'title' => __( 'Author: David Decker', 'advanced-scripts-quicknav' ),
+ 'url' => 'https://deckerweb.de/',
+ ),
+ 'github' => array(
+ 'title' => __( 'Plugin on GitHub', 'advanced-scripts-quicknav' ),
+ 'url' => 'https://github.com/deckerweb/advanced-scripts-quicknav',
+ ),
+ 'kofi' => array(
+ 'title' => __( 'Buy Me a Coffee', 'advanced-scripts-quicknav' ),
+ 'url' => 'https://ko-fi.com/deckerweb',
+ ),
+ );
+
+ foreach ( $about_links as $id => $info ) {
+ $wp_admin_bar->add_node( array(
+ 'id' => 'asqn-about-' . sanitize_key( $id ),
+ 'title' => esc_html( $info[ 'title' ] ),
+ 'href' => esc_url( $info[ 'url' ] ),
+ 'parent' => 'asqn-about',
+ 'meta' => array( 'target' => '_blank', 'rel' => 'nofollow noopener noreferrer' ),
+ ) );
+ } // end foreach
+ }
+
+ /**
+ * Add additional plugin related info to the Site Health Debug Info section.
+ *
+ * @link https://make.wordpress.org/core/2019/04/25/site-health-check-in-5-2/
+ *
+ * @param array $debug_info Array holding all Debug Info items.
+ * @return array Modified array of Debug Info.
+ */
+ public function site_health_debug_info( $debug_info ) {
+ $fields = [
+ 'quicknav' => [ 'label' => __( 'Plugin version', 'advanced-scripts-quicknav' ), 'value' => ASQN_Config::VERSION ],
+ 'advanced_scripts' => [ 'label' => 'Advanced Scripts', 'value' => defined( 'EPXADVSC_VER' ) ? EPXADVSC_VER : __( 'Not active', 'advanced-scripts-quicknav' ) ],
+ 'capability' => [ 'label' => 'ASQN_VIEW_CAPABILITY', 'value' => ASQN_Config::capability() ],
+ 'safe_mode' => [ 'label' => __( 'Safe Mode', 'advanced-scripts-quicknav' ), 'value' => $this->is_safe_mode_active() ? __( 'Enabled', 'advanced-scripts-quicknav' ) : __( 'Disabled', 'advanced-scripts-quicknav' ) ],
+ 'limit' => [ 'label' => __( 'Menu entry limit', 'advanced-scripts-quicknav' ), 'value' => ASQN_Config::limit() ],
+ ];
+ foreach ( [ 'counter' => ASQN_Config::counter(), 'expert' => ASQN_Config::expert(), 'library' => ! ASQN_Config::flag( 'ASQN_DISABLE_LIBRARY', false ), 'footer' => ! ASQN_Config::flag( 'ASQN_DISABLE_FOOTER', false ) ] as $key => $value ) {
+ $fields[$key] = [ 'label' => $key, 'value' => $value ? __( 'Enabled', 'advanced-scripts-quicknav' ) : __( 'Disabled', 'advanced-scripts-quicknav' ) ];
+ }
+ $debug_info['advanced-scripts-quicknav'] = [ 'label' => 'Advanced Scripts QuickNav', 'fields' => $fields ];
+ return $debug_info;
+ }
+}
diff --git a/includes/class-asqn-settings.php b/includes/class-asqn-settings.php
new file mode 100644
index 0000000..84b75c3
--- /dev/null
+++ b/includes/class-asqn-settings.php
@@ -0,0 +1,113 @@
+ 403 ] ); }
+ check_admin_referer( 'asqn_preferences' );
+ $scripts = ( new ASQN_Advanced_Scripts_Adapter() )->scripts();
+ if ( ! ASQN_Advanced_Scripts_Adapter::available() ) { wp_die( esc_html__( 'Activate Advanced Scripts first.', 'advanced-scripts-quicknav' ) ); }
+ $ids = isset( $_POST['favorites'] ) && is_array( $_POST['favorites'] ) ? wp_unslash( $_POST['favorites'] ) : [];
+ if ( count( array_unique( array_filter( $ids, 'is_scalar' ), SORT_REGULAR ) ) > 100 ) { wp_die( esc_html__( 'Please select no more than 100 favorites.', 'advanced-scripts-quicknav' ) ); }
+ update_user_meta( get_current_user_id(), ASQN_Config::key( 'favorites' ), ASQN_Favorites::sanitize( $ids, $scripts ) );
+ $limit = isset( $_POST['limit'] ) && is_scalar( $_POST['limit'] ) ? absint( $_POST['limit'] ) : 40;
+ update_user_meta( get_current_user_id(), ASQN_Config::key( 'preferences' ), [ 'counter' => ! empty( $_POST['counter'] ), 'expert' => ! empty( $_POST['expert'] ), 'limit' => max( 10, min( 200, $limit ) ) ] );
+ wp_safe_redirect( admin_url( 'options-general.php?page=advanced-scripts-quicknav&saved=1' ) ); exit;
+ }
+ public static function german(): bool { return 0 === strpos( determine_locale(), 'de' ); }
+ public static function document_url(): string {
+ return 'https://github.com/deckerweb/advanced-scripts-quicknav/blob/main/docs/wiki/' . ( self::german() ? 'Deutsch.md' : 'English.md' );
+ }
+ public function page(): void {
+ if ( ! ASQN_Config::allowed() ) { return; }
+ $nav = new ASQN_Navigation_Builder( ( new ASQN_Advanced_Scripts_Adapter() )->scripts() );
+ $scripts = $nav->scripts(); $favorites = ASQN_Favorites::ids( $scripts ); $prefs = ASQN_Config::preferences();
+ echo '';
+ if ( ! defined( 'ASQN_SNIPPET_MODE' ) ) { echo ' '; }
+ echo 'Advanced Scripts QuickNav ' . esc_html__( 'Your scripts. One click away.', 'advanced-scripts-quicknav' ) . '
';
+ if ( isset( $_GET['saved'] ) ) { echo '
' . esc_html__( 'Your preferences have been saved.', 'advanced-scripts-quicknav' ) . '
'; }
+ if ( ! ASQN_Advanced_Scripts_Adapter::available() ) {
+ echo '
' . esc_html__( 'Activate Advanced Scripts first. Your saved preferences are kept.', 'advanced-scripts-quicknav' ) . '
';
+ $this->footer(); echo '
'; return;
+ }
+ echo '' . esc_html__( 'These preferences apply only to your account on this website. Constants in wp-config.php take precedence over display preferences.', 'advanced-scripts-quicknav' ) . '
';
+ $this->statistics( $nav, count( $favorites ) );
+ echo '';
+ $this->footer();
+ if ( defined( 'ASQN_SNIPPET_MODE' ) ) { echo ''; }
+ echo '';
+ }
+ private function statistics( ASQN_Navigation_Builder $nav, int $favorites ): void {
+ $stats = $nav->statistics();
+ $metrics = [ 'total' => __( 'All snippets', 'advanced-scripts-quicknav' ), 'active' => __( 'Active', 'advanced-scripts-quicknav' ), 'inactive' => __( 'Inactive', 'advanced-scripts-quicknav' ), 'folders' => __( 'Folders', 'advanced-scripts-quicknav' ), 'blocked' => __( 'Active, blocked by folder', 'advanced-scripts-quicknav' ), 'favorites' => __( 'Saved favorites', 'advanced-scripts-quicknav' ) ];
+ $stats['favorites'] = $favorites;
+ echo '' . esc_html__( 'Your snippet overview', 'advanced-scripts-quicknav' ) . ' ';
+ foreach ( $metrics as $key => $label ) {
+ echo '
' . esc_html( $label ) . ' ' . esc_html( $stats[$key] ) . ' ';
+ }
+ echo '
' . esc_html__( 'Active and inactive refer to saved snippet status. Folder blocks are counted within active snippets; conditions and execution location can also affect execution.', 'advanced-scripts-quicknav' ) . '
';
+ if ( $stats['types'] ) {
+ echo '';
+ foreach ( $stats['types'] as $type => $count ) { echo '' . esc_html( $type ) . ' ' . esc_html( $count ) . ' '; }
+ echo '
';
+ }
+ echo ' ';
+ }
+ private function favorites_preview( ASQN_Navigation_Builder $nav, array $favorites ): void {
+ $scripts = $nav->scripts();
+ echo '
' . esc_html__( 'Your favorites', 'advanced-scripts-quicknav' ) . ' ' . esc_html( count( $favorites ) ) . ' ' . esc_html__( 'Open a favorite directly from its card. This preview follows your selection; save to update the toolbar.', 'advanced-scripts-quicknav' ) . '
' . esc_html__( 'No favorites selected yet. Choose snippets below to build your quick-access list.', 'advanced-scripts-quicknav' ) . '
';
+ }
+ private function footer(): void {
+ $changelog = defined( 'ASQN_SNIPPET_MODE' ) ? '' : plugins_url( self::german() ? 'docs/changelog-de.txt' : 'docs/changelog.txt', ASQN_PLUGIN_FILE );
+ echo '';
+ if ( defined( 'ASQN_SNIPPET_MODE' ) ) { $content = self::german() ? ASQN_SNIPPET_CHANGELOG_DE : ASQN_SNIPPET_CHANGELOG_EN; }
+ else { $file = dirname( ASQN_PLUGIN_FILE ) . '/docs/' . ( self::german() ? 'changelog-de.txt' : 'changelog.txt' ); $content = is_readable( $file ) ? file_get_contents( $file ) : ''; }
+ echo 'Advanced Scripts QuickNav · ' . esc_html__( 'Changelog', 'advanced-scripts-quicknav' ) . ' ' . esc_html__( 'Close', 'advanced-scripts-quicknav' ) . ' ' . Deckerweb_Changelog_Renderer_V1::render( $content ) . '
';
+ }
+}
diff --git a/includes/deckerweb-changelog-v1.php b/includes/deckerweb-changelog-v1.php
new file mode 100644
index 0000000..44fdb1d
--- /dev/null
+++ b/includes/deckerweb-changelog-v1.php
@@ -0,0 +1,36 @@
+ 262144 ) { return ''; }
+ if ( preg_match( '/^== Changelog ==\s*\R(.*?)(?=^== [^\r\n]+ ==\s*$|\z)/ms', $text, $match ) ) { $text = $match[1]; }
+ $releases = []; $current = null;
+ foreach ( preg_split( '/\R/', $text ) as $line ) {
+ $line = trim( $line );
+ if ( preg_match( '/^= (.+) =$/', $line, $heading ) ) {
+ $releases[] = [ 'heading' => $heading[1], 'items' => [] ]; $current = count( $releases ) - 1;
+ } elseif ( null !== $current && preg_match( '/^[*\-] (.+)$/', $line, $item ) ) { $releases[$current]['items'][] = $item[1]; }
+ }
+ $html = '';
+ $categories = [ 'New' => 'new', 'Neu' => 'new', 'Improved' => 'improved', 'Verbessert' => 'improved', 'Fixed' => 'fixed', 'Behoben' => 'fixed', 'Misc' => 'misc', 'Sonstiges' => 'misc' ];
+ foreach ( $releases as $release ) {
+ $parts = array_map( 'trim', explode( '·', $release['heading'], 2 ) );
+ $html .= '' . esc_html( $parts[0] ) . ' ' . ( isset( $parts[1] ) ? '' . esc_html( $parts[1] ) . ' ' : '' ) . '';
+ foreach ( $release['items'] as $item ) {
+ $prefix = ''; $category = 'misc'; $body = $item;
+ if ( preg_match( '/^([^:]+):\s*(.*)$/', $item, $entry ) && isset( $categories[$entry[1]] ) ) { $prefix = $entry[1]; $category = $categories[$prefix]; $body = $entry[2]; }
+ $html .= '' . ( $prefix ? '' . esc_html( $prefix ) . ' ' : '' ) . '' . self::inline( $body ) . ' ';
+ }
+ $html .= ' ';
+ }
+ return $html;
+ }
+ private static function inline( string $text ): string {
+ // Escape first; only literal code spans become fixed markup.
+ return preg_replace( '/`([^`]+)`/', '$1', esc_html( $text ) );
+ }
+ }
+}
diff --git a/includes/deckerweb-github-release-updater-v2.php b/includes/deckerweb-github-release-updater-v2.php
new file mode 100644
index 0000000..2a78956
--- /dev/null
+++ b/includes/deckerweb-github-release-updater-v2.php
@@ -0,0 +1,332 @@
+
+ */
+ private array $icons;
+ /**
+ * Validated banner URLs, keyed by low/high resolution.
+ *
+ * @var array
+ */
+ private array $banners;
+ /**
+ * Validate the installed plugin basename and repository identity.
+ *
+ * @param string $main_file Absolute path to the main plugin file.
+ * @param string $repository_url Public https://github.com/owner/repository URL.
+ * @param string $name Plugin display name.
+ * @param string $description Short description for update details.
+ * @param array $artwork Optional icons (svg, 1x, 2x, default) and banners (low, high) URL maps.
+ * @throws \InvalidArgumentException If the slug or repository URL is invalid.
+ */
+ public function __construct( string $main_file, string $repository_url, string $name, string $description, array $artwork = array() ) {
+ $this->file = \plugin_basename( $main_file );
+ $this->slug = \dirname( $this->file );
+ if ( '.' === $this->slug || ! \preg_match( '/^[a-z0-9_-]+$/D', $this->slug ) ) {
+ throw new \InvalidArgumentException( 'The plugin must be installed in a stable slug directory.' );
+ }
+ if ( ! \preg_match( '~^https://github\.com/([A-Za-z0-9-]+)/([A-Za-z0-9._-]+)/?$~D', $repository_url, $matches ) ) {
+ throw new \InvalidArgumentException( 'Invalid public GitHub repository URL.' );
+ }
+ $this->repo = 'https://github.com/' . $matches[1] . '/' . $matches[2];
+ $this->api = 'https://api.github.com/repos/' . $matches[1] . '/' . $matches[2];
+ $this->cache = 'ddw_ghru_' . \substr( \md5( $this->repo ), 0, 24 );
+ $this->name = $name;
+ $this->description = $description;
+ $this->icons = $this->artwork_urls( $artwork['icons'] ?? array(), array( 'svg', '1x', '2x', 'default' ) );
+ $this->banners = $this->artwork_urls( $artwork['banners'] ?? array(), array( 'low', 'high' ) );
+ }
+
+ /**
+ * Accept only known artwork keys and absolute HTTP(S) URLs without credentials.
+ * Values come from the integrating plugin, never from release metadata.
+ * No image fetch, remote probing or filesystem access is performed here.
+ *
+ * @param mixed $urls Optional URL map.
+ * @param array $keys Supported WordPress artwork keys.
+ * @return array Safe URLs.
+ */
+ private function artwork_urls( $urls, array $keys ): array {
+ $result = array();
+ if ( ! is_array( $urls ) ) {
+ return $result;
+ }
+ foreach ( $keys as $key ) {
+ if ( ! isset( $urls[ $key ] ) || ! is_string( $urls[ $key ] ) ) {
+ continue;
+ }
+ $url = $urls[ $key ];
+ $parts = \wp_parse_url( $url );
+ if ( ! is_array( $parts ) || ! in_array( $parts['scheme'] ?? '', array( 'https', 'http' ), true ) || empty( $parts['host'] ) || isset( $parts['user'] ) || isset( $parts['pass'] ) || preg_match( '/[\x00-\x20\x7f]/', $url ) ) {
+ continue;
+ }
+ $clean = \esc_url_raw( $url, array( 'https', 'http' ) );
+ if ( '' !== $clean ) {
+ $result[ $key ] = $clean;
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Add icons to an existing cached offer for this plugin only.
+ * Preserve cache timestamps, package fields and unrelated plugin objects.
+ *
+ * @param mixed $transient WordPress update transient.
+ * @return mixed Decorated copy or original value.
+ */
+ public function cached_icons( $transient ) {
+ if ( ! $this->icons || ! is_object( $transient ) || ! isset( $transient->response[ $this->file ] ) || ! is_object( $transient->response[ $this->file ] ) ) {
+ return $transient;
+ }
+ $transient = clone $transient;
+ $transient->response[ $this->file ] = clone $transient->response[ $this->file ];
+ $transient->response[ $this->file ]->icons = $this->icons;
+ return $transient;
+ }
+
+ /**
+ * Register the update, information, and extracted archive hooks.
+ */
+ public function register(): void {
+ \add_filter( 'update_plugins_github.com', array( $this, 'update' ), 10, 4 );
+ \add_filter( 'site_transient_update_plugins', array( $this, 'cached_icons' ) );
+ \add_filter( 'plugins_api', array( $this, 'information' ), 20, 3 );
+ \add_filter( 'upgrader_source_selection', array( $this, 'select_source' ), 20, 4 );
+ }
+
+ /**
+ * Fetch the latest published release and choose its installable ZIP.
+ * Caches successful responses for 30 minutes and failures for 10 minutes.
+ *
+ * @return array{version:string,package:string,notes:string,published:string}|null
+ */
+ private function release(): ?array {
+ $cached = \get_site_transient( $this->cache );
+ if ( \is_array( $cached ) ) {
+ return ! empty( $cached['failed'] ) ? null : $cached;
+ }
+ $response = \wp_remote_get(
+ $this->api . '/releases/latest',
+ array(
+ 'timeout' => 6,
+ 'redirection' => 2,
+ 'headers' => array(
+ 'Accept' => 'application/vnd.github+json',
+ 'User-Agent' => 'DECKERWEB-WordPress-GitHub-Release-Updater',
+ 'X-GitHub-Api-Version' => '2022-11-28',
+ ),
+ )
+ );
+ if ( \is_wp_error( $response ) || \wp_remote_retrieve_response_code( $response ) !== 200 ) {
+ \set_site_transient( $this->cache, array( 'failed' => true ), 10 * MINUTE_IN_SECONDS );
+ return null;
+ }
+ $data = \json_decode( \wp_remote_retrieve_body( $response ), true );
+ if ( ! \is_array( $data ) || ! empty( $data['draft'] ) || ! empty( $data['prerelease'] ) || ! \is_string( $data['tag_name'] ?? null ) || ! \preg_match( '/^v?(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)$/D', $data['tag_name'], $match ) ) {
+ \set_site_transient( $this->cache, array( 'failed' => true ), 10 * MINUTE_IN_SECONDS );
+ return null;
+ }
+ // Prefer a release asset with the stable slug; otherwise use the source archive.
+ $version = $match[1];
+ $package = '';
+ foreach ( ( \is_array( $data['assets'] ?? null ) ? $data['assets'] : array() ) as $asset ) {
+ if ( ! \is_array( $asset ) || ( $asset['state'] ?? 'uploaded' ) !== 'uploaded' ) {
+ continue;
+ }
+ $asset_name = \is_string( $asset['name'] ?? null ) ? $asset['name'] : '';
+ if ( $asset_name !== $this->slug . '.zip' && $asset_name !== $this->slug . '-' . $version . '.zip' ) {
+ continue;
+ }
+ $url = \is_string( $asset['browser_download_url'] ?? null ) ? $asset['browser_download_url'] : '';
+ if ( \strpos( $url, $this->repo . '/releases/download/' ) === 0 ) {
+ $package = $url;
+ break;
+ }
+ }
+ if ( ! $package ) {
+ $url = \is_string( $data['zipball_url'] ?? null ) ? $data['zipball_url'] : '';
+ if ( \strpos( $url, $this->api . '/zipball/' ) === 0 ) {
+ $package = $url;
+ }
+ }
+ if ( ! $package ) {
+ \set_site_transient( $this->cache, array( 'failed' => true ), 10 * MINUTE_IN_SECONDS );
+ return null;
+ }
+ $release = array(
+ 'version' => $version,
+ 'package' => $package,
+ 'notes' => \is_string( $data['body'] ?? null ) ? $data['body'] : '',
+ 'published' => \is_string( $data['published_at'] ?? null ) ? $data['published_at'] : '',
+ );
+ \set_site_transient( $this->cache, $release, 30 * MINUTE_IN_SECONDS );
+ return $release;
+ }
+
+ /**
+ * Provide an update only for this plugin and its matching Update URI.
+ *
+ * @param mixed $response Current WordPress update response.
+ * @param array $plugin_data Installed plugin headers.
+ * @param string $plugin_file Plugin basename supplied by WordPress.
+ * @param mixed $locales Requested locales, unused.
+ * @return mixed Existing response or WordPress update data.
+ */
+ public function update( $response, $plugin_data, $plugin_file, $locales ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed -- WordPress hostname filter signature.
+ // The hostname hook may run for multiple GitHub plugins on the same site.
+ if ( $plugin_file !== $this->file || ( $plugin_data['UpdateURI'] ?? '' ) !== $this->repo ) {
+ return $response;
+ }
+ $release = $this->release();
+ if ( ! $release || \version_compare( $release['version'], (string) ( $plugin_data['Version'] ?? '0' ), '<=' ) ) {
+ return $response;
+ }
+ return array(
+ 'id' => $this->repo,
+ 'slug' => $this->slug,
+ 'version' => $release['version'],
+ 'url' => $this->repo . '/releases',
+ 'package' => $release['package'],
+ 'requires' => (string) ( $plugin_data['RequiresWP'] ?? '' ),
+ 'requires_php' => (string) ( $plugin_data['RequiresPHP'] ?? '' ),
+ 'icons' => $this->icons,
+ );
+ }
+
+ /**
+ * Populate the WordPress plugin details modal from release metadata.
+ *
+ * @param mixed $response Current plugin information response.
+ * @param string $action WordPress plugins API action.
+ * @param mixed $args Request arguments.
+ * @return mixed Existing response or plugin information object.
+ */
+ public function information( $response, $action, $args ) {
+ if ( 'plugin_information' !== $action || ! \is_object( $args ) || ( $args->slug ?? '' ) !== $this->slug ) {
+ return $response;
+ }
+ $release = $this->release();
+ if ( ! $release ) {
+ return $response;
+ }
+ return (object) array(
+ 'name' => $this->name,
+ 'slug' => $this->slug,
+ 'version' => $release['version'],
+ 'author' => 'David Decker – DECKERWEB',
+ 'author_profile' => 'https://github.com/deckerweb',
+ 'homepage' => $this->repo,
+ 'last_updated' => $release['published'],
+ 'download_link' => $release['package'],
+ 'icons' => $this->icons,
+ 'banners' => $this->banners,
+ 'sections' => array(
+ 'description' => \esc_html( $this->description ),
+ 'changelog' => \wpautop( \esc_html( ! empty( $release['notes'] ) ? $release['notes'] : 'See the release on GitHub.' ) ),
+ ),
+ );
+ }
+
+ /**
+ * Verify and normalize the extracted plugin directory before installation.
+ *
+ * @param mixed $source Path to extracted archive, or a WP_Error.
+ * @param mixed $remote_source Remote source path, unused.
+ * @param mixed $upgrader WordPress upgrader instance, unused.
+ * @param array $hook_extra Upgrade context.
+ * @return mixed Valid source directory or WP_Error.
+ */
+ public function select_source( $source, $remote_source, $upgrader, $hook_extra ) {
+ if ( \is_wp_error( $source ) || ! \is_string( $source ) || ( $hook_extra['plugin'] ?? '' ) !== $this->file || ( $hook_extra['type'] ?? '' ) !== 'plugin' || ( $hook_extra['action'] ?? '' ) !== 'update' ) {
+ return $source;
+ }
+ global $wp_filesystem;
+ if ( ! $wp_filesystem ) {
+ return new \WP_Error( 'ddw_ghru_filesystem', 'Could not access the update filesystem.' );
+ }
+ $root = \untrailingslashit( $source );
+ $main = \basename( $this->file );
+ // Accept an asset's slug folder; a GitHub source ZIP needs renaming.
+ // Refuse archives missing the expected main file before WordPress replaces anything.
+ if ( $wp_filesystem->is_file( $root . '/' . $this->slug . '/' . $main ) ) {
+ return \trailingslashit( $root . '/' . $this->slug );
+ }
+ if ( ! $wp_filesystem->is_file( $root . '/' . $main ) ) {
+ return new \WP_Error( 'ddw_ghru_archive', 'GitHub release does not contain the plugin main file.' );
+ }
+ $target = \dirname( $root ) . '/' . $this->slug;
+ if ( $root === $target ) {
+ return $source;
+ }
+ if ( $wp_filesystem->exists( $target ) || ! $wp_filesystem->move( $root, $target, false ) ) {
+ return new \WP_Error( 'ddw_ghru_rename', 'Could not prepare the GitHub release package.' );
+ }
+ return \trailingslashit( $target );
+ }
+}
+
diff --git a/includes/deckerweb-plugin-library/assets/icons/advanced-scripts-quicknav.png b/includes/deckerweb-plugin-library/assets/icons/advanced-scripts-quicknav.png
new file mode 100644
index 0000000..7638eaa
Binary files /dev/null and b/includes/deckerweb-plugin-library/assets/icons/advanced-scripts-quicknav.png differ
diff --git a/includes/deckerweb-plugin-library/assets/icons/brand-admin-schemes.png b/includes/deckerweb-plugin-library/assets/icons/brand-admin-schemes.png
new file mode 100644
index 0000000..3109ef8
Binary files /dev/null and b/includes/deckerweb-plugin-library/assets/icons/brand-admin-schemes.png differ
diff --git a/includes/deckerweb-plugin-library/assets/icons/breakdance-quicknav.png b/includes/deckerweb-plugin-library/assets/icons/breakdance-quicknav.png
new file mode 100644
index 0000000..cee05b2
Binary files /dev/null and b/includes/deckerweb-plugin-library/assets/icons/breakdance-quicknav.png differ
diff --git a/includes/deckerweb-plugin-library/assets/icons/bricks-quicknav.png b/includes/deckerweb-plugin-library/assets/icons/bricks-quicknav.png
new file mode 100644
index 0000000..6ac3016
Binary files /dev/null and b/includes/deckerweb-plugin-library/assets/icons/bricks-quicknav.png differ
diff --git a/includes/deckerweb-plugin-library/assets/icons/builder-list-pages.png b/includes/deckerweb-plugin-library/assets/icons/builder-list-pages.png
new file mode 100644
index 0000000..0298404
Binary files /dev/null and b/includes/deckerweb-plugin-library/assets/icons/builder-list-pages.png differ
diff --git a/includes/deckerweb-plugin-library/assets/icons/daily-scripture.png b/includes/deckerweb-plugin-library/assets/icons/daily-scripture.png
new file mode 100644
index 0000000..56b2498
Binary files /dev/null and b/includes/deckerweb-plugin-library/assets/icons/daily-scripture.png differ
diff --git a/includes/deckerweb-plugin-library/assets/icons/quick-edit-featured-image.png b/includes/deckerweb-plugin-library/assets/icons/quick-edit-featured-image.png
new file mode 100644
index 0000000..7a4cfea
Binary files /dev/null and b/includes/deckerweb-plugin-library/assets/icons/quick-edit-featured-image.png differ
diff --git a/includes/deckerweb-plugin-library/assets/library.css b/includes/deckerweb-plugin-library/assets/library.css
new file mode 100644
index 0000000..0a30c6d
--- /dev/null
+++ b/includes/deckerweb-plugin-library/assets/library.css
@@ -0,0 +1,33 @@
+/* Scoped to the deckerweb tab; uses WordPress admin accent colors. */
+.plugin-install-tab-deckerweb .search-form.search-plugins{display:none}
+.dwl-install .wrap{max-width:900px;margin:40px auto;padding:0 24px}
+.dwl-install .dwl-action{display:inline-block;margin:0 12px 0 0}
+#deckerweb-library{margin:20px 0;color:#1d2327}
+#deckerweb-library *{box-sizing:border-box}
+#deckerweb-library .dwl-heading{display:flex;justify-content:space-between;align-items:baseline;gap:20px;flex-wrap:wrap;margin-bottom:20px}
+#deckerweb-library h2{margin:0;font-size:22px;font-weight:400}
+#deckerweb-library .dwl-heading p{margin:6px 0 0;color:#50575e}
+#deckerweb-library .dwl-filter{display:flex;align-items:center;gap:18px;flex-wrap:wrap;margin-bottom:20px}
+#deckerweb-library .dwl-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,340px),1fr));gap:20px}
+#deckerweb-library .dwl-card{background:#fff;border:1px solid #c3c4c7;display:flex;flex-direction:column}
+#deckerweb-library .dwl-body{padding:22px;flex:1}
+#deckerweb-library .dwl-title{display:flex;align-items:flex-start;gap:16px}
+#deckerweb-library .dwl-title h3{margin:0 0 4px;font-size:18px;line-height:1.35}
+#deckerweb-library .dwl-icon{width:54px;height:54px;flex-shrink:0;background:#f0f6fc;color:var(--wp-admin-theme-color,#2271b1);display:flex;align-items:center;justify-content:center;font-size:23px;font-weight:600}
+#deckerweb-library .dwl-meta{font-size:12px;color:#50575e}
+#deckerweb-library .dwl-active{display:inline-block;color:#135e3b;padding:5px 0;font-weight:600}
+#deckerweb-library .dwl-requirements{margin-top:16px;padding:10px 12px;background:#f6f7f7;border-left:3px solid var(--wp-admin-theme-color,#2271b1)}
+#deckerweb-library .dwl-requirements ul{margin:0 0 6px}
+#deckerweb-library .dwl-details{margin-top:18px}
+#deckerweb-library .dwl-details summary{color:var(--wp-admin-theme-color,#2271b1);cursor:pointer}
+#deckerweb-library .dwl-details p{margin:8px 0}
+#deckerweb-library footer{display:flex;justify-content:space-between;gap:10px;flex-wrap:wrap;padding:12px 22px;background:#f6f7f7;border-top:1px solid #dcdcde;color:#50575e}
+#deckerweb-library .dwl-bottom{margin-top:22px;display:flex;gap:22px;flex-wrap:wrap;align-items:center;color:#50575e}
+#deckerweb-library .dwl-action{display:inline-block;margin:0}
+#deckerweb-library .notice{margin:12px 0}
+@media(max-width:600px){#deckerweb-library .dwl-body{padding:16px}#deckerweb-library .dwl-filter{align-items:flex-start;flex-direction:column}#deckerweb-library .dwl-filter>label{max-width:100%}#deckerweb-library .dwl-filter>label:first-of-type{width:100%}#deckerweb-library .dwl-filter input[type=search]{margin:0;min-width:0;max-width:100%;width:100%}}
+
+#deckerweb-library .dwl-icon img{width:54px;height:54px;object-fit:contain}
+#deckerweb-library .dwl-icon-fallback{display:flex;align-items:center;justify-content:center;width:100%;height:100%;color:#536273}
+#deckerweb-library .dwl-stars{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
+#deckerweb-library .dwl-stars a{text-decoration:none;color:#50575e}
diff --git a/includes/deckerweb-plugin-library/bootstrap.php b/includes/deckerweb-plugin-library/bootstrap.php
new file mode 100644
index 0000000..503f936
--- /dev/null
+++ b/includes/deckerweb-plugin-library/bootstrap.php
@@ -0,0 +1,39 @@
+ $version, 'dir' => $library_dir, 'host' => $plugin_file, 'config' => $config,
+ ];
+ if ( empty( $GLOBALS['deckerweb_library_election_v1'] ) ) {
+ $GLOBALS['deckerweb_library_election_v1'] = true;
+ add_action( 'plugins_loaded', 'deckerweb_library_elect_v1', PHP_INT_MAX );
+ }
+ }
+
+ /** One runtime per request, independent of host plugin load order. */
+ function deckerweb_library_elect_v1(): void {
+ if ( ! empty( $GLOBALS['deckerweb_library_runtime_v1'] ) ) { return; }
+ $candidates = $GLOBALS['deckerweb_library_candidates_v1'] ?? [];
+ if ( ! $candidates ) { return; }
+ usort( $candidates, static function( array $a, array $b ): int {
+ $comparison = version_compare( $b['version'], $a['version'] );
+ return $comparison ?: strcmp( $a['host'], $b['host'] );
+ } );
+ $chosen = $candidates[0];
+ $factory = require $chosen['dir'] . '/runtime.php';
+ $GLOBALS['deckerweb_library_runtime_v1'] = $factory( $chosen, $candidates );
+ }
+}
diff --git a/includes/deckerweb-plugin-library/catalog.json b/includes/deckerweb-plugin-library/catalog.json
new file mode 100644
index 0000000..bf5e950
--- /dev/null
+++ b/includes/deckerweb-plugin-library/catalog.json
@@ -0,0 +1,225 @@
+{
+ "schema_version": 1,
+ "plugins": [
+ {
+ "approved": true,
+ "slug": "brand-admin-schemes",
+ "name": "Brand Admin Schemes",
+ "description": "Bring your brand colors to the WordPress admin, login, toolbar and browser tabs.",
+ "description_de": "Deine Markenfarben für WordPress-Admin, Login, Toolbar und Browser-Tabs.",
+ "version": "0.16.3",
+ "repository": "deckerweb/brand-admin-schemes",
+ "plugin_file": "brand-admin-schemes/brand-admin-schemes.php",
+ "download_url": "https://github.com/deckerweb/brand-admin-schemes/releases/download/v0.16.3/brand-admin-schemes-0.16.3.zip",
+ "sha256": "efbe8d2b9fec014dcbd3f1dd00ab8e661f50f3a432495f2e9202d11fe74c469c",
+ "requires_wp": "6.4",
+ "requires_php": "8.0",
+ "category": "Admin",
+ "dependencies": [],
+ "github_stars": 1,
+ "stars_checked_at": "2026-10-01",
+ "icon_label": "BA",
+ "icon_background": "#edf3f0",
+ "icon": "assets/icons/brand-admin-schemes.png"
+ },
+ {
+ "approved": true,
+ "slug": "breakdance-quicknav",
+ "name": "Breakdance QuickNav",
+ "description": "Quick access to Breakdance templates, headers, footers, global blocks and settings from the toolbar.",
+ "description_de": "Schneller Zugriff auf Breakdance-Templates, Header, Footer, globale Blöcke und Einstellungen über die Toolbar.",
+ "version": "1.0.0",
+ "repository": "deckerweb/breakdance-quicknav",
+ "plugin_file": "breakdance-quicknav/breakdance-quicknav.php",
+ "download_url": "https://github.com/deckerweb/breakdance-quicknav/releases/download/v1.0.0/breakdance-quicknav.zip",
+ "sha256": "8c319c59a25da2ea500eb7691fa0e1c3b7b335f6cc0afffd211e3efae5f60b71",
+ "requires_wp": "6.7",
+ "requires_php": "7.4",
+ "category": "Breakdance",
+ "dependencies": [
+ {
+ "name": "Breakdance Builder",
+ "plugin_file": "breakdance/plugin.php",
+ "min_version": "",
+ "detector": "breakdance",
+ "url": "https://breakdance.com/"
+ }
+ ],
+ "github_stars": 3,
+ "stars_checked_at": "2026-10-01",
+ "icon": "assets/icons/breakdance-quicknav.png",
+ "icon_label": "QN",
+ "icon_background": "#f0f3f6"
+ },
+ {
+ "approved": true,
+ "slug": "daily-scripture",
+ "name": "Daily Scripture",
+ "description": "Daily Bible verses, flexible layouts, editor integrations and a compact dashboard widget.",
+ "description_de": "Tägliche Bibelverse, flexible Layouts, Editor-Integrationen und ein kompaktes Dashboard-Widget.",
+ "version": "0.16.4",
+ "repository": "deckerweb/daily-scripture",
+ "plugin_file": "daily-scripture/daily-scripture.php",
+ "download_url": "https://github.com/deckerweb/daily-scripture/releases/download/v0.16.4/daily-scripture-0.16.4.zip",
+ "sha256": "b702f85c0ce06bf3ac4352be82b1a6296c73d36aa83a61668f5050af5cd0dca4",
+ "requires_wp": "6.6",
+ "requires_php": "8.0",
+ "category": "Content",
+ "dependencies": [],
+ "github_stars": 1,
+ "stars_checked_at": "2026-10-01",
+ "icon": "assets/icons/daily-scripture.png",
+ "icon_label": "QN",
+ "icon_background": "#f0f3f6",
+ "network_activation": false
+ },
+ {
+ "approved": true,
+ "slug": "bricks-quicknav",
+ "name": "Bricks QuickNav",
+ "description": "Quick access to Bricks templates, pages and settings from the toolbar.",
+ "description_de": "Schneller Zugriff auf Bricks-Templates, Seiten und Einstellungen über die Toolbar.",
+ "version": "1.0.0",
+ "repository": "deckerweb/bricks-quicknav",
+ "plugin_file": "bricks-quicknav/bricks-quicknav.php",
+ "download_url": "https://github.com/deckerweb/bricks-quicknav/releases/download/v1.0.0/bricks-quicknav.zip",
+ "sha256": "23a6bc16a4023783790efebc2a07ed2e59d11f5eadf32f6a19919981e4c8b33d",
+ "requires_wp": "6.7",
+ "requires_php": "7.4",
+ "category": "Bricks",
+ "dependencies": [
+ {
+ "name": "Bricks Builder",
+ "plugin_file": "bricks/theme.php",
+ "min_version": "",
+ "detector": "bricks",
+ "url": "https://bricksbuilder.io/"
+ }
+ ],
+ "github_stars": 7,
+ "stars_checked_at": "2026-10-01",
+ "icon": "assets/icons/bricks-quicknav.png",
+ "icon_label": "QN",
+ "icon_background": "#f0f3f6"
+ },
+ {
+ "approved": true,
+ "slug": "oxygen-quicknav",
+ "name": "Oxygen QuickNav",
+ "description": "Quick access to templates, components and settings for Oxygen 6 and newer.",
+ "description_de": "Schneller Zugriff auf Templates, Komponenten und Einstellungen für Oxygen 6 und neuer.",
+ "version": "1.0.0",
+ "repository": "deckerweb/oxygen-quicknav",
+ "plugin_file": "oxygen-quicknav/oxygen-quicknav.php",
+ "download_url": "https://github.com/deckerweb/oxygen-quicknav/releases/download/v1.0.0/oxygen-quicknav.zip",
+ "sha256": "3cd3a70d578d75fb5786b54e9dc5df60771cd63d9785234fe12eba19d218be3c",
+ "requires_wp": "6.7",
+ "requires_php": "7.4",
+ "category": "Oxygen",
+ "dependencies": [
+ {
+ "name": "Oxygen Builder 6+",
+ "plugin_file": "oxygen/plugin.php",
+ "min_version": "",
+ "detector": "oxygen",
+ "url": "https://oxygenbuilder.com/"
+ }
+ ],
+ "github_stars": 1,
+ "stars_checked_at": "2026-10-01",
+ "icon_label": "OQ",
+ "icon_background": "#eef1f8"
+ },
+ {
+ "approved": true,
+ "slug": "advanced-scripts-quicknav",
+ "name": "Advanced Scripts QuickNav",
+ "description": "Reach your Advanced Scripts snippets and tools directly from the toolbar.",
+ "description_de": "Advanced-Scripts-Snippets und Werkzeuge direkt über die Toolbar erreichen.",
+ "version": "1.1.0",
+ "repository": "deckerweb/advanced-scripts-quicknav",
+ "plugin_file": "advanced-scripts-quicknav/advanced-scripts-quicknav.php",
+ "download_url": "https://github.com/deckerweb/advanced-scripts-quicknav/releases/download/v1.1.0/advanced-scripts-quicknav.zip",
+ "sha256": "33678b2fcd310f21463c1e04bdd9f8b591008e28651a4dca129b5a7022ca2eba",
+ "requires_wp": "6.7",
+ "requires_php": "7.4",
+ "category": "Scripts",
+ "dependencies": [
+ {
+ "name": "Advanced Scripts",
+ "plugin_file": "advanced-scripts/advanced-scripts.php",
+ "min_version": "",
+ "detector": "advanced_scripts",
+ "url": "https://cleanplugins.com/"
+ }
+ ],
+ "github_stars": 1,
+ "stars_checked_at": "2026-10-01",
+ "icon": "assets/icons/advanced-scripts-quicknav.png",
+ "icon_label": "QN",
+ "icon_background": "#f0f3f6"
+ },
+ {
+ "approved": true,
+ "slug": "quick-edit-featured-image",
+ "name": "Quick Edit Featured Image",
+ "description": "Set or replace featured images directly in Quick Edit.",
+ "description_de": "Beitragsbilder direkt über Quick Edit setzen oder austauschen.",
+ "version": "1.3.0",
+ "repository": "deckerweb/quick-edit-featured-image",
+ "plugin_file": "quick-edit-featured-image/quick-edit-featured-image.php",
+ "download_url": "https://github.com/deckerweb/quick-edit-featured-image/releases/download/v1.3.0/quick-edit-featured-image.zip",
+ "sha256": "ffb0ecd37a16b73c93316ec4154c1d36bdf4d0a9f3b1b6c81762ff1a8e42d9b5",
+ "requires_wp": "6.7",
+ "requires_php": "7.4",
+ "category": "Content",
+ "dependencies": [],
+ "github_stars": 4,
+ "stars_checked_at": "2026-10-01",
+ "icon": "assets/icons/quick-edit-featured-image.png",
+ "icon_label": "QN",
+ "icon_background": "#f0f3f6"
+ },
+ {
+ "approved": true,
+ "slug": "shortcode-item-updated",
+ "name": "Shortcode Item Updated",
+ "description": "Show the last updated date and time of posts or other content through a shortcode.",
+ "description_de": "Datum und Uhrzeit der letzten Inhaltsänderung per Shortcode anzeigen.",
+ "version": "2.2.0",
+ "repository": "deckerweb/shortcode-item-updated",
+ "plugin_file": "shortcode-item-updated/shortcode-item-updated.php",
+ "download_url": "https://github.com/deckerweb/shortcode-item-updated/releases/download/v2.2.0/shortcode-item-updated.zip",
+ "sha256": "a300cb4dc9c437b1aa126e7148266ea52f080072ed60565fee5117d608776d4a",
+ "requires_wp": "6.7",
+ "requires_php": "7.4",
+ "category": "Content",
+ "dependencies": [],
+ "github_stars": 4,
+ "stars_checked_at": "2026-10-01",
+ "icon_label": "SU",
+ "icon_background": "#f4f0e9"
+ },
+ {
+ "approved": true,
+ "slug": "builder-list-pages",
+ "name": "Builder List Pages",
+ "description": "Find pages and other content edited with your page builder through filtered list views.",
+ "description_de": "Mit deinem Page Builder bearbeitete Seiten und Inhalte über gefilterte Listen finden.",
+ "version": "1.0.0",
+ "repository": "deckerweb/builder-list-pages",
+ "plugin_file": "builder-list-pages/builder-list-pages.php",
+ "download_url": "https://github.com/deckerweb/builder-list-pages/releases/download/v1.0.0/builder-list-pages.zip",
+ "sha256": "7a858fee0d6430707c15bf92e3bff4a4f56539f3d240a2ac2c0598f072a840d4",
+ "requires_wp": "6.7",
+ "requires_php": "7.4",
+ "category": "Builder",
+ "dependencies": [],
+ "github_stars": 3,
+ "stars_checked_at": "2026-10-01",
+ "icon": "assets/icons/builder-list-pages.png",
+ "icon_label": "QN",
+ "icon_background": "#f0f3f6"
+ }
+ ]
+}
diff --git a/includes/deckerweb-plugin-library/runtime.php b/includes/deckerweb-plugin-library/runtime.php
new file mode 100644
index 0000000..a7ef494
--- /dev/null
+++ b/includes/deckerweb-plugin-library/runtime.php
@@ -0,0 +1,11 @@
+register();
+ return $runtime;
+};
diff --git a/includes/deckerweb-plugin-library/src/Catalog.php b/includes/deckerweb-plugin-library/src/Catalog.php
new file mode 100644
index 0000000..60c1810
--- /dev/null
+++ b/includes/deckerweb-plugin-library/src/Catalog.php
@@ -0,0 +1,114 @@
+dir = $dir; }
+
+ /** Only first-party catalog endpoints; HTTP redirects are deliberately disabled. */
+ public static function trusted_source( string $url ): bool {
+ $p = wp_parse_url( $url );
+ if ( ! is_array( $p ) || ( $p['scheme'] ?? '' ) !== 'https' || isset( $p['user'], $p['pass'] ) || isset( $p['port'] ) || isset( $p['query'] ) || isset( $p['fragment'] ) ) { return false; }
+ if ( isset( $p['user'] ) || isset( $p['pass'] ) ) { return false; }
+ $host = strtolower( $p['host'] ?? '' );
+ $path = $p['path'] ?? '';
+ if ( strpos( $path, '..' ) !== false || strpos( $path, '%' ) !== false ) { return false; }
+ return ( in_array( $host, [ 'deckerweb.de', 'www.deckerweb.de' ], true ) && preg_match( '~^/[a-zA-Z0-9_./-]+\.json$~D', $path ) )
+ || ( $host === 'raw.githubusercontent.com' && preg_match( '~^/deckerweb/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_./-]+\.json$~D', $path ) );
+ }
+
+ public static function download_url( string $url, string $repo ): bool {
+ return (bool) preg_match( '~^https://github\.com/' . preg_quote( $repo, '~' ) . '/releases/download/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+\.zip$~D', $url );
+ }
+
+ /** Validate the whole document atomically, including every dependency. */
+ public static function validate( $data ) {
+ if ( ! is_array( $data ) || ( $data['schema_version'] ?? null ) !== 1 || ! isset( $data['plugins'] ) || ! is_array( $data['plugins'] ) || count( $data['plugins'] ) > 100 ) {
+ return new \WP_Error( 'dwl_catalog', 'Invalid catalog schema.' );
+ }
+ $result = [];
+ foreach ( $data['plugins'] as $entry ) {
+ if ( ! is_array( $entry ) || ! isset( $entry['approved'] ) || ! is_bool( $entry['approved'] ) ) { return new \WP_Error( 'dwl_catalog', 'Invalid approval flag.' ); }
+ if ( ! $entry['approved'] ) { continue; }
+ foreach ( [ 'slug', 'name', 'description', 'version', 'repository', 'plugin_file', 'download_url', 'sha256', 'requires_wp', 'requires_php' ] as $field ) {
+ if ( ! isset( $entry[$field] ) || ! is_string( $entry[$field] ) || strlen( $entry[$field] ) > 2000 ) { return new \WP_Error( 'dwl_catalog', 'Invalid catalog field: ' . $field ); }
+ }
+ $slug = $entry['slug'];
+ if ( ! preg_match( '/^[a-z0-9]+(?:-[a-z0-9]+)*$/D', $slug ) || isset( $result[$slug] )
+ || ! preg_match( '~^deckerweb/[a-zA-Z0-9_.-]+$~D', $entry['repository'] )
+ || ! preg_match( '~^' . preg_quote( $slug, '~' ) . '/[a-zA-Z0-9_-]+\.php$~D', $entry['plugin_file'] )
+ || ! self::download_url( $entry['download_url'], $entry['repository'] )
+ || ! preg_match( '/^[a-f0-9]{64}$/D', $entry['sha256'] )
+ || ! preg_match( '/^\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.-]+)?$/D', $entry['version'] ) ) {
+ return new \WP_Error( 'dwl_catalog', 'Invalid identity or release metadata.' );
+ }
+ foreach ( [ 'requires_wp', 'requires_php' ] as $field ) {
+ if ( ! preg_match( '/^\d+\.\d+(?:\.\d+)?$/D', $entry[$field] ) ) { return new \WP_Error( 'dwl_catalog', 'Invalid platform version.' ); }
+ }
+ if ( trim( $entry['name'] ) === '' || trim( $entry['description'] ) === '' ) { return new \WP_Error( 'dwl_catalog', 'Missing display text.' ); }
+ $dependencies = $entry['dependencies'] ?? [];
+ if ( ! is_array( $dependencies ) || count( $dependencies ) > 10 ) { return new \WP_Error( 'dwl_catalog', 'Invalid dependencies.' ); }
+ foreach ( $dependencies as $d ) {
+ if ( ! is_array( $d ) || ! is_string( $d['name'] ?? null ) || strlen( $d['name'] ) > 150
+ || ! is_string( $d['plugin_file'] ?? null ) || ! preg_match( '~^[a-z0-9-]+/[a-zA-Z0-9_-]+\.php$~D', $d['plugin_file'] )
+ || ! is_string( $d['min_version'] ?? null ) || ! preg_match( '/^(?:\d+\.\d+(?:\.\d+)?)?$/D', $d['min_version'] )
+ || ! in_array( $d['detector'] ?? '', [ '', 'breakdance', 'bricks', 'oxygen', 'advanced_scripts' ], true ) ) { return new \WP_Error( 'dwl_catalog', 'Invalid dependency metadata.' ); }
+ if ( isset( $d['url'] ) && ( ! is_string( $d['url'] ) || ! preg_match( '~^https://(?:breakdance\.com|bricksbuilder\.io|oxygenbuilder\.com|cleanplugins\.com|github\.com/deckerweb|deckerweb\.de)/[a-zA-Z0-9_./-]*$~D', $d['url'] ) ) ) { return new \WP_Error( 'dwl_catalog', 'Invalid dependency URL.' ); }
+ }
+ foreach ( [ 'name_de', 'description_de', 'category' ] as $field ) {
+ if ( isset( $entry[$field] ) && ( ! is_string( $entry[$field] ) || strlen( $entry[$field] ) > 2000 ) ) { return new \WP_Error( 'dwl_catalog', 'Invalid display field.' ); }
+ }
+
+ if ( isset( $entry['icon'] ) && ( ! is_string( $entry['icon'] ) || ! preg_match( '~^assets/icons/[a-z0-9-]+\.png$~D', $entry['icon'] ) ) ) { return new \WP_Error( 'dwl_catalog', 'Invalid local icon.' ); }
+ if ( isset( $entry['icon_label'] ) && ( ! is_string( $entry['icon_label'] ) || ! preg_match( '/^[A-Z0-9]{1,3}$/D', $entry['icon_label'] ) ) ) { return new \WP_Error( 'dwl_catalog', 'Invalid icon label.' ); }
+ if ( isset( $entry['icon_background'] ) && ( ! is_string( $entry['icon_background'] ) || ! preg_match( '/^#[a-f0-9]{6}$/D', $entry['icon_background'] ) ) ) { return new \WP_Error( 'dwl_catalog', 'Invalid icon color.' ); }
+ if ( isset( $entry['github_stars'] ) && ( ! is_int( $entry['github_stars'] ) || $entry['github_stars'] < 0 ) ) { return new \WP_Error( 'dwl_catalog', 'Invalid star count.' ); }
+ if ( isset( $entry['stars_checked_at'] ) && ( ! is_string( $entry['stars_checked_at'] ) || ! preg_match( '/^\d{4}-\d{2}-\d{2}$/D', $entry['stars_checked_at'] ) ) ) { return new \WP_Error( 'dwl_catalog', 'Invalid stars date.' ); }
+ if ( isset( $entry['network_activation'] ) && ! is_bool( $entry['network_activation'] ) ) { return new \WP_Error( 'dwl_catalog', 'Invalid network policy.' ); }
+ $entry['dependencies'] = $dependencies;
+ $result[$slug] = $entry;
+ }
+ return $result;
+ }
+
+ /** Cached reads are cheap. Mutations can require a successful fresh remote read. */
+ public function entries( bool $fresh = false ) {
+ $settings = Library::settings();
+ $url = $settings['catalog_url'];
+ if ( $settings['online'] && ! self::trusted_source( $url ) ) {
+ if ( $fresh ) { return new \WP_Error( 'dwl_source', Library::t( 'The online catalog source is not trusted.', 'Die Online-Katalogquelle ist nicht vertrauenswürdig.' ) ); }
+ $this->status = 'offline';
+ }
+ if ( $settings['online'] && self::trusted_source( $url ) ) {
+ $key = 'dwl_catalog_' . md5( $url );
+ $cache = get_site_transient( $key );
+ if ( ! $fresh && is_array( $cache ) && isset( $cache['data'] ) ) {
+ $this->status = 'online';
+ return self::validate( $cache['data'] );
+ }
+ if ( $fresh || ! get_site_transient( $key . '_retry' ) ) {
+ $response = wp_safe_remote_get( $url, [ 'timeout' => 6, 'redirection' => 0, 'limit_response_size' => 262145, 'headers' => [ 'Accept' => 'application/json' ] ] );
+ $body = is_wp_error( $response ) ? '' : wp_remote_retrieve_body( $response );
+ $data = json_decode( $body, true );
+ $valid = self::validate( $data );
+ if ( ! is_wp_error( $response ) && wp_remote_retrieve_response_code( $response ) === 200 && strlen( $body ) <= 262144 && ! is_wp_error( $valid ) ) {
+ set_site_transient( $key, [ 'data' => $data ], 12 * HOUR_IN_SECONDS );
+ set_site_transient( $key . '_last', [ 'data' => $data ], 48 * HOUR_IN_SECONDS );
+ delete_site_transient( $key . '_retry' );
+ $this->status = 'online';
+ return $valid;
+ }
+ set_site_transient( $key . '_retry', true, 15 * MINUTE_IN_SECONDS );
+ }
+ if ( $fresh ) { return new \WP_Error( 'dwl_offline', Library::t( 'The catalog could not be verified. Please try again later.', 'Der Katalog konnte nicht geprüft werden. Bitte später erneut versuchen.' ) ); }
+ $last = get_site_transient( $key . '_last' );
+ $this->status = 'offline';
+ if ( is_array( $last ) && isset( $last['data'] ) ) { return self::validate( $last['data'] ); }
+ }
+ $data = json_decode( (string) file_get_contents( $this->dir . '/catalog.json' ), true );
+ return self::validate( $data );
+ }
+}
diff --git a/includes/deckerweb-plugin-library/src/Library.php b/includes/deckerweb-plugin-library/src/Library.php
new file mode 100644
index 0000000..30b30a4
--- /dev/null
+++ b/includes/deckerweb-plugin-library/src/Library.php
@@ -0,0 +1,309 @@
+chosen = $chosen; $this->hosts = $hosts;
+ $this->catalog = new Catalog( $chosen['dir'] );
+ }
+
+ /** English source and a small built-in German dictionary, independent of host domains. */
+ public static function t( string $en, string $de ): string {
+ return str_starts_with( determine_locale(), 'de' ) ? $de : $en;
+ }
+ public static function settings(): array {
+ $saved = get_site_option( self::OPTION, [] );
+ $saved = is_array( $saved ) ? $saved : [];
+ return [ 'enabled' => ! isset( $saved['enabled'] ) || ! empty( $saved['enabled'] ), 'online' => ! empty( $saved['online'] ), 'catalog_url' => is_string( $saved['catalog_url'] ?? null ) ? $saved['catalog_url'] : '' ];
+ }
+ public function register(): void {
+ add_filter( 'install_plugins_tabs', [ $this, 'tabs' ] );
+ add_action( 'install_plugins_deckerweb', [ $this, 'render' ] );
+ add_action( 'admin_notices', [ $this, 'notice' ] );
+ add_action( 'network_admin_notices', [ $this, 'notice' ] );
+ add_action( 'admin_enqueue_scripts', [ $this, 'assets' ] );
+ add_action( 'admin_menu', [ $this, 'menu' ] );
+ add_action( 'network_admin_menu', [ $this, 'menu' ] );
+ add_action( 'admin_post_dwl_preferences', [ $this, 'preferences' ] );
+ add_action( 'admin_post_dwl_dismiss', [ $this, 'dismiss' ] );
+ add_action( 'admin_post_dwl_install', [ $this, 'install' ] );
+ add_action( 'admin_post_dwl_activate', [ $this, 'activate' ] );
+ add_action( 'admin_post_dwl_refresh', [ $this, 'refresh' ] );
+ add_action( 'activate_plugin', [ $this, 'activation_guard' ], 0, 2 );
+ add_filter( 'pre_set_site_transient_update_plugins', [ $this, 'updates' ] );
+ add_filter( 'upgrader_pre_download', [ $this, 'update_download' ], 10, 4 );
+ foreach ( $this->hosts as $host ) {
+ add_filter( 'plugin_action_links_' . plugin_basename( $host['host'] ), [ $this, 'links' ] );
+ add_filter( 'network_admin_plugin_action_links_' . plugin_basename( $host['host'] ), [ $this, 'links' ] );
+ }
+ }
+ public function links( array $links ): array {
+ if ( current_user_can( 'install_plugins' ) ) { $links['deckerweb_library'] = '' . esc_html( self::t( 'More by deckerweb', 'Mehr von deckerweb' ) ) . ' '; }
+ return $links;
+ }
+ public function tabs( array $tabs ): array {
+ if ( self::settings()['enabled'] && current_user_can( 'install_plugins' ) ) { $tabs['deckerweb'] = 'deckerweb'; }
+ return $tabs;
+ }
+ public function assets( string $hook ): void {
+ if ( ! current_user_can( 'install_plugins' ) ) { return; }
+ if ( $hook === 'plugin-install.php' && ( $_GET['tab'] ?? '' ) === 'deckerweb' ) {
+ wp_enqueue_style( 'deckerweb-plugin-library', plugins_url( 'assets/library.css', $this->chosen['dir'] . '/bootstrap.php' ), [], self::VERSION );
+ }
+ }
+ public function notice(): void {
+ global $pagenow;
+ if ( $pagenow !== 'plugin-install.php' || ( $_GET['tab'] ?? '' ) === 'deckerweb' || ! self::settings()['enabled'] || ! current_user_can( 'install_plugins' ) || get_user_meta( get_current_user_id(), 'deckerweb_library_intro_seen_v1', true ) ) { return; }
+ // Once per administrator, not once per request and not once per host plugin.
+ update_user_meta( get_current_user_id(), 'deckerweb_library_intro_seen_v1', 1 );
+ echo '' . esc_html( self::t( 'Discover more plugins by deckerweb', 'Weitere Plugins von deckerweb entdecken' ) ) . ' ' . esc_html( self::t( 'You already use one of my plugins. Find selected additions in the deckerweb tab.', 'Du nutzt bereits ein Plugin von mir. Ausgewählte Ergänzungen findest du im deckerweb-Tab.' ) ) . ' ' . esc_html( self::t( 'View plugins', 'Plugins ansehen' ) ) . '
';
+ $this->form( 'dismiss', '', self::t( 'Dismiss permanently', 'Dauerhaft schließen' ), 'button-link', [ 'location' => 'installer' ] );
+ echo '
';
+ }
+ public function menu(): void {
+ $cap = is_network_admin() ? 'manage_network_options' : 'manage_options';
+ if ( is_multisite() && ! is_network_admin() ) { return; }
+ add_submenu_page( is_network_admin() ? 'settings.php' : 'options-general.php', 'deckerweb Library', 'deckerweb Library', $cap, 'deckerweb-library', [ $this, 'settings_page' ] );
+ }
+ private function catalog_url(): string { return ( $this->network ? network_admin_url( 'plugin-install.php?tab=deckerweb' ) : self_admin_url( 'plugin-install.php?tab=deckerweb' ) ); }
+ private function settings_url(): string { return is_multisite() ? network_admin_url( 'settings.php?page=deckerweb-library' ) : admin_url( 'options-general.php?page=deckerweb-library' ); }
+ private function action_url( string $action, string $slug = '' ): string {
+ return add_query_arg( [ 'action' => 'dwl_' . $action, 'slug' => $slug, 'network' => ( is_network_admin() || $this->network ) ? '1' : '0' ], admin_url( 'admin-post.php' ) );
+ }
+ private function form( string $action, string $slug, string $label, string $class = 'button', array $extra = [] ): void {
+ echo '';
+ }
+ private static function text( array $entry, string $key ): string {
+ return str_starts_with( determine_locale(), 'de' ) && isset( $entry[$key . '_de'] ) ? $entry[$key . '_de'] : $entry[$key];
+ }
+
+ private function icon( array $entry ): string {
+ // Only bundled PNG files; catalog metadata never triggers remote image requests.
+ $file = $entry['icon'] ?? '';
+ if ( $file && is_file( $this->chosen['dir'] . '/' . $file ) ) {
+ return ' ';
+ }
+ return '' . esc_html( $entry['icon_label'] ?? 'DW' ) . ' ';
+ }
+ public function render(): void {
+ if ( ! current_user_can( 'install_plugins' ) ) { wp_die( esc_html( self::t( 'Permission denied.', 'Keine Berechtigung.' ) ), '', [ 'response' => 403 ] ); }
+ if ( ! self::settings()['enabled'] ) {
+ echo '' . esc_html( self::t( 'The catalog is hidden.', 'Der Katalog ist ausgeblendet.' ) ) . ' ' . esc_html( self::t( 'Library settings', 'Library-Einstellungen' ) ) . '
'; return;
+ }
+ update_user_meta( get_current_user_id(), 'deckerweb_library_intro_seen_v1', 1 );
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
+ $entries = $this->catalog->entries();
+ if ( is_wp_error( $entries ) ) { echo '' . esc_html( $entries->get_error_message() ) . '
'; return; }
+ $plugins = get_plugins();
+ $search = isset( $_GET['dwl_search'] ) && is_string( $_GET['dwl_search'] ) ? sanitize_text_field( wp_unslash( $_GET['dwl_search'] ) ) : '';
+ $compatible = ! empty( $_GET['dwl_compatible'] );
+ echo '';
+ if ( isset( $_GET['dwl_done'] ) ) { echo '
' . esc_html( self::t( 'Done. The plugin is active.', 'Erledigt. Das Plugin ist aktiv.' ) ) . '
'; }
+ if ( $this->catalog->status === 'offline' ) { echo '
' . esc_html( self::t( 'The online catalog is unavailable. Showing cached or bundled entries; online approval is checked again before installation.', 'Der Online-Katalog ist nicht erreichbar. Angezeigt wird der gespeicherte oder mitgelieferte Katalog; vor der Installation wird die Online-Freigabe erneut geprüft.' ) ) . '
'; }
+ echo '
';
+ echo '
'; $count = 0;
+ foreach ( $entries as $entry ) {
+ $name = self::text( $entry, 'name' ); $description = self::text( $entry, 'description' );
+ $issues = Requirements::check( $entry, $plugins );
+ if ( $search !== '' && stripos( remove_accents( $name . ' ' . $description ), remove_accents( $search ) ) === false ) { continue; }
+ if ( $compatible && $issues ) { continue; }
+ $count++;
+ $installed = isset( $plugins[$entry['plugin_file']] );
+ $active = $installed && ( is_network_admin() ? is_plugin_active_for_network( $entry['plugin_file'] ) : is_plugin_active( $entry['plugin_file'] ) );
+ echo '
' . $this->icon( $entry ) . '
' . esc_html( $name ) . ' deckerweb · ' . esc_html( $entry['category'] ?? 'WordPress' ) . ' ' . esc_html( $description ) . '
';
+
+ if ( isset( $entry['github_stars'], $entry['stars_checked_at'] ) ) {
+ echo '
★ ' . esc_html( number_format_i18n( $entry['github_stars'] ) . ' GitHub Stars' ) . ' ' . esc_html( self::t( 'As of ', 'Stand: ' ) . $entry['stars_checked_at'] ) . '
';
+ }
+ if ( $active ) { echo '
' . esc_html( self::t( 'Already active', 'Bereits aktiv' ) ) . ' '; }
+ elseif ( $issues ) { echo '
' . esc_html( $installed ? self::t( 'Activate', 'Aktivieren' ) : self::t( 'Install now', 'Jetzt installieren' ) ) . ' '; }
+ elseif ( $installed && current_user_can( 'activate_plugin', $entry['plugin_file'] ) ) { $this->form( 'activate', $entry['slug'], is_network_admin() ? self::t( 'Network activate', 'Netzwerkweit aktivieren' ) : self::t( 'Activate', 'Aktivieren' ), 'button button-primary' ); }
+ elseif ( ! $installed ) { $this->form( 'install', $entry['slug'], self::t( 'Install now', 'Jetzt installieren' ), 'button' ); }
+ else { echo '
' . esc_html( self::t( 'Installed', 'Installiert' ) ) . ' '; }
+ if ( $issues ) { echo '
'; }
+ echo '
' . esc_html( self::t( 'Details & requirements', 'Details & Voraussetzungen' ) ) . ' WordPress ≥ ' . esc_html( $entry['requires_wp'] ) . ' · PHP ≥ ' . esc_html( $entry['requires_php'] ) . '
';
+ foreach ( $entry['dependencies'] as $dep ) { echo '' . esc_html( $dep['name'] . ( $dep['min_version'] !== '' ? ' ≥ ' . $dep['min_version'] : '' ) ) . ( isset( $dep['url'] ) ? ' · ' . esc_html( self::t( 'Product website', 'Produktwebsite' ) ) . ' ' : '' ) . '
'; }
+ echo '' . esc_html( self::t( 'Repository & documentation', 'Repository & Dokumentation' ) ) . '
' . esc_html( self::t( 'Release checksum is verified before installation. Activation is a separate step.', 'Vor der Installation wird die Release-Prüfsumme geprüft. Die Aktivierung erfolgt separat.' ) ) . '
' . esc_html( self::t( 'Source: GitHub', 'Quelle: GitHub' ) ) . ' ' . esc_html( $installed ? self::t( 'Installed: ', 'Installiert: ' ) . $plugins[$entry['plugin_file']]['Version'] : 'v' . $entry['version'] ) . ' ';
+ }
+ echo '
';
+ if ( ! $count ) { echo '
' . esc_html( self::t( 'No approved plugins match this selection.', 'Keine freigegebenen Plugins passen zu dieser Auswahl.' ) ) . '
'; }
+ echo '
' . esc_html( self::t( 'Library', 'Library' ) ) . ' ' . esc_html( self::VERSION ) . ' · ' . esc_html( self::t( 'Only approved releases', 'Nur freigegebene Releases' ) ) . ' ';
+ $this->form( 'preferences', '', self::t( 'Hide catalog', 'Katalog ausblenden' ), 'button-link', [ 'enabled' => '0', 'hide_only' => '1' ] );
+ if ( self::settings()['online'] ) { $this->form( 'refresh', '', self::t( 'Refresh catalog', 'Katalog aktualisieren' ), 'button-link' ); }
+ echo '
';
+ }
+
+ public function settings_page(): void {
+ $cap = is_multisite() ? 'manage_network_options' : 'manage_options';
+ if ( ! current_user_can( $cap ) ) { wp_die( 'Permission denied.', '', [ 'response' => 403 ] ); }
+ $s = self::settings();
+ echo '';
+ }
+ private function authorize( string $action, string $cap, string $slug = '' ): void {
+ if ( ( $_SERVER['REQUEST_METHOD'] ?? '' ) !== 'POST' ) { wp_die( 'POST required.', '', [ 'response' => 405 ] ); }
+ if ( ! current_user_can( $cap ) ) { wp_die( 'Permission denied.', '', [ 'response' => 403 ] ); }
+ $this->network = is_multisite() && ( $_REQUEST['network'] ?? '' ) === '1';
+ if ( $this->network && ! current_user_can( 'manage_network_plugins' ) ) { wp_die( 'Network permission denied.', '', [ 'response' => 403 ] ); }
+ check_admin_referer( 'dwl_' . $action . '_' . $slug );
+ }
+ private function slug(): string { return isset( $_REQUEST['slug'] ) && is_string( $_REQUEST['slug'] ) ? sanitize_key( wp_unslash( $_REQUEST['slug'] ) ) : ''; }
+ private function entry( string $slug, bool $fresh = true ): array {
+ if ( ! self::settings()['enabled'] ) { wp_die( esc_html( self::t( 'The catalog is disabled.', 'Der Katalog ist deaktiviert.' ) ), '', [ 'response' => 403 ] ); }
+ $entries = $this->catalog->entries( $fresh );
+ if ( is_wp_error( $entries ) ) { wp_die( esc_html( $entries->get_error_message() ) ); }
+ if ( ! isset( $entries[$slug] ) ) { wp_die( esc_html( self::t( 'This release is not approved.', 'Dieses Release ist nicht freigegeben.' ) ), '', [ 'response' => 404 ] ); }
+ return $entries[$slug];
+ }
+ private function enforce_requirements( array $entry, ?bool $network = null ): void {
+ $issues = Requirements::check( $entry, null, $network ?? $this->network );
+ if ( $issues ) { wp_die( esc_html( implode( ' ', $issues ) ) ); }
+ }
+ public function preferences(): void {
+ $this->authorize( 'preferences', is_multisite() ? 'manage_network_options' : 'manage_options' );
+ $s = self::settings();
+ if ( ! empty( $_POST['hide_only'] ) ) { $s['enabled'] = false; }
+ else {
+ $url = isset( $_POST['catalog_url'] ) && is_string( $_POST['catalog_url'] ) ? trim( wp_unslash( $_POST['catalog_url'] ) ) : '';
+ if ( $url !== '' && ! Catalog::trusted_source( $url ) ) { wp_die( esc_html( self::t( 'The catalog URL is not an allowed first-party HTTPS endpoint.', 'Die Katalog-URL ist keine erlaubte eigene HTTPS-Quelle.' ) ) ); }
+ if ( ! empty( $_POST['online'] ) && $url === '' ) { wp_die( esc_html( self::t( 'Enter a catalog URL first.', 'Bitte zuerst eine Katalog-URL eintragen.' ) ) ); }
+ $s = [ 'enabled' => ! empty( $_POST['enabled'] ), 'online' => ! empty( $_POST['online'] ), 'catalog_url' => $url ];
+ }
+ update_site_option( self::OPTION, $s );
+ wp_safe_redirect( add_query_arg( 'saved', '1', $this->settings_url() ) ); exit;
+ }
+ public function dismiss(): void {
+ $this->authorize( 'dismiss', 'install_plugins' );
+ update_user_meta( get_current_user_id(), 'deckerweb_library_intro_seen_v1', 1 );
+ wp_safe_redirect( $this->network ? network_admin_url( 'plugin-install.php' ) : admin_url( 'plugin-install.php' ) ); exit;
+ }
+ public function refresh(): void {
+ $this->authorize( 'refresh', 'install_plugins' );
+ $result = $this->catalog->entries( true );
+ if ( is_wp_error( $result ) ) { wp_die( esc_html( $result->get_error_message() ) ); }
+ wp_safe_redirect( $this->catalog_url() ); exit;
+ }
+ public function install(): void {
+ $slug = $this->slug(); $this->authorize( 'install', 'install_plugins', $slug );
+ $entry = $this->entry( $slug ); $this->enforce_requirements( $entry );
+ if ( is_dir( WP_PLUGIN_DIR . '/' . $entry['slug'] ) ) { wp_die( esc_html( self::t( 'This plugin directory already exists. Use the installed plugin; it will not be overwritten.', 'Dieses Plugin-Verzeichnis existiert bereits. Bitte das installierte Plugin verwenden; es wird nicht überschrieben.' ) ) ); }
+ require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
+ require_once ABSPATH . 'wp-admin/includes/file.php';
+ $title = self::t( 'Install deckerweb plugin', 'deckerweb-Plugin installieren' );
+ if ( $this->network && ! defined( 'WP_NETWORK_ADMIN' ) ) { define( 'WP_NETWORK_ADMIN', true ); }
+ // admin-post.php has no initialized sidebar/menu. Use WordPress's native
+ // standalone upgrader chrome instead of including admin-header prematurely.
+ require_once ABSPATH . 'wp-admin/includes/admin.php';
+ $GLOBALS['hook_suffix'] = 'deckerweb-library-install';
+ set_current_screen( $this->network ? 'plugin-install-network' : 'plugin-install' );
+ wp_enqueue_style( 'common' ); wp_enqueue_style( 'forms' );
+ wp_enqueue_style( 'deckerweb-plugin-library', plugins_url( 'assets/library.css', $this->chosen['dir'] . '/bootstrap.php' ), [], self::VERSION );
+ $body_class = static fn( string $classes ): string => $classes . ' dwl-install';
+ add_filter( 'admin_body_class', $body_class );
+ iframe_header( esc_html( $title ) );
+ remove_filter( 'admin_body_class', $body_class );
+ echo '
' . esc_html( $title ) . ' ';
+ $url = wp_nonce_url( $this->action_url( 'install', $slug ), 'dwl_install_' . $slug );
+ $credentials = request_filesystem_credentials( $url, '', false, WP_PLUGIN_DIR );
+ if ( $credentials === false ) { echo ''; iframe_footer(); return; }
+ if ( ! WP_Filesystem( $credentials, WP_PLUGIN_DIR ) ) { request_filesystem_credentials( $url, '', true, WP_PLUGIN_DIR ); echo ''; iframe_footer(); return; }
+ $package = Package::download( $entry );
+ if ( is_wp_error( $package ) ) { echo '' . esc_html( $package->get_error_message() ) . '
'; }
+ else {
+ $skin = new class( [ 'type' => 'web', 'url' => $url, 'nonce' => 'dwl_install_' . $slug, 'title' => $title, 'api' => (object) [ 'name' => $entry['name'], 'slug' => $entry['slug'], 'version' => $entry['version'] ] ] ) extends \Plugin_Installer_Skin {
+ // The standalone page already owns its heading and wrapper.
+ public function header() {}
+ public function footer() {}
+ };
+ $upgrader = new \Plugin_Upgrader( $skin );
+ $actions = function( array $links, $api, string $file ) use ( $entry ): array {
+ unset( $links['activate_plugin'], $links['network_activate'] );
+ if ( $file === $entry['plugin_file'] && ! Requirements::check( $entry, null, $this->network ) && current_user_can( 'activate_plugin', $file ) ) {
+ ob_start(); $this->form( 'activate', $entry['slug'], $this->network ? self::t( 'Network activate', 'Netzwerkweit aktivieren' ) : self::t( 'Activate', 'Aktivieren' ), 'button button-primary' ); $links['dwl_activate'] = ob_get_clean();
+ }
+ $links['dwl_catalog'] = '' . esc_html( self::t( 'Back to deckerweb', 'Zurück zu deckerweb' ) ) . ' ';
+ return $links;
+ };
+ add_filter( 'install_plugin_complete_actions', $actions, 10, 3 );
+ try { $result = $upgrader->install( $package, [ 'clear_update_cache' => false, 'overwrite_package' => false ] ); }
+ finally { remove_filter( 'install_plugin_complete_actions', $actions, 10 ); wp_delete_file( $package ); }
+ if ( $result === true ) {
+ $managed = get_site_option( self::MANAGED, [] ); $managed = is_array( $managed ) ? $managed : [];
+ $managed[$entry['plugin_file']] = $entry['repository']; update_site_option( self::MANAGED, $managed );
+ }
+ }
+ echo ''; iframe_footer();
+ }
+ public function activate(): void {
+ $slug = $this->slug(); $this->authorize( 'activate', 'activate_plugins', $slug );
+ $entry = $this->entry( $slug );
+ if ( ! current_user_can( 'activate_plugin', $entry['plugin_file'] ) ) { wp_die( 'Permission denied.', '', [ 'response' => 403 ] ); }
+ $this->enforce_requirements( $entry );
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
+ if ( ! is_file( WP_PLUGIN_DIR . '/' . $entry['plugin_file'] ) ) { wp_die( 'Plugin is not installed.' ); }
+ $result = activate_plugin( $entry['plugin_file'], '', $this->network, false );
+ if ( is_wp_error( $result ) ) { wp_die( esc_html( $result->get_error_message() ) ); }
+ wp_safe_redirect( add_query_arg( 'dwl_done', '1', $this->catalog_url() ) ); exit;
+ }
+ /** Cover activation from the ordinary Plugins page too, while this library is loaded. */
+ public function activation_guard( string $file, bool $network ): void {
+ $managed = get_site_option( self::MANAGED, [] );
+ $bundled = json_decode( (string) file_get_contents( $this->chosen['dir'] . '/catalog.json' ), true );
+ $known = array_column( $bundled['plugins'] ?? [], 'plugin_file' );
+ if ( ! in_array( $file, $known, true ) && ! isset( $managed[$file] ) ) { return; }
+ // Hiding discovery must never remove runtime dependency protection.
+ $entries = $this->catalog->entries();
+ if ( is_wp_error( $entries ) ) { return; }
+ foreach ( $entries as $entry ) { if ( $entry['plugin_file'] === $file ) { $this->enforce_requirements( $entry, $network ); return; } }
+ }
+ /** Only installed-through-library plugins lacking their own updater are managed here. */
+ public function updates( $transient ) {
+ if ( ! is_object( $transient ) || empty( $transient->checked ) ) { return $transient; }
+ $managed = get_site_option( self::MANAGED, [] );
+ if ( ! is_array( $managed ) || ! $managed ) { return $transient; }
+ $entries = $this->catalog->entries();
+ if ( is_wp_error( $entries ) ) { return $transient; }
+ require_once ABSPATH . 'wp-admin/includes/plugin.php'; $plugins = get_plugins();
+ foreach ( $entries as $e ) {
+ $file = $e['plugin_file'];
+ if ( ! isset( $managed[$file], $plugins[$file], $transient->checked[$file] ) || $managed[$file] !== $e['repository'] || ! empty( $plugins[$file]['UpdateURI'] ) || isset( $transient->response[$file] ) || ! version_compare( $e['version'], $transient->checked[$file], '>' ) ) { continue; }
+ $transient->response[$file] = (object) [ 'id' => 'https://github.com/' . $e['repository'], 'slug' => $e['slug'], 'plugin' => $file, 'new_version' => $e['version'], 'url' => 'https://github.com/' . $e['repository'], 'package' => $e['download_url'], 'requires' => $e['requires_wp'], 'requires_php' => $e['requires_php'], 'dwl_managed' => true ];
+ }
+ return $transient;
+ }
+ /** Native update downloads retain the same hash, archive and dependency checks. */
+ public function update_download( $reply, string $package, $upgrader, array $extra = [] ) {
+ $file = $extra['plugin'] ?? '';
+ $managed = get_site_option( self::MANAGED, [] );
+ if ( ! is_string( $file ) || ! is_array( $managed ) || ! isset( $managed[$file] ) ) { return $reply; }
+ $entries = $this->catalog->entries( true );
+ if ( is_wp_error( $entries ) ) { return $entries; }
+ foreach ( $entries as $entry ) {
+ if ( $entry['plugin_file'] !== $file || $entry['repository'] !== $managed[$file] ) { continue; }
+ // A native updater owned by the plugin must remain in control of its releases.
+ $plugins = get_plugins();
+ if ( ! empty( $plugins[$file]['UpdateURI'] ) ) { return $reply; }
+ if ( $package !== $entry['download_url'] ) { return new \WP_Error( 'dwl_release_changed', self::t( 'The approved release changed. Refresh updates first.', 'Das freigegebene Release hat sich geändert. Bitte zuerst die Updates aktualisieren.' ) ); }
+ $issues = Requirements::check( $entry, null, is_plugin_active_for_network( $file ) );
+ if ( $issues ) { return new \WP_Error( 'dwl_requirements', implode( ' ', $issues ) ); }
+ return Package::download( $entry );
+ }
+ return new \WP_Error( 'dwl_withdrawn', self::t( 'This release is no longer approved.', 'Dieses Release ist nicht mehr freigegeben.' ) );
+ }
+}
diff --git a/includes/deckerweb-plugin-library/src/Package.php b/includes/deckerweb-plugin-library/src/Package.php
new file mode 100644
index 0000000..4a993e6
--- /dev/null
+++ b/includes/deckerweb-plugin-library/src/Package.php
@@ -0,0 +1,73 @@
+ 45, 'redirection' => 5, 'stream' => true, 'filename' => $file,
+ 'limit_response_size' => self::MAX_DOWNLOAD + 1,
+ 'headers' => [ 'Accept' => 'application/octet-stream' ],
+ ] );
+ if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) !== 200 ) {
+ wp_delete_file( $file );
+ return new \WP_Error( 'dwl_download', Library::t( 'The GitHub release could not be downloaded.', 'Das GitHub-Release konnte nicht heruntergeladen werden.' ) );
+ }
+ $result = self::verify( $file, $entry );
+ wp_delete_file( $file );
+ return $result;
+ }
+
+ /** Return a new safe archive path; callers must delete it after use. */
+ public static function verify( string $file, array $entry ) {
+ require_once ABSPATH . 'wp-admin/includes/file.php';
+ if ( ! class_exists( '\ZipArchive' ) ) { return new \WP_Error( 'dwl_zip', 'PHP ZIP support is required.' ); }
+ if ( ! is_file( $file ) || filesize( $file ) > self::MAX_DOWNLOAD || ! hash_equals( $entry['sha256'], (string) hash_file( 'sha256', $file ) ) ) {
+ return new \WP_Error( 'dwl_hash', Library::t( 'The release checksum does not match. Installation stopped.', 'Die Prüfsumme des Releases stimmt nicht überein. Installation gestoppt.' ) );
+ }
+ $zip = new \ZipArchive();
+ if ( $zip->open( $file ) !== true ) { return new \WP_Error( 'dwl_zip', 'Invalid ZIP archive.' ); }
+ $names = []; $expanded = 0; $seen = [];
+ $error = '';
+ if ( $zip->numFiles > 3000 ) { $error = 'Archive contains too many files.'; }
+ for ( $i = 0; $i < $zip->numFiles && $error === ''; $i++ ) {
+ $stat = $zip->statIndex( $i );
+ if ( ! is_array( $stat ) ) { $error = 'Cannot read archive entry.'; break; }
+ $name = $stat['name'];
+ if ( strpos( $name, "\0" ) !== false || strpos( $name, '\\' ) !== false || preg_match( '~(^|/)\.\.?(/|$)~', $name ) || str_starts_with( $name, '/' ) ) { $error = 'Unsafe archive path.'; break; }
+ $expanded += (int) $stat['size'];
+ if ( $expanded > self::MAX_EXPANDED ) { $error = 'Expanded archive is too large.'; break; }
+ $opsys = 0; $attrs = 0;
+ $zip->getExternalAttributesIndex( $i, $opsys, $attrs );
+ if ( ( ( $attrs >> 16 ) & 0170000 ) === 0120000 ) { $error = 'Archive symlinks are not permitted.'; break; }
+ // Old release ZIPs may contain Finder resource forks; never install them.
+ if ( str_starts_with( $name, '__MACOSX/' ) || basename( $name ) === '.DS_Store' ) { continue; }
+ if ( ! str_starts_with( $name, $entry['slug'] . '/' ) || isset( $seen[$name] ) ) { $error = 'Release must contain exactly the approved plugin directory.'; break; }
+ $seen[$name] = true;
+ $names[] = $name;
+ }
+ $main = $zip->getFromName( $entry['plugin_file'] );
+ if ( $error === '' && ( ! is_string( $main ) || strlen( $main ) > 2097152 || ! preg_match( '/^[ \t\/*#@]*Version:\s*([^\r\n]+)/mi', substr( $main, 0, 8192 ), $match ) || trim( $match[1] ) !== $entry['version'] ) ) { $error = 'Plugin identity or version does not match the catalog.'; }
+ if ( $error === '' && ! preg_match( '/^[ \t\/*#@]*Plugin Name:\s*\S+/mi', substr( $main, 0, 8192 ) ) ) { $error = 'Missing plugin header.'; }
+ if ( $error !== '' ) { $zip->close(); return new \WP_Error( 'dwl_package', $error ); }
+ $safe_file = wp_tempnam( $entry['slug'] . '-verified.zip' );
+ $safe = new \ZipArchive();
+ if ( ! $safe_file || $safe->open( $safe_file, \ZipArchive::OVERWRITE ) !== true ) { $zip->close(); if ( $safe_file ) { wp_delete_file( $safe_file ); } return new \WP_Error( 'dwl_temp', 'Cannot create verified archive.' ); }
+ foreach ( $names as $name ) {
+ if ( str_ends_with( $name, '/' ) ) { $ok = $safe->addEmptyDir( rtrim( $name, '/' ) ); }
+ else { $content = $zip->getFromName( $name ); $ok = is_string( $content ) && $safe->addFromString( $name, $content ); }
+ if ( ! $ok ) { $error = 'Cannot normalize archive.'; break; }
+ }
+ $closed = $safe->close(); $zip->close();
+ if ( $error !== '' || ! $closed ) { wp_delete_file( $safe_file ); return new \WP_Error( 'dwl_zip', $error ?: 'Cannot finalize archive.' ); }
+ return $safe_file;
+ }
+}
diff --git a/includes/deckerweb-plugin-library/src/Requirements.php b/includes/deckerweb-plugin-library/src/Requirements.php
new file mode 100644
index 0000000..29a4655
--- /dev/null
+++ b/includes/deckerweb-plugin-library/src/Requirements.php
@@ -0,0 +1,61 @@
+exists() && $theme->get( 'Name' ) === 'Bricks';
+ $active = $present && defined( 'BRICKS_VERSION' ) && function_exists( 'bricks_is_builder' );
+ $version = $theme->get( 'Version' );
+ }
+ if ( $detector === 'oxygen' || $detector === 'advanced_scripts' ) {
+ $loaded = $detector === 'oxygen'
+ ? ( defined( 'BREAKDANCE_MODE' ) && constant( 'BREAKDANCE_MODE' ) === 'oxygen' && defined( '__BREAKDANCE_VERSION' ) )
+ : ( defined( 'EPXADVSC_VER' ) && function_exists( 'cpas_scripts_manager' ) );
+ // Runtime markers also support renamed plugin directories. On a network,
+ // a matching active network plugin is required in addition to the marker.
+ foreach ( $plugins as $candidate => $info ) {
+ $expected = $detector === 'oxygen' ? 'Oxygen' : 'Advanced Scripts';
+ if ( isset( $info['Name'] ) && $info['Name'] === $expected ) {
+ $present = true;
+ if ( $network && is_plugin_active_for_network( $candidate ) ) { $active = true; }
+ }
+ }
+ if ( ! $network && $loaded ) { $present = true; $active = true; }
+ $active = $active && $loaded;
+ if ( $loaded ) { $version = (string) constant( $detector === 'oxygen' ? '__BREAKDANCE_VERSION' : 'EPXADVSC_VER' ); }
+ }
+ if ( ! $present ) { $issues[] = sprintf( Library::t( '%s is missing. Install and activate it first.', '%s fehlt. Bitte zuerst installieren und aktivieren.' ), $dep['name'] ); }
+ elseif ( ! $active ) { $issues[] = sprintf( $network ? Library::t( '%s must be network activated first.', '%s muss zuerst netzwerkweit aktiviert werden.' ) : Library::t( '%s is installed but inactive. Activate it first.', '%s ist installiert, aber inaktiv. Bitte zuerst aktivieren.' ), $dep['name'] ); }
+ elseif ( $dep['min_version'] !== '' && ( $version === '' || version_compare( $version, $dep['min_version'], '<' ) ) ) { $issues[] = sprintf( Library::t( 'Update %1$s to version %2$s or newer.', '%1$s bitte auf Version %2$s oder neuer aktualisieren.' ), $dep['name'], $dep['min_version'] ); }
+ }
+ return $issues;
+ }
+}
diff --git a/includes/deckerweb-plugin-library/version.php b/includes/deckerweb-plugin-library/version.php
new file mode 100644
index 0000000..c96f147
--- /dev/null
+++ b/includes/deckerweb-plugin-library/version.php
@@ -0,0 +1,3 @@
+NULL,'plural-forms'=>'nplurals=2; plural=(n != 1);','language'=>'de_DE','pot-creation-date'=>'2025-04-05 13:22+0200','po-revision-date'=>'2025-04-05 13:22+0200','translation-revision-date'=>'2025-04-05 13:22+0200','project-id-version'=>'Advanced Scripts QuickNav','x-generator'=>'Poedit 3.6','messages'=>['Script/ Snippet scope: everywhere (global)' . "\4" . 'everywhere'=>'überall','Script/ Snippet scope: front-end' . "\4" . 'front-end'=>'frontend','Scripts'=>'Skripte','SAFE MODE is active 🔥'=>'SAFE MODE ist aktiv 🔥','Advanced Scripts Safe Mode active 🔥'=>'Advanced Scripts abgesicherter Modus aktiv 🔥','SCRIPT_DEBUG is on ⚠'=>'SCRIPT_DEBUG ist eingeschaltet ⚠','Active Scripts'=>'Aktive Skripte','Inactive Scripts'=>'Inaktive Skripte','Scripts by Folder'=>'Skripte nach Ordnern','active'=>'aktiv','inactive'=>'inaktiv','Add New'=>'Neu hinzufügen','Code Script'=>'Code/ Skript','System Dashboard'=>'System-Dashboard','Site Health Info'=>'Website-Zustand Bericht','Variable Inspector'=>'Variablen-Inspektor','Debug Log Manager'=>'Debug-Log Manager','File Manager'=>'Dateimanager','Error Logs'=>'Fehlerprotokolle','Adminer: Manage DB'=>'Adminer: DB verwalten','Tools'=>'Werkzeuge','Find Snippets'=>'Snippets finden','Code Snippets Cloud'=>'Code Snippets Cloud','WP Snippets Library'=>'WP Snippets Bibliothek','Codes by Web Squadron'=>'Codes von Web Squadron','WP SnippetClub Archive'=>'WP SnippetClub Archiv','Snippets Library by dPlugins'=>'Snippet-Bibliothek von dPlugins','WPCodeBin by WPCodeBox'=>'WPCodeBin von WPCodeBox','Snippets Library by WPCode'=>'Snippet-Bibliothek von WPCode','Links'=>'Links','Advanced Scripts'=>'Advanced Scripts','Emergency Fixes 🔥'=>'Notfallkorrekturen 🔥','Clean Plugins Blog'=>'Clean Plugins Blog','CP YouTube Channel'=>'CP YouTube-Kanal','CP Facbook Group (official)'=>'CP Facbook Gruppe (offiziell)','About'=>'Über','Author: David Decker'=>'Autor: David Decker','Plugin on GitHub'=>'Plugin auf GitHub','Buy Me a Coffee'=>'Kaufe mir einen Kaffee','Site Health Debug info' . "\4" . 'Undefined'=>'Nicht definiert','Site Health Debug info' . "\4" . 'Enabled'=>'Aktiviert','Site Health Debug info' . "\4" . 'Disabled'=>'Deaktiviert','Site Health Debug info' . "\4" . 'value'=>'Wert','Advanced Scripts QuickNav'=>'Advanced Scripts QuickNav','Plugin'=>'Plugin','Plugin version'=>'Pluginversion','WordPress Install Type'=>'WordPress Installationstyp','Multisite install'=>'Multisite-Installation','Single Site install'=>'Single-Site-Installation','Plugin not installed'=>'Plugin nicht installiert','Plugins page listing' . "\4" . 'Donate'=>'Spenden','Plugins page listing' . "\4" . 'Join our Newsletter'=>'Jetzt Newsletter','https://github.com/deckerweb/advanced-scripts-quicknav'=>'https://github.com/deckerweb/advanced-scripts-quicknav','For Script and Code Snippets enthusiasts: Adds a quick-access navigator (aka QuickNav) to the WordPress Admin Bar (Toolbar). It allows easy access to your Scripts/ Code Snippets listed by Active, Inactive or Folder group. Safe Mode is supported. Comes with inspiring links to snippet libraries.'=>'Für Fans von Skripten und Codeschnipseln: Fügt der WordPress Admin Bar (Toolbar) einen Schnellzugriffsnavigator (auch QuickNav genannt) hinzu. Dies ermöglicht den einfachen Zugriff auf alle Skripte/ Code Snippets, die nach Status (aktiv/ inaktiv) oder Ordnergruppe aufgelistet sind. Der Safe Mode wird unterstützt. Enthält inspirierende Links zu Snippet-Bibliotheken.','David Decker – DECKERWEB'=>'David Decker - DECKERWEB','https://deckerweb.de/'=>'https://deckerweb.de/']];
+return ['project-id-version' => 'Advanced Scripts QuickNav 1.2.0', 'language' => 'de_DE', 'plural-forms' => 'nplurals=2; plural=(n != 1);', 'messages' => [
+'SAFE MODE is active 🔥' => 'SAFE MODE ist aktiv 🔥',
+'The package version differs from the offered update. Please check for updates again.' => 'Die Paketversion weicht vom angebotenen Update ab. Bitte prüfe erneut auf Updates.',
+'Permission denied.' => 'Keine Berechtigung.',
+'WP Snippets Library' => 'WP Snippets Bibliothek',
+'CP Facebook Group (official)' => 'CP-Facebook-Gruppe (offiziell)',
+'Site Health Info' => 'Website-Zustand Bericht',
+'Emergency Fixes 🔥' => 'Notfallkorrekturen 🔥',
+'★ Favorites' => '★ Favoriten',
+'Clean Plugins Blog' => 'Clean Plugins Blog',
+'Enabled' => 'Aktiviert',
+'Activate Advanced Scripts first.' => 'Aktiviere zuerst Advanced Scripts.',
+'Show script count in the toolbar' => 'Script-Zähler in der Toolbar anzeigen',
+'Menu entry limit' => 'Maximale Menüeinträge',
+'Show developer links' => 'Entwicklerlinks anzeigen',
+'No scripts yet. Create your first script in Advanced Scripts.' => 'Noch keine Scripts. Lege dein erstes Script in Advanced Scripts an.',
+'SCSS partial' => 'SCSS-Teil',
+'Your favorites' => 'Deine Favoriten',
+'Your snippet overview' => 'Deine Snippet-Übersicht',
+'Snippets Library by WPCode' => 'Snippet-Bibliothek von WPCode',
+'Preferences' => 'Einstellungen',
+'About' => 'Über',
+'View all in Advanced Scripts…' => 'Alle in Advanced Scripts anzeigen…',
+'The GitHub package does not contain the Advanced Scripts QuickNav plugin file.' => 'Das GitHub-Paket enthält die Plugin-Datei von Advanced Scripts QuickNav nicht.',
+'Active, blocked by folder' => 'Aktiv, durch Ordner blockiert',
+'Newsletter' => 'Newsletter',
+'administration' => 'Adminbereich',
+'Variable Inspector' => 'Variablen-Inspektor',
+'These preferences apply only to your account on this website. Constants in wp-config.php take precedence over display preferences.' => 'Diese Einstellungen gelten nur für dein Konto auf dieser Website. Konstanten in wp-config.php haben Vorrang vor Anzeigeoptionen.',
+'Plugin version' => 'Plugin-Version',
+'SCRIPT_DEBUG is on ⚠' => 'SCRIPT_DEBUG ist eingeschaltet ⚠',
+'Support the project' => 'Projekt unterstützen',
+'This release requires a newer WordPress or PHP version. The installed version has been kept.' => 'Dieses Release benötigt eine neuere WordPress- oder PHP-Version. Die installierte Version bleibt erhalten.',
+'Code Snippets Cloud' => 'Code Snippets Cloud',
+'Personal favorites' => 'Persönliche Favoriten',
+'shortcode' => 'Shortcode',
+'Close' => 'Schließen',
+'Manage favorites…' => 'Favoriten verwalten…',
+'Plugin website' => 'Plugin-Website',
+'Reach your scripts through favorites, status lists and folders in the WordPress toolbar.' => 'Erreiche deine Scripts über Favoriten, Statuslisten und Ordner in der WordPress-Toolbar.',
+'active' => 'aktiv',
+'The GitHub package could not be prepared. The installed version has been kept.' => 'Das GitHub-Paket konnte nicht vorbereitet werden. Die installierte Version bleibt erhalten.',
+'Filter by title or folder' => 'Nach Titel oder Ordner filtern',
+'Safe Mode' => 'Safe Mode',
+'Disabled' => 'Deaktiviert',
+'Snippets Library by dPlugins' => 'Snippet-Bibliothek von dPlugins',
+'File Manager' => 'Dateimanager',
+'Error Logs' => 'Fehlerprotokolle',
+'Saved favorites' => 'Gespeicherte Favoriten',
+'Advanced Scripts Safe Mode active 🔥' => 'Advanced Scripts abgesicherter Modus aktiv 🔥',
+'Folders' => 'Ordner',
+'No favorites selected yet. Choose snippets below to build your quick-access list.' => 'Noch keine Favoriten ausgewählt. Wähle unten Snippets für deinen Schnellzugriff aus.',
+'front-end' => 'Frontend',
+'The update package has missing or invalid WordPress/PHP requirements.' => 'Die WordPress- oder PHP-Voraussetzungen im Update-Paket fehlen oder sind ungültig.',
+'Find Snippets' => 'Snippets finden',
+'Code Script' => 'Code/ Skript',
+'Plugin information' => 'Plugin-Informationen',
+'WP SnippetClub Archive' => 'WP SnippetClub Archiv',
+'Plugin on GitHub' => 'Plugin auf GitHub',
+'Display preferences' => 'Anzeigeoptionen',
+'Changelog' => 'Änderungsverlauf',
+'Scripts by Folder' => 'Skripte nach Ordnern',
+'Not active' => 'Nicht aktiv',
+'inactive' => 'inaktiv',
+'Choose favorites' => 'Favoriten auswählen',
+'Inactive' => 'Inaktiv',
+'Your preferences have been saved.' => 'Deine Einstellungen wurden gespeichert.',
+'The update package could not be checked.' => 'Das Update-Paket konnte nicht geprüft werden.',
+'Inactive Scripts' => 'Inaktive Skripte',
+'Applies to each status list, favorites, and the folder tree. Additional entries remain accessible in Advanced Scripts.' => 'Gilt für jede Statusliste, die Favoriten und den Ordnerbaum. Weitere Einträge bleiben über Advanced Scripts erreichbar.',
+'Active and inactive refer to saved snippet status. Folder blocks are counted within active snippets; conditions and execution location can also affect execution.' => 'Aktiv und inaktiv beziehen sich auf den gespeicherten Snippet-Status. Ordnerblockaden zählen innerhalb der aktiven Snippets; auch Bedingungen und Ausführungsort können die Ausführung beeinflussen.',
+'Please select no more than 100 favorites.' => 'Bitte wähle höchstens 100 Favoriten aus.',
+'Adminer: Manage DB' => 'Adminer: DB verwalten',
+'Buy Me a Coffee' => 'Kaufe mir einen Kaffee',
+'Scripts' => 'Skripte',
+'Disabled by folder' => 'Durch Ordner deaktiviert',
+'Links' => 'Links',
+'Add New' => 'Neu hinzufügen',
+'Active Scripts' => 'Aktive Skripte',
+'Select up to 100 scripts. Deleted scripts disappear from the list automatically. The limit above controls how many favorites appear in the toolbar.' => 'Wähle bis zu 100 Scripts. Gelöschte Scripts verschwinden automatisch aus der Liste. Das obige Limit bestimmt, wie viele Favoriten in der Toolbar erscheinen.',
+'Save my preferences' => 'Meine Einstellungen speichern',
+'The package identity or version does not match a newer Advanced Scripts QuickNav release.' => 'Die Paketidentität oder Version entspricht keinem neueren Advanced-Scripts-QuickNav-Release.',
+'Untitled script' => 'Script ohne Titel',
+'Codes by Web Squadron' => 'Codes von Web Squadron',
+'WPCodeBin by WPCodeBox' => 'WPCodeBin von WPCodeBox',
+'Your scripts. One click away.' => 'Deine Scripts. Einen Klick entfernt.',
+'The update filesystem is unavailable. Please try again.' => 'Das Dateisystem für Updates ist nicht verfügbar. Bitte versuche es erneut.',
+'Debug Log Manager' => 'Debug-Log Manager',
+'Folder' => 'Ordner',
+'Documentation' => 'Dokumentation',
+'Activate Advanced Scripts first. Your saved preferences are kept.' => 'Aktiviere zuerst Advanced Scripts. Deine gespeicherten Einstellungen bleiben erhalten.',
+'All snippets' => 'Alle Snippets',
+'Author: David Decker' => 'Autor: David Decker',
+'Tools' => 'Werkzeuge',
+'CP YouTube Channel' => 'CP YouTube-Kanal',
+'Advanced Scripts' => 'Advanced Scripts',
+'Add script here…' => 'Script hier anlegen…',
+'QuickNav preferences' => 'QuickNav-Einstellungen',
+'Snippet types' => 'Snippet-Typen',
+'Open a favorite directly from its card. This preview follows your selection; save to update the toolbar.' => 'Öffne einen Favoriten direkt über seine Karte. Die Vorschau folgt deiner Auswahl; speichere sie zum Aktualisieren der Toolbar.',
+'Code' => 'Code',
+'System Dashboard' => 'System-Dashboard',
+'Active' => 'Aktiv',
+'everywhere' => 'überall',
+'Version' => 'Version'
+]];
diff --git a/languages/advanced-scripts-quicknav-de_DE.mo b/languages/advanced-scripts-quicknav-de_DE.mo
index 570b27f..b358f39 100644
Binary files a/languages/advanced-scripts-quicknav-de_DE.mo and b/languages/advanced-scripts-quicknav-de_DE.mo differ
diff --git a/languages/advanced-scripts-quicknav-de_DE.po b/languages/advanced-scripts-quicknav-de_DE.po
index 2b435b8..064ccb0 100644
--- a/languages/advanced-scripts-quicknav-de_DE.po
+++ b/languages/advanced-scripts-quicknav-de_DE.po
@@ -1,262 +1,314 @@
msgid ""
-msgstr ""
-"Project-Id-Version: Advanced Scripts QuickNav\n"
-"POT-Creation-Date: 2025-04-05 13:22+0200\n"
-"PO-Revision-Date: 2025-04-05 13:22+0200\n"
-"Last-Translator: David Decker DECKERWEB.de \n"
-"Language-Team: David Decker DECKERWEB.de \n"
-"Language: de_DE\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Poedit 3.6\n"
-"X-Poedit-Basepath: ..\n"
-"X-Poedit-Flags-xgettext: --add-comments=translators:\n"
-"X-Poedit-WPHeader: advanced-scripts-quicknav.php\n"
-"X-Poedit-SourceCharset: UTF-8\n"
-"X-Poedit-KeywordsList: "
-"__;_e;_n:1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;esc_attr__;esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c;_n_noop:1,2;_nx_noop:3c,1,2;__ngettext_noop:1,2\n"
-"X-Poedit-SearchPath-0: .\n"
-"X-Poedit-SearchPathExcluded-0: *.min.js\n"
-
-#: advanced-scripts-quicknav.php:348
-msgctxt "Script/ Snippet scope: everywhere (global)"
-msgid "everywhere"
-msgstr "überall"
+msgstr "Project-Id-Version: Advanced Scripts QuickNav 1.2.0\nLanguage: de_DE\nMIME-Version: 1.0\nContent-Type: text/plain; charset=UTF-8\nContent-Transfer-Encoding: 8bit\nPlural-Forms: nplurals=2; plural=(n != 1);\nPO-Revision-Date: 2026-10-01 00:00+0200\nLast-Translator: David Decker – DECKERWEB\nLanguage-Team: German\n"
-#: advanced-scripts-quicknav.php:352
-msgctxt "Script/ Snippet scope: front-end"
-msgid "front-end"
-msgstr "frontend"
+msgid "About"
+msgstr "Über"
-#: advanced-scripts-quicknav.php:424
-msgid "Scripts"
-msgstr "Skripte"
+msgid "Activate Advanced Scripts first."
+msgstr "Aktiviere zuerst Advanced Scripts."
-#: advanced-scripts-quicknav.php:482
-msgid "SAFE MODE is active 🔥"
-msgstr "SAFE MODE ist aktiv 🔥"
+msgid "Activate Advanced Scripts first. Your saved preferences are kept."
+msgstr "Aktiviere zuerst Advanced Scripts. Deine gespeicherten Einstellungen bleiben erhalten."
+
+msgid "Active"
+msgstr "Aktiv"
+
+msgid "Active Scripts"
+msgstr "Aktive Skripte"
+
+msgid "Active and inactive refer to saved snippet status. Folder blocks are counted within active snippets; conditions and execution location can also affect execution."
+msgstr "Aktiv und inaktiv beziehen sich auf den gespeicherten Snippet-Status. Ordnerblockaden zählen innerhalb der aktiven Snippets; auch Bedingungen und Ausführungsort können die Ausführung beeinflussen."
+
+msgid "Active, blocked by folder"
+msgstr "Aktiv, durch Ordner blockiert"
+
+msgid "Add New"
+msgstr "Neu hinzufügen"
+
+msgid "Add script here…"
+msgstr "Script hier anlegen…"
+
+msgid "Adminer: Manage DB"
+msgstr "Adminer: DB verwalten"
+
+msgid "Advanced Scripts"
+msgstr "Advanced Scripts"
-#: advanced-scripts-quicknav.php:490
msgid "Advanced Scripts Safe Mode active 🔥"
msgstr "Advanced Scripts abgesicherter Modus aktiv 🔥"
-#: advanced-scripts-quicknav.php:501 advanced-scripts-quicknav.php:509
-msgid "SCRIPT_DEBUG is on ⚠"
-msgstr "SCRIPT_DEBUG ist eingeschaltet ⚠"
+msgid "All snippets"
+msgstr "Alle Snippets"
-#: advanced-scripts-quicknav.php:591
-msgid "Active Scripts"
-msgstr "Aktive Skripte"
+msgid "Applies to each status list, favorites, and the folder tree. Additional entries remain accessible in Advanced Scripts."
+msgstr "Gilt für jede Statusliste, die Favoriten und den Ordnerbaum. Weitere Einträge bleiben über Advanced Scripts erreichbar."
-#: advanced-scripts-quicknav.php:642
-msgid "Inactive Scripts"
-msgstr "Inaktive Skripte"
+msgid "Author: David Decker"
+msgstr "Autor: David Decker"
-#: advanced-scripts-quicknav.php:687
-msgid "Scripts by Folder"
-msgstr "Skripte nach Ordnern"
+msgid "Buy Me a Coffee"
+msgstr "Kaufe mir einen Kaffee"
-#: advanced-scripts-quicknav.php:702
-msgid "active"
-msgstr "aktiv"
+msgid "CP Facebook Group (official)"
+msgstr "CP-Facebook-Gruppe (offiziell)"
-#: advanced-scripts-quicknav.php:702
-msgid "inactive"
-msgstr "inaktiv"
+msgid "CP YouTube Channel"
+msgstr "CP YouTube-Kanal"
-#: advanced-scripts-quicknav.php:745
-msgid "Add New"
-msgstr "Neu hinzufügen"
+msgid "Changelog"
+msgstr "Änderungsverlauf"
+
+msgid "Choose favorites"
+msgstr "Favoriten auswählen"
+
+msgid "Clean Plugins Blog"
+msgstr "Clean Plugins Blog"
+
+msgid "Close"
+msgstr "Schließen"
+
+msgid "Code"
+msgstr "Code"
-#: advanced-scripts-quicknav.php:754
msgid "Code Script"
msgstr "Code/ Skript"
-#: advanced-scripts-quicknav.php:786
-msgid "System Dashboard"
-msgstr "System-Dashboard"
-
-#: advanced-scripts-quicknav.php:797
-msgid "Site Health Info"
-msgstr "Website-Zustand Bericht"
+msgid "Code Snippets Cloud"
+msgstr "Code Snippets Cloud"
-#: advanced-scripts-quicknav.php:808
-msgid "Variable Inspector"
-msgstr "Variablen-Inspektor"
+msgid "Codes by Web Squadron"
+msgstr "Codes von Web Squadron"
-#: advanced-scripts-quicknav.php:824
msgid "Debug Log Manager"
msgstr "Debug-Log Manager"
-#: advanced-scripts-quicknav.php:844
-msgid "File Manager"
-msgstr "Dateimanager"
+msgid "Disabled"
+msgstr "Deaktiviert"
+
+msgid "Disabled by folder"
+msgstr "Durch Ordner deaktiviert"
+
+msgid "Display preferences"
+msgstr "Anzeigeoptionen"
+
+msgid "Documentation"
+msgstr "Dokumentation"
+
+msgid "Emergency Fixes 🔥"
+msgstr "Notfallkorrekturen 🔥"
+
+msgid "Enabled"
+msgstr "Aktiviert"
-#: advanced-scripts-quicknav.php:852
msgid "Error Logs"
msgstr "Fehlerprotokolle"
-#: advanced-scripts-quicknav.php:861
-msgid "Adminer: Manage DB"
-msgstr "Adminer: DB verwalten"
+msgid "File Manager"
+msgstr "Dateimanager"
-#: advanced-scripts-quicknav.php:870
-msgid "Tools"
-msgstr "Werkzeuge"
+msgid "Filter by title or folder"
+msgstr "Nach Titel oder Ordner filtern"
-#: advanced-scripts-quicknav.php:905
msgid "Find Snippets"
msgstr "Snippets finden"
-#: advanced-scripts-quicknav.php:913
-msgid "Code Snippets Cloud"
-msgstr "Code Snippets Cloud"
+msgid "Folder"
+msgstr "Ordner"
-#: advanced-scripts-quicknav.php:917
-msgid "WP Snippets Library"
-msgstr "WP Snippets Bibliothek"
+msgid "Folders"
+msgstr "Ordner"
-#: advanced-scripts-quicknav.php:921
-msgid "Codes by Web Squadron"
-msgstr "Codes von Web Squadron"
+msgid "Inactive"
+msgstr "Inaktiv"
-#: advanced-scripts-quicknav.php:925
-msgid "WP SnippetClub Archive"
-msgstr "WP SnippetClub Archiv"
+msgid "Inactive Scripts"
+msgstr "Inaktive Skripte"
-#: advanced-scripts-quicknav.php:929
-msgid "Snippets Library by dPlugins"
-msgstr "Snippet-Bibliothek von dPlugins"
+msgid "Links"
+msgstr "Links"
-#: advanced-scripts-quicknav.php:933
-msgid "WPCodeBin by WPCodeBox"
-msgstr "WPCodeBin von WPCodeBox"
+msgid "Manage favorites…"
+msgstr "Favoriten verwalten…"
-#: advanced-scripts-quicknav.php:937
-msgid "Snippets Library by WPCode"
-msgstr "Snippet-Bibliothek von WPCode"
+msgid "Menu entry limit"
+msgstr "Maximale Menüeinträge"
-#: advanced-scripts-quicknav.php:981
-msgid "Links"
-msgstr "Links"
+msgid "Newsletter"
+msgstr "Newsletter"
-#: advanced-scripts-quicknav.php:989
-msgid "Advanced Scripts"
-msgstr "Advanced Scripts"
+msgid "No favorites selected yet. Choose snippets below to build your quick-access list."
+msgstr "Noch keine Favoriten ausgewählt. Wähle unten Snippets für deinen Schnellzugriff aus."
-#: advanced-scripts-quicknav.php:993
-msgid "Emergency Fixes 🔥"
-msgstr "Notfallkorrekturen 🔥"
+msgid "No scripts yet. Create your first script in Advanced Scripts."
+msgstr "Noch keine Scripts. Lege dein erstes Script in Advanced Scripts an."
-#: advanced-scripts-quicknav.php:997
-msgid "Clean Plugins Blog"
-msgstr "Clean Plugins Blog"
+msgid "Not active"
+msgstr "Nicht aktiv"
-#: advanced-scripts-quicknav.php:1001
-msgid "CP YouTube Channel"
-msgstr "CP YouTube-Kanal"
+msgid "Open a favorite directly from its card. This preview follows your selection; save to update the toolbar."
+msgstr "Öffne einen Favoriten direkt über seine Karte. Die Vorschau folgt deiner Auswahl; speichere sie zum Aktualisieren der Toolbar."
-#: advanced-scripts-quicknav.php:1005
-msgid "CP Facbook Group (official)"
-msgstr "CP Facbook Gruppe (offiziell)"
+msgid "Permission denied."
+msgstr "Keine Berechtigung."
-#: advanced-scripts-quicknav.php:1033
-msgid "About"
-msgstr "Über"
+msgid "Personal favorites"
+msgstr "Persönliche Favoriten"
-#: advanced-scripts-quicknav.php:1041
-msgid "Author: David Decker"
-msgstr "Autor: David Decker"
+msgid "Please select no more than 100 favorites."
+msgstr "Bitte wähle höchstens 100 Favoriten aus."
+
+msgid "Plugin information"
+msgstr "Plugin-Informationen"
-#: advanced-scripts-quicknav.php:1045
msgid "Plugin on GitHub"
msgstr "Plugin auf GitHub"
-#: advanced-scripts-quicknav.php:1049
-msgid "Buy Me a Coffee"
-msgstr "Kaufe mir einen Kaffee"
+msgid "Plugin version"
+msgstr "Plugin-Version"
-#: advanced-scripts-quicknav.php:1161
-msgctxt "Site Health Debug info"
-msgid "Undefined"
-msgstr "Nicht definiert"
+msgid "Plugin website"
+msgstr "Plugin-Website"
-#: advanced-scripts-quicknav.php:1162
-msgctxt "Site Health Debug info"
-msgid "Enabled"
-msgstr "Aktiviert"
+msgid "Preferences"
+msgstr "Einstellungen"
-#: advanced-scripts-quicknav.php:1163
-msgctxt "Site Health Debug info"
-msgid "Disabled"
-msgstr "Deaktiviert"
+msgid "QuickNav preferences"
+msgstr "QuickNav-Einstellungen"
-#: advanced-scripts-quicknav.php:1164
-msgctxt "Site Health Debug info"
-msgid "value"
-msgstr "Wert"
+msgid "Reach your scripts through favorites, status lists and folders in the WordPress toolbar."
+msgstr "Erreiche deine Scripts über Favoriten, Statuslisten und Ordner in der WordPress-Toolbar."
-#. Plugin Name of the plugin/theme
-#: advanced-scripts-quicknav.php:1169
-msgid "Advanced Scripts QuickNav"
-msgstr "Advanced Scripts QuickNav"
+msgid "SAFE MODE is active 🔥"
+msgstr "SAFE MODE ist aktiv 🔥"
-#: advanced-scripts-quicknav.php:1169
-msgid "Plugin"
-msgstr "Plugin"
+msgid "SCRIPT_DEBUG is on ⚠"
+msgstr "SCRIPT_DEBUG ist eingeschaltet ⚠"
-#: advanced-scripts-quicknav.php:1174
-msgid "Plugin version"
-msgstr "Pluginversion"
+msgid "SCSS partial"
+msgstr "SCSS-Teil"
-#: advanced-scripts-quicknav.php:1178
-msgid "WordPress Install Type"
-msgstr "WordPress Installationstyp"
+msgid "Safe Mode"
+msgstr "Safe Mode"
-#: advanced-scripts-quicknav.php:1179
-msgid "Multisite install"
-msgstr "Multisite-Installation"
+msgid "Save my preferences"
+msgstr "Meine Einstellungen speichern"
-#: advanced-scripts-quicknav.php:1179
-msgid "Single Site install"
-msgstr "Single-Site-Installation"
+msgid "Saved favorites"
+msgstr "Gespeicherte Favoriten"
-#: advanced-scripts-quicknav.php:1225
-msgid "Plugin not installed"
-msgstr "Plugin nicht installiert"
+msgid "Scripts"
+msgstr "Skripte"
-#: advanced-scripts-quicknav.php:1270
-msgctxt "Plugins page listing"
-msgid "Donate"
-msgstr "Spenden"
+msgid "Scripts by Folder"
+msgstr "Skripte nach Ordnern"
-#: advanced-scripts-quicknav.php:1276
-msgctxt "Plugins page listing"
-msgid "Join our Newsletter"
-msgstr "Jetzt Newsletter"
+msgid "Select up to 100 scripts. Deleted scripts disappear from the list automatically. The limit above controls how many favorites appear in the toolbar."
+msgstr "Wähle bis zu 100 Scripts. Gelöschte Scripts verschwinden automatisch aus der Liste. Das obige Limit bestimmt, wie viele Favoriten in der Toolbar erscheinen."
-#. Plugin URI of the plugin/theme
-msgid "https://github.com/deckerweb/advanced-scripts-quicknav"
-msgstr "https://github.com/deckerweb/advanced-scripts-quicknav"
+msgid "Show developer links"
+msgstr "Entwicklerlinks anzeigen"
-#. Description of the plugin/theme
-msgid ""
-"For Script and Code Snippets enthusiasts: Adds a quick-access navigator (aka "
-"QuickNav) to the WordPress Admin Bar (Toolbar). It allows easy access to "
-"your Scripts/ Code Snippets listed by Active, Inactive or Folder group. Safe "
-"Mode is supported. Comes with inspiring links to snippet libraries."
-msgstr ""
-"Für Fans von Skripten und Codeschnipseln: Fügt der WordPress Admin Bar "
-"(Toolbar) einen Schnellzugriffsnavigator (auch QuickNav genannt) hinzu. Dies "
-"ermöglicht den einfachen Zugriff auf alle Skripte/ Code Snippets, die nach "
-"Status (aktiv/ inaktiv) oder Ordnergruppe aufgelistet sind. Der Safe Mode "
-"wird unterstützt. Enthält inspirierende Links zu Snippet-Bibliotheken."
-
-#. Author of the plugin/theme
-msgid "David Decker – DECKERWEB"
-msgstr "David Decker - DECKERWEB"
-
-#. Author URI of the plugin/theme
-msgid "https://deckerweb.de/"
-msgstr "https://deckerweb.de/"
+msgid "Show script count in the toolbar"
+msgstr "Script-Zähler in der Toolbar anzeigen"
+
+msgid "Site Health Info"
+msgstr "Website-Zustand Bericht"
+
+msgid "Snippet types"
+msgstr "Snippet-Typen"
+
+msgid "Snippets Library by WPCode"
+msgstr "Snippet-Bibliothek von WPCode"
+
+msgid "Snippets Library by dPlugins"
+msgstr "Snippet-Bibliothek von dPlugins"
+
+msgid "Support the project"
+msgstr "Projekt unterstützen"
+
+msgid "System Dashboard"
+msgstr "System-Dashboard"
+
+msgid "The GitHub package could not be prepared. The installed version has been kept."
+msgstr "Das GitHub-Paket konnte nicht vorbereitet werden. Die installierte Version bleibt erhalten."
+
+msgid "The GitHub package does not contain the Advanced Scripts QuickNav plugin file."
+msgstr "Das GitHub-Paket enthält die Plugin-Datei von Advanced Scripts QuickNav nicht."
+
+msgid "The package identity or version does not match a newer Advanced Scripts QuickNav release."
+msgstr "Die Paketidentität oder Version entspricht keinem neueren Advanced-Scripts-QuickNav-Release."
+
+msgid "The package version differs from the offered update. Please check for updates again."
+msgstr "Die Paketversion weicht vom angebotenen Update ab. Bitte prüfe erneut auf Updates."
+
+msgid "The update filesystem is unavailable. Please try again."
+msgstr "Das Dateisystem für Updates ist nicht verfügbar. Bitte versuche es erneut."
+
+msgid "The update package could not be checked."
+msgstr "Das Update-Paket konnte nicht geprüft werden."
+
+msgid "The update package has missing or invalid WordPress/PHP requirements."
+msgstr "Die WordPress- oder PHP-Voraussetzungen im Update-Paket fehlen oder sind ungültig."
+
+msgid "These preferences apply only to your account on this website. Constants in wp-config.php take precedence over display preferences."
+msgstr "Diese Einstellungen gelten nur für dein Konto auf dieser Website. Konstanten in wp-config.php haben Vorrang vor Anzeigeoptionen."
+
+msgid "This release requires a newer WordPress or PHP version. The installed version has been kept."
+msgstr "Dieses Release benötigt eine neuere WordPress- oder PHP-Version. Die installierte Version bleibt erhalten."
+
+msgid "Tools"
+msgstr "Werkzeuge"
+
+msgid "Untitled script"
+msgstr "Script ohne Titel"
+
+msgid "Variable Inspector"
+msgstr "Variablen-Inspektor"
+
+msgid "Version"
+msgstr "Version"
+
+msgid "View all in Advanced Scripts…"
+msgstr "Alle in Advanced Scripts anzeigen…"
+
+msgid "WP SnippetClub Archive"
+msgstr "WP SnippetClub Archiv"
+
+msgid "WP Snippets Library"
+msgstr "WP Snippets Bibliothek"
+
+msgid "WPCodeBin by WPCodeBox"
+msgstr "WPCodeBin von WPCodeBox"
+
+msgid "Your favorites"
+msgstr "Deine Favoriten"
+
+msgid "Your preferences have been saved."
+msgstr "Deine Einstellungen wurden gespeichert."
+
+msgid "Your scripts. One click away."
+msgstr "Deine Scripts. Einen Klick entfernt."
+
+msgid "Your snippet overview"
+msgstr "Deine Snippet-Übersicht"
+
+msgid "active"
+msgstr "aktiv"
+
+msgid "administration"
+msgstr "Adminbereich"
+
+msgid "everywhere"
+msgstr "überall"
+
+msgid "front-end"
+msgstr "Frontend"
+
+msgid "inactive"
+msgstr "inaktiv"
+
+msgid "shortcode"
+msgstr "Shortcode"
+
+msgid "★ Favorites"
+msgstr "★ Favoriten"
diff --git a/languages/advanced-scripts-quicknav-de_DE_formal.l10n.php b/languages/advanced-scripts-quicknav-de_DE_formal.l10n.php
index 22e5f01..a8f89e1 100644
--- a/languages/advanced-scripts-quicknav-de_DE_formal.l10n.php
+++ b/languages/advanced-scripts-quicknav-de_DE_formal.l10n.php
@@ -1,3 +1,107 @@
NULL,'plural-forms'=>'nplurals=2; plural=(n != 1);','language'=>'de_DE@formal','pot-creation-date'=>'2025-04-05 13:22+0200','po-revision-date'=>'2025-04-05 13:22+0200','translation-revision-date'=>'2025-04-05 13:22+0200','project-id-version'=>'Advanced Scripts QuickNav','x-generator'=>'Poedit 3.6','messages'=>['Script/ Snippet scope: everywhere (global)' . "\4" . 'everywhere'=>'überall','Script/ Snippet scope: front-end' . "\4" . 'front-end'=>'frontend','Scripts'=>'Skripte','SAFE MODE is active 🔥'=>'SAFE MODE ist aktiv 🔥','Advanced Scripts Safe Mode active 🔥'=>'Advanced Scripts abgesicherter Modus aktiv 🔥','SCRIPT_DEBUG is on ⚠'=>'SCRIPT_DEBUG ist eingeschaltet ⚠','Active Scripts'=>'Aktive Skripte','Inactive Scripts'=>'Inaktive Skripte','Scripts by Folder'=>'Skripte nach Ordnern','active'=>'aktiv','inactive'=>'inaktiv','Add New'=>'Neu hinzufügen','Code Script'=>'Code/ Skript','System Dashboard'=>'System-Dashboard','Site Health Info'=>'Website-Zustand Bericht','Variable Inspector'=>'Variablen-Inspektor','Debug Log Manager'=>'Debug-Log Manager','File Manager'=>'Dateimanager','Error Logs'=>'Fehlerprotokolle','Adminer: Manage DB'=>'Verwalter: DB verwalten','Tools'=>'Werkzeuge','Find Snippets'=>'Snippets finden','Code Snippets Cloud'=>'Code Snippets Cloud','WP Snippets Library'=>'WP Snippets Bibliothek','Codes by Web Squadron'=>'Codes von Web Squadron','WP SnippetClub Archive'=>'WP SnippetClub Archiv','Snippets Library by dPlugins'=>'Snippet-Bibliothek von dPlugins','WPCodeBin by WPCodeBox'=>'WPCodeBin von WPCodeBox','Snippets Library by WPCode'=>'Snippet-Bibliothek von WPCode','Links'=>'Links','Advanced Scripts'=>'Advanced Scripts','Emergency Fixes 🔥'=>'Notfallkorrekturen 🔥','Clean Plugins Blog'=>'Clean Plugins Blog','CP YouTube Channel'=>'CP YouTube-Kanal','CP Facbook Group (official)'=>'CP Facbook Gruppe (offiziell)','About'=>'Über','Author: David Decker'=>'Autor: David Decker','Plugin on GitHub'=>'Plugin auf GitHub','Buy Me a Coffee'=>'Kaufen Sie mir einen Kaffee','Site Health Debug info' . "\4" . 'Undefined'=>'Nicht definiert','Site Health Debug info' . "\4" . 'Enabled'=>'Aktiviert','Site Health Debug info' . "\4" . 'Disabled'=>'Deaktiviert','Site Health Debug info' . "\4" . 'value'=>'Wert','Advanced Scripts QuickNav'=>'Advanced Scripts QuickNav','Plugin'=>'Plugin','Plugin version'=>'Pluginversion','WordPress Install Type'=>'WordPress Installationstyp','Multisite install'=>'Multisite-Installation','Single Site install'=>'Single-Site-Installation','Plugin not installed'=>'Plugin nicht installiert','Plugins page listing' . "\4" . 'Donate'=>'Spenden','Plugins page listing' . "\4" . 'Join our Newsletter'=>'Jetzt Newsletter','https://github.com/deckerweb/advanced-scripts-quicknav'=>'https://github.com/deckerweb/advanced-scripts-quicknav','For Script and Code Snippets enthusiasts: Adds a quick-access navigator (aka QuickNav) to the WordPress Admin Bar (Toolbar). It allows easy access to your Scripts/ Code Snippets listed by Active, Inactive or Folder group. Safe Mode is supported. Comes with inspiring links to snippet libraries.'=>'Für Fans von Skripten und Codeschnipseln: Fügt der WordPress Admin Bar (Toolbar) einen Schnellzugriffsnavigator (auch QuickNav genannt) hinzu. Dies ermöglicht den einfachen Zugriff auf alle Skripte/ Code Snippets, die nach Status (aktiv/ inaktiv) oder Ordnergruppe aufgelistet sind. Der Safe Mode wird unterstützt. Enthält inspirierende Links zu Snippet-Bibliotheken.','David Decker – DECKERWEB'=>'David Decker - DECKERWEB','https://deckerweb.de/'=>'https://deckerweb.de/']];
+return ['project-id-version' => 'Advanced Scripts QuickNav 1.2.0', 'language' => 'de_DE_formal', 'plural-forms' => 'nplurals=2; plural=(n != 1);', 'messages' => [
+'SAFE MODE is active 🔥' => 'SAFE MODE ist aktiv 🔥',
+'The package version differs from the offered update. Please check for updates again.' => 'Die Paketversion weicht vom angebotenen Update ab. Bitte prüfe erneut auf Updates.',
+'Permission denied.' => 'Keine Berechtigung.',
+'WP Snippets Library' => 'WP Snippets Bibliothek',
+'CP Facebook Group (official)' => 'CP-Facebook-Gruppe (offiziell)',
+'Site Health Info' => 'Website-Zustand Bericht',
+'Emergency Fixes 🔥' => 'Notfallkorrekturen 🔥',
+'★ Favorites' => '★ Favoriten',
+'Clean Plugins Blog' => 'Clean Plugins Blog',
+'Enabled' => 'Aktiviert',
+'Activate Advanced Scripts first.' => 'Aktivieren Sie zuerst Advanced Scripts.',
+'Show script count in the toolbar' => 'Script-Zähler in der Toolbar anzeigen',
+'Menu entry limit' => 'Maximale Menüeinträge',
+'Show developer links' => 'Entwicklerlinks anzeigen',
+'No scripts yet. Create your first script in Advanced Scripts.' => 'Noch keine Scripts. Lege Ihr erstes Script in Advanced Scripts an.',
+'SCSS partial' => 'SCSS-Teil',
+'Your favorites' => 'Ihre Favoriten',
+'Your snippet overview' => 'Ihre Snippet-Übersicht',
+'Snippets Library by WPCode' => 'Snippet-Bibliothek von WPCode',
+'Preferences' => 'Einstellungen',
+'About' => 'Über',
+'View all in Advanced Scripts…' => 'Alle in Advanced Scripts anzeigen…',
+'The GitHub package does not contain the Advanced Scripts QuickNav plugin file.' => 'Das GitHub-Paket enthält die Plugin-Datei von Advanced Scripts QuickNav nicht.',
+'Active, blocked by folder' => 'Aktiv, durch Ordner blockiert',
+'Newsletter' => 'Newsletter',
+'administration' => 'Adminbereich',
+'Variable Inspector' => 'Variablen-Inspektor',
+'These preferences apply only to your account on this website. Constants in wp-config.php take precedence over display preferences.' => 'Diese Einstellungen gelten nur für Ihr Konto auf dieser Website. Konstanten in wp-config.php haben Vorrang vor Anzeigeoptionen.',
+'Plugin version' => 'Plugin-Version',
+'SCRIPT_DEBUG is on ⚠' => 'SCRIPT_DEBUG ist eingeschaltet ⚠',
+'Support the project' => 'Projekt unterstützen',
+'This release requires a newer WordPress or PHP version. The installed version has been kept.' => 'Dieses Release benötigt eine neuere WordPress- oder PHP-Version. Die installierte Version bleibt erhalten.',
+'Code Snippets Cloud' => 'Code Snippets Cloud',
+'Personal favorites' => 'Persönliche Favoriten',
+'shortcode' => 'Shortcode',
+'Close' => 'Schließen',
+'Manage favorites…' => 'Favoriten verwalten…',
+'Plugin website' => 'Plugin-Website',
+'Reach your scripts through favorites, status lists and folders in the WordPress toolbar.' => 'Erreiche Ihre Scripts über Favoriten, Statuslisten und Ordner in der WordPress-Toolbar.',
+'active' => 'aktiv',
+'The GitHub package could not be prepared. The installed version has been kept.' => 'Das GitHub-Paket konnte nicht vorbereitet werden. Die installierte Version bleibt erhalten.',
+'Filter by title or folder' => 'Nach Titel oder Ordner filtern',
+'Safe Mode' => 'Safe Mode',
+'Disabled' => 'Deaktiviert',
+'Snippets Library by dPlugins' => 'Snippet-Bibliothek von dPlugins',
+'File Manager' => 'Dateimanager',
+'Error Logs' => 'Fehlerprotokolle',
+'Saved favorites' => 'Gespeicherte Favoriten',
+'Advanced Scripts Safe Mode active 🔥' => 'Advanced Scripts abgesicherter Modus aktiv 🔥',
+'Folders' => 'Ordner',
+'No favorites selected yet. Choose snippets below to build your quick-access list.' => 'Noch keine Favoriten ausgewählt. Wählen Sie unten Snippets für Ihren Schnellzugriff aus.',
+'front-end' => 'Frontend',
+'The update package has missing or invalid WordPress/PHP requirements.' => 'Die WordPress- oder PHP-Voraussetzungen im Update-Paket fehlen oder sind ungültig.',
+'Find Snippets' => 'Snippets finden',
+'Code Script' => 'Code/ Skript',
+'Plugin information' => 'Plugin-Informationen',
+'WP SnippetClub Archive' => 'WP SnippetClub Archiv',
+'Plugin on GitHub' => 'Plugin auf GitHub',
+'Display preferences' => 'Anzeigeoptionen',
+'Changelog' => 'Änderungsverlauf',
+'Scripts by Folder' => 'Skripte nach Ordnern',
+'Not active' => 'Nicht aktiv',
+'inactive' => 'inaktiv',
+'Choose favorites' => 'Favoriten auswählen',
+'Inactive' => 'Inaktiv',
+'Your preferences have been saved.' => 'Ihre Einstellungen wurden gespeichert.',
+'The update package could not be checked.' => 'Das Update-Paket konnte nicht geprüft werden.',
+'Inactive Scripts' => 'Inaktive Skripte',
+'Applies to each status list, favorites, and the folder tree. Additional entries remain accessible in Advanced Scripts.' => 'Gilt für jede Statusliste, die Favoriten und den Ordnerbaum. Weitere Einträge bleiben über Advanced Scripts erreichbar.',
+'Active and inactive refer to saved snippet status. Folder blocks are counted within active snippets; conditions and execution location can also affect execution.' => 'Aktiv und inaktiv beziehen sich auf den gespeicherten Snippet-Status. Ordnerblockaden zählen innerhalb der aktiven Snippets; auch Bedingungen und Ausführungsort können die Ausführung beeinflussen.',
+'Please select no more than 100 favorites.' => 'Bitte wähle höchstens 100 Favoriten aus.',
+'Adminer: Manage DB' => 'Adminer: DB verwalten',
+'Buy Me a Coffee' => 'Kaufe mir einen Kaffee',
+'Scripts' => 'Skripte',
+'Disabled by folder' => 'Durch Ordner deaktiviert',
+'Links' => 'Links',
+'Add New' => 'Neu hinzufügen',
+'Active Scripts' => 'Aktive Skripte',
+'Select up to 100 scripts. Deleted scripts disappear from the list automatically. The limit above controls how many favorites appear in the toolbar.' => 'Wählen Sie bis zu 100 Scripts. Gelöschte Scripts verschwinden automatisch aus der Liste. Das obige Limit bestimmt, wie viele Favoriten in der Toolbar erscheinen.',
+'Save my preferences' => 'Meine Einstellungen speichern',
+'The package identity or version does not match a newer Advanced Scripts QuickNav release.' => 'Die Paketidentität oder Version entspricht keinem neueren Advanced-Scripts-QuickNav-Release.',
+'Untitled script' => 'Script ohne Titel',
+'Codes by Web Squadron' => 'Codes von Web Squadron',
+'WPCodeBin by WPCodeBox' => 'WPCodeBin von WPCodeBox',
+'Your scripts. One click away.' => 'Ihre Scripts. Einen Klick entfernt.',
+'The update filesystem is unavailable. Please try again.' => 'Das Dateisystem für Updates ist nicht verfügbar. Bitte versuche es erneut.',
+'Debug Log Manager' => 'Debug-Log Manager',
+'Folder' => 'Ordner',
+'Documentation' => 'Dokumentation',
+'Activate Advanced Scripts first. Your saved preferences are kept.' => 'Aktivieren Sie zuerst Advanced Scripts. Ihre gespeicherten Einstellungen bleiben erhalten.',
+'All snippets' => 'Alle Snippets',
+'Author: David Decker' => 'Autor: David Decker',
+'Tools' => 'Werkzeuge',
+'CP YouTube Channel' => 'CP YouTube-Kanal',
+'Advanced Scripts' => 'Advanced Scripts',
+'Add script here…' => 'Script hier anlegen…',
+'QuickNav preferences' => 'QuickNav-Einstellungen',
+'Snippet types' => 'Snippet-Typen',
+'Open a favorite directly from its card. This preview follows your selection; save to update the toolbar.' => 'Öffnen Sie einen Favoriten direkt über seine Karte. Die Vorschau folgt Ihrer Auswahl; speichern Sie sie zum Aktualisieren der Toolbar.',
+'Code' => 'Code',
+'System Dashboard' => 'System-Dashboard',
+'Active' => 'Aktiv',
+'everywhere' => 'überall',
+'Version' => 'Version'
+]];
diff --git a/languages/advanced-scripts-quicknav-de_DE_formal.mo b/languages/advanced-scripts-quicknav-de_DE_formal.mo
index 947ad46..81d2756 100644
Binary files a/languages/advanced-scripts-quicknav-de_DE_formal.mo and b/languages/advanced-scripts-quicknav-de_DE_formal.mo differ
diff --git a/languages/advanced-scripts-quicknav-de_DE_formal.po b/languages/advanced-scripts-quicknav-de_DE_formal.po
index 046698b..2473fd4 100644
--- a/languages/advanced-scripts-quicknav-de_DE_formal.po
+++ b/languages/advanced-scripts-quicknav-de_DE_formal.po
@@ -1,262 +1,314 @@
msgid ""
-msgstr ""
-"Project-Id-Version: Advanced Scripts QuickNav\n"
-"POT-Creation-Date: 2025-04-05 13:22+0200\n"
-"PO-Revision-Date: 2025-04-05 13:22+0200\n"
-"Last-Translator: David Decker DECKERWEB.de \n"
-"Language-Team: David Decker DECKERWEB.de \n"
-"Language: de_DE@formal\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Poedit 3.6\n"
-"X-Poedit-Basepath: ..\n"
-"X-Poedit-Flags-xgettext: --add-comments=translators:\n"
-"X-Poedit-WPHeader: advanced-scripts-quicknav.php\n"
-"X-Poedit-SourceCharset: UTF-8\n"
-"X-Poedit-KeywordsList: "
-"__;_e;_n:1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;esc_attr__;esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c;_n_noop:1,2;_nx_noop:3c,1,2;__ngettext_noop:1,2\n"
-"X-Poedit-SearchPath-0: .\n"
-"X-Poedit-SearchPathExcluded-0: *.min.js\n"
-
-#: advanced-scripts-quicknav.php:348
-msgctxt "Script/ Snippet scope: everywhere (global)"
-msgid "everywhere"
-msgstr "überall"
+msgstr "Project-Id-Version: Advanced Scripts QuickNav 1.2.0\nLanguage: de_DE_formal\nMIME-Version: 1.0\nContent-Type: text/plain; charset=UTF-8\nContent-Transfer-Encoding: 8bit\nPlural-Forms: nplurals=2; plural=(n != 1);\nPO-Revision-Date: 2026-10-01 00:00+0200\nLast-Translator: David Decker – DECKERWEB\nLanguage-Team: German\n"
-#: advanced-scripts-quicknav.php:352
-msgctxt "Script/ Snippet scope: front-end"
-msgid "front-end"
-msgstr "frontend"
+msgid "About"
+msgstr "Über"
-#: advanced-scripts-quicknav.php:424
-msgid "Scripts"
-msgstr "Skripte"
+msgid "Activate Advanced Scripts first."
+msgstr "Aktivieren Sie zuerst Advanced Scripts."
-#: advanced-scripts-quicknav.php:482
-msgid "SAFE MODE is active 🔥"
-msgstr "SAFE MODE ist aktiv 🔥"
+msgid "Activate Advanced Scripts first. Your saved preferences are kept."
+msgstr "Aktivieren Sie zuerst Advanced Scripts. Ihre gespeicherten Einstellungen bleiben erhalten."
+
+msgid "Active"
+msgstr "Aktiv"
+
+msgid "Active Scripts"
+msgstr "Aktive Skripte"
+
+msgid "Active and inactive refer to saved snippet status. Folder blocks are counted within active snippets; conditions and execution location can also affect execution."
+msgstr "Aktiv und inaktiv beziehen sich auf den gespeicherten Snippet-Status. Ordnerblockaden zählen innerhalb der aktiven Snippets; auch Bedingungen und Ausführungsort können die Ausführung beeinflussen."
+
+msgid "Active, blocked by folder"
+msgstr "Aktiv, durch Ordner blockiert"
+
+msgid "Add New"
+msgstr "Neu hinzufügen"
+
+msgid "Add script here…"
+msgstr "Script hier anlegen…"
+
+msgid "Adminer: Manage DB"
+msgstr "Adminer: DB verwalten"
+
+msgid "Advanced Scripts"
+msgstr "Advanced Scripts"
-#: advanced-scripts-quicknav.php:490
msgid "Advanced Scripts Safe Mode active 🔥"
msgstr "Advanced Scripts abgesicherter Modus aktiv 🔥"
-#: advanced-scripts-quicknav.php:501 advanced-scripts-quicknav.php:509
-msgid "SCRIPT_DEBUG is on ⚠"
-msgstr "SCRIPT_DEBUG ist eingeschaltet ⚠"
+msgid "All snippets"
+msgstr "Alle Snippets"
-#: advanced-scripts-quicknav.php:591
-msgid "Active Scripts"
-msgstr "Aktive Skripte"
+msgid "Applies to each status list, favorites, and the folder tree. Additional entries remain accessible in Advanced Scripts."
+msgstr "Gilt für jede Statusliste, die Favoriten und den Ordnerbaum. Weitere Einträge bleiben über Advanced Scripts erreichbar."
-#: advanced-scripts-quicknav.php:642
-msgid "Inactive Scripts"
-msgstr "Inaktive Skripte"
+msgid "Author: David Decker"
+msgstr "Autor: David Decker"
-#: advanced-scripts-quicknav.php:687
-msgid "Scripts by Folder"
-msgstr "Skripte nach Ordnern"
+msgid "Buy Me a Coffee"
+msgstr "Kaufe mir einen Kaffee"
-#: advanced-scripts-quicknav.php:702
-msgid "active"
-msgstr "aktiv"
+msgid "CP Facebook Group (official)"
+msgstr "CP-Facebook-Gruppe (offiziell)"
-#: advanced-scripts-quicknav.php:702
-msgid "inactive"
-msgstr "inaktiv"
+msgid "CP YouTube Channel"
+msgstr "CP YouTube-Kanal"
-#: advanced-scripts-quicknav.php:745
-msgid "Add New"
-msgstr "Neu hinzufügen"
+msgid "Changelog"
+msgstr "Änderungsverlauf"
+
+msgid "Choose favorites"
+msgstr "Favoriten auswählen"
+
+msgid "Clean Plugins Blog"
+msgstr "Clean Plugins Blog"
+
+msgid "Close"
+msgstr "Schließen"
+
+msgid "Code"
+msgstr "Code"
-#: advanced-scripts-quicknav.php:754
msgid "Code Script"
msgstr "Code/ Skript"
-#: advanced-scripts-quicknav.php:786
-msgid "System Dashboard"
-msgstr "System-Dashboard"
-
-#: advanced-scripts-quicknav.php:797
-msgid "Site Health Info"
-msgstr "Website-Zustand Bericht"
+msgid "Code Snippets Cloud"
+msgstr "Code Snippets Cloud"
-#: advanced-scripts-quicknav.php:808
-msgid "Variable Inspector"
-msgstr "Variablen-Inspektor"
+msgid "Codes by Web Squadron"
+msgstr "Codes von Web Squadron"
-#: advanced-scripts-quicknav.php:824
msgid "Debug Log Manager"
msgstr "Debug-Log Manager"
-#: advanced-scripts-quicknav.php:844
-msgid "File Manager"
-msgstr "Dateimanager"
+msgid "Disabled"
+msgstr "Deaktiviert"
+
+msgid "Disabled by folder"
+msgstr "Durch Ordner deaktiviert"
+
+msgid "Display preferences"
+msgstr "Anzeigeoptionen"
+
+msgid "Documentation"
+msgstr "Dokumentation"
+
+msgid "Emergency Fixes 🔥"
+msgstr "Notfallkorrekturen 🔥"
+
+msgid "Enabled"
+msgstr "Aktiviert"
-#: advanced-scripts-quicknav.php:852
msgid "Error Logs"
msgstr "Fehlerprotokolle"
-#: advanced-scripts-quicknav.php:861
-msgid "Adminer: Manage DB"
-msgstr "Verwalter: DB verwalten"
+msgid "File Manager"
+msgstr "Dateimanager"
-#: advanced-scripts-quicknav.php:870
-msgid "Tools"
-msgstr "Werkzeuge"
+msgid "Filter by title or folder"
+msgstr "Nach Titel oder Ordner filtern"
-#: advanced-scripts-quicknav.php:905
msgid "Find Snippets"
msgstr "Snippets finden"
-#: advanced-scripts-quicknav.php:913
-msgid "Code Snippets Cloud"
-msgstr "Code Snippets Cloud"
+msgid "Folder"
+msgstr "Ordner"
-#: advanced-scripts-quicknav.php:917
-msgid "WP Snippets Library"
-msgstr "WP Snippets Bibliothek"
+msgid "Folders"
+msgstr "Ordner"
-#: advanced-scripts-quicknav.php:921
-msgid "Codes by Web Squadron"
-msgstr "Codes von Web Squadron"
+msgid "Inactive"
+msgstr "Inaktiv"
-#: advanced-scripts-quicknav.php:925
-msgid "WP SnippetClub Archive"
-msgstr "WP SnippetClub Archiv"
+msgid "Inactive Scripts"
+msgstr "Inaktive Skripte"
-#: advanced-scripts-quicknav.php:929
-msgid "Snippets Library by dPlugins"
-msgstr "Snippet-Bibliothek von dPlugins"
+msgid "Links"
+msgstr "Links"
-#: advanced-scripts-quicknav.php:933
-msgid "WPCodeBin by WPCodeBox"
-msgstr "WPCodeBin von WPCodeBox"
+msgid "Manage favorites…"
+msgstr "Favoriten verwalten…"
-#: advanced-scripts-quicknav.php:937
-msgid "Snippets Library by WPCode"
-msgstr "Snippet-Bibliothek von WPCode"
+msgid "Menu entry limit"
+msgstr "Maximale Menüeinträge"
-#: advanced-scripts-quicknav.php:981
-msgid "Links"
-msgstr "Links"
+msgid "Newsletter"
+msgstr "Newsletter"
-#: advanced-scripts-quicknav.php:989
-msgid "Advanced Scripts"
-msgstr "Advanced Scripts"
+msgid "No favorites selected yet. Choose snippets below to build your quick-access list."
+msgstr "Noch keine Favoriten ausgewählt. Wählen Sie unten Snippets für Ihren Schnellzugriff aus."
-#: advanced-scripts-quicknav.php:993
-msgid "Emergency Fixes 🔥"
-msgstr "Notfallkorrekturen 🔥"
+msgid "No scripts yet. Create your first script in Advanced Scripts."
+msgstr "Noch keine Scripts. Lege Ihr erstes Script in Advanced Scripts an."
-#: advanced-scripts-quicknav.php:997
-msgid "Clean Plugins Blog"
-msgstr "Clean Plugins Blog"
+msgid "Not active"
+msgstr "Nicht aktiv"
-#: advanced-scripts-quicknav.php:1001
-msgid "CP YouTube Channel"
-msgstr "CP YouTube-Kanal"
+msgid "Open a favorite directly from its card. This preview follows your selection; save to update the toolbar."
+msgstr "Öffnen Sie einen Favoriten direkt über seine Karte. Die Vorschau folgt Ihrer Auswahl; speichern Sie sie zum Aktualisieren der Toolbar."
-#: advanced-scripts-quicknav.php:1005
-msgid "CP Facbook Group (official)"
-msgstr "CP Facbook Gruppe (offiziell)"
+msgid "Permission denied."
+msgstr "Keine Berechtigung."
-#: advanced-scripts-quicknav.php:1033
-msgid "About"
-msgstr "Über"
+msgid "Personal favorites"
+msgstr "Persönliche Favoriten"
-#: advanced-scripts-quicknav.php:1041
-msgid "Author: David Decker"
-msgstr "Autor: David Decker"
+msgid "Please select no more than 100 favorites."
+msgstr "Bitte wähle höchstens 100 Favoriten aus."
+
+msgid "Plugin information"
+msgstr "Plugin-Informationen"
-#: advanced-scripts-quicknav.php:1045
msgid "Plugin on GitHub"
msgstr "Plugin auf GitHub"
-#: advanced-scripts-quicknav.php:1049
-msgid "Buy Me a Coffee"
-msgstr "Kaufen Sie mir einen Kaffee"
+msgid "Plugin version"
+msgstr "Plugin-Version"
-#: advanced-scripts-quicknav.php:1161
-msgctxt "Site Health Debug info"
-msgid "Undefined"
-msgstr "Nicht definiert"
+msgid "Plugin website"
+msgstr "Plugin-Website"
-#: advanced-scripts-quicknav.php:1162
-msgctxt "Site Health Debug info"
-msgid "Enabled"
-msgstr "Aktiviert"
+msgid "Preferences"
+msgstr "Einstellungen"
-#: advanced-scripts-quicknav.php:1163
-msgctxt "Site Health Debug info"
-msgid "Disabled"
-msgstr "Deaktiviert"
+msgid "QuickNav preferences"
+msgstr "QuickNav-Einstellungen"
-#: advanced-scripts-quicknav.php:1164
-msgctxt "Site Health Debug info"
-msgid "value"
-msgstr "Wert"
+msgid "Reach your scripts through favorites, status lists and folders in the WordPress toolbar."
+msgstr "Erreiche Ihre Scripts über Favoriten, Statuslisten und Ordner in der WordPress-Toolbar."
-#. Plugin Name of the plugin/theme
-#: advanced-scripts-quicknav.php:1169
-msgid "Advanced Scripts QuickNav"
-msgstr "Advanced Scripts QuickNav"
+msgid "SAFE MODE is active 🔥"
+msgstr "SAFE MODE ist aktiv 🔥"
+
+msgid "SCRIPT_DEBUG is on ⚠"
+msgstr "SCRIPT_DEBUG ist eingeschaltet ⚠"
-#: advanced-scripts-quicknav.php:1169
-msgid "Plugin"
-msgstr "Plugin"
+msgid "SCSS partial"
+msgstr "SCSS-Teil"
-#: advanced-scripts-quicknav.php:1174
-msgid "Plugin version"
-msgstr "Pluginversion"
+msgid "Safe Mode"
+msgstr "Safe Mode"
-#: advanced-scripts-quicknav.php:1178
-msgid "WordPress Install Type"
-msgstr "WordPress Installationstyp"
+msgid "Save my preferences"
+msgstr "Meine Einstellungen speichern"
-#: advanced-scripts-quicknav.php:1179
-msgid "Multisite install"
-msgstr "Multisite-Installation"
+msgid "Saved favorites"
+msgstr "Gespeicherte Favoriten"
-#: advanced-scripts-quicknav.php:1179
-msgid "Single Site install"
-msgstr "Single-Site-Installation"
+msgid "Scripts"
+msgstr "Skripte"
-#: advanced-scripts-quicknav.php:1225
-msgid "Plugin not installed"
-msgstr "Plugin nicht installiert"
+msgid "Scripts by Folder"
+msgstr "Skripte nach Ordnern"
-#: advanced-scripts-quicknav.php:1270
-msgctxt "Plugins page listing"
-msgid "Donate"
-msgstr "Spenden"
+msgid "Select up to 100 scripts. Deleted scripts disappear from the list automatically. The limit above controls how many favorites appear in the toolbar."
+msgstr "Wählen Sie bis zu 100 Scripts. Gelöschte Scripts verschwinden automatisch aus der Liste. Das obige Limit bestimmt, wie viele Favoriten in der Toolbar erscheinen."
-#: advanced-scripts-quicknav.php:1276
-msgctxt "Plugins page listing"
-msgid "Join our Newsletter"
-msgstr "Jetzt Newsletter"
+msgid "Show developer links"
+msgstr "Entwicklerlinks anzeigen"
-#. Plugin URI of the plugin/theme
-msgid "https://github.com/deckerweb/advanced-scripts-quicknav"
-msgstr "https://github.com/deckerweb/advanced-scripts-quicknav"
+msgid "Show script count in the toolbar"
+msgstr "Script-Zähler in der Toolbar anzeigen"
-#. Description of the plugin/theme
-msgid ""
-"For Script and Code Snippets enthusiasts: Adds a quick-access navigator (aka "
-"QuickNav) to the WordPress Admin Bar (Toolbar). It allows easy access to "
-"your Scripts/ Code Snippets listed by Active, Inactive or Folder group. Safe "
-"Mode is supported. Comes with inspiring links to snippet libraries."
-msgstr ""
-"Für Fans von Skripten und Codeschnipseln: Fügt der WordPress Admin Bar "
-"(Toolbar) einen Schnellzugriffsnavigator (auch QuickNav genannt) hinzu. Dies "
-"ermöglicht den einfachen Zugriff auf alle Skripte/ Code Snippets, die nach "
-"Status (aktiv/ inaktiv) oder Ordnergruppe aufgelistet sind. Der Safe Mode "
-"wird unterstützt. Enthält inspirierende Links zu Snippet-Bibliotheken."
-
-#. Author of the plugin/theme
-msgid "David Decker – DECKERWEB"
-msgstr "David Decker - DECKERWEB"
-
-#. Author URI of the plugin/theme
-msgid "https://deckerweb.de/"
-msgstr "https://deckerweb.de/"
+msgid "Site Health Info"
+msgstr "Website-Zustand Bericht"
+
+msgid "Snippet types"
+msgstr "Snippet-Typen"
+
+msgid "Snippets Library by WPCode"
+msgstr "Snippet-Bibliothek von WPCode"
+
+msgid "Snippets Library by dPlugins"
+msgstr "Snippet-Bibliothek von dPlugins"
+
+msgid "Support the project"
+msgstr "Projekt unterstützen"
+
+msgid "System Dashboard"
+msgstr "System-Dashboard"
+
+msgid "The GitHub package could not be prepared. The installed version has been kept."
+msgstr "Das GitHub-Paket konnte nicht vorbereitet werden. Die installierte Version bleibt erhalten."
+
+msgid "The GitHub package does not contain the Advanced Scripts QuickNav plugin file."
+msgstr "Das GitHub-Paket enthält die Plugin-Datei von Advanced Scripts QuickNav nicht."
+
+msgid "The package identity or version does not match a newer Advanced Scripts QuickNav release."
+msgstr "Die Paketidentität oder Version entspricht keinem neueren Advanced-Scripts-QuickNav-Release."
+
+msgid "The package version differs from the offered update. Please check for updates again."
+msgstr "Die Paketversion weicht vom angebotenen Update ab. Bitte prüfe erneut auf Updates."
+
+msgid "The update filesystem is unavailable. Please try again."
+msgstr "Das Dateisystem für Updates ist nicht verfügbar. Bitte versuche es erneut."
+
+msgid "The update package could not be checked."
+msgstr "Das Update-Paket konnte nicht geprüft werden."
+
+msgid "The update package has missing or invalid WordPress/PHP requirements."
+msgstr "Die WordPress- oder PHP-Voraussetzungen im Update-Paket fehlen oder sind ungültig."
+
+msgid "These preferences apply only to your account on this website. Constants in wp-config.php take precedence over display preferences."
+msgstr "Diese Einstellungen gelten nur für Ihr Konto auf dieser Website. Konstanten in wp-config.php haben Vorrang vor Anzeigeoptionen."
+
+msgid "This release requires a newer WordPress or PHP version. The installed version has been kept."
+msgstr "Dieses Release benötigt eine neuere WordPress- oder PHP-Version. Die installierte Version bleibt erhalten."
+
+msgid "Tools"
+msgstr "Werkzeuge"
+
+msgid "Untitled script"
+msgstr "Script ohne Titel"
+
+msgid "Variable Inspector"
+msgstr "Variablen-Inspektor"
+
+msgid "Version"
+msgstr "Version"
+
+msgid "View all in Advanced Scripts…"
+msgstr "Alle in Advanced Scripts anzeigen…"
+
+msgid "WP SnippetClub Archive"
+msgstr "WP SnippetClub Archiv"
+
+msgid "WP Snippets Library"
+msgstr "WP Snippets Bibliothek"
+
+msgid "WPCodeBin by WPCodeBox"
+msgstr "WPCodeBin von WPCodeBox"
+
+msgid "Your favorites"
+msgstr "Ihre Favoriten"
+
+msgid "Your preferences have been saved."
+msgstr "Ihre Einstellungen wurden gespeichert."
+
+msgid "Your scripts. One click away."
+msgstr "Ihre Scripts. Einen Klick entfernt."
+
+msgid "Your snippet overview"
+msgstr "Ihre Snippet-Übersicht"
+
+msgid "active"
+msgstr "aktiv"
+
+msgid "administration"
+msgstr "Adminbereich"
+
+msgid "everywhere"
+msgstr "überall"
+
+msgid "front-end"
+msgstr "Frontend"
+
+msgid "inactive"
+msgstr "inaktiv"
+
+msgid "shortcode"
+msgstr "Shortcode"
+
+msgid "★ Favorites"
+msgstr "★ Favoriten"
diff --git a/languages/advanced-scripts-quicknav.pot b/languages/advanced-scripts-quicknav.pot
index c404e03..205e433 100644
--- a/languages/advanced-scripts-quicknav.pot
+++ b/languages/advanced-scripts-quicknav.pot
@@ -1,258 +1,314 @@
-#, fuzzy
msgid ""
+msgstr "Project-Id-Version: Advanced Scripts QuickNav 1.2.0\nContent-Type: text/plain; charset=UTF-8\n"
+
+msgid "About"
msgstr ""
-"Project-Id-Version: Advanced Scripts QuickNav\n"
-"POT-Creation-Date: 2025-04-05 13:22+0200\n"
-"PO-Revision-Date: 2025-03-24 09:43+0100\n"
-"Last-Translator: David Decker DECKERWEB.de \n"
-"Language-Team: David Decker DECKERWEB.de \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
-"X-Generator: Poedit 3.6\n"
-"X-Poedit-Basepath: ..\n"
-"X-Poedit-Flags-xgettext: --add-comments=translators:\n"
-"X-Poedit-WPHeader: advanced-scripts-quicknav.php\n"
-"X-Poedit-SourceCharset: UTF-8\n"
-"X-Poedit-KeywordsList: "
-"__;_e;_n:1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;esc_attr__;esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c;_n_noop:1,2;_nx_noop:3c,1,2;__ngettext_noop:1,2\n"
-"X-Poedit-SearchPath-0: .\n"
-"X-Poedit-SearchPathExcluded-0: *.min.js\n"
-
-#: advanced-scripts-quicknav.php:348
-msgctxt "Script/ Snippet scope: everywhere (global)"
-msgid "everywhere"
+
+msgid "Activate Advanced Scripts first."
msgstr ""
-#: advanced-scripts-quicknav.php:352
-msgctxt "Script/ Snippet scope: front-end"
-msgid "front-end"
+msgid "Activate Advanced Scripts first. Your saved preferences are kept."
msgstr ""
-#: advanced-scripts-quicknav.php:424
-msgid "Scripts"
+msgid "Active"
msgstr ""
-#: advanced-scripts-quicknav.php:482
-msgid "SAFE MODE is active 🔥"
+msgid "Active Scripts"
+msgstr ""
+
+msgid "Active and inactive refer to saved snippet status. Folder blocks are counted within active snippets; conditions and execution location can also affect execution."
+msgstr ""
+
+msgid "Active, blocked by folder"
+msgstr ""
+
+msgid "Add New"
+msgstr ""
+
+msgid "Add script here…"
+msgstr ""
+
+msgid "Adminer: Manage DB"
+msgstr ""
+
+msgid "Advanced Scripts"
msgstr ""
-#: advanced-scripts-quicknav.php:490
msgid "Advanced Scripts Safe Mode active 🔥"
msgstr ""
-#: advanced-scripts-quicknav.php:501 advanced-scripts-quicknav.php:509
-msgid "SCRIPT_DEBUG is on ⚠"
+msgid "All snippets"
msgstr ""
-#: advanced-scripts-quicknav.php:591
-msgid "Active Scripts"
+msgid "Applies to each status list, favorites, and the folder tree. Additional entries remain accessible in Advanced Scripts."
msgstr ""
-#: advanced-scripts-quicknav.php:642
-msgid "Inactive Scripts"
+msgid "Author: David Decker"
msgstr ""
-#: advanced-scripts-quicknav.php:687
-msgid "Scripts by Folder"
+msgid "Buy Me a Coffee"
msgstr ""
-#: advanced-scripts-quicknav.php:702
-msgid "active"
+msgid "CP Facebook Group (official)"
msgstr ""
-#: advanced-scripts-quicknav.php:702
-msgid "inactive"
+msgid "CP YouTube Channel"
msgstr ""
-#: advanced-scripts-quicknav.php:745
-msgid "Add New"
+msgid "Changelog"
msgstr ""
-#: advanced-scripts-quicknav.php:754
-msgid "Code Script"
+msgid "Choose favorites"
msgstr ""
-#: advanced-scripts-quicknav.php:786
-msgid "System Dashboard"
+msgid "Clean Plugins Blog"
msgstr ""
-#: advanced-scripts-quicknav.php:797
-msgid "Site Health Info"
+msgid "Close"
msgstr ""
-#: advanced-scripts-quicknav.php:808
-msgid "Variable Inspector"
+msgid "Code"
+msgstr ""
+
+msgid "Code Script"
+msgstr ""
+
+msgid "Code Snippets Cloud"
+msgstr ""
+
+msgid "Codes by Web Squadron"
msgstr ""
-#: advanced-scripts-quicknav.php:824
msgid "Debug Log Manager"
msgstr ""
-#: advanced-scripts-quicknav.php:844
-msgid "File Manager"
+msgid "Disabled"
+msgstr ""
+
+msgid "Disabled by folder"
+msgstr ""
+
+msgid "Display preferences"
+msgstr ""
+
+msgid "Documentation"
+msgstr ""
+
+msgid "Emergency Fixes 🔥"
+msgstr ""
+
+msgid "Enabled"
msgstr ""
-#: advanced-scripts-quicknav.php:852
msgid "Error Logs"
msgstr ""
-#: advanced-scripts-quicknav.php:861
-msgid "Adminer: Manage DB"
+msgid "File Manager"
msgstr ""
-#: advanced-scripts-quicknav.php:870
-msgid "Tools"
+msgid "Filter by title or folder"
msgstr ""
-#: advanced-scripts-quicknav.php:905
msgid "Find Snippets"
msgstr ""
-#: advanced-scripts-quicknav.php:913
-msgid "Code Snippets Cloud"
+msgid "Folder"
msgstr ""
-#: advanced-scripts-quicknav.php:917
-msgid "WP Snippets Library"
+msgid "Folders"
msgstr ""
-#: advanced-scripts-quicknav.php:921
-msgid "Codes by Web Squadron"
+msgid "Inactive"
msgstr ""
-#: advanced-scripts-quicknav.php:925
-msgid "WP SnippetClub Archive"
+msgid "Inactive Scripts"
msgstr ""
-#: advanced-scripts-quicknav.php:929
-msgid "Snippets Library by dPlugins"
+msgid "Links"
msgstr ""
-#: advanced-scripts-quicknav.php:933
-msgid "WPCodeBin by WPCodeBox"
+msgid "Manage favorites…"
msgstr ""
-#: advanced-scripts-quicknav.php:937
-msgid "Snippets Library by WPCode"
+msgid "Menu entry limit"
msgstr ""
-#: advanced-scripts-quicknav.php:981
-msgid "Links"
+msgid "Newsletter"
msgstr ""
-#: advanced-scripts-quicknav.php:989
-msgid "Advanced Scripts"
+msgid "No favorites selected yet. Choose snippets below to build your quick-access list."
msgstr ""
-#: advanced-scripts-quicknav.php:993
-msgid "Emergency Fixes 🔥"
+msgid "No scripts yet. Create your first script in Advanced Scripts."
msgstr ""
-#: advanced-scripts-quicknav.php:997
-msgid "Clean Plugins Blog"
+msgid "Not active"
msgstr ""
-#: advanced-scripts-quicknav.php:1001
-msgid "CP YouTube Channel"
+msgid "Open a favorite directly from its card. This preview follows your selection; save to update the toolbar."
msgstr ""
-#: advanced-scripts-quicknav.php:1005
-msgid "CP Facbook Group (official)"
+msgid "Permission denied."
msgstr ""
-#: advanced-scripts-quicknav.php:1033
-msgid "About"
+msgid "Personal favorites"
msgstr ""
-#: advanced-scripts-quicknav.php:1041
-msgid "Author: David Decker"
+msgid "Please select no more than 100 favorites."
+msgstr ""
+
+msgid "Plugin information"
msgstr ""
-#: advanced-scripts-quicknav.php:1045
msgid "Plugin on GitHub"
msgstr ""
-#: advanced-scripts-quicknav.php:1049
-msgid "Buy Me a Coffee"
+msgid "Plugin version"
msgstr ""
-#: advanced-scripts-quicknav.php:1161
-msgctxt "Site Health Debug info"
-msgid "Undefined"
+msgid "Plugin website"
msgstr ""
-#: advanced-scripts-quicknav.php:1162
-msgctxt "Site Health Debug info"
-msgid "Enabled"
+msgid "Preferences"
msgstr ""
-#: advanced-scripts-quicknav.php:1163
-msgctxt "Site Health Debug info"
-msgid "Disabled"
+msgid "QuickNav preferences"
msgstr ""
-#: advanced-scripts-quicknav.php:1164
-msgctxt "Site Health Debug info"
-msgid "value"
+msgid "Reach your scripts through favorites, status lists and folders in the WordPress toolbar."
msgstr ""
-#. Plugin Name of the plugin/theme
-#: advanced-scripts-quicknav.php:1169
-msgid "Advanced Scripts QuickNav"
+msgid "SAFE MODE is active 🔥"
msgstr ""
-#: advanced-scripts-quicknav.php:1169
-msgid "Plugin"
+msgid "SCRIPT_DEBUG is on ⚠"
msgstr ""
-#: advanced-scripts-quicknav.php:1174
-msgid "Plugin version"
+msgid "SCSS partial"
msgstr ""
-#: advanced-scripts-quicknav.php:1178
-msgid "WordPress Install Type"
+msgid "Safe Mode"
msgstr ""
-#: advanced-scripts-quicknav.php:1179
-msgid "Multisite install"
+msgid "Save my preferences"
msgstr ""
-#: advanced-scripts-quicknav.php:1179
-msgid "Single Site install"
+msgid "Saved favorites"
msgstr ""
-#: advanced-scripts-quicknav.php:1225
-msgid "Plugin not installed"
+msgid "Scripts"
msgstr ""
-#: advanced-scripts-quicknav.php:1270
-msgctxt "Plugins page listing"
-msgid "Donate"
+msgid "Scripts by Folder"
msgstr ""
-#: advanced-scripts-quicknav.php:1276
-msgctxt "Plugins page listing"
-msgid "Join our Newsletter"
+msgid "Select up to 100 scripts. Deleted scripts disappear from the list automatically. The limit above controls how many favorites appear in the toolbar."
msgstr ""
-#. Plugin URI of the plugin/theme
-msgid "https://github.com/deckerweb/advanced-scripts-quicknav"
+msgid "Show developer links"
msgstr ""
-#. Description of the plugin/theme
-msgid ""
-"For Script and Code Snippets enthusiasts: Adds a quick-access navigator "
-"(aka QuickNav) to the WordPress Admin Bar (Toolbar). It allows easy access "
-"to your Scripts/ Code Snippets listed by Active, Inactive or Folder group. "
-"Safe Mode is supported. Comes with inspiring links to snippet libraries."
+msgid "Show script count in the toolbar"
+msgstr ""
+
+msgid "Site Health Info"
+msgstr ""
+
+msgid "Snippet types"
+msgstr ""
+
+msgid "Snippets Library by WPCode"
+msgstr ""
+
+msgid "Snippets Library by dPlugins"
+msgstr ""
+
+msgid "Support the project"
+msgstr ""
+
+msgid "System Dashboard"
+msgstr ""
+
+msgid "The GitHub package could not be prepared. The installed version has been kept."
+msgstr ""
+
+msgid "The GitHub package does not contain the Advanced Scripts QuickNav plugin file."
+msgstr ""
+
+msgid "The package identity or version does not match a newer Advanced Scripts QuickNav release."
+msgstr ""
+
+msgid "The package version differs from the offered update. Please check for updates again."
+msgstr ""
+
+msgid "The update filesystem is unavailable. Please try again."
+msgstr ""
+
+msgid "The update package could not be checked."
+msgstr ""
+
+msgid "The update package has missing or invalid WordPress/PHP requirements."
+msgstr ""
+
+msgid "These preferences apply only to your account on this website. Constants in wp-config.php take precedence over display preferences."
+msgstr ""
+
+msgid "This release requires a newer WordPress or PHP version. The installed version has been kept."
+msgstr ""
+
+msgid "Tools"
+msgstr ""
+
+msgid "Untitled script"
+msgstr ""
+
+msgid "Variable Inspector"
+msgstr ""
+
+msgid "Version"
+msgstr ""
+
+msgid "View all in Advanced Scripts…"
+msgstr ""
+
+msgid "WP SnippetClub Archive"
+msgstr ""
+
+msgid "WP Snippets Library"
+msgstr ""
+
+msgid "WPCodeBin by WPCodeBox"
+msgstr ""
+
+msgid "Your favorites"
+msgstr ""
+
+msgid "Your preferences have been saved."
+msgstr ""
+
+msgid "Your scripts. One click away."
+msgstr ""
+
+msgid "Your snippet overview"
+msgstr ""
+
+msgid "active"
+msgstr ""
+
+msgid "administration"
+msgstr ""
+
+msgid "everywhere"
+msgstr ""
+
+msgid "front-end"
+msgstr ""
+
+msgid "inactive"
msgstr ""
-#. Author of the plugin/theme
-msgid "David Decker – DECKERWEB"
+msgid "shortcode"
msgstr ""
-#. Author URI of the plugin/theme
-msgid "https://deckerweb.de/"
+msgid "★ Favorites"
msgstr ""
diff --git a/readme-de.txt b/readme-de.txt
new file mode 100644
index 0000000..ea5e9cc
--- /dev/null
+++ b/readme-de.txt
@@ -0,0 +1,135 @@
+=== Advanced Scripts QuickNav ===
+Contributors: deckerweb
+Tags: advanced scripts, admin bar, toolbar, code snippets, favorites
+Requires at least: 6.7
+Requires PHP: 8.0
+Stable tag: 1.2.0
+License: GPL-2.0-or-later
+License URI: https://www.gnu.org/licenses/gpl-2.0.html
+
+Deine Scripts. Einen Klick entfernt.
+
+== Description ==
+
+- Persönliche Favoriten je Benutzer und Website.
+- Script-Links im Ordnerbaum und Script hier anlegen.
+- Eigener Aktivstatus plus Hinweise auf deaktivierte Elternordner.
+- Typ, Ordnerpfad, Ausführungsort und Hook als Zusatzinfos.
+- Visuelle Favoritenkarten, Snippet-Statistik, Anzeigeoptionen, Safe-Mode-Hinweise, SCRIPT_DEBUG und optionale Entwicklerlinks.
+- Bestehende Ressourcenlinks und Unterstützung für DevKit Pro, System Dashboard, Variable Inspector und Debug Log Manager bleiben enthalten.
+
+== Installation ==
+
+1. Lade das Release-ZIP hoch: Plugins → Plugin hinzufügen → Plugin hochladen.
+2. Aktiviere Advanced Scripts und QuickNav.
+3. Öffne Einstellungen → Advanced Scripts QuickNav und wähle Favoriten.
+4. Speichere deine Einstellungen und öffne Scripts in der Toolbar.
+
+Alternativ: Importiere ddw-advanced-scripts-quicknav.as.json in Advanced Scripts. Verwende nur eine Installationsart. Der PHP-Safe-Mode kann das Snippet im Admin blockieren; Updater und deckerweb Library sind nur im Plugin enthalten.
+
+Der Code wurde mit Advanced Scripts 2.6.2 abgeglichen und rc3 vom Plugin-Autor erfolgreich getestet. Die umfassende WordPress-Integrationsmatrix bleibt zurückgestellt. PHP 7.4 wird wegen der gemeinsamen Library nicht mehr unterstützt.
+
+== Frequently Asked Questions ==
+
+= Für wen ist QuickNav gedacht? =
+
+Für Administratoren, die häufig mit Advanced Scripts arbeiten und aus der Backend- oder Frontend-Toolbar direkt auf Scripts zugreifen möchten.
+
+= Wo wähle ich Favoriten aus? =
+
+Öffne Einstellungen → Advanced Scripts QuickNav. Sieh klickbare Favoritenkarten und die Snippet-Statistik, filtere nach Titel oder Ordner, wähle Scripts und speichere deine Einstellungen.
+
+= Was bedeutet Durch Ordner deaktiviert? =
+
+Mindestens ein übergeordneter Ordner ist inaktiv. Advanced Scripts überspringt diesen Zweig auch dann, wenn das Script selbst aktiv ist.
+
+= Bedeutet Aktiv, dass ein Script gerade ausgeführt wird? =
+
+Nein. Gemeint ist der gespeicherte Script-Status. Ordnerstatus, Ausführungsort, Bedingungen, Hooks und PHP-Safe-Mode können die Ausführung beeinflussen.
+
+= Wie erkennt QuickNav den Safe Mode? =
+
+Eine definierte Konstante AS_SAFE_MODE hat Vorrang, auch mit false. Andernfalls liest QuickNav die Option advanced-scripts-safemode. Es verändert den Safe Mode nicht.
+
+= Wie funktionieren Updates? =
+
+Das Plugin integriert deckerweb GitHub Updater V2 in das reguläre WordPress-Updatesystem. Er prüft öffentliche stabile GitHub-Releases und stellt das passende Plugin-ZIP bereit. Automatische Updates werden nicht eingeschaltet.
+
+= Kann ich stattdessen das Snippet installieren? =
+
+Ja. Importiere das erzeugte JSON in Advanced Scripts und aktiviere es mit dem Hook plugins_loaded. Es enthält Navigation und persönliche Einstellungen, aber keinen Plugin-Updater und keine deckerweb Library. Verwende entweder Plugin oder Snippet.
+
+== Changelog ==
+
+= 1.2.0 · 2026-10-01 =
+
+* Neu: Persönliche Favoriten mit klickbaren Karten, direkter Auswahlvorschau und Filter nach Titel oder Ordner.
+* Neu: Snippet-Statistik, Script-Links im Ordnerbaum und die Aktion Script hier hinzufügen.
+* Verbessert: Modulare Metadaten-Anbindung an Advanced Scripts, Script-Details, Hinweise auf Ordnersperren und begrenzte Menüs.
+* Verbessert: Gemeinsamer deckerweb GitHub Updater V2, deckerweb Library, Einstellungs-Header/-Footer und zugänglicher HTML-Changelog-Dialog.
+* Verbessert: Code-Compass-Grafiken sowie abgestimmte deutsche/englische Dokumentation, FAQs und Übersetzungen.
+* Behoben: Safe-Mode-Erkennung, Verarbeitung der Link-Filter, Menü-IDs, Sichtbarkeitsoptionen und der veraltete Toolbar-Callback, der in rc2 einen Fatal Error verursachte.
+* Sonstiges: Benötigt WordPress 6.7+ und PHP 8.0+. Entfernt Vollbild-Blockeditor-Anpassungen. Plugin und eigenständiges Snippet entstehen aus derselben Quelle.
+* Sonstiges: Veröffentlicht nach erfolgreichem Benutzertest von rc3 und gezielten Release-Prüfungen. Die umfassende WordPress-Integrationsmatrix bleibt zurückgestellt.
+
+= 1.2.0-rc3 · 2026-10-01 (Testkandidat) =
+
+* Behoben: Verbliebene admin_bar_menu-Registrierung der entfernten Methode remove_adminbar_nodes gelöscht; verhindert einen Fatal Error bei aktivierten Expertenlinks.
+* Verbessert: Regressionstests erkennen jetzt ungültige Action-Callbacks.
+
+= 1.2.0-rc2 · 2026-10-01 (Testkandidat) =
+
+* Neu: Visuelle Favoritenkarten mit direkten Editor-Links und sofortiger Vorschau der aktuellen Auswahl.
+* Neu: Kompakte Snippet-Statistik mit Gesamtzahl, Aktiv-/Inaktiv-Zahlen, Ordnern, gespeicherten Favoriten, durch Ordner blockierten aktiven Snippets und Typverteilung.
+* Verbessert: Formatiert den lokalen Changelog-Dialog als maskiertes HTML mit Versions- und Datumsüberschriften, lesbaren Listen und dezenten Kategorie-Badges nach dem gemeinsamen deckerweb-Footer-Standard.
+* Verbessert: Behält die gewählte Gestaltung Code Compass (Variante A) bei und aktualisiert die abgestimmten deutschen und englischen Dokumentationen und Übersetzungen.
+* Sonstiges: Entfernt die Vollbild-Blockeditor-Option, zugehörige Hooks, CSS-Anpassungen und das Entfernen des WordPress-Logos.
+* Sonstiges: Vorhandene Favoriten bleiben erhalten. Die umfassende WordPress-Integrationsmatrix bleibt auf später verschoben.
+
+= 1.2.0-rc1 · 2026-10-01 (Testkandidat) =
+
+* Neu: Persönliche Favoriten getrennt nach Benutzer und Website, mit Filter nach Titel oder Ordner auf der Einstellungsseite.
+* Neu: Direkte Script-Links im Ordnerbaum und die Aktion Script hier anlegen für jeden Ordner.
+* Neu: Persönliche Einstellungen für Script-Zähler, Entwicklerlinks, Vollbild-Toolbar und begrenzte Menülisten.
+* Verbessert: Zeigt Durch Ordner deaktiviert an, ohne den eigenen Aktivstatus umzudeuten; ergänzt Typ, Ordnerpfad, Ausführungsort und Hook.
+* Verbessert: Trennt Konfiguration, Advanced-Scripts-Adapter, Navigation, Darstellung, persönliche Einstellungen und Hook-Registrierung.
+* Verbessert: Integriert den gemeinsamen deckerweb GitHub Updater V2, sprachabhängige Update-Grafiken und die deckerweb Library 0.2.0.
+* Verbessert: Ergänzt den deckerweb-Header und -Footer der Einstellungsseite sowie einen zugänglichen lokalen Changelog-Dialog.
+* Behoben: Erkennt den über die Advanced-Scripts-Oberfläche aktivierten Safe Mode und berücksichtigt den Vorrang von AS_SAFE_MODE.
+* Behoben: Übernimmt Link-Filter-Rückgaben, trennt die Sichtbarkeit von Bibliothek und Footer und berücksichtigt leere Sammlungen und eigene Admin-Farbschemata.
+* Behoben: Verwendet gemeinsame Berechtigungsprüfungen für Toolbar und Vollbildintegration; schützt Einstellungen durch POST, Berechtigungen und Nonce.
+* Sonstiges: Benötigt PHP 8.0+, behält WordPress 6.7+ bei und liefert abgestimmte deutsche und englische Readmes, FAQ, Wiki-Quellen und vollständige Changelogs.
+* Sonstiges: Enthält vorläufig Design A; drei Icon- und Banner-Alternativen werden separat zur Auswahl bereitgestellt.
+* Sonstiges: Erzeugt das eigenständige Advanced-Scripts-JSON-Snippet aus derselben Quelle. Updater und deckerweb Library sind der Plugin-Installation vorbehalten.
+* Sonstiges: Dies ist ein lokaler Testkandidat. Die umfassende WordPress-Integrationsprüfung und die öffentliche Veröffentlichung stehen noch aus.
+
+= 1.1.0 · 2025-04-05 =
+
+* Neu: Beschränkt QuickNav optional mit ASQN_ENABLED_USERS auf festgelegte Benutzer-IDs.
+* Verbessert: Unterstützt Installation und Aktualisierung mit Git Updater.
+* Behoben: Behebt eine PHP-Warnung im Frontend.
+
+= 1.0.0 · 2025-03-24 =
+
+* Neu: Erste öffentliche Version mit Statuslisten, Ordnerlinks, Safe-Mode-Hinweisen, Ressourcenlinks, Entwickleranbindungen und Konstanten.
+
+= 0.5.0 · 2025-03-23 =
+
+* Sonstiges: Interne Testversion.
+
+= 0.0.0 · 2025-03-23 =
+
+* Sonstiges: Entwicklungsbeginn.
+
+== Upgrade Notice ==
+
+= 1.2.0 =
+Benötigt PHP 8.0+. Ergänzt Favoriten, Statistik und deckerweb-Updates. Entweder Plugin oder Snippet installieren.
+
+
+= 1.2.0-rc3 =
+Behoben: Verhindert den Fatal Error durch den ungültigen Toolbar-Callback. rc2 durch rc3 ersetzen.
+
+
+= 1.2.0-rc2 =
+Testkandidat. Benötigt jetzt PHP 8.0+. Umfassende Integrationsprüfung folgt später.
diff --git a/readme.txt b/readme.txt
new file mode 100644
index 0000000..a67461f
--- /dev/null
+++ b/readme.txt
@@ -0,0 +1,135 @@
+=== Advanced Scripts QuickNav ===
+Contributors: deckerweb
+Tags: advanced scripts, admin bar, toolbar, code snippets, favorites
+Requires at least: 6.7
+Requires PHP: 8.0
+Stable tag: 1.2.0
+License: GPL-2.0-or-later
+License URI: https://www.gnu.org/licenses/gpl-2.0.html
+
+Your scripts. One click away.
+
+== Description ==
+
+- Personal favorites per user and website.
+- Script links in the folder tree and Add script here.
+- Own activation status plus inactive-ancestor hints.
+- Type, folder path, execution location and hook details.
+- Visual favorite cards, snippet statistics, display preferences, Safe Mode notices, SCRIPT_DEBUG and optional developer links.
+- Existing resource links and integrations with DevKit Pro, System Dashboard, Variable Inspector and Debug Log Manager remain included.
+
+== Installation ==
+
+1. Upload the release ZIP through Plugins → Add New → Upload Plugin.
+2. Activate Advanced Scripts and QuickNav.
+3. Open Settings → Advanced Scripts QuickNav and select favorites.
+4. Save preferences and open Scripts in the toolbar.
+
+Alternatively, import ddw-advanced-scripts-quicknav.as.json into Advanced Scripts. Use one installation mode only. PHP Safe Mode can block the snippet in the admin; updater and deckerweb Library are plugin-only.
+
+Source was reviewed against Advanced Scripts 2.6.2 and rc3 was successfully tested by the plugin author. The broader WordPress integration matrix remains deferred. PHP 7.4 is no longer supported because of the shared Library.
+
+== Frequently Asked Questions ==
+
+= Who is QuickNav for? =
+
+Administrators who frequently use Advanced Scripts and want direct access from the backend and frontend toolbar.
+
+= Where do I select favorites? =
+
+Open Settings → Advanced Scripts QuickNav. See clickable favorite cards and snippet statistics, filter by title or folder, select scripts and save your preferences.
+
+= What does Disabled by folder mean? =
+
+The script has at least one inactive ancestor folder. Advanced Scripts skips that branch even if the script itself is active.
+
+= Does Active mean currently running? =
+
+No. It is the saved script flag. Folder status, execution location, conditions, hooks and PHP Safe Mode can affect execution.
+
+= How does Safe Mode detection work? =
+
+A defined AS_SAFE_MODE takes precedence, including false. Otherwise QuickNav reads the advanced-scripts-safemode option. It does not change Safe Mode.
+
+= How do updates work? =
+
+The plugin integrates deckerweb GitHub Updater V2 with the regular WordPress update system. It checks public stable GitHub releases and provides the matching plugin ZIP. It does not enable automatic updates.
+
+= Can I install the snippet instead? =
+
+Yes. Import the generated JSON into Advanced Scripts and enable it with the plugins_loaded hook. It contains navigation and personal preferences, but no plugin updater or deckerweb Library. Use either the plugin or the snippet.
+
+== Changelog ==
+
+= 1.2.0 · 2026-10-01 =
+
+* New: Personal favorites with clickable cards, a live selection preview and filtering by title or folder.
+* New: Snippet statistics, folder-tree script links and an Add script here shortcut.
+* Improved: Modular metadata-only integration with Advanced Scripts, script details, folder-blocking hints and bounded menus.
+* Improved: Shared deckerweb GitHub Updater V2, deckerweb Library, settings header/footer and an accessible HTML changelog dialog.
+* Improved: Code Compass artwork and synchronized English/German documentation, FAQs and translations.
+* Fixed: Safe Mode detection, link-filter handling, menu IDs, visibility controls and the obsolete toolbar callback that caused a fatal error in rc2.
+* Misc: Requires WordPress 6.7+ and PHP 8.0+. Removes fullscreen block-editor adjustments. Plugin and standalone snippet are built from one source.
+* Misc: Released after successful user testing of rc3 and focused release checks. The broader WordPress integration matrix remains deferred.
+
+= 1.2.0-rc3 · 2026-10-01 (test candidate) =
+
+* Fixed: Removed the remaining admin_bar_menu registration for the deleted remove_adminbar_nodes method, preventing a fatal error when expert links are enabled.
+* Improved: Regression checks now reject invalid action callbacks.
+
+= 1.2.0-rc2 · 2026-10-01 (test candidate) =
+
+* New: Visual favorites cards with direct editor links and a live preview of the current selection.
+* New: A compact snippet overview with totals, active/inactive counts, folders, saved favorites, active snippets blocked by folders and a type breakdown.
+* Improved: Formats the local changelog dialog as escaped HTML with version/date headings, readable lists and subtle category badges, following the shared deckerweb footer standard.
+* Improved: Keeps the selected Code Compass artwork (variant A) and updates synchronized English/German documentation and translations.
+* Misc: Removes the fullscreen block-editor preference, hooks, CSS adjustments and WordPress-logo removal.
+* Misc: Existing favorites remain unchanged. The broad WordPress integration matrix remains deferred.
+
+= 1.2.0-rc1 · 2026-10-01 (test candidate) =
+
+* New: Personal favorites stored separately for each user and website, with a filter by title or folder on the preferences page.
+* New: Direct script links in the folder tree and an Add script here shortcut for each folder.
+* New: Personal preferences for script counts, developer links, fullscreen toolbar and bounded menu lists.
+* Improved: Shows Disabled by folder without changing the script’s own active/inactive status; includes type, folder path, location and hook details.
+* Improved: Separates configuration, the Advanced Scripts adapter, navigation, rendering, personal settings and hook registration.
+* Improved: Adds the shared deckerweb GitHub Updater V2, localized update artwork and embedded deckerweb Library 0.2.0.
+* Improved: Adds the deckerweb settings header/footer and an accessible local changelog dialog.
+* Fixed: Recognizes Safe Mode enabled through the Advanced Scripts interface, respecting AS_SAFE_MODE precedence.
+* Fixed: Applies link-filter return values, separates library/footer visibility and handles empty collections and custom admin color schemes.
+* Fixed: Uses shared permission checks for the toolbar and fullscreen integration; protects preferences with POST, capabilities and a nonce.
+* Misc: Requires PHP 8.0+, preserves WordPress 6.7+, and provides synchronized English/German readmes, FAQs, Wiki sources and complete changelogs.
+* Misc: Includes provisional design A; three icon/banner alternatives are supplied separately for selection.
+* Misc: Generates the standalone Advanced Scripts JSON snippet from the same source. Updater and deckerweb Library are plugin-only.
+* Misc: This is a local test candidate. Full WordPress integration validation and public release publication are pending.
+
+= 1.1.0 · 2025-04-05 =
+
+* New: Optionally restrict QuickNav to defined user IDs with ASQN_ENABLED_USERS.
+* Improved: Supports installation and updates with Git Updater.
+* Fixed: Addresses a PHP warning on the frontend.
+
+= 1.0.0 · 2025-03-24 =
+
+* New: Initial public release with status lists, folder links, Safe Mode notices, resource links, developer integrations and constants.
+
+= 0.5.0 · 2025-03-23 =
+
+* Misc: Internal test version.
+
+= 0.0.0 · 2025-03-23 =
+
+* Misc: Development started.
+
+== Upgrade Notice ==
+
+= 1.2.0 =
+Requires PHP 8.0+. Adds favorites, statistics and deckerweb updates. Install either plugin or snippet.
+
+
+= 1.2.0-rc3 =
+Fixed: Prevents the invalid toolbar callback fatal error. Replace rc2 with rc3.
+
+
+= 1.2.0-rc2 =
+Test candidate. Now requires PHP 8.0+. Full integration validation follows later.
diff --git a/tests/fixture.php b/tests/fixture.php
new file mode 100644
index 0000000..577e68e
--- /dev/null
+++ b/tests/fixture.php
@@ -0,0 +1,37 @@
+';}function checked($a,$b=true,$echo=true){$x=$a==$b?'checked="checked"':'';if($echo)echo $x;return $x;}
+function submit_button($text){echo ''.esc_html($text).'
';}
+function get_user_option($key){return 'custom-scheme';}function sanitize_hex_color($x){return $x;}
+function wp_add_inline_style($handle,$css){$GLOBALS['inline'][$handle]=$css;}
+class FakeBar {public array $nodes=[]; public function add_node($node){$this->nodes[$node['id']]=$node;}public function add_group($node){$this->add_node($node);}public function remove_node($id){unset($this->nodes[$id]);}}
+class FixtureManager {public array $items=[];public function get_scripts(){return $this->items;}}
+$GLOBALS['manager']=new FixtureManager();function cpas_scripts_manager(){return $GLOBALS['manager'];}
+function require_quicknav($root){require_once $root.'/includes/deckerweb-changelog-v1.php';foreach(['config','adapter','favorites','navigation','renderer','settings'] as $part)require_once $root.'/includes/class-asqn-'.$part.'.php';}
+function expect($value,$message){if(!$value)throw new RuntimeException($message);}
+function items(){return [
+['term_id'=>1,'parent'=>0,'title'=>'Shop','type'=>'folder','status'=>false],
+['term_id'=>2,'parent'=>1,'title'=>'Checkout `\n* Behoben: Fehler\n\n== Installation ==\nPrivate text");
+expect(strpos($html,'1.2.0-rc2 ')!==false&&strpos($html,'ddw-changelog-new')!==false,'HTML changelog version and category');
+expect(strpos($html,'