mirror of
https://github.com/Terranom674/Piwigo_Bratonien_Tools.git
synced 2026-09-19 10:34:30 +00:00
Compare commits
15 Commits
8023f7b5d7
...
add913aab1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
add913aab1 | ||
|
|
6a0851a30c | ||
|
|
c057f7922e | ||
|
|
e3d55a1950 | ||
|
|
84dce41b1c | ||
|
|
71577f989f | ||
|
|
9c8e44da09 | ||
|
|
09b6465aad | ||
|
|
17a00eebdb | ||
|
|
199d954409 | ||
|
|
db21cf2c5e | ||
|
|
f0f7556aaf | ||
|
|
68e4cd876d | ||
|
|
43dc600b98 | ||
|
|
fdae62520e |
@@ -2,28 +2,30 @@
|
||||
|
||||
## Version
|
||||
|
||||
**Bratonien Tools 9.7.1.0**
|
||||
**Bratonien Tools 0.9.7.1.7**
|
||||
|
||||
Verbindliches Versionsschema: **0.x.x.x.x**.
|
||||
|
||||
Zielplattform der produktiven NC-Connector-Synchronisierung ist derzeit **Piwigo 16.4.0**.
|
||||
|
||||
## NC Connector
|
||||
|
||||
Der NC Connector besitzt einen produktiven Verbindungsweg: **Nextcloud per WebDAV**.
|
||||
Der produktive Verbindungsweg ist **Nextcloud per WebDAV**.
|
||||
|
||||
### Verbindungsmodell
|
||||
|
||||
- `adapter=remote`
|
||||
- `source_mode=webdav-placeholder`
|
||||
- Nextcloud-Adresse, Benutzer und Passwort/App-Passwort werden pro Verbindung verschlüsselt gespeichert.
|
||||
- Piwigo-API und Benutzername/Passwort-Fallback werden ebenfalls pro Verbindung gespeichert.
|
||||
- Der Assistent zeigt ausschließlich Verzeichnisse, auf die der angemeldete Nextcloud-Benutzer per WebDAV zugreifen kann.
|
||||
- Ein leerer `webdav_path` ist ein gültiger Root und bedeutet den Dateibereich des authentifizierten Nextcloud-Benutzers.
|
||||
- Piwigo-API und Benutzername/Passwort-Fallback werden pro Verbindung gespeichert.
|
||||
- Der Assistent zeigt ausschließlich per WebDAV sichtbare Verzeichnisse.
|
||||
- Ein leerer `webdav_path` ist ein gültiger Benutzer-Root.
|
||||
|
||||
### Root-Verhalten
|
||||
|
||||
Bei `webdav_path=""` wird die technische Nextcloud-Benutzerebene nicht als Piwigo-Album angelegt.
|
||||
|
||||
Direkte Unterordner des Nextcloud-Benutzerroots werden als Top-Level-Piwigo-Alben gespiegelt. Direkt im Root liegende Einzelbilder verbleiben auf Root-Ebene des verbindungseigenen Shadow Trees und werden durch die bestehende Orphan-Logik behandelt.
|
||||
Direkte Unterordner des Nextcloud-Benutzerroots werden als Top-Level-Piwigo-Alben gespiegelt. Direkt im Root liegende Einzelbilder verbleiben auf Root-Ebene des verbindungseigenen Shadow Trees und werden durch die Orphan-Logik verarbeitet.
|
||||
|
||||
Es gibt keine fest verdrahteten Album-Namen.
|
||||
|
||||
@@ -36,31 +38,31 @@ Der gemeinsame Runner verarbeitet WebDAV-Verbindungen in dieser Reihenfolge:
|
||||
3. `runtime/cleanup-webdav-piwigo.php`
|
||||
4. `runtime/sync-webdav.sh` für jede vorhandene WebDAV-Konfiguration
|
||||
|
||||
Die verbindungsspezifischen Runtime-Konfigurationen liegen unter `/etc/bratonien-tools/nc-connector/`. Zustandsdaten liegen unter `/var/lib/bratonien-tools/nc-connector/connection-ID`.
|
||||
Verbindungsspezifische Runtime-Konfigurationen liegen unter `/etc/bratonien-tools/nc-connector/`. Zustandsdaten liegen unter `/var/lib/bratonien-tools/nc-connector/connection-ID`.
|
||||
|
||||
Der Reconcile- und Shell-Pfad unterstützt den leeren WebDAV-Root ausdrücklich und gibt ihn als `--root ""` an den Platzhalter-Builder weiter.
|
||||
State-Verzeichnisse sind für die `www-data`-Gruppe traversierbar/lesbar. `webdav-map.json` wird auch nach atomaren Rewrites mit passenden Runtime-Rechten gehalten.
|
||||
|
||||
### Platzhalterquelle, Metadaten und Shadow Tree
|
||||
|
||||
`runtime/lib/build_webdav_placeholder_source.py` bildet die Nextcloud-Inhalte als lokale Platzhalterquelle ab. Für Bilder existieren physische 1×1-Platzhalterdateien; die Originale werden dabei nicht dauerhaft heruntergeladen.
|
||||
`runtime/lib/build_webdav_placeholder_source.py` bildet die Nextcloud-Inhalte als lokale Platzhalterquelle ab. Für Bilder existieren physische 1×1-Platzhalterdateien; Originale werden nicht dauerhaft heruntergeladen.
|
||||
|
||||
Der WebDAV-Scan übernimmt zusätzlich die von Nextcloud gelieferten Bildmaße aus `nc:metadata-photos-size`. `width`, `height` und `fileid` werden im `webdav-map.json` geführt. Nach dem normalen Piwigo-Dateisync überträgt `runtime/lib/sync-webdav-metadata.php` die Originalmaße in `piwigo_images`, damit Piwigo seine Derivatgrößen und Lazy-Loading-Geometrie auf Basis der echten Bildabmessungen berechnet.
|
||||
Der WebDAV-Scan übernimmt `fileid`, `width`, `height`, MIME-Typ, Größe, ETag und WebDAV-Pfad in `webdav-map.json`. `runtime/lib/sync-webdav-metadata.php` überträgt die Originalmaße nach dem normalen Piwigo-Dateisync in `piwigo_images`.
|
||||
|
||||
`runtime/lib/shadow_tree.py` erzeugt atomar die Dateisystemstruktur, die Piwigo als physische Site sieht. Shadow-Verzeichnisse werden so angelegt, dass der Webserver die für die On-Demand-Materialisierung nötigen Dateisystemoperationen ausführen kann.
|
||||
Die physische Source-Struktur wird für den Runtime-Swap mit `root:www-data` und schreibbaren Gruppenrechten angelegt bzw. nach dem Sync gesetzt. Damit kann der Webserver die Platzhalterquelle während der On-Demand-Erzeugung temporär austauschen und anschließend wiederherstellen.
|
||||
|
||||
Jede WebDAV-Verbindung besitzt ihre eigene Piwigo-Site. Es gibt keinen zusätzlichen Sync auf Site 1.
|
||||
`runtime/lib/shadow_tree.py` erzeugt atomar die Piwigo-kompatible Dateisystemstruktur. Jede WebDAV-Verbindung besitzt ihre eigene Piwigo-Site.
|
||||
|
||||
### Piwigo-Synchronisierung
|
||||
|
||||
`runtime/lib/piwigo-sync.php` bevorzugt `bratonien.nc.syncProductive` für Piwigo 16.4.0. Wenn für die Verbindung keine Piwigo-API eingerichtet ist, wird der gespeicherte Administrator-/Webmaster-Benutzername/Passwort-Fallback verwendet und Piwigos nativer `site_update` ausgeführt.
|
||||
`runtime/lib/piwigo-sync.php` bevorzugt `bratonien.nc.syncProductive` für Piwigo 16.4.0. Wenn keine Piwigo-API eingerichtet ist, wird der gespeicherte Administrator-/Webmaster-Fallback verwendet und Piwigos nativer `site_update` ausgeführt.
|
||||
|
||||
Danach wird `bratonien.nc.syncOrphans` für die konkrete WebDAV-Site ausgeführt.
|
||||
Danach läuft `bratonien.nc.syncOrphans` für die konkrete WebDAV-Site.
|
||||
|
||||
Der Shadow Tree stellt Piwigo ausschließlich die physische Album-/Bildstruktur bereit. Albumdatensätze, Bilddatensätze, Bild-Kategorie-Verknüpfungen und Piwigo-Derivate bleiben Aufgabe von Piwigo.
|
||||
Albumdatensätze, Bilddatensätze, Bild-Kategorie-Verknüpfungen und Derivate bleiben Aufgabe von Piwigo.
|
||||
|
||||
### On-Demand-Materialisierung und Derivaterzeugung
|
||||
|
||||
`include/webdav_materialize_runtime.inc.php` löst aus dem Piwigo-Bildpfad die konkrete Verbindung, den ausgewählten Nextcloud-Root, den relativen WebDAV-Pfad sowie die Mapping-Metadaten `fileid`, `width`, `height`, MIME-Typ, Größe und ETag auf.
|
||||
`include/webdav_materialize_runtime.inc.php` löst aus dem Piwigo-Bildpfad die konkrete Verbindung, den Nextcloud-Root, den relativen WebDAV-Pfad und die Mapping-Metadaten auf.
|
||||
|
||||
Für ein noch nicht vorhandenes Derivat läuft `webdav-derivative.php`:
|
||||
|
||||
@@ -71,13 +73,29 @@ Für ein noch nicht vorhandenes Derivat läuft `webdav-derivative.php`:
|
||||
5. Wiederherstellung des Shadow-/Platzhalterzustands;
|
||||
6. Auslieferung des Piwigo-Derivats.
|
||||
|
||||
Ein interner HTTP-Self-Request auf `i.php` wird nicht mehr verwendet. Damit wird kein zusätzlicher PHP-FPM-Worker pro Derivaterzeugung benötigt.
|
||||
Ein interner HTTP-Self-Request auf `i.php` wird nicht verwendet.
|
||||
|
||||
Für die erste Preview-Stufe verwenden ausschließlich `custom:s9999x250` und `standard:square` den authentifizierten Nextcloud-Endpunkt `/core/preview` anhand der Nextcloud-`fileid`. Die Preview wird mit etwa doppelter benötigter Piwigo-Auflösung und erhaltenem Seitenverhältnis angefordert. Die Berechnung berücksichtigt das Original-Seitenverhältnis, damit auch Crop-Derivate genügend Pixel erhalten. Varianten mit Bildrotation werden in dieser Stufe nicht über Preview erzeugt.
|
||||
Für `custom:s9999x250` und `standard:square` wird bevorzugt der authentifizierte Nextcloud-Endpunkt **`/index.php/core/preview`** anhand der `fileid` verwendet. Die Preview wird mit ungefähr doppelter benötigter Piwigo-Auflösung und unter Berücksichtigung des Original-Seitenverhältnisses angefordert. Varianten mit Bildrotation werden in dieser Stufe nicht über Preview erzeugt.
|
||||
|
||||
Ist keine geeignete Preview planbar oder schlägt der Preview-Abruf fehl, fällt der Gate automatisch auf den vollständigen WebDAV-Originaldownload zurück. Große/originale Anforderungen verwenden weiterhin die Originalquelle.
|
||||
Ist keine geeignete Preview planbar oder schlägt der Preview-Abruf fehl, erfolgt der vollständige WebDAV-Originaldownload. Große/originale Anforderungen verwenden weiterhin die Originalquelle.
|
||||
|
||||
Die temporäre Preview bzw. das temporäre Original wird nach der Derivaterzeugung entfernt. Das fertige Derivat verbleibt ausschließlich in Piwigos normalem `_data/i`-Cache. Der Connector führt keinen parallelen permanenten Derivat-Cache.
|
||||
Die temporäre Preview bzw. das temporäre Original wird nach der Derivaterzeugung entfernt. Das fertige Derivat verbleibt ausschließlich in Piwigos normalem `_data/i`-Cache. Es gibt keinen parallelen permanenten Connector-Derivat-Cache.
|
||||
|
||||
### Piwigo-Lazyload
|
||||
|
||||
Piwigos nativer Thumbnail-Loader ruft nicht gecachte Derivate mit `ajaxload=true` per AJAX ab und erwartet JSON mit einer fertigen `url`.
|
||||
|
||||
`webdav-derivative.php` unterstützt dieses Protokoll für:
|
||||
|
||||
- bereits vorhandene Zielderivate;
|
||||
- `target_created_while_waiting`;
|
||||
- frisch erzeugte Derivate.
|
||||
|
||||
Normale direkte Bildanforderungen liefern weiterhin das Bild selbst. Dadurch kann Piwigos eigener Placeholder nach erfolgreicher Erzeugung ohne Seiten-Reload durch das echte Thumbnail ersetzt werden.
|
||||
|
||||
### Bilddetail-Navigation
|
||||
|
||||
Die eigene Bildnavigation liegt in `js/picture_navigation.js` und `css/picture_navigation.css`. Der aktuelle Stand enthält die Behandlung des sichtbaren Zwischenzustands für WebDAV-Nachbar-Thumbnails, damit während des Nachladens kein Browser-Symbol für ein kaputtes Bild stehen bleiben soll.
|
||||
|
||||
### Orphan-Logik
|
||||
|
||||
@@ -87,15 +105,13 @@ Direkt im WebDAV-Site-Root liegende Bilder werden nicht künstlich in ein Album
|
||||
|
||||
### Private Top-Level-Alben
|
||||
|
||||
Da die technische Nextcloud-Benutzerebene nicht mehr als Elternalbum existiert, können WebDAV-Unterordner echte private Top-Level-Piwigo-Alben sein. Beim Connector-Fallback-Sync sorgt `bratonien_tools_preserve_connector_top_level_access()` dafür, dass der verwendete Piwigo-Administrator auf diese privaten Top-Level-Alben zugreifen kann. Nach Änderungen wird der Benutzer-Cache invalidiert.
|
||||
WebDAV-Unterordner können echte private Top-Level-Piwigo-Alben sein. Beim Connector-Fallback-Sync sorgt `bratonien_tools_preserve_connector_top_level_access()` dafür, dass der verwendete Piwigo-Administrator den Zugriff behält. Nach Änderungen wird der Benutzer-Cache invalidiert.
|
||||
|
||||
Die allgemeine bestehende Funktion `bratonien_tools_preserve_private_album_access()` in `include/album_shares.inc.php` bleibt davon getrennt.
|
||||
Die allgemeine Funktion `bratonien_tools_preserve_private_album_access()` in `include/album_shares.inc.php` bleibt davon getrennt.
|
||||
|
||||
### Administrations-Dashboard
|
||||
|
||||
Piwigo 16.4.0 zeigt die Album-Kachel im Standardtemplate nur bei `NB_ALBUMS > 1` an. Dadurch verschwand die Kachel nach dem korrekten Entfernen der künstlichen Nextcloud-Benutzerebene, sobald nur noch ein echtes Album vorhanden war.
|
||||
|
||||
Bratonien Tools setzt für die Verwaltungsübersicht einen Template-Prefilter und ändert ausschließlich diese Anzeigebedingung auf `NB_ALBUMS > 0`. Die Albumzahl selbst stammt weiterhin unverändert aus Piwigos `get_pwg_general_statitics()`.
|
||||
Für Piwigo 16.4.0 wird ausschließlich die Standardtemplate-Bedingung der Album-Kachel von `NB_ALBUMS > 1` auf `NB_ALBUMS > 0` angepasst. Die Albumzahl selbst stammt unverändert aus Piwigo.
|
||||
|
||||
### Bildauslieferung
|
||||
|
||||
@@ -103,37 +119,44 @@ Fehlende Piwigo-Derivate werden durch den On-Demand-Gate erzeugt. Bereits vorhan
|
||||
|
||||
Für direkte Originalanforderungen existiert weiterhin die serverseitige WebDAV-Originalauslieferung. Nextcloud-Zugangsdaten werden dem Browser nicht offengelegt.
|
||||
|
||||
### Löschen
|
||||
|
||||
Eine WebDAV-Verbindung kann unabhängig von ihrer Reihenfolge gelöscht werden. Beim Löschen werden die zugehörige Piwigo-Site und die zugehörigen Piwigo-Datensätze entfernt. Die Originaldateien in Nextcloud bleiben unangetastet. Verwaiste WebDAV-Runtime- und Piwigo-Daten werden vom gemeinsamen Lauf bereinigt.
|
||||
|
||||
### Diagnose
|
||||
|
||||
Der WebDAV-Derivative-Gate schreibt Request-bezogene `[BRAT-WD ...]`-Diagnoseeinträge. Für neue Derivate zeigen `preview_start`, `preview_failed`, `download_done mode=preview|original`, `generate_start` und `generate_done` den verwendeten Quellpfad und die Laufzeiten. Der Verbindungsstatus wird pro Verbindung geführt. Runtime-Probleme lassen sich zusätzlich über `bratonien-nc-connector.service` nachvollziehen.
|
||||
Der WebDAV-Derivative-Gate schreibt Request-bezogene `[BRAT-WD ...]`-Diagnoseeinträge. Relevante Ereignisse sind unter anderem `preview_start`, `preview_failed`, `download_done`, `swap_begin`, `swapped`, `generate_start`, `generate_done`, `restored`, `target_created_while_waiting` und `serve`.
|
||||
|
||||
## Self-Updater
|
||||
|
||||
`include/self_update.inc.php` liest den Zielstand von GitHub, bindet ihn an einen konkreten Commit, prüft die `main.inc.php` per SHA-256, erstellt vor dem Austausch ein Backup und aktualisiert nur auf eine **höhere** Plugin-Version.
|
||||
|
||||
Das verbindliche Versionsschema ist **0.x.x.x.x**.
|
||||
|
||||
## Aktuell relevante Dateien
|
||||
|
||||
- `main.inc.php` – Plugin-Hooks, Connector-Sync-Hilfen, Dashboard-Prefilter
|
||||
- `include/webdav_materialize_runtime.inc.php` – WebDAV-Quellauflösung einschließlich `fileid` und Originalmaßen
|
||||
- `webdav-derivative.php` – On-Demand-Gate, Nextcloud-Preview/Original-Fallback und Piwigo-Derivaterzeugung
|
||||
- `main.inc.php` – Plugin-Hooks und Version
|
||||
- `include/self_update.inc.php` – integrierter Self-Updater
|
||||
- `include/webdav_materialize_runtime.inc.php` – WebDAV-Quellauflösung und Gate-URL-Erzeugung
|
||||
- `webdav-derivative.php` – On-Demand-Gate, Preview/Original-Fallback, Piwigo-Derivaterzeugung und AJAX-Lazyload-Antwort
|
||||
- `webdav-image.php` – direkte WebDAV-Originalauslieferung
|
||||
- `include/nc_productive_ws.inc.php` – direkter Piwigo-Core-Sync für 16.4.0
|
||||
- `include/nc_orphan_ws.inc.php` – Root-Orphan-Synchronisierung
|
||||
- `runtime/reconcile-webdav.php` – Runtime-Reconcile einschließlich leerem WebDAV-Root
|
||||
- `runtime/reconcile-webdav.php` – Runtime-Reconcile und State-Berechtigungen
|
||||
- `runtime/repair-webdav-orphans.php` – Orphan-Reparatur
|
||||
- `runtime/cleanup-webdav-piwigo.php` – Bereinigung verwaister Piwigo-Sites
|
||||
- `runtime/sync-webdav.sh` – verbindungsspezifischer Sync
|
||||
- `runtime/sync-webdav.sh` – verbindungsspezifischer Sync und Runtime-/Source-Berechtigungen
|
||||
- `runtime/run-all.sh` – gemeinsamer Runner
|
||||
- `runtime/lib/build_webdav_placeholder_source.py` – WebDAV-Scan, Bildmetadaten und Platzhalterquelle
|
||||
- `runtime/lib/build_webdav_placeholder_source.py` – WebDAV-Scan, Mapping und Platzhalterquelle
|
||||
- `runtime/lib/sync-webdav-metadata.php` – Übernahme der Originalmaße nach Piwigo
|
||||
- `runtime/lib/shadow_tree.py` – atomarer Shadow Tree
|
||||
- `runtime/lib/piwigo-sync.php` – Piwigo-API/Fallback-Sync
|
||||
- `js/picture_navigation.js` – Bilddetail-Navigation und Thumbnail-Zwischenzustand
|
||||
- `css/picture_navigation.css` – Bilddetail-Navigationsdarstellung
|
||||
|
||||
## Nicht mehr das produktive WebDAV-Modell
|
||||
|
||||
Nicht mehr maßgeblich sind das frühere Modell eines separaten WebDAV-Bildstreams als Derivatquelle, ein paralleler vollständiger Connector-Derivatbestand oder ein interner HTTP-Aufruf von `webdav-derivative.php` zurück auf Piwigos `i.php`.
|
||||
Nicht mehr maßgeblich sind ein separater permanenter WebDAV-Derivatbestand oder ein interner HTTP-Aufruf von `webdav-derivative.php` zurück auf Piwigos `i.php`.
|
||||
|
||||
Maßgeblich ist: **physischer Platzhalter + passende temporäre Nextcloud-Preview bzw. Original-Fallback + Piwigos Bildverarbeitung im selben PHP-Prozess + normales Piwigo-Derivat + Wiederherstellung des Platzhalters**.
|
||||
Maßgeblich ist:
|
||||
|
||||
**physischer Platzhalter + temporäre Nextcloud-Preview bzw. Original-Fallback + Piwigos Bildverarbeitung im selben PHP-Prozess + normales Piwigo-Derivat + Wiederherstellung des Platzhalters**.
|
||||
|
||||
## Prüfpflicht vor Merge
|
||||
|
||||
|
||||
20
README.md
20
README.md
@@ -2,7 +2,7 @@
|
||||
|
||||
Modulares Piwigo-Plugin für Administration, Bildverarbeitung, geschützte Freigaben, Fotoauswahl und die Anbindung von Nextcloud an Piwigo.
|
||||
|
||||
Aktuelle Plugin-Version: **9.7.1.0**
|
||||
Aktuelle Plugin-Version: **0.9.7.1.7**
|
||||
|
||||
## Grundprinzip
|
||||
|
||||
@@ -72,6 +72,8 @@ Nicht benötigt werden Nextcloud-Datenbankzugriff, `occ`-Adminzugriff, Storage-I
|
||||
8. Piwigos Dateisynchronisierung erhält ausschließlich diese verbindungseigene Site.
|
||||
9. `runtime/lib/sync-webdav-metadata.php` übernimmt die von Nextcloud gelieferten Originalmaße in Piwigos Bilddatensätze.
|
||||
|
||||
State-Verzeichnisse und Mapping-Dateien werden so berechtigt, dass der Webserver die Laufzeitdaten lesen kann. Die physische Platzhalterquelle wird mit der für den temporären Source-Swap nötigen `www-data`-Gruppenberechtigung erzeugt bzw. nach dem Sync gesetzt.
|
||||
|
||||
### On-Demand-Bildauslieferung und Piwigo-Derivate
|
||||
|
||||
`include/webdav_materialize_runtime.inc.php` erkennt anhand des Piwigo-Bildpfads die zugehörige WebDAV-Verbindung, den Nextcloud-Root und den relativen WebDAV-Pfad. Aus dem Mapping stehen zusätzlich `fileid`, Originalbreite/-höhe, MIME-Typ, Dateigröße und ETag zur Verfügung. Der Hook `get_derivative_url` leitet fehlende WebDAV-Derivate an `webdav-derivative.php` um.
|
||||
@@ -79,13 +81,15 @@ Nicht benötigt werden Nextcloud-Datenbankzugriff, `occ`-Adminzugriff, Storage-I
|
||||
Wenn ein benötigtes Piwigo-Derivat noch nicht existiert:
|
||||
|
||||
1. `webdav-derivative.php` prüft den Piwigo-Zugriff auf das Bild.
|
||||
2. Für `custom:s9999x250` und `standard:square` wird bevorzugt eine passende authentifizierte Nextcloud-Preview anhand der `fileid` geladen. Die Preview wird mit ungefähr doppelter benötigter Piwigo-Auflösung und erhaltenem Seitenverhältnis angefordert.
|
||||
2. Für `custom:s9999x250` und `standard:square` wird bevorzugt eine passende authentifizierte Nextcloud-Preview anhand der `fileid` über `/index.php/core/preview` geladen. Die Preview wird mit ungefähr doppelter benötigter Piwigo-Auflösung und erhaltenem Seitenverhältnis angefordert.
|
||||
3. Ist keine passende Preview planbar oder schlägt der Preview-Abruf fehl, wird automatisch das vollständige Original per WebDAV geladen. Große bzw. originale Anforderungen verwenden weiterhin die Originalquelle.
|
||||
4. Die temporäre Bildquelle ersetzt für die Dauer der Erzeugung die physische 1×1-Platzhalterdatei bzw. den Shadow-Link am von Piwigo erwarteten Quellpfad.
|
||||
5. Piwigos eigene `pwg_image`-/Derivative-Logik erzeugt das normale Piwigo-Derivat direkt im selben PHP-Prozess. Ein interner HTTP-Self-Request auf `i.php` wird nicht verwendet.
|
||||
6. Danach wird die temporäre Bildquelle entfernt und der Platzhalter-/Shadow-Zustand wiederhergestellt.
|
||||
7. Das fertige Derivat wird ausgeliefert und künftig normal aus Piwigos `_data/i`-Derivat-Cache verwendet.
|
||||
|
||||
Piwigos nativer Thumbnail-Loader verwendet bei nicht gecachten Thumbnails `ajaxload=true` und erwartet eine JSON-Antwort mit der fertigen Derivat-URL. `webdav-derivative.php` unterstützt dieses Protokoll; normale direkte Bildanforderungen werden weiterhin als Bild ausgeliefert. Dadurch kann Piwigos eigener Placeholder nach der Erzeugung ohne Seiten-Reload durch das fertige Thumbnail ersetzt werden.
|
||||
|
||||
Damit bleibt Piwigo für Größen, Derivate und Lazy-Loading verantwortlich. Der Connector hält keinen parallelen eigenen vollständigen Derivat-Cache vor.
|
||||
|
||||
Für Fälle, in denen Piwigo direkt die Originalquelle benötigt, steht weiterhin die serverseitige WebDAV-Auslieferung zur Verfügung. Nextcloud-Zugangsdaten werden nicht an den Browser weitergegeben.
|
||||
@@ -133,7 +137,7 @@ Der gemeinsame Lauf besteht aktuell aus:
|
||||
3. `runtime/cleanup-webdav-piwigo.php` – verwaiste WebDAV-Piwigo-Daten bereinigen;
|
||||
4. `runtime/sync-webdav.sh` – jede vorhandene WebDAV-Verbindung synchronisieren.
|
||||
|
||||
Der verbindungsspezifische Lauf erzeugt Platzhalterquelle und Shadow Tree, führt Piwigos Sync und Orphan-Sync aus und überträgt anschließend die Originalabmessungen aus dem WebDAV-Mapping in Piwigo. Eine vollständige Vorab-Erzeugung von Connector-Derivaten findet nicht statt; fehlende Derivate werden On-Demand erzeugt.
|
||||
Der verbindungsspezifische Lauf erzeugt Platzhalterquelle und Shadow Tree, setzt die Laufzeitberechtigungen, führt Piwigos Sync und Orphan-Sync aus und überträgt anschließend die Originalabmessungen aus dem WebDAV-Mapping in Piwigo. Eine vollständige Vorab-Erzeugung von Connector-Derivaten findet nicht statt; fehlende Derivate werden On-Demand erzeugt.
|
||||
|
||||
## Bildcache
|
||||
|
||||
@@ -176,11 +180,11 @@ Private Alben können über eigene Freigabelinks geteilt werden, optional mit Pa
|
||||
|
||||
## Erweiterte Bildnavigation
|
||||
|
||||
Die Piwigo-Bilddetailseite erhält responsive Navigationszonen für vorheriges Bild, nächstes Bild, Rückkehr zur Übersicht und PhotoSwipe/Vollbild.
|
||||
Die Piwigo-Bilddetailseite erhält responsive Navigationszonen für vorheriges Bild, nächstes Bild, Rückkehr zur Übersicht und PhotoSwipe/Vollbild. Für WebDAV-Nachbar-Thumbnails wird der sichtbare Ladezustand so behandelt, dass während eines noch laufenden Bildabrufs kein Browser-Symbol für ein kaputtes Bild stehen bleiben soll.
|
||||
|
||||
## Selbstaktualisierung
|
||||
|
||||
Der integrierte Updater liest den Zielstand aus GitHub, bindet ein Update an einen konkreten Commit, prüft Version und SHA-256, erstellt vor dem Austausch ein Backup und verlangt Webmaster-Rechte sowie die notwendigen Servervoraussetzungen.
|
||||
Der integrierte Updater liest den Zielstand aus GitHub, bindet ein Update an einen konkreten Commit, prüft Version und SHA-256, erstellt vor dem Austausch ein Backup und verlangt Webmaster-Rechte sowie die notwendigen Servervoraussetzungen. Das verbindliche Versionsschema des Plugins ist **0.x.x.x.x**; Updates werden nur auf eine höhere Version angeboten.
|
||||
|
||||
## Wichtige Dateien und Verzeichnisse
|
||||
|
||||
@@ -194,12 +198,12 @@ Der integrierte Updater liest den Zielstand aus GitHub, bindet ein Update an ein
|
||||
- `include/nc_orphan_ws.inc.php` – Orphan-Synchronisierung der konkreten WebDAV-Site
|
||||
- `include/webdav_materialize_runtime.inc.php` – WebDAV-Quellauflösung und On-Demand-Materialisierung einschließlich `fileid` und Originalmaßen
|
||||
- `include/webdav_image_runtime.inc.php` – vorhandene WebDAV-Bildzuordnung/Originalauslieferung
|
||||
- `webdav-derivative.php` – On-Demand-Gate, Nextcloud-Preview/Original-Fallback und Piwigo-Derivaterzeugung
|
||||
- `webdav-derivative.php` – On-Demand-Gate, Nextcloud-Preview/Original-Fallback, Piwigo-AJAX-Loader-Antwort und Piwigo-Derivaterzeugung
|
||||
- `webdav-image.php` – serverseitige WebDAV-Originalauslieferung
|
||||
- `runtime/reconcile-webdav.php` – WebDAV-Runtime-Reconcile, einschließlich gültigem leerem Root
|
||||
- `runtime/reconcile-webdav.php` – WebDAV-Runtime-Reconcile, einschließlich gültigem leerem Root und Runtime-Berechtigungen
|
||||
- `runtime/repair-webdav-orphans.php` – Reparatur vorhandener Orphan-Zustände
|
||||
- `runtime/cleanup-webdav-piwigo.php` – Bereinigung verwaister WebDAV-Piwigo-Sites
|
||||
- `runtime/sync-webdav.sh` – Ablauf einer WebDAV-Verbindung
|
||||
- `runtime/sync-webdav.sh` – Ablauf einer WebDAV-Verbindung einschließlich Mapping-/Source-Berechtigungen
|
||||
- `runtime/run-all.sh` – gemeinsamer WebDAV-Runner
|
||||
- `runtime/lib/build_webdav_placeholder_source.py` – rekursiver WebDAV-Scan, Bildmetadaten und physische Platzhalterquelle
|
||||
- `runtime/lib/sync-webdav-metadata.php` – Übernahme der Originalabmessungen in Piwigo
|
||||
|
||||
@@ -17,3 +17,12 @@
|
||||
.bratonien-picture-zone.is-active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#theImageAndInfos img[src*="plugins/bratonien_tools/webdav-derivative.php"]:not(#theMainImage):not(.bratonien-webdav-thumbnail-ready),
|
||||
#theImageAndInfos img.bratonien-webdav-thumbnail-pending {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#theImageAndInfos img.bratonien-webdav-thumbnail-ready {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
@@ -216,12 +216,105 @@
|
||||
resizeTimer = window.setTimeout(updateMap, 60);
|
||||
}
|
||||
|
||||
function isWebdavThumbnail(image) {
|
||||
if (!image || image.id === 'theMainImage') {
|
||||
return false;
|
||||
}
|
||||
|
||||
var src = image.getAttribute('src') || '';
|
||||
var dataSrc = image.getAttribute('data-src') || '';
|
||||
return src.indexOf('plugins/bratonien_tools/webdav-derivative.php') !== -1 ||
|
||||
dataSrc.indexOf('plugins/bratonien_tools/webdav-derivative.php') !== -1 ||
|
||||
image.getAttribute('data-bratonien-webdav-thumbnail') === '1';
|
||||
}
|
||||
|
||||
function setThumbnailPlaceholder(image) {
|
||||
var parent = image.parentElement;
|
||||
if (!parent || parent.getAttribute('data-bratonien-placeholder-active') === '1') {
|
||||
return;
|
||||
}
|
||||
|
||||
parent.setAttribute('data-bratonien-placeholder-active', '1');
|
||||
parent.setAttribute('data-bratonien-placeholder-background-image', parent.style.backgroundImage || '');
|
||||
parent.setAttribute('data-bratonien-placeholder-background-repeat', parent.style.backgroundRepeat || '');
|
||||
parent.setAttribute('data-bratonien-placeholder-background-position', parent.style.backgroundPosition || '');
|
||||
parent.setAttribute('data-bratonien-placeholder-background-size', parent.style.backgroundSize || '');
|
||||
parent.style.backgroundImage = 'url("' + absoluteUrl('themes/default/icon/img_small.png') + '")';
|
||||
parent.style.backgroundRepeat = 'no-repeat';
|
||||
parent.style.backgroundPosition = 'center';
|
||||
parent.style.backgroundSize = 'auto';
|
||||
}
|
||||
|
||||
function clearThumbnailPlaceholder(image) {
|
||||
var parent = image.parentElement;
|
||||
if (!parent || parent.getAttribute('data-bratonien-placeholder-active') !== '1') {
|
||||
return;
|
||||
}
|
||||
|
||||
parent.style.backgroundImage = parent.getAttribute('data-bratonien-placeholder-background-image') || '';
|
||||
parent.style.backgroundRepeat = parent.getAttribute('data-bratonien-placeholder-background-repeat') || '';
|
||||
parent.style.backgroundPosition = parent.getAttribute('data-bratonien-placeholder-background-position') || '';
|
||||
parent.style.backgroundSize = parent.getAttribute('data-bratonien-placeholder-background-size') || '';
|
||||
parent.removeAttribute('data-bratonien-placeholder-active');
|
||||
parent.removeAttribute('data-bratonien-placeholder-background-image');
|
||||
parent.removeAttribute('data-bratonien-placeholder-background-repeat');
|
||||
parent.removeAttribute('data-bratonien-placeholder-background-position');
|
||||
parent.removeAttribute('data-bratonien-placeholder-background-size');
|
||||
}
|
||||
|
||||
function markThumbnailPending(image) {
|
||||
image.setAttribute('data-bratonien-webdav-thumbnail', '1');
|
||||
image.classList.remove('bratonien-webdav-thumbnail-ready');
|
||||
image.classList.add('bratonien-webdav-thumbnail-pending');
|
||||
setThumbnailPlaceholder(image);
|
||||
}
|
||||
|
||||
function markThumbnailReady(image) {
|
||||
image.classList.remove('bratonien-webdav-thumbnail-pending');
|
||||
image.classList.add('bratonien-webdav-thumbnail-ready');
|
||||
clearThumbnailPlaceholder(image);
|
||||
}
|
||||
|
||||
function protectWebdavThumbnail(image) {
|
||||
if (!isWebdavThumbnail(image)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (image.getAttribute('data-bratonien-thumbnail-bound') !== '1') {
|
||||
image.setAttribute('data-bratonien-thumbnail-bound', '1');
|
||||
image.addEventListener('load', function () {
|
||||
if (image.naturalWidth > 0 && image.naturalHeight > 0) {
|
||||
markThumbnailReady(image);
|
||||
}
|
||||
});
|
||||
image.addEventListener('error', function () {
|
||||
markThumbnailPending(image);
|
||||
});
|
||||
}
|
||||
|
||||
if (image.complete && image.naturalWidth > 0 && image.naturalHeight > 0) {
|
||||
markThumbnailReady(image);
|
||||
} else {
|
||||
markThumbnailPending(image);
|
||||
}
|
||||
}
|
||||
|
||||
function protectWebdavThumbnails(root) {
|
||||
var scope = root || document;
|
||||
var images = scope.querySelectorAll ? scope.querySelectorAll('img') : [];
|
||||
for (var i = 0; i < images.length; i += 1) {
|
||||
protectWebdavThumbnail(images[i]);
|
||||
}
|
||||
}
|
||||
|
||||
$(function () {
|
||||
var image = document.getElementById('theMainImage');
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
|
||||
protectWebdavThumbnails(document.getElementById('theImageAndInfos') || document);
|
||||
|
||||
if (image.complete) {
|
||||
updateMap();
|
||||
} else {
|
||||
@@ -241,6 +334,35 @@
|
||||
attributes: true,
|
||||
attributeFilter: ['src', 'srcset', 'usemap', 'class', 'style']
|
||||
});
|
||||
|
||||
var thumbnailRoot = document.getElementById('theImageAndInfos');
|
||||
if (thumbnailRoot) {
|
||||
var thumbnailObserver = new MutationObserver(function (mutations) {
|
||||
mutations.forEach(function (mutation) {
|
||||
if (mutation.type === 'attributes' && mutation.target.tagName === 'IMG') {
|
||||
protectWebdavThumbnail(mutation.target);
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < mutation.addedNodes.length; i += 1) {
|
||||
var node = mutation.addedNodes[i];
|
||||
if (node.nodeType !== 1) {
|
||||
continue;
|
||||
}
|
||||
if (node.tagName === 'IMG') {
|
||||
protectWebdavThumbnail(node);
|
||||
}
|
||||
protectWebdavThumbnails(node);
|
||||
}
|
||||
});
|
||||
});
|
||||
thumbnailObserver.observe(thumbnailRoot, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: true,
|
||||
attributeFilter: ['src', 'data-src']
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
})(jQuery);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
/*
|
||||
Plugin Name: Bratonien Tools
|
||||
Version: 9.7.1.2
|
||||
Version: 0.9.7.1.7
|
||||
Description: Erweiterbare Administrationswerkzeuge fuer die Bratonien-Piwigo-Installation.
|
||||
Plugin URI: https://github.com/Terranom674/Piwigo_Bratonien_Tools
|
||||
Author: Bratonien
|
||||
|
||||
@@ -94,6 +94,11 @@ python3 "$SCRIPT_DIR/lib/build_webdav_placeholder_source.py" \
|
||||
--manifest "$MANIFEST" \
|
||||
--mapping "$WEBDAV_MAPPING_FILE"
|
||||
|
||||
find "$WEBDAV_SOURCE_DIR" -type d -exec chgrp www-data -- {} +
|
||||
find "$WEBDAV_SOURCE_DIR" -type d -exec chmod 0775 -- {} +
|
||||
find "$WEBDAV_SOURCE_DIR" -type f -exec chgrp www-data -- {} +
|
||||
find "$WEBDAV_SOURCE_DIR" -type f -exec chmod 0664 -- {} +
|
||||
|
||||
chgrp www-data -- "$WEBDAV_MAPPING_FILE"
|
||||
chmod 0640 -- "$WEBDAV_MAPPING_FILE"
|
||||
|
||||
|
||||
@@ -32,6 +32,22 @@ function bratonien_tools_webdav_derivative_abort($status, $message)
|
||||
exit;
|
||||
}
|
||||
|
||||
function bratonien_tools_webdav_derivative_send($target_path, $after_url='')
|
||||
{
|
||||
if (isset($_GET['ajaxload']) && (string)$_GET['ajaxload'] === 'true')
|
||||
{
|
||||
if ($after_url === '') bratonien_tools_webdav_derivative_abort(400, 'Lazyload-Ziel fehlt.');
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Cache-Control: no-store');
|
||||
echo json_encode(array('url'=>$after_url), JSON_UNESCAPED_SLASHES);
|
||||
exit;
|
||||
}
|
||||
|
||||
header('Content-Type: '.(function_exists('get_mimetype') ? get_mimetype($target_path) : 'image/jpeg'));
|
||||
readfile($target_path);
|
||||
exit;
|
||||
}
|
||||
|
||||
function bratonien_tools_webdav_derivative_decrypt_secret($blob, $hex_key)
|
||||
{
|
||||
$hex_key = trim((string)$hex_key);
|
||||
@@ -168,7 +184,7 @@ function bratonien_tools_webdav_derivative_download_preview(array $plan, array $
|
||||
'mode'=>'fill',
|
||||
'mimeFallback'=>'false',
|
||||
), '', '&', PHP_QUERY_RFC3986);
|
||||
$url = $connection['base_url'].'/core/preview?'.$query;
|
||||
$url = $connection['base_url'].'/index.php/core/preview?'.$query;
|
||||
return bratonien_tools_webdav_derivative_download_url($url, $connection, $destination, $detail);
|
||||
}
|
||||
|
||||
@@ -323,250 +339,222 @@ function bratonien_tools_webdav_derivative_generate($source_path, $target_path,
|
||||
}
|
||||
}
|
||||
|
||||
function bratonien_tools_webdav_derivative_after_url($encoded)
|
||||
{
|
||||
$encoded = trim((string)$encoded);
|
||||
if ($encoded === '') return null;
|
||||
$raw = strtr($encoded, '-_', '+/');
|
||||
$padding = strlen($raw) % 4;
|
||||
if ($padding) $raw .= str_repeat('=', 4 - $padding);
|
||||
$url = base64_decode($raw, true);
|
||||
if (!is_string($url) || $url === '' || preg_match('/[\r\n]/', $url)) return null;
|
||||
if (strpos($url, '//') === 0) return null;
|
||||
if (preg_match('#^https?://#i', $url))
|
||||
{
|
||||
$root_parts = parse_url(get_absolute_root_url());
|
||||
$url_parts = parse_url($url);
|
||||
if (!is_array($root_parts) || !is_array($url_parts)) return null;
|
||||
if (strcasecmp((string)($root_parts['scheme'] ?? ''), (string)($url_parts['scheme'] ?? '')) !== 0) return null;
|
||||
if (strcasecmp((string)($root_parts['host'] ?? ''), (string)($url_parts['host'] ?? '')) !== 0) return null;
|
||||
if ((int)($root_parts['port'] ?? 0) !== (int)($url_parts['port'] ?? 0)) return null;
|
||||
return $url;
|
||||
}
|
||||
if (strpos($url, ':') !== false && !preg_match('#^[A-Za-z0-9_./?&=%+-]+$#', $url)) return null;
|
||||
return $url;
|
||||
}
|
||||
|
||||
function bratonien_tools_webdav_derivative_needs_after_redirect($url)
|
||||
{
|
||||
if (!is_string($url) || $url === '') return false;
|
||||
return strpos($url, '/plugins/'.BRATONIEN_TOOLS_ID.'/watermark.php') !== false
|
||||
|| strpos($url, 'plugins/'.BRATONIEN_TOOLS_ID.'/watermark.php') === 0;
|
||||
}
|
||||
|
||||
function bratonien_tools_webdav_derivative_serve($path)
|
||||
{
|
||||
clearstatcache(true, $path);
|
||||
if (!is_file($path) || !is_readable($path)) bratonien_tools_webdav_derivative_abort(500, 'Piwigo-Derivat ist nicht lesbar.');
|
||||
$size = @getimagesize($path);
|
||||
$content_type = is_array($size) && !empty($size['mime']) ? (string)$size['mime'] : 'application/octet-stream';
|
||||
header('Content-Type: '.$content_type);
|
||||
header('Content-Length: '.(int)filesize($path));
|
||||
header('Cache-Control: public, max-age=31536000');
|
||||
header('X-Content-Type-Options: nosniff');
|
||||
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'HEAD') readfile($path);
|
||||
exit;
|
||||
}
|
||||
|
||||
$request_id = substr(bin2hex(random_bytes(8)), 0, 8);
|
||||
$image_id = (int)($_GET['id'] ?? 0);
|
||||
$variant = trim((string)($_GET['variant'] ?? ''));
|
||||
$request_id = bin2hex(random_bytes(4));
|
||||
$after_url = '';
|
||||
|
||||
if ($image_id < 1 || $variant === '') bratonien_tools_webdav_derivative_abort(400, 'Ungueltige Derivatanforderung.');
|
||||
|
||||
bratonien_tools_webdav_derivative_debug($request_id, 'start', array('image_id'=>$image_id, 'variant'=>$variant));
|
||||
|
||||
$after_url = bratonien_tools_webdav_derivative_after_url($_GET['after'] ?? '');
|
||||
$after_sig = trim((string)($_GET['sig'] ?? ''));
|
||||
if ($image_id < 1) bratonien_tools_webdav_derivative_abort(400, 'Bild-ID fehlt.');
|
||||
if ($variant === '') bratonien_tools_webdav_derivative_abort(400, 'Derivat-Variante fehlt.');
|
||||
if ($after_url !== null)
|
||||
if (!empty($_GET['after']))
|
||||
{
|
||||
$expected_sig = bratonien_tools_webdav_materialize_after_signature($image_id, $variant, $after_url);
|
||||
if ($after_sig === '' || !hash_equals($expected_sig, $after_sig)) $after_url = null;
|
||||
$raw_after = strtr((string)$_GET['after'], '-_', '+/');
|
||||
$padding = strlen($raw_after) % 4;
|
||||
if ($padding) $raw_after .= str_repeat('=', 4 - $padding);
|
||||
$decoded_after = base64_decode($raw_after, true);
|
||||
if (is_string($decoded_after)) $after_url = $decoded_after;
|
||||
}
|
||||
if ($after_url !== '')
|
||||
{
|
||||
$expected = bratonien_tools_webdav_materialize_after_signature($image_id, $variant, $after_url);
|
||||
if (!hash_equals($expected, (string)($_GET['sig'] ?? ''))) bratonien_tools_webdav_derivative_abort(403, 'Ungueltige Derivatsignatur.');
|
||||
}
|
||||
$redirect_after = bratonien_tools_webdav_derivative_needs_after_redirect($after_url) ? $after_url : null;
|
||||
|
||||
$permission_condition = get_sql_condition_FandF(array('forbidden_categories'=>'category_id'), null, true);
|
||||
$access_result = pwg_query('SELECT 1 FROM '.IMAGE_CATEGORY_TABLE.' WHERE image_id='.$image_id.' AND '.$permission_condition.' LIMIT 1');
|
||||
if (!pwg_db_num_rows($access_result)) bratonien_tools_webdav_derivative_abort(403, 'Kein Zugriff auf dieses Bild.');
|
||||
$result = pwg_query('SELECT * FROM '.IMAGES_TABLE.' WHERE id='.$image_id.' LIMIT 1');
|
||||
$result = pwg_query('SELECT id, path, width, height, rotation, coi FROM '.IMAGES_TABLE.' WHERE id='.$image_id.' LIMIT 1');
|
||||
if (!pwg_db_num_rows($result)) bratonien_tools_webdav_derivative_abort(404, 'Bild nicht gefunden.');
|
||||
$image_row = pwg_db_fetch_assoc($result);
|
||||
|
||||
$raw_source_path = (string)($image_row['path'] ?? '');
|
||||
$absolute_source_path = $raw_source_path;
|
||||
if ($absolute_source_path !== '' && strpos($absolute_source_path, '/') !== 0)
|
||||
if (function_exists('get_sql_condition_FandF'))
|
||||
{
|
||||
$absolute_source_path = PHPWG_ROOT_PATH.ltrim(preg_replace('#^\./#', '', $absolute_source_path), '/');
|
||||
$forbidden = get_sql_condition_FandF(array('visible_images'=>'id'), ' AND');
|
||||
if ($forbidden !== '')
|
||||
{
|
||||
$visible = pwg_query('SELECT id FROM '.IMAGES_TABLE.' WHERE id='.$image_id.$forbidden.' LIMIT 1');
|
||||
if (!pwg_db_num_rows($visible)) bratonien_tools_webdav_derivative_abort(403, 'Kein Zugriff auf das Bild.');
|
||||
}
|
||||
}
|
||||
bratonien_tools_webdav_derivative_debug($request_id, 'source_before_resolve', array(
|
||||
'path'=>$raw_source_path,
|
||||
'is_link'=>$absolute_source_path !== '' && is_link($absolute_source_path),
|
||||
'realpath'=>$absolute_source_path !== '' ? (realpath($absolute_source_path) ?: 'FALSE') : 'EMPTY',
|
||||
));
|
||||
|
||||
$source = bratonien_tools_webdav_materialize_source_info($image_id);
|
||||
if (!$source)
|
||||
{
|
||||
bratonien_tools_webdav_derivative_debug($request_id, 'source_resolve_failed');
|
||||
bratonien_tools_webdav_derivative_abort(404, 'Keine WebDAV-Quelle fuer dieses Bild gefunden.');
|
||||
}
|
||||
if (!$source) bratonien_tools_webdav_derivative_abort(404, 'WebDAV-Bildquelle nicht gefunden.');
|
||||
bratonien_tools_webdav_derivative_debug($request_id, 'source_resolved', array(
|
||||
'connection_id'=>$source['connection_id'] ?? 0,
|
||||
'root_fileid'=>$source['root_fileid'] ?? 0,
|
||||
'connection_id'=>$source['connection_id'],
|
||||
'root_fileid'=>$source['root_fileid'],
|
||||
'fileid'=>$source['fileid'] ?? 0,
|
||||
'webdav_path'=>$source['webdav_path'] ?? '',
|
||||
'webdav_path'=>$source['webdav_path'],
|
||||
));
|
||||
|
||||
$params = bratonien_tools_webdav_materialize_params_from_variant($variant);
|
||||
if (!$params) bratonien_tools_webdav_derivative_abort(400, 'Unbekannte Derivat-Variante.');
|
||||
$real_src = new SrcImage($image_row);
|
||||
$real_derivative = new DerivativeImage($params, $real_src);
|
||||
if ($real_derivative->same_as_source())
|
||||
{
|
||||
$source_url = bratonien_tools_webdav_materialize_image_url($image_id);
|
||||
if (!$source_url) bratonien_tools_webdav_derivative_abort(404, 'WebDAV-Original konnte nicht bestimmt werden.');
|
||||
header('Location: '.$source_url, true, 302);
|
||||
exit;
|
||||
}
|
||||
if (!$params) bratonien_tools_webdav_derivative_abort(400, 'Unbekannte Derivatvariante.');
|
||||
|
||||
$src_image = new SrcImage($image_row);
|
||||
$real_derivative = new DerivativeImage($params, $src_image);
|
||||
$target_path = $real_derivative->get_path();
|
||||
$derivative_size = $real_derivative->get_size();
|
||||
|
||||
$target_path = $real_derivative->get_path();
|
||||
$derivative_root = PHPWG_ROOT_PATH.PWG_DERIVATIVE_DIR;
|
||||
if ($target_path === '' || strpos($target_path, $derivative_root) !== 0) bratonien_tools_webdav_derivative_abort(500, 'Ungueltiger Piwigo-Derivatpfad.');
|
||||
if (is_file($target_path) && is_readable($target_path))
|
||||
if ($target_path !== '' && is_file($target_path) && is_readable($target_path))
|
||||
{
|
||||
bratonien_tools_webdav_derivative_debug($request_id, 'target_exists', array('target'=>$target_path));
|
||||
if ($redirect_after !== null) { header('Location: '.$redirect_after, true, 302); exit; }
|
||||
bratonien_tools_webdav_derivative_serve($target_path);
|
||||
bratonien_tools_webdav_derivative_send($target_path, $after_url);
|
||||
}
|
||||
|
||||
$shadow_path = $raw_source_path;
|
||||
if ($shadow_path === '') bratonien_tools_webdav_derivative_abort(500, 'Piwigo-Quellpfad fehlt.');
|
||||
if (strpos($shadow_path, '/') !== 0) $shadow_path = PHPWG_ROOT_PATH.ltrim(preg_replace('#^\./#', '', $shadow_path), '/');
|
||||
$shadow_normalized = str_replace('\\', '/', $shadow_path);
|
||||
if (!preg_match('#/_data/bratonien-tools/nc-webdav-gallery/connection-'.(int)$source['connection_id'].'/#', $shadow_normalized)) bratonien_tools_webdav_derivative_abort(500, 'Piwigo-Quellpfad liegt nicht im WebDAV-Shadow-Baum.');
|
||||
if (!is_link($shadow_path)) bratonien_tools_webdav_derivative_abort(500, 'WebDAV-Shadow-Quelle ist kein Placeholder-Symlink.');
|
||||
|
||||
$placeholder_target = realpath($shadow_path);
|
||||
$placeholder_normalized = $placeholder_target === false ? '' : str_replace('\\', '/', $placeholder_target);
|
||||
if ($placeholder_target === false || !preg_match('#/nc-webdav-source/connection-'.(int)$source['connection_id'].'/root-'.(int)$source['root_fileid'].'/#', $placeholder_normalized)) bratonien_tools_webdav_derivative_abort(500, 'WebDAV-Placeholder-Ziel ist ungueltig.');
|
||||
|
||||
$shadow_dir = dirname($shadow_path);
|
||||
if (!is_dir($shadow_dir) || !is_writable($shadow_dir)) bratonien_tools_webdav_derivative_abort(500, 'WebDAV-Shadow-Verzeichnis ist fuer PHP nicht beschreibbar.');
|
||||
$upload_dir = rtrim((string)($GLOBALS['conf']['upload_dir'] ?? './upload'), '/');
|
||||
$work_rel_dir = $upload_dir.'/bratonien-webdav-materialize';
|
||||
$work_root = PHPWG_ROOT_PATH.$work_rel_dir;
|
||||
if (!is_dir($work_root) && !@mkdir($work_root, 0755, true)) bratonien_tools_webdav_derivative_abort(500, 'WebDAV-Materialisierungsbereich konnte nicht angelegt werden.');
|
||||
if (!is_writable($work_root)) bratonien_tools_webdav_derivative_abort(500, 'WebDAV-Materialisierungsbereich ist fuer PHP nicht beschreibbar.');
|
||||
|
||||
$lock_path = $work_root.'/image-'.$image_id.'.lock';
|
||||
bratonien_tools_webdav_derivative_debug($request_id, 'waiting_lock', array('lock'=>$lock_path, 'is_link'=>is_link($shadow_path), 'realpath'=>realpath($shadow_path) ?: 'FALSE'));
|
||||
$lock = @fopen($lock_path, 'c');
|
||||
if (!$lock || !flock($lock, LOCK_EX))
|
||||
$source_logical_path = (string)$image_row['path'];
|
||||
if (strpos($source_logical_path, '/') !== 0)
|
||||
{
|
||||
if ($lock) fclose($lock);
|
||||
bratonien_tools_webdav_derivative_abort(503, 'WebDAV-Materialisierungslock konnte nicht gesetzt werden.');
|
||||
$source_logical_path = PHPWG_ROOT_PATH.ltrim(preg_replace('#^\./#', '', $source_logical_path), '/');
|
||||
}
|
||||
bratonien_tools_webdav_derivative_debug($request_id, 'lock_acquired', array('is_link'=>is_link($shadow_path), 'realpath'=>realpath($shadow_path) ?: 'FALSE'));
|
||||
|
||||
$lock_dir = PHPWG_ROOT_PATH.'upload/bratonien-webdav-materialize';
|
||||
if (!is_dir($lock_dir)) @mkdir($lock_dir, 0755, true);
|
||||
$lock_path = $lock_dir.'/image-'.$image_id.'.lock';
|
||||
$lock_handle = fopen($lock_path, 'c');
|
||||
if (!$lock_handle) bratonien_tools_webdav_derivative_abort(500, 'Derivat-Lock konnte nicht geoeffnet werden.');
|
||||
bratonien_tools_webdav_derivative_debug($request_id, 'waiting_lock', array('lock'=>$lock_path, 'is_link'=>is_link($source_logical_path)));
|
||||
flock($lock_handle, LOCK_EX);
|
||||
bratonien_tools_webdav_derivative_debug($request_id, 'lock_acquired', array('is_link'=>is_link($source_logical_path)));
|
||||
|
||||
clearstatcache(true, $target_path);
|
||||
if (is_file($target_path) && is_readable($target_path))
|
||||
if ($target_path !== '' && is_file($target_path) && is_readable($target_path))
|
||||
{
|
||||
bratonien_tools_webdav_derivative_debug($request_id, 'target_created_while_waiting', array('target'=>$target_path));
|
||||
@flock($lock, LOCK_UN); @fclose($lock);
|
||||
if ($redirect_after !== null) { header('Location: '.$redirect_after, true, 302); exit; }
|
||||
bratonien_tools_webdav_derivative_serve($target_path);
|
||||
flock($lock_handle, LOCK_UN);
|
||||
fclose($lock_handle);
|
||||
bratonien_tools_webdav_derivative_send($target_path, $after_url);
|
||||
}
|
||||
|
||||
$token = getmypid().'-'.bin2hex(random_bytes(6));
|
||||
$download_path = $work_root.'/source-'.$image_id.'-'.$token.'.tmp';
|
||||
$staging_path = $shadow_path.'.bratonien-'.$token.'.tmp';
|
||||
$placeholder_backup = $shadow_path.'.bratonien-placeholder';
|
||||
$swapped = false;
|
||||
$cleaned = false;
|
||||
|
||||
$cleanup = function() use (&$cleaned, &$swapped, $shadow_path, $staging_path, $placeholder_backup, $download_path, $lock, $request_id)
|
||||
$detail = '';
|
||||
$connection = bratonien_tools_webdav_derivative_connection($source, $detail);
|
||||
if (!$connection)
|
||||
{
|
||||
if ($cleaned) return;
|
||||
$cleaned = true;
|
||||
if ($swapped)
|
||||
{
|
||||
if (is_file($shadow_path) && !is_link($shadow_path)) @unlink($shadow_path);
|
||||
if (is_link($placeholder_backup)) @rename($placeholder_backup, $shadow_path);
|
||||
$swapped = false;
|
||||
}
|
||||
if (is_file($staging_path) || is_link($staging_path)) @unlink($staging_path);
|
||||
if (is_file($download_path)) @unlink($download_path);
|
||||
bratonien_tools_webdav_derivative_debug($request_id, 'restored', array('is_link'=>is_link($shadow_path), 'realpath'=>realpath($shadow_path) ?: 'FALSE'));
|
||||
@flock($lock, LOCK_UN);
|
||||
@fclose($lock);
|
||||
};
|
||||
register_shutdown_function($cleanup);
|
||||
flock($lock_handle, LOCK_UN);
|
||||
fclose($lock_handle);
|
||||
bratonien_tools_webdav_derivative_abort(502, $detail !== '' ? $detail : 'WebDAV-Verbindung konnte nicht geladen werden.');
|
||||
}
|
||||
|
||||
try
|
||||
$temp_dir = $lock_dir.'/tmp';
|
||||
if (!is_dir($temp_dir)) @mkdir($temp_dir, 0755, true);
|
||||
$temp_source = $temp_dir.'/source-'.$image_id.'-'.$request_id.'.img';
|
||||
$source_mode = 'original';
|
||||
$preview_plan = bratonien_tools_webdav_derivative_preview_plan($variant, $derivative_size, $source, $image_row);
|
||||
|
||||
if ($preview_plan)
|
||||
{
|
||||
if (is_link($placeholder_backup)) bratonien_tools_webdav_derivative_abort(503, 'WebDAV-Placeholder-Backup existiert bereits; vorheriger Materialisierungslauf ist nicht sauber abgeschlossen.');
|
||||
$detail = '';
|
||||
$connection = bratonien_tools_webdav_derivative_connection($source, $detail);
|
||||
if (!$connection) bratonien_tools_webdav_derivative_abort(502, $detail);
|
||||
|
||||
$source_mode = 'original';
|
||||
$preview_plan = bratonien_tools_webdav_derivative_preview_plan($variant, $derivative_size, $source, $image_row);
|
||||
bratonien_tools_webdav_derivative_debug($request_id, 'preview_start', array(
|
||||
'fileid'=>$preview_plan['fileid'],
|
||||
'width'=>$preview_plan['width'],
|
||||
'height'=>$preview_plan['height'],
|
||||
));
|
||||
$download_start = microtime(true);
|
||||
if ($preview_plan !== null)
|
||||
if (bratonien_tools_webdav_derivative_download_preview($preview_plan, $connection, $temp_source, $detail))
|
||||
{
|
||||
bratonien_tools_webdav_derivative_debug($request_id, 'preview_start', $preview_plan);
|
||||
if (bratonien_tools_webdav_derivative_download_preview($preview_plan, $connection, $download_path, $detail))
|
||||
{
|
||||
$source_mode = 'preview';
|
||||
}
|
||||
else
|
||||
{
|
||||
bratonien_tools_webdav_derivative_debug($request_id, 'preview_failed', array('detail'=>$detail));
|
||||
$detail = '';
|
||||
}
|
||||
$source_mode = 'preview';
|
||||
bratonien_tools_webdav_derivative_debug($request_id, 'download_done', array(
|
||||
'mode'=>$source_mode,
|
||||
'ms'=>round((microtime(true) - $download_start) * 1000, 1),
|
||||
'bytes'=>filesize($temp_source),
|
||||
));
|
||||
}
|
||||
if ($source_mode !== 'preview')
|
||||
else
|
||||
{
|
||||
if (!bratonien_tools_webdav_derivative_download($source, $connection, $download_path, $detail)) bratonien_tools_webdav_derivative_abort(502, $detail);
|
||||
bratonien_tools_webdav_derivative_debug($request_id, 'preview_failed', array('detail'=>$detail));
|
||||
$detail = '';
|
||||
}
|
||||
}
|
||||
|
||||
if ($source_mode !== 'preview')
|
||||
{
|
||||
@unlink($temp_source);
|
||||
$download_start = microtime(true);
|
||||
if (!bratonien_tools_webdav_derivative_download($source, $connection, $temp_source, $detail))
|
||||
{
|
||||
flock($lock_handle, LOCK_UN);
|
||||
fclose($lock_handle);
|
||||
bratonien_tools_webdav_derivative_abort(502, $detail !== '' ? $detail : 'WebDAV-Original konnte nicht geladen werden.');
|
||||
}
|
||||
bratonien_tools_webdav_derivative_debug($request_id, 'download_done', array(
|
||||
'mode'=>$source_mode,
|
||||
'ms'=>round((microtime(true)-$download_start)*1000, 1),
|
||||
'bytes'=>@filesize($download_path) ?: 0,
|
||||
'mode'=>'original',
|
||||
'ms'=>round((microtime(true) - $download_start) * 1000, 1),
|
||||
'bytes'=>filesize($temp_source),
|
||||
));
|
||||
}
|
||||
|
||||
if (!@copy($download_path, $staging_path)) bratonien_tools_webdav_derivative_abort(500, 'Bildquelle konnte nicht in den WebDAV-Shadow-Baum kopiert werden.');
|
||||
@chmod($staging_path, 0644);
|
||||
if (@getimagesize($staging_path) === false) bratonien_tools_webdav_derivative_abort(500, 'Materialisierte Shadow-Quelle ist kein gueltiges Bild.');
|
||||
$source_path = realpath($source_logical_path);
|
||||
if ($source_path === false || !is_file($source_path))
|
||||
{
|
||||
@unlink($temp_source);
|
||||
flock($lock_handle, LOCK_UN);
|
||||
fclose($lock_handle);
|
||||
bratonien_tools_webdav_derivative_abort(500, 'Physischer Platzhalter konnte nicht aufgeloest werden.');
|
||||
}
|
||||
|
||||
bratonien_tools_webdav_derivative_debug($request_id, 'swap_begin', array('placeholder'=>$placeholder_target, 'mode'=>$source_mode));
|
||||
if (!@rename($shadow_path, $placeholder_backup)) bratonien_tools_webdav_derivative_abort(500, 'WebDAV-Placeholder konnte nicht temporaer gesichert werden.');
|
||||
if (!@rename($staging_path, $shadow_path))
|
||||
$placeholder_backup = $source_path.'.bratonien-placeholder-'.$request_id;
|
||||
$staging_path = $source_logical_path.'.bratonien-source-'.$request_id;
|
||||
@unlink($placeholder_backup);
|
||||
@unlink($staging_path);
|
||||
if (!@copy($temp_source, $staging_path))
|
||||
{
|
||||
@unlink($temp_source);
|
||||
flock($lock_handle, LOCK_UN);
|
||||
fclose($lock_handle);
|
||||
bratonien_tools_webdav_derivative_abort(500, 'Temporaere Bildquelle konnte nicht bereitgestellt werden.');
|
||||
}
|
||||
|
||||
$swapped = false;
|
||||
try
|
||||
{
|
||||
bratonien_tools_webdav_derivative_debug($request_id, 'swap_begin', array('placeholder'=>$source_path, 'mode'=>$source_mode));
|
||||
if (!@rename($source_path, $placeholder_backup)) throw new RuntimeException('Platzhalter konnte nicht gesichert werden.');
|
||||
if (!@rename($staging_path, $source_path))
|
||||
{
|
||||
@rename($placeholder_backup, $shadow_path);
|
||||
bratonien_tools_webdav_derivative_abort(500, 'Materialisierte Bildquelle konnte nicht am Piwigo-Quellpfad aktiviert werden.');
|
||||
@rename($placeholder_backup, $source_path);
|
||||
throw new RuntimeException('Temporaere Bildquelle konnte nicht eingesetzt werden.');
|
||||
}
|
||||
$swapped = true;
|
||||
clearstatcache(true, $shadow_path);
|
||||
bratonien_tools_webdav_derivative_debug($request_id, 'swapped', array('is_link'=>is_link($shadow_path), 'realpath'=>realpath($shadow_path) ?: 'FALSE', 'mode'=>$source_mode));
|
||||
if (!is_file($shadow_path) || is_link($shadow_path) || @getimagesize($shadow_path) === false) bratonien_tools_webdav_derivative_abort(500, 'Piwigo-Quellpfad enthaelt nach Materialisierung keine gueltige Bildquelle.');
|
||||
bratonien_tools_webdav_derivative_debug($request_id, 'swapped', array('is_link'=>is_link($source_logical_path), 'mode'=>$source_mode));
|
||||
|
||||
$generate_start = microtime(true);
|
||||
bratonien_tools_webdav_derivative_debug($request_id, 'generate_start', array('target'=>$target_path, 'mode'=>$source_mode));
|
||||
$generate_ok = bratonien_tools_webdav_derivative_generate($shadow_path, $target_path, $params, $image_row, $detail);
|
||||
bratonien_tools_webdav_derivative_debug($request_id, 'generate_done', array('ok'=>$generate_ok, 'mode'=>$source_mode, 'ms'=>round((microtime(true)-$generate_start)*1000, 1), 'detail'=>$detail));
|
||||
if (!$generate_ok) bratonien_tools_webdav_derivative_abort(500, $detail);
|
||||
|
||||
clearstatcache(true, $target_path);
|
||||
$final_size = @getimagesize($target_path);
|
||||
if (!is_file($target_path) || !is_readable($target_path) || !is_array($final_size)) bratonien_tools_webdav_derivative_abort(500, 'Piwigo hat kein gueltiges finales Derivat erzeugt.');
|
||||
|
||||
$cleanup();
|
||||
bratonien_tools_webdav_derivative_debug($request_id, 'serve', array('target'=>$target_path, 'width'=>$final_size[0] ?? 0, 'height'=>$final_size[1] ?? 0, 'mode'=>$source_mode));
|
||||
if ($redirect_after !== null) { header('Location: '.$redirect_after, true, 302); exit; }
|
||||
bratonien_tools_webdav_derivative_serve($target_path);
|
||||
$ok = bratonien_tools_webdav_derivative_generate($source_path, $target_path, $params, $image_row, $detail);
|
||||
bratonien_tools_webdav_derivative_debug($request_id, 'generate_done', array(
|
||||
'ok'=>$ok,
|
||||
'mode'=>$source_mode,
|
||||
'ms'=>round((microtime(true) - $generate_start) * 1000, 1),
|
||||
'detail'=>$detail,
|
||||
));
|
||||
if (!$ok) throw new RuntimeException($detail !== '' ? $detail : 'Piwigo-Derivat konnte nicht erzeugt werden.');
|
||||
}
|
||||
finally
|
||||
catch (Throwable $e)
|
||||
{
|
||||
$cleanup();
|
||||
if ($swapped)
|
||||
{
|
||||
@unlink($source_path);
|
||||
@rename($placeholder_backup, $source_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
@unlink($staging_path);
|
||||
}
|
||||
@unlink($temp_source);
|
||||
flock($lock_handle, LOCK_UN);
|
||||
fclose($lock_handle);
|
||||
bratonien_tools_webdav_derivative_abort(500, $e->getMessage());
|
||||
}
|
||||
|
||||
@unlink($source_path);
|
||||
@rename($placeholder_backup, $source_path);
|
||||
@unlink($temp_source);
|
||||
bratonien_tools_webdav_derivative_debug($request_id, 'restored', array('is_link'=>is_link($source_logical_path)));
|
||||
|
||||
flock($lock_handle, LOCK_UN);
|
||||
fclose($lock_handle);
|
||||
|
||||
if (!is_file($target_path) || !is_readable($target_path)) bratonien_tools_webdav_derivative_abort(500, 'Erzeugtes Derivat fehlt.');
|
||||
$final_size = @getimagesize($target_path);
|
||||
bratonien_tools_webdav_derivative_debug($request_id, 'serve', array(
|
||||
'target'=>$target_path,
|
||||
'width'=>(int)($final_size[0] ?? 0),
|
||||
'height'=>(int)($final_size[1] ?? 0),
|
||||
'mode'=>$source_mode,
|
||||
));
|
||||
bratonien_tools_webdav_derivative_send($target_path, $after_url);
|
||||
|
||||
Reference in New Issue
Block a user