From a43a15a9d39e80deb895a5d4d4efabe1d9c696ff Mon Sep 17 00:00:00 2001 From: Terranom674 Date: Wed, 19 Aug 2026 23:47:13 +0200 Subject: [PATCH 01/19] Restore stable watermark runtime --- include/watermark_runtime.inc.php | 68 +------------------------------ 1 file changed, 1 insertion(+), 67 deletions(-) diff --git a/include/watermark_runtime.inc.php b/include/watermark_runtime.inc.php index 4e78c07..97adee4 100644 --- a/include/watermark_runtime.inc.php +++ b/include/watermark_runtime.inc.php @@ -27,72 +27,6 @@ function bratonien_tools_runtime_category_id() return 0; } -function bratonien_tools_watermark_album_cover_category($src_image, $category_id=null) -{ - static $map = null; - - if ($map === null) - { - $map = new SplObjectStorage(); - } - - if (!is_object($src_image)) - { - return 0; - } - - if ($category_id !== null) - { - $category_id = (int)$category_id; - if ($category_id > 0) - { - $map[$src_image] = $category_id; - } - return $category_id; - } - - return $map->contains($src_image) ? (int)$map[$src_image] : 0; -} - -function bratonien_tools_watermark_prepare_album_overview($thumbnails) -{ - if (!is_array($thumbnails)) - { - return $thumbnails; - } - - foreach ($thumbnails as &$thumbnail) - { - $category_id = (int)($thumbnail['id'] ?? $thumbnail['ID'] ?? 0); - if ($category_id < 1 || empty($thumbnail['representative']['src_image']) || !is_object($thumbnail['representative']['src_image'])) - { - continue; - } - - // Ein Repraesentantenbild kann fuer mehrere Alben verwendet werden. Piwigo - // reicht in der Albumuebersicht aber nur das SrcImage an get_derivative_url - // weiter. Deshalb bekommt jedes Album-Cover eine eigene SrcImage-Instanz, - // damit seine konkrete Albumregel erhalten bleibt. - $src_image = clone $thumbnail['representative']['src_image']; - $thumbnail['representative']['src_image'] = $src_image; - bratonien_tools_watermark_album_cover_category($src_image, $category_id); - } - unset($thumbnail); - - return $thumbnails; -} - -function bratonien_tools_runtime_derivative_category_id($src_image) -{ - $category_id = bratonien_tools_runtime_category_id(); - if ($category_id > 0) - { - return $category_id; - } - - return bratonien_tools_watermark_album_cover_category($src_image); -} - function bratonien_tools_runtime_effective_rule($category_id) { static $categories = null; @@ -296,7 +230,7 @@ function bratonien_tools_filter_derivative_url($url, $params, $src_image, $rel_u return $url; } - $rule = bratonien_tools_runtime_effective_rule(bratonien_tools_runtime_derivative_category_id($src_image)); + $rule = bratonien_tools_runtime_effective_rule(bratonien_tools_runtime_category_id()); if ($rule['mode'] !== 'profile' || empty($rule['profile_id'])) { return $url; From ad4c281fdc9df8fa0827a471641302b086fa727d Mon Sep 17 00:00:00 2001 From: Terranom674 Date: Wed, 19 Aug 2026 23:47:31 +0200 Subject: [PATCH 02/19] Restore stable watermark engine --- include/watermark_engine.inc.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/include/watermark_engine.inc.php b/include/watermark_engine.inc.php index 5f5f5a6..3826954 100644 --- a/include/watermark_engine.inc.php +++ b/include/watermark_engine.inc.php @@ -8,22 +8,12 @@ require_once(BRATONIEN_TOOLS_PATH . 'include/watermark_base.inc.php'); function bratonien_tools_watermark_engine_enabled() { - static $initialized = false; - static $enabled = false; - - if ($initialized) - { - return $enabled; - } - $config = bratonien_tools_get_watermark_engine_config(); $enabled = !empty($config['enabled']); - $initialized = true; // Piwigo recalculates use_watermark for custom derivatives from the native // watermark file itself. Therefore an active Bratonien engine must keep the // native watermark file empty, not only the stored use_watermark flags false. - // This initialization is required only once per PHP request. if ($enabled) { bratonien_tools_disable_piwigo_watermarks(); From 1d62dc17e80538512f226649d3af53cd8b962ccf Mon Sep 17 00:00:00 2001 From: Terranom674 Date: Wed, 19 Aug 2026 23:47:46 +0200 Subject: [PATCH 03/19] Restore stable album watermark rules --- tools/album_rules.inc.php | 35 +++++------------------------------ 1 file changed, 5 insertions(+), 30 deletions(-) diff --git a/tools/album_rules.inc.php b/tools/album_rules.inc.php index 4046c24..eae4c7b 100644 --- a/tools/album_rules.inc.php +++ b/tools/album_rules.inc.php @@ -61,7 +61,6 @@ function bratonien_tools_save_album_rule() ))); } - bratonien_tools_clear_watermark_cache(); return array('message'=>'Albumregel gespeichert.'); } @@ -94,34 +93,7 @@ function bratonien_tools_resolve_album_rule($category_id, array $categories, arr $by_id[(int)$category['id']] = $category; } - $category_id = (int)$category_id; - $root = $by_id[$category_id] ?? null; - $is_private = $root && isset($root['status']) && $root['status'] === 'private'; - - if ($is_private) - { - if (isset($rules[$category_id])) - { - $rule = $rules[$category_id]; - if ($rule['mode'] === 'disabled') - { - return array('mode'=>'disabled','profile_id'=>null,'source'=>'album'); - } - if ($rule['mode'] === 'profile') - { - return array('mode'=>'profile','profile_id'=>(int)$rule['profile_id'],'source'=>'album'); - } - } - - $profile_id = $defaults['private_profile'] ?? null; - if (empty($profile_id)) - { - return array('mode'=>'disabled','profile_id'=>null,'source'=>'global'); - } - return array('mode'=>'profile','profile_id'=>(int)$profile_id,'source'=>'global'); - } - - $current = $category_id; + $current = (int)$category_id; $visited = array(); while ($current > 0 && isset($by_id[$current]) && !isset($visited[$current])) @@ -144,7 +116,10 @@ function bratonien_tools_resolve_album_rule($category_id, array $categories, arr $current = (int)($by_id[$current]['id_uppercat'] ?? 0); } - $profile_id = $defaults['public_profile'] ?? null; + $root = $by_id[(int)$category_id] ?? null; + $is_private = $root && isset($root['status']) && $root['status'] === 'private'; + $profile_id = $is_private ? ($defaults['private_profile'] ?? null) : ($defaults['public_profile'] ?? null); + if (empty($profile_id)) { return array('mode'=>'disabled','profile_id'=>null,'source'=>'global'); From c358c157e4a3ec2b9b7ebcc1e0cbd8693b3081cd Mon Sep 17 00:00:00 2001 From: Terranom674 Date: Wed, 19 Aug 2026 23:48:06 +0200 Subject: [PATCH 04/19] Restore stable watermark profile behavior --- tools/watermark_profiles.inc.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/tools/watermark_profiles.inc.php b/tools/watermark_profiles.inc.php index cd45e25..7ee37bd 100644 --- a/tools/watermark_profiles.inc.php +++ b/tools/watermark_profiles.inc.php @@ -128,7 +128,6 @@ function bratonien_tools_save_watermark_profile() mass_inserts($table, array_keys($data), array($data)); } - bratonien_tools_clear_watermark_cache(); return array('message'=>'Wasserzeichenprofil gespeichert.'); } @@ -159,7 +158,6 @@ function bratonien_tools_delete_watermark_profile() } pwg_query('DELETE FROM '.bratonien_tools_table('watermark_profiles').' WHERE id='.$id); - bratonien_tools_clear_watermark_cache(); return array('message'=>'Wasserzeichenprofil geloescht.'); } @@ -176,7 +174,6 @@ function bratonien_tools_duplicate_watermark_profile() $profile['name'] .= ' (Kopie)'; $profile['created'] = date('Y-m-d H:i:s'); mass_inserts(bratonien_tools_table('watermark_profiles'), array_keys($profile), array($profile)); - bratonien_tools_clear_watermark_cache(); return array('message'=>'Wasserzeichenprofil dupliziert.'); } From 47d7000cc8442644b1b55e90c65e13e245ba3533 Mon Sep 17 00:00:00 2001 From: Terranom674 Date: Wed, 19 Aug 2026 23:48:16 +0200 Subject: [PATCH 05/19] Restore stable watermark settings --- tools/watermark_settings.inc.php | 54 -------------------------------- 1 file changed, 54 deletions(-) diff --git a/tools/watermark_settings.inc.php b/tools/watermark_settings.inc.php index 4e29868..cfa2948 100644 --- a/tools/watermark_settings.inc.php +++ b/tools/watermark_settings.inc.php @@ -4,59 +4,6 @@ if (!defined('PHPWG_ROOT_PATH')) die('Hacking attempt!'); } -function bratonien_tools_watermark_cache_dir() -{ - return PHPWG_ROOT_PATH.PWG_DERIVATIVE_DIR.'bratonien-watermark'; -} - -function bratonien_tools_remove_tree($path) -{ - $path = rtrim((string)$path, DIRECTORY_SEPARATOR); - if ($path === '' || !file_exists($path)) return; - - if (is_file($path) || is_link($path)) - { - @unlink($path); - return; - } - - foreach (scandir($path) ?: array() as $entry) - { - if ($entry === '.' || $entry === '..') continue; - bratonien_tools_remove_tree($path.DIRECTORY_SEPARATOR.$entry); - } - @rmdir($path); -} - -function bratonien_tools_clear_watermark_cache() -{ - $dir = bratonien_tools_watermark_cache_dir(); - $derivative_root = realpath(PHPWG_ROOT_PATH.PWG_DERIVATIVE_DIR); - $parent = realpath(dirname($dir)); - - if ($derivative_root === false || $parent === false || $parent !== $derivative_root) - { - throw new RuntimeException('Wasserzeichen-Cachepfad ist ungueltig.'); - } - - if (is_dir($dir)) - { - bratonien_tools_remove_tree($dir); - } -} - -function bratonien_tools_watermark_cache_upgrade() -{ - $version = '0.9.7.13'; - if ((string)conf_get_param('bratonien_watermark_cache_version', '') === $version) - { - return; - } - - bratonien_tools_clear_watermark_cache(); - conf_update_param('bratonien_watermark_cache_version', $version); -} - function bratonien_tools_get_watermark_defaults() { $defaults = conf_get_param('bratonien_watermark_defaults', null); @@ -93,7 +40,6 @@ function bratonien_tools_save_watermark_defaults() ); conf_update_param('bratonien_watermark_defaults', json_encode($config)); - bratonien_tools_clear_watermark_cache(); return array('message'=>'Globale Wasserzeichenregeln gespeichert.'); } From ef1c1265a970aae2743c06a8010ad8bd704cce3c Mon Sep 17 00:00:00 2001 From: Terranom674 Date: Wed, 19 Aug 2026 23:48:40 +0200 Subject: [PATCH 06/19] 0.9.7.18: restore stable non-connector hooks --- main.inc.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/main.inc.php b/main.inc.php index 260baa5..aef62f0 100644 --- a/main.inc.php +++ b/main.inc.php @@ -1,7 +1,7 @@ Date: Wed, 19 Aug 2026 23:49:13 +0200 Subject: [PATCH 07/19] Disable parallel NC editor layer --- js/nc_connector_edit_v2.js | 236 +------------------------------------ 1 file changed, 3 insertions(+), 233 deletions(-) diff --git a/js/nc_connector_edit_v2.js b/js/nc_connector_edit_v2.js index a2d4c01..15787a0 100644 --- a/js/nc_connector_edit_v2.js +++ b/js/nc_connector_edit_v2.js @@ -1,236 +1,6 @@ (function () { 'use strict'; - - function ready(callback) { - if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', callback); - else callback(); - } - - ready(function () { - var section = document.getElementById('nc-connector'); - if (!section) return; - - var tokenInput = section.querySelector('input[name="pwg_token"]'); - var pwgToken = tokenInput ? tokenInput.value : ''; - - var technicalButton = document.getElementById('bratonien-nc-technical-open'); - if (technicalButton) technicalButton.remove(); - var technicalCreate = document.getElementById('bratonien-nc-technical-create'); - if (technicalCreate) technicalCreate.remove(); - - var statusHeading = Array.prototype.find.call(section.querySelectorAll('h4'), function (heading) { - return heading.textContent.trim() === 'Status'; - }); - var statusCard = statusHeading ? statusHeading.closest('.bratonien-card') : null; - if (statusCard && pwgToken && !statusCard.querySelector('[data-nc-run-now]')) { - var runForm = document.createElement('form'); - runForm.method = 'post'; - runForm.className = 'bratonien-actions'; - runForm.style.marginTop = '1rem'; - runForm.setAttribute('data-nc-run-now', '1'); - runForm.innerHTML = '' - + ''; - runForm.addEventListener('submit', function () { - var button = runForm.querySelector('button'); - if (button) { - button.disabled = true; - button.textContent = 'Abgleich wird gestartet …'; - } - }); - statusCard.appendChild(runForm); - } - - function escapeHtml(value) { - return String(value == null ? '' : value).replace(/[&<>"']/g, function (c) { - return {'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]; - }); - } - - function loadConnection(id) { - return fetch('plugins/bratonien_tools/nc-connector-edit-data.php?connection_id='+encodeURIComponent(id)+'&_='+Date.now(), { - credentials:'same-origin', cache:'no-store', headers:{'Accept':'application/json'} - }).then(function (response) { - return response.json().then(function (data) { - if (!response.ok) throw new Error(data.error || ('HTTP '+response.status)); - return data; - }); - }); - } - - function createPostForm(label, tool, id) { - var form = document.createElement('form'); - form.method = 'post'; - form.style.display = 'inline'; - form.innerHTML = '' - + '' - + ''; - return form; - } - - function dialog() { - var node = document.getElementById('bratonien-nc-connection-edit-v2'); - if (node) return node; - node = document.createElement('dialog'); - node.id = 'bratonien-nc-connection-edit-v2'; - node.className = 'bratonien-edit-dialog'; - node.innerHTML = '
' - + '
' - + '

Verbindung bearbeiten

Die Einstellungen gelten nur für diese Verbindung.

' - + '
' - + '
'; - document.body.appendChild(node); - node.querySelector('[data-edit-close]').addEventListener('click', function () { node.close(); }); - node.addEventListener('click', function (event) { if (event.target === node) node.close(); }); - return node; - } - - function storageRow(storage) { - storage = storage || {}; - return '
' - + '' - + '' - + '' - + '
'; - } - - function showError(form, data) { - var old = form.querySelector('[data-edit-error]'); - if (old) old.remove(); - var box = document.createElement('div'); - box.dataset.editError = '1'; - box.className = 'bratonien-main-cache__warning'; - box.style.margin = '0 0 1rem'; - box.textContent = (data && data.message) ? data.message : 'Die Verbindung konnte nicht gespeichert werden.'; - form.insertBefore(box, form.firstChild); - } - - function submitLocal(form, editDialog) { - if (!form.reportValidity()) return; - var button = form.querySelector('button[type="submit"]'); - if (button) button.disabled = true; - fetch('plugins/bratonien_tools/nc-connector-edit-save.php', { - method:'POST', credentials:'same-origin', cache:'no-store', headers:{'Accept':'application/json'}, body:new FormData(form) - }).then(function (response) { - return response.json().then(function (data) { - if (!response.ok || !data.ok) { showError(form, data); return; } - editDialog.close(); - window.location.reload(); - }); - }).catch(function (error) { - showError(form, {message:error.message || String(error)}); - }).finally(function () { - if (button) button.disabled = false; - }); - } - - function openLocalEditor(id) { - var editDialog = dialog(); - var content = editDialog.querySelector('[data-edit-content]'); - content.innerHTML = '

Verbindung wird geladen …

'; - if (typeof editDialog.showModal === 'function') editDialog.showModal(); - else editDialog.setAttribute('open', 'open'); - - loadConnection(id).then(function (data) { - if (data.adapter !== 'local') throw new Error('Diese Verbindung wird über den WebDAV-Assistenten bearbeitet.'); - var legacy = data.legacy || {}; - var webdav = data.webdav || {}; - var storages = Array.isArray(legacy.storages) ? legacy.storages : []; - var rows = storages.map(storageRow).join('') || storageRow({}); - - content.innerHTML = '
' - + '' - + '' - + '
Verbindung
' - + '' - + '
' - + '
Nextcloud
' - + '' - + '' - + '' - + '
' - + '
Piwigo-Zugang
' - + '' - + '' - + '' - + '' - + '
' - + '
Lokaler Connector
' - + '' - + '' - + '' - + '' - + '' - + '
' - + '
Speicherorte
'+rows+'
' - + '' - + '
Erweiterte Einstellungen
' - + '' - + '' - + '' - + '' - + '' - + '' - + '
' - + '
' - + '
'; - - var form = content.querySelector('[data-edit-form]'); - form.querySelector('[data-cancel]').addEventListener('click', function () { editDialog.close(); }); - form.querySelector('[data-add-storage]').addEventListener('click', function () { - form.querySelector('[data-storage-list]').insertAdjacentHTML('beforeend', storageRow({})); - }); - form.addEventListener('click', function (event) { - var remove = event.target.closest('[data-remove-storage]'); - if (remove) { - var row = remove.closest('[data-storage-row]'); - if (row) row.remove(); - } - }); - form.addEventListener('submit', function (event) { - event.preventDefault(); - submitLocal(form, editDialog); - }); - }).catch(function (error) { - content.innerHTML = '

Bearbeiten nicht möglich: '+escapeHtml(error.message || String(error))+'

'; - }); - } - - [].slice.call(section.querySelectorAll('button[value="nc_connector_edit_start"]')).forEach(function (button) { - var form = button.closest('form'); - if (form) form.remove(); - }); - - [].slice.call(section.querySelectorAll('button[value="nc_connector_migrate_start"]')).forEach(function (button) { - var form = button.closest('form'); - if (form) form.remove(); - }); - - [].slice.call(section.querySelectorAll('button[value="nc_connector_delete"]')).forEach(function (deleteButton) { - var deleteForm = deleteButton.closest('form'); - if (!deleteForm || !deleteForm.parentElement) return; - var idInput = deleteForm.querySelector('input[name="connection_id"]'); - if (!idInput) return; - var id = idInput.value; - var actions = deleteForm.parentElement; - - loadConnection(id).then(function (data) { - var edit; - if (data.adapter === 'local') { - edit = document.createElement('button'); - edit.type = 'button'; - edit.className = 'buttonLike'; - edit.textContent = 'Bearbeiten'; - edit.addEventListener('click', function () { openLocalEditor(id); }); - } else { - edit = createPostForm('Bearbeiten', 'nc_connector_edit_start', id); - } - actions.insertBefore(edit, deleteForm); - }).catch(function (error) { - var info = document.createElement('span'); - info.className = 'bratonien-main-cache__warning'; - info.textContent = 'Verbindung konnte nicht geladen werden: '+(error.message || String(error)); - actions.insertBefore(info, deleteForm); - }); - }); - }); + // 0.9.7.18: Die parallele AJAX-Editor-Schicht ist absichtlich deaktiviert. + // Der NC Connector benutzt wieder ausschliesslich die vorhandenen + // Piwigo-Formulare, die Tool-Registry und den WebDAV-Wizard. })(); From b8f3ed44a3d0b47f49d61ba624349e4fe3185b24 Mon Sep 17 00:00:00 2001 From: Terranom674 Date: Wed, 19 Aug 2026 23:49:21 +0200 Subject: [PATCH 08/19] Remove parallel NC edit data endpoint --- nc-connector-edit-data.php | 93 -------------------------------------- 1 file changed, 93 deletions(-) delete mode 100644 nc-connector-edit-data.php diff --git a/nc-connector-edit-data.php b/nc-connector-edit-data.php deleted file mode 100644 index 53fcee5..0000000 --- a/nc-connector-edit-data.php +++ /dev/null @@ -1,93 +0,0 @@ -'Bratonien Tools ist nicht aktiv.')); - exit; -} -if (!function_exists('is_admin') || !is_admin()) -{ - http_response_code(403); - echo json_encode(array('error'=>'Administratorrechte erforderlich.')); - exit; -} - -require_once(BRATONIEN_TOOLS_PATH.'include/nc_connector.inc.php'); -require_once(BRATONIEN_TOOLS_PATH.'include/nc_connector_manage.inc.php'); -require_once(BRATONIEN_TOOLS_PATH.'include/nc_connector_connection_scope.inc.php'); - -$id = (int)($_GET['connection_id'] ?? 0); -$connection = bratonien_tools_nc_connector_connection($id, true); -if (!$connection) -{ - http_response_code(404); - echo json_encode(array('error'=>'Connector-Verbindung wurde nicht gefunden.')); - exit; -} - -$config = isset($connection['config']) && is_array($connection['config']) ? $connection['config'] : array(); -$credentials = array(); -try -{ - $credentials = bratonien_tools_nc_connector_scoped_secret($connection); -} -catch (Throwable $e) -{ - $credentials = array(); -} - -$storages = array(); -foreach ((array)($config['storages'] ?? array()) as $storage) -{ - $storages[] = array( - 'storage_id'=>(string)($storage['storage_id'] ?? ''), - 'source_prefix'=>(string)($storage['source_prefix'] ?? ''), - 'local_mount'=>(string)($storage['local_mount'] ?? ''), - ); -} - -$payload = array( - 'id'=>(int)$connection['id'], - 'name'=>(string)$connection['name'], - 'adapter'=>(string)$connection['adapter'], - 'enabled'=>(bool)$connection['enabled'], - 'takeover_state'=>(string)$connection['takeover_state'], - 'source_mode'=>(string)($config['source_mode'] ?? ''), - 'legacy'=>array( - 'host'=>(string)($config['host'] ?? ''), - 'port'=>(string)($config['port'] ?? '5432'), - 'database'=>(string)($config['database'] ?? ''), - 'user'=>(string)($config['user'] ?? ''), - 'has_db_password'=>trim((string)($credentials['db_password'] ?? '')) !== '', - 'source_view'=>(string)($config['source_view'] ?? ''), - 'activity_view'=>(string)($config['activity_view'] ?? ''), - 'gallery_root'=>(string)($config['gallery_root'] ?? ''), - 'quiet_seconds'=>(int)($config['quiet_seconds'] ?? 120), - 'max_wait_seconds'=>(int)($config['max_wait_seconds'] ?? 900), - 'full_sync_seconds'=>(int)($config['full_sync_seconds'] ?? 86400), - 'storages'=>$storages, - ), - 'webdav'=>array( - 'nextcloud_url'=>(string)($config['nextcloud_url'] ?? ''), - 'nextcloud_user'=>(string)($credentials['nextcloud_user'] ?? $config['nextcloud_access_user'] ?? $config['access_user'] ?? ''), - 'has_nextcloud_password'=>(string)($credentials['nextcloud_password'] ?? '') !== '', - 'api_key_id'=>(string)($credentials['api_key_id'] ?? ''), - 'has_api_key_secret'=>trim((string)($credentials['api_key_secret'] ?? '')) !== '', - 'fallback_user'=>(string)($credentials['piwigo_user'] ?? ''), - 'has_fallback_password'=>(string)($credentials['piwigo_password'] ?? '') !== '', - ), -); - -echo json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); From 6e96b0adc32bf0c34c9145918e6d40d71737b6a5 Mon Sep 17 00:00:00 2001 From: Terranom674 Date: Wed, 19 Aug 2026 23:49:28 +0200 Subject: [PATCH 09/19] Remove parallel NC edit save endpoint --- nc-connector-edit-save.php | 356 ------------------------------------- 1 file changed, 356 deletions(-) delete mode 100644 nc-connector-edit-save.php diff --git a/nc-connector-edit-save.php b/nc-connector-edit-save.php deleted file mode 100644 index a2173ad..0000000 --- a/nc-connector-edit-save.php +++ /dev/null @@ -1,356 +0,0 @@ -false, - 'message'=>$visible, - 'stage'=>(string)$stage, - 'detail'=>(string)$detail, - 'fields'=>array_values(array_unique($fields)), - )); -} - -function bratonien_tools_nc_edit_probe($url, $username = '', $password = '', array $headers = array()) -{ - if (!function_exists('curl_init')) - { - return array('ok'=>false, 'http'=>0, 'errno'=>-1, 'error'=>'cURL ist in PHP nicht verfügbar.', 'body'=>''); - } - - $ch = curl_init($url); - $options = array( - CURLOPT_RETURNTRANSFER=>true, - CURLOPT_FOLLOWLOCATION=>true, - CURLOPT_MAXREDIRS=>3, - CURLOPT_CONNECTTIMEOUT=>8, - CURLOPT_TIMEOUT=>15, - CURLOPT_HTTPHEADER=>array_merge(array('Accept: application/json'), $headers), - CURLOPT_USERAGENT=>'Bratonien-Tools-NC-Editor/'.(function_exists('bratonien_tools_current_version') ? bratonien_tools_current_version() : 'dev'), - ); - if ($username !== '') - { - $options[CURLOPT_HTTPAUTH] = CURLAUTH_BASIC; - $options[CURLOPT_USERPWD] = $username.':'.$password; - } - curl_setopt_array($ch, $options); - $body = curl_exec($ch); - $errno = curl_errno($ch); - $error = curl_error($ch); - $http = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE); - curl_close($ch); - - return array( - 'ok'=>$body !== false && $errno === 0, - 'http'=>$http, - 'errno'=>$errno, - 'error'=>$error, - 'body'=>$body === false ? '' : (string)$body, - ); -} - -if (!defined('BRATONIEN_TOOLS_PATH')) -{ - bratonien_tools_nc_edit_json(404, array('ok'=>false, 'message'=>'Bratonien Tools ist nicht aktiv.', 'fields'=>array())); -} -if (!function_exists('is_admin') || !is_admin()) -{ - bratonien_tools_nc_edit_json(403, array('ok'=>false, 'message'=>'Administratorrechte erforderlich.', 'fields'=>array())); -} -if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') -{ - bratonien_tools_nc_edit_json(405, array('ok'=>false, 'message'=>'Nur POST ist erlaubt.', 'fields'=>array())); -} - -try -{ - check_pwg_token(); - require_once(BRATONIEN_TOOLS_PATH.'include/tool_registry.inc.php'); - - $id = (int)($_POST['connection_id'] ?? 0); - $connection = bratonien_tools_nc_connector_connection($id, true); - if (!$connection) - { - bratonien_tools_nc_edit_fail('Die zu bearbeitende Verbindung wurde nicht gefunden.', array(), 'Verbindung'); - } - - $config = isset($connection['config']) && is_array($connection['config']) ? $connection['config'] : array(); - $credentials = bratonien_tools_nc_connector_scoped_secret($connection); - - $nextcloud_input = trim((string)($_POST['nc_nextcloud_url'] ?? ($config['nextcloud_url'] ?? ''))); - $nextcloud_user = trim((string)($_POST['nc_nextcloud_user'] ?? ($credentials['nextcloud_user'] ?? ''))); - $submitted_nc_password = (string)($_POST['nc_nextcloud_password'] ?? ''); - $nextcloud_password = $submitted_nc_password !== '' ? $submitted_nc_password : (string)($credentials['nextcloud_password'] ?? ''); - - $has_any_nextcloud = $nextcloud_input !== '' || $nextcloud_user !== '' || $nextcloud_password !== ''; - if ($has_any_nextcloud) - { - $missing = array(); - if ($nextcloud_input === '') $missing[] = 'nc_nextcloud_url'; - if ($nextcloud_user === '') $missing[] = 'nc_nextcloud_user'; - if ($nextcloud_password === '') $missing[] = 'nc_nextcloud_password'; - if ($missing) - { - bratonien_tools_nc_edit_fail( - 'Die WebDAV-Daten sind unvollständig. Adresse, Benutzer und Passwort werden gemeinsam benötigt.', - $missing, - 'Nextcloud / WebDAV' - ); - } - - try - { - $candidate_urls = bratonien_tools_nc_wizard_candidate_urls($nextcloud_input); - } - catch (Throwable $e) - { - bratonien_tools_nc_edit_fail($e->getMessage(), array('nc_nextcloud_url'), 'Nextcloud / WebDAV'); - } - - $nextcloud_url = ''; - $last_probe = null; - $last_status_url = ''; - foreach ($candidate_urls as $candidate_url) - { - $status_url = $candidate_url.'/status.php'; - $probe = bratonien_tools_nc_edit_probe($status_url); - $last_probe = $probe; - $last_status_url = $status_url; - - if (!$probe['ok'] || $probe['http'] < 200 || $probe['http'] >= 300) - { - continue; - } - - $status = json_decode($probe['body'], true); - if (!is_array($status) || empty($status['installed'])) - { - continue; - } - - $nextcloud_url = $candidate_url; - break; - } - - if ($nextcloud_url === '') - { - $attempts = array(); - foreach ($candidate_urls as $candidate_url) - { - $attempts[] = $candidate_url.'/status.php'; - } - if (is_array($last_probe) && !$last_probe['ok']) - { - $technical = $last_probe['errno'] >= 0 - ? 'cURL '.$last_probe['errno'].($last_probe['error'] !== '' ? ': '.$last_probe['error'] : '') - : $last_probe['error']; - bratonien_tools_nc_edit_fail( - 'Die Nextcloud-Adresse ist vom Piwigo-Server aus nicht erreichbar. Ohne angegebenes Protokoll wurden HTTPS und HTTP geprüft.', - array('nc_nextcloud_url'), - 'Nextcloud / WebDAV – Erreichbarkeit', - $technical.' · Geprüft: '.implode(' ; ', $attempts) - ); - } - - bratonien_tools_nc_edit_fail( - 'Unter der angegebenen Adresse wurde keine erreichbare installierte Nextcloud gefunden. Ohne angegebenes Protokoll wurden HTTPS und HTTP geprüft.', - array('nc_nextcloud_url'), - 'Nextcloud / WebDAV – Erkennung', - 'Geprüft: '.implode(' ; ', $attempts).($last_probe ? ' · Letzter HTTP-Status: '.$last_probe['http'] : '') - ); - } - - $_POST['nc_nextcloud_url'] = $nextcloud_url; - - $user_url = $nextcloud_url.'/ocs/v2.php/cloud/user?format=json'; - $user_probe = bratonien_tools_nc_edit_probe($user_url, $nextcloud_user, $nextcloud_password, array('OCS-APIRequest: true')); - if (!$user_probe['ok']) - { - $technical = $user_probe['errno'] >= 0 - ? 'cURL '.$user_probe['errno'].($user_probe['error'] !== '' ? ': '.$user_probe['error'] : '') - : $user_probe['error']; - bratonien_tools_nc_edit_fail( - 'Die Nextcloud-Anmeldung konnte technisch nicht geprüft werden.', - array('nc_nextcloud_url','nc_nextcloud_user','nc_nextcloud_password'), - 'Nextcloud / WebDAV – Anmeldung', - $technical.' · Ziel: '.$user_url - ); - } - if ($user_probe['http'] === 401 || $user_probe['http'] === 403) - { - bratonien_tools_nc_edit_fail( - 'Nextcloud hat Benutzername oder Passwort abgelehnt.', - array('nc_nextcloud_user','nc_nextcloud_password'), - 'Nextcloud / WebDAV – Anmeldung', - 'HTTP '.$user_probe['http'] - ); - } - if ($user_probe['http'] < 200 || $user_probe['http'] >= 300) - { - bratonien_tools_nc_edit_fail( - 'Nextcloud ist erreichbar, aber die Benutzerprüfung wurde vom Server abgelehnt.', - array('nc_nextcloud_user','nc_nextcloud_password'), - 'Nextcloud / WebDAV – Anmeldung', - 'HTTP '.$user_probe['http'].' · Ziel: '.$user_url - ); - } - try - { - bratonien_tools_nc_wizard_ocs_data($user_probe['body']); - } - catch (Throwable $e) - { - bratonien_tools_nc_edit_fail( - 'Nextcloud antwortet auf die Benutzerprüfung, die Antwort ist aber nicht gültig: '.$e->getMessage(), - array('nc_nextcloud_user','nc_nextcloud_password'), - 'Nextcloud / WebDAV – Anmeldung' - ); - } - } - - $api_key_id = trim((string)($_POST['nc_connection_api_key_id'] ?? ($credentials['api_key_id'] ?? ''))); - $submitted_api_secret = trim((string)($_POST['nc_connection_api_key_secret'] ?? '')); - $api_key_secret = $submitted_api_secret !== '' ? $submitted_api_secret : trim((string)($credentials['api_key_secret'] ?? '')); - if (($api_key_id === '') !== ($api_key_secret === '')) - { - $missing = $api_key_id === '' ? array('nc_connection_api_key_id') : array('nc_connection_api_key_secret'); - bratonien_tools_nc_edit_fail( - 'API-Schlüssel-ID und API-Geheimnis müssen gemeinsam vollständig sein.', - $missing, - 'Piwigo API' - ); - } - if ($api_key_id !== '') - { - try - { - bratonien_tools_nc_connector_validate_scoped_api($api_key_id, $api_key_secret); - } - catch (Throwable $e) - { - bratonien_tools_nc_edit_fail( - 'Der Piwigo-API-Zugang konnte nicht bestätigt werden: '.$e->getMessage(), - array('nc_connection_api_key_id','nc_connection_api_key_secret'), - 'Piwigo API' - ); - } - } - - $fallback_user = trim((string)($_POST['nc_fallback_user'] ?? ($credentials['piwigo_user'] ?? ''))); - $submitted_fallback_password = (string)($_POST['nc_fallback_password'] ?? ''); - $fallback_password = $submitted_fallback_password !== '' ? $submitted_fallback_password : (string)($credentials['piwigo_password'] ?? ''); - if (($fallback_user === '') !== ($fallback_password === '')) - { - bratonien_tools_nc_edit_fail( - 'Fallback-Benutzer und Fallback-Passwort müssen gemeinsam vollständig sein.', - array('nc_fallback_user','nc_fallback_password'), - 'Piwigo-Fallback' - ); - } - if ($fallback_user !== '') - { - try - { - bratonien_tools_nc_connector_validate_fallback_credentials($fallback_user, $fallback_password); - } - catch (Throwable $e) - { - bratonien_tools_nc_edit_fail( - 'Der Piwigo-Fallback wurde abgelehnt: '.$e->getMessage(), - array('nc_fallback_user','nc_fallback_password'), - 'Piwigo-Fallback' - ); - } - } - - $result = bratonien_tools_nc_connector_update_local_friendly(); - - if (array_key_exists('nc_fallback_user', $_POST) || array_key_exists('nc_fallback_password', $_POST)) - { - $updated = bratonien_tools_nc_connector_connection($id, true); - if (!$updated) throw new RuntimeException('Die Verbindung konnte nach dem Speichern nicht erneut geladen werden.'); - $updated_credentials = bratonien_tools_nc_connector_scoped_secret($updated); - $updated_credentials['piwigo_user'] = $fallback_user; - $updated_credentials['piwigo_password'] = $fallback_password; - bratonien_tools_nc_connector_store_scoped_secret($id, $updated, $updated_credentials); - } - - bratonien_tools_nc_edit_json(200, array( - 'ok'=>true, - 'message'=>(string)($result['message'] ?? 'Verbindung wurde gespeichert.'), - 'fields'=>array(), - )); -} -catch (Throwable $e) -{ - $message = trim($e->getMessage()); - if ($message === '') $message = 'Die Verbindung konnte nicht gespeichert werden.'; - - $fields = array(); - $stage = 'Verbindung'; - $add = function($name) use (&$fields) - { - if (!in_array($name, $fields, true)) $fields[] = $name; - }; - - if (stripos($message, 'Name') !== false && stripos($message, 'Datenbankname') === false) $add('connection_name'); - if (stripos($message, 'Datenbank-Server') !== false) { $add('nc_host'); $stage = 'Legacy-Datenbank'; } - if (stripos($message, 'Datenbank-Port') !== false) { $add('nc_port'); $stage = 'Legacy-Datenbank'; } - if (stripos($message, 'Datenbankname') !== false) { $add('nc_database'); $stage = 'Legacy-Datenbank'; } - if (stripos($message, 'Reader-Benutzer') !== false) { $add('nc_user'); $stage = 'Legacy-Datenbank'; } - if (stripos($message, 'Datenbankpasswort') !== false) { $add('nc_db_password'); $stage = 'Legacy-Datenbank'; } - if (stripos($message, 'Storage-ID') !== false) { $add('nc_storage_id[]'); $stage = 'Speicherorte'; } - if (stripos($message, 'lokaler Pfad') !== false || stripos($message, 'Speicherort') !== false) { $add('nc_local_mount[]'); $stage = 'Speicherorte'; } - if (stripos($message, 'Galerieordner') !== false) { $add('nc_gallery_root'); $stage = 'Erweiterte Legacy-Einstellungen'; } - if (stripos($message, 'Datenbankansichten') !== false || stripos($message, 'Source-View') !== false) { $add('nc_source_view'); $stage = 'Erweiterte Legacy-Einstellungen'; } - if (stripos($message, 'Datenbankansichten') !== false || stripos($message, 'Activity-View') !== false) { $add('nc_activity_view'); $stage = 'Erweiterte Legacy-Einstellungen'; } - - if (stripos($message, 'Nextcloud') !== false) - { - $stage = 'Nextcloud / WebDAV'; - if (stripos($message, 'Adresse') !== false) $add('nc_nextcloud_url'); - if (stripos($message, 'Benutzer') !== false) $add('nc_nextcloud_user'); - if (stripos($message, 'Passwort') !== false) $add('nc_nextcloud_password'); - } - if (stripos($message, 'API') !== false) - { - $stage = 'Piwigo API'; - $add('nc_connection_api_key_id'); - $add('nc_connection_api_key_secret'); - } - if (stripos($message, 'Fallback') !== false) - { - $stage = 'Piwigo-Fallback'; - $add('nc_fallback_user'); - $add('nc_fallback_password'); - } - - bratonien_tools_nc_edit_fail( - $message, - $fields, - $stage, - 'Interne Prüfung: '.get_class($e) - ); -} From 46924db618bac3b92b4a630e7fe1eed8bb56a21c Mon Sep 17 00:00:00 2001 From: Terranom674 Date: Wed, 19 Aug 2026 23:49:36 +0200 Subject: [PATCH 10/19] Remove parallel NC source picker endpoint --- nc-connector-source-picker-start.php | 104 --------------------------- 1 file changed, 104 deletions(-) delete mode 100644 nc-connector-source-picker-start.php diff --git a/nc-connector-source-picker-start.php b/nc-connector-source-picker-start.php deleted file mode 100644 index 752e396..0000000 --- a/nc-connector-source-picker-start.php +++ /dev/null @@ -1,104 +0,0 @@ -false, - 'message'=>(string)$message, - 'fields'=>array_values(array_unique($fields)), - )); -} - -if (!defined('BRATONIEN_TOOLS_PATH')) -{ - bratonien_tools_nc_source_picker_json(404, array('ok'=>false, 'message'=>'Bratonien Tools ist nicht aktiv.', 'fields'=>array())); -} -if (!function_exists('is_admin') || !is_admin()) -{ - bratonien_tools_nc_source_picker_json(403, array('ok'=>false, 'message'=>'Administratorrechte erforderlich.', 'fields'=>array())); -} -if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') -{ - bratonien_tools_nc_source_picker_json(405, array('ok'=>false, 'message'=>'Nur POST ist erlaubt.', 'fields'=>array())); -} - -try -{ - check_pwg_token(); - require_once(BRATONIEN_TOOLS_PATH.'include/tool_registry.inc.php'); - - $id = (int)($_POST['connection_id'] ?? 0); - $connection = bratonien_tools_nc_connector_connection($id, true); - if (!$connection) - { - bratonien_tools_nc_source_picker_fail('Die zu bearbeitende Verbindung wurde nicht gefunden.'); - } - if ((string)$connection['adapter'] !== 'local') - { - bratonien_tools_nc_source_picker_fail('Die automatische Migrationsauswahl ist nur für die bestehende Legacy-Verbindung vorgesehen.'); - } - - $config = isset($connection['config']) && is_array($connection['config']) ? $connection['config'] : array(); - $credentials = bratonien_tools_nc_connector_scoped_secret($connection); - - $base_url = trim((string)($config['nextcloud_url'] ?? '')); - $username = trim((string)($credentials['nextcloud_user'] ?? '')); - $password = (string)($credentials['nextcloud_password'] ?? ''); - - $missing = array(); - if ($base_url === '') $missing[] = 'nc_nextcloud_url'; - if ($username === '') $missing[] = 'nc_nextcloud_user'; - if ($password === '') $missing[] = 'nc_nextcloud_password'; - if ($missing) - { - bratonien_tools_nc_source_picker_fail( - 'Für die automatische Ordnerauswahl müssen Nextcloud-Adresse, Benutzer und Passwort gespeichert sein.', - $missing - ); - } - - bratonien_tools_nc_connector_prepare_webdav_wizard_from_connection($connection, 'migrate'); - - $state = bratonien_tools_nc_wizard_state(); - if ((int)($state['step'] ?? 0) !== 2 || empty($state['scan_ok']) || empty($state['directory_selection_ready'])) - { - $detail = trim((string)($state['technical_error'] ?? '')); - bratonien_tools_nc_source_picker_fail( - $detail !== '' ? 'Die Nextcloud-Ordner konnten nicht geladen werden: '.$detail : 'Die Nextcloud-Ordner konnten nicht geladen werden.', - array('nc_nextcloud_url','nc_nextcloud_user','nc_nextcloud_password') - ); - } - - bratonien_tools_nc_source_picker_json(200, array( - 'ok'=>true, - 'message'=>'Nextcloud-Ordner wurden geladen. Die Auswahl wird geöffnet.', - 'fields'=>array(), - )); -} -catch (Throwable $e) -{ - $message = trim($e->getMessage()); - if ($message === '') $message = 'Die Nextcloud-Ordnerauswahl konnte nicht vorbereitet werden.'; - bratonien_tools_nc_source_picker_fail( - $message, - array('nc_nextcloud_url','nc_nextcloud_user','nc_nextcloud_password') - ); -} From ab116d29c20ee63599bfba412a887b0bf33008d1 Mon Sep 17 00:00:00 2001 From: Terranom674 Date: Wed, 19 Aug 2026 23:49:54 +0200 Subject: [PATCH 11/19] Remove destructive legacy reset from updates --- maintain.class.php | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/maintain.class.php b/maintain.class.php index 6f9bcf6..39c9d54 100644 --- a/maintain.class.php +++ b/maintain.class.php @@ -9,7 +9,6 @@ require_once(dirname(__FILE__) . '/include/database.class.php'); require_once(dirname(__FILE__) . '/tools/watermark_profiles.inc.php'); require_once(dirname(__FILE__) . '/include/dependencies.inc.php'); require_once(dirname(__FILE__) . '/include/nc_connector_scheduler.inc.php'); -require_once(dirname(__FILE__) . '/include/nc_legacy_reset.inc.php'); class bratonien_tools_maintain extends PluginMaintain { @@ -52,18 +51,6 @@ class bratonien_tools_maintain extends PluginMaintain public function update($old_version, $new_version, &$errors = array()) { $this->prepare($errors); - - if (version_compare((string)$old_version, '0.9.7.7', '<') && version_compare((string)$new_version, '0.9.7.7', '>=')) - { - try - { - bratonien_tools_nc_reset_legacy_imports(); - } - catch (Throwable $e) - { - $errors[] = 'Connector-Altbestand konnte nicht vollständig bereinigt werden: '.$e->getMessage(); - } - } } public function uninstall() From 31e29db7c2ce670d8ffd78656085622a567fcb7e Mon Sep 17 00:00:00 2001 From: Terranom674 Date: Wed, 19 Aug 2026 23:53:11 +0200 Subject: [PATCH 12/19] Remove obsolete destructive legacy reset --- include/nc_legacy_reset.inc.php | 115 -------------------------------- 1 file changed, 115 deletions(-) delete mode 100644 include/nc_legacy_reset.inc.php diff --git a/include/nc_legacy_reset.inc.php b/include/nc_legacy_reset.inc.php deleted file mode 100644 index cdf6f85..0000000 --- a/include/nc_legacy_reset.inc.php +++ /dev/null @@ -1,115 +0,0 @@ -time(), - 'removed_image_records'=>count($ids), - 'removed_generated_entries'=>$removed_files, - 'removed_state_files'=>$removed_state_files, - ); - - if (function_exists('conf_update_param')) - { - conf_update_param('bratonien_nc_legacy_reset_0977', json_encode($result)); - } - - return $result; -} From 7a763ba62ab5e220337e702e2a9f65988411bf2d Mon Sep 17 00:00:00 2001 From: Terranom674 Date: Wed, 19 Aug 2026 23:53:38 +0200 Subject: [PATCH 13/19] Reduce WebDAV builder to metadata refresh only --- runtime/lib/build-webdav-derivatives.php | 205 +++-------------------- 1 file changed, 22 insertions(+), 183 deletions(-) diff --git a/runtime/lib/build-webdav-derivatives.php b/runtime/lib/build-webdav-derivatives.php index 1770994..292cda9 100644 --- a/runtime/lib/build-webdav-derivatives.php +++ b/runtime/lib/build-webdav-derivatives.php @@ -6,8 +6,6 @@ if (PHP_SAPI !== 'cli') exit(1); } -const BRATONIEN_WEBDAV_DERIVATIVE_BUILDER_VERSION = '0.9.7.8'; - $options = getopt('', array('piwigo-root:', 'connection-id:')); $piwigo_root = rtrim((string)($options['piwigo-root'] ?? ''), '/'); $connection_id = (int)($options['connection-id'] ?? 0); @@ -33,99 +31,24 @@ $_SERVER['REQUEST_URI'] = '/'; $_SERVER['SCRIPT_NAME'] = '/plugins/bratonien_tools/runtime/lib/build-webdav-derivatives.php'; $_SERVER['PHP_SELF'] = $_SERVER['SCRIPT_NAME']; $_SERVER['QUERY_STRING'] = ''; -$_SERVER['HTTP_USER_AGENT'] = 'Bratonien-WebDAV-Derivative-Builder/'.BRATONIEN_WEBDAV_DERIVATIVE_BUILDER_VERSION; $_SERVER['HTTPS'] = 'off'; require_once(PHPWG_ROOT_PATH.'include/common.inc.php'); -require_once(PHPWG_ROOT_PATH.'include/derivative.inc.php'); -require_once(PHPWG_ROOT_PATH.'admin/include/image.class.php'); -if (!function_exists('bratonien_tools_webdav_image_source_info') || !function_exists('bratonien_tools_webdav_generate_derivative')) +if (!function_exists('bratonien_tools_webdav_image_source_info') || !function_exists('bratonien_tools_webdav_preview_path')) { fwrite(STDERR, "Bratonien WebDAV-Bildruntime ist nicht aktiv.\n"); exit(1); } -function bratonien_tools_webdav_modus_config() -{ - global $conf; - - $modus = $conf['modus_theme'] ?? array(); - if (is_string($modus) && $modus !== '') - { - $decoded = @unserialize($modus); - if (is_array($decoded)) $modus = $decoded; - } - if (!is_array($modus)) $modus = array(); - - return array_merge( - array( - 'index_photo_deriv'=>'2small', - 'index_photo_deriv_hdpi'=>'xsmall', - ), - $modus - ); -} - -function bratonien_tools_webdav_gallery_base_params() -{ - $modus = bratonien_tools_webdav_modus_config(); - $type = trim((string)($modus['index_photo_deriv'] ?? '2small')); - $params = $type !== '' ? @ImageStdParams::get_by_type($type) : null; - if ($params) return $params; - - $params = ImageStdParams::get_by_type(IMG_THUMB); - if (!$params) - { - throw new RuntimeException('Piwigo-Galeriederivat ist nicht konfiguriert.'); - } - return $params; -} - -function bratonien_tools_webdav_modus_gallery_params(SrcImage $src, $base_params) -{ - $row_height = (int)$base_params->max_height(); - $candidates = array($base_params); - - foreach (ImageStdParams::get_defined_type_map() as $params) - { - if ( - $params->max_height() > $row_height - && $params->sizing->max_crop == $base_params->sizing->max_crop - ) - { - $candidates[] = $params; - if (count($candidates) === 3) break; - } - } - - $selected = $base_params; - foreach ($candidates as $params) - { - $selected = $params; - $probe = new DerivativeImage($params, $src); - $size = $probe->get_size(); - if ((int)$size[1] >= $row_height - 2) break; - } - - return $selected; -} - try { - $gallery_base_params = bratonien_tools_webdav_gallery_base_params(); - $gallery_base_type = (string)$gallery_base_params->type; - $images = 0; - $generated_or_ready = 0; - $identity = 0; - $metadata_repaired = 0; - $legacy_derivatives_rebuilt = 0; + $updated = 0; $errors = 0; $error_lines = array(); - $variant_counts = array(); - $result = pwg_query('SELECT * FROM '.IMAGES_TABLE.' ORDER BY id'); + $result = pwg_query('SELECT id, width, height, rotation FROM '.IMAGES_TABLE.' ORDER BY id'); while ($row = pwg_db_fetch_assoc($result)) { $image_id = (int)$row['id']; @@ -141,14 +64,6 @@ try continue; } - $preview_ext = strtolower(pathinfo($preview, PATHINFO_EXTENSION)); - if (!in_array($preview_ext, array('jpg', 'jpeg', 'png', 'gif'), true)) - { - $errors++; - $error_lines[] = 'Bild #'.$image_id.': Preview-Format '.$preview_ext.' ist nicht Piwigo-kompatibel; Precache muss neu erzeugt werden.'; - continue; - } - $size = @getimagesize($preview); if (!is_array($size) || empty($size[0]) || empty($size[1])) { @@ -157,117 +72,41 @@ try continue; } - $preview_width = (int)$size[0]; - $preview_height = (int)$size[1]; - if ((int)($row['width'] ?? 0) !== $preview_width || (int)($row['height'] ?? 0) !== $preview_height || (int)($row['rotation'] ?? 0) !== 0) + $width = (int)$size[0]; + $height = (int)$size[1]; + if ((int)($row['width'] ?? 0) === $width && (int)($row['height'] ?? 0) === $height && (int)($row['rotation'] ?? 0) === 0) { - pwg_query( - 'UPDATE '.IMAGES_TABLE. - ' SET width='.$preview_width.', height='.$preview_height.', rotation=0'. - ' WHERE id='.$image_id - ); - $row['width'] = $preview_width; - $row['height'] = $preview_height; - $row['rotation'] = 0; - $metadata_repaired++; + continue; } - $src = new SrcImage($row); - try - { - $gallery_params = bratonien_tools_webdav_modus_gallery_params($src, $gallery_base_params); - $variant = (string)$gallery_params->type; - if ($variant === '') $variant = 'custom'; - $variant_counts[$variant] = ($variant_counts[$variant] ?? 0) + 1; - - $probe = new DerivativeImage($gallery_params, $src); - if ($probe->same_as_source()) - { - $identity++; - continue; - } - - $target = $probe->get_path(); - if ($target !== '' && is_file($target) && is_readable($target)) - { - $expected_size = $gallery_params->compute_final_size(array($preview_width, $preview_height)); - $existing_size = @getimagesize($target); - if ( - is_array($expected_size) - && isset($expected_size[0], $expected_size[1]) - && is_array($existing_size) - && isset($existing_size[0], $existing_size[1]) - && ((int)$existing_size[0] !== (int)$expected_size[0] || (int)$existing_size[1] !== (int)$expected_size[1]) - ) - { - if (!@unlink($target)) - { - $errors++; - $error_lines[] = 'Bild #'.$image_id.' Galerie: falsches Alt-Derivat konnte nicht entfernt werden: '.$target; - continue; - } - $legacy_derivatives_rebuilt++; - } - } - - $detail = ''; - if (bratonien_tools_webdav_generate_derivative($gallery_params, $src, $detail)) - { - $generated_or_ready++; - } - else - { - $errors++; - $error_lines[] = 'Bild #'.$image_id.' Galerie: '.($detail !== '' ? $detail : 'Galeriederivat konnte nicht erzeugt werden.'); - } - } - catch (Throwable $e) - { - $errors++; - $error_lines[] = 'Bild #'.$image_id.' Galerie: '.get_class($e).': '.$e->getMessage(); - } + pwg_query( + 'UPDATE '.IMAGES_TABLE. + ' SET width='.$width.', height='.$height.', rotation=0'. + ' WHERE id='.$image_id + ); + $updated++; } - if ($metadata_repaired > 0) + if ($updated > 0) { update_category('all'); invalidate_user_cache(true); } - if ($errors > 0) + foreach (array_slice($error_lines, 0, 30) as $line) { - foreach (array_slice($error_lines, 0, 30) as $line) - { - fwrite(STDERR, $line."\n"); - } - if (count($error_lines) > 30) - { - fwrite(STDERR, 'Weitere Fehler: '.(count($error_lines) - 30)."\n"); - } + fwrite(STDERR, $line."\n"); + } + if (count($error_lines) > 30) + { + fwrite(STDERR, 'Weitere Fehler: '.(count($error_lines) - 30)."\n"); } - ksort($variant_counts); - $variant_parts = array(); - foreach ($variant_counts as $type=>$count) - { - $variant_parts[] = $type.':'.$count; - } - - echo 'WebDAV-Derivative-Builder: version='.BRATONIEN_WEBDAV_DERIVATIVE_BUILDER_VERSION. - ' bilder='.$images. - ' varianten_pro_bild=1'. - ' modus_basis='.$gallery_base_type. - ' modus_varianten='.implode(',', $variant_parts). - ' bereit='.$generated_or_ready. - ' identisch='.$identity. - ' metadaten_repariert='.$metadata_repaired. - ' alt_derivate_neu='.$legacy_derivatives_rebuilt. - ' fehler='.$errors."\n"; - + echo 'WebDAV-Metadaten: bilder='.$images.' aktualisiert='.$updated.' fehler='.$errors."\n"; exit($errors > 0 ? 1 : 0); } catch (Throwable $e) { - fwrite(STDERR, 'WebDAV-Derivate: '.get_class($e).': '.$e->getMessage()."\n"); + fwrite(STDERR, 'WebDAV-Metadaten: '.get_class($e).': '.$e->getMessage()."\n"); exit(1); } From afaea3b4247407825cda31177f1b1cdfadceee3a Mon Sep 17 00:00:00 2001 From: Terranom674 Date: Wed, 19 Aug 2026 23:54:17 +0200 Subject: [PATCH 14/19] Delete all known path variants for one WebDAV connection --- include/nc_connector_delete_safe.inc.php | 112 +++++++++++++++-------- 1 file changed, 74 insertions(+), 38 deletions(-) diff --git a/include/nc_connector_delete_safe.inc.php b/include/nc_connector_delete_safe.inc.php index 5e4fe91..110e7e3 100644 --- a/include/nc_connector_delete_safe.inc.php +++ b/include/nc_connector_delete_safe.inc.php @@ -4,58 +4,84 @@ if (!defined('PHPWG_ROOT_PATH')) die('Hacking attempt!'); } -function bratonien_tools_nc_connector_webdav_site_id(array $connection) +function bratonien_tools_nc_connector_db_prefix_from_absolute($path) { - $config = isset($connection['config']) && is_array($connection['config']) ? $connection['config'] : array(); - if ((string)($connection['adapter'] ?? '') !== 'remote') return 0; - if ((string)($config['source_mode'] ?? '') !== 'webdav-placeholder') return 0; + $path = rtrim(str_replace('\\', '/', (string)$path), '/'); + $piwigo_root = rtrim(str_replace('\\', '/', PHPWG_ROOT_PATH), '/'); + if ($path === '' || strpos($path, $piwigo_root.'/') !== 0) return ''; - $gallery_root = rtrim((string)($config['parallel_gallery_root'] ?? ''), '/'); - $piwigo_root = rtrim(PHPWG_ROOT_PATH, '/'); - if ($gallery_root === '' || strpos($gallery_root, $piwigo_root.'/') !== 0) return 0; - - $relative = ltrim(substr($gallery_root, strlen($piwigo_root)), '/'); - if ($relative === '') return 0; - $site_url = './'.rtrim($relative, '/').'/'; - - $result = pwg_query( - "SELECT id FROM ".SITES_TABLE. - " WHERE galleries_url='".pwg_db_real_escape_string($site_url)."' LIMIT 1" - ); - if (!pwg_db_num_rows($result)) return 0; - - $row = pwg_db_fetch_assoc($result); - return (int)$row['id']; + $relative = ltrim(substr($path, strlen($piwigo_root)), '/'); + return $relative === '' ? '' : './'.rtrim($relative, '/').'/'; } -function bratonien_tools_nc_connector_gallery_db_prefix(array $connection) +function bratonien_tools_nc_connector_owned_db_prefixes(array $connection) { - $config = isset($connection['config']) && is_array($connection['config']) ? $connection['config'] : array(); - $gallery_root = rtrim((string)($config['parallel_gallery_root'] ?? ''), '/'); - $piwigo_root = rtrim(PHPWG_ROOT_PATH, '/'); - if ($gallery_root === '' || strpos($gallery_root, $piwigo_root.'/') !== 0) return ''; + $id = (int)($connection['id'] ?? 0); + if ($id < 1) return array(); - $relative = ltrim(substr($gallery_root, strlen($piwigo_root)), '/'); - return $relative === '' ? '' : './'.rtrim($relative, '/').'/'; + $prefixes = array( + './_data/bratonien-tools/nc-webdav-gallery/connection-'.$id.'/', + './_data/bratonien-tools/nc-webdav-source/connection-'.$id.'/', + './galleries/bratonien-webdav-'.$id.'/', + ); + + $config = isset($connection['config']) && is_array($connection['config']) ? $connection['config'] : array(); + $configured = bratonien_tools_nc_connector_db_prefix_from_absolute($config['parallel_gallery_root'] ?? ''); + if ($configured !== '') $prefixes[] = $configured; + + return array_values(array_unique($prefixes)); +} + +function bratonien_tools_nc_connector_owned_site_urls(array $connection) +{ + $id = (int)($connection['id'] ?? 0); + if ($id < 1) return array(); + + $urls = array( + './_data/bratonien-tools/nc-webdav-gallery/connection-'.$id.'/', + './galleries/bratonien-webdav-'.$id.'/', + ); + + $config = isset($connection['config']) && is_array($connection['config']) ? $connection['config'] : array(); + $configured = bratonien_tools_nc_connector_db_prefix_from_absolute($config['parallel_gallery_root'] ?? ''); + if ($configured !== '') $urls[] = $configured; + + return array_values(array_unique($urls)); } function bratonien_tools_nc_connector_remove_webdav_piwigo_content(array $connection) { include_once(PHPWG_ROOT_PATH.'admin/include/functions.php'); - $site_id = bratonien_tools_nc_connector_webdav_site_id($connection); - $db_prefix = bratonien_tools_nc_connector_gallery_db_prefix($connection); - if ($db_prefix === '') return array('site_id'=>$site_id, 'images'=>0); + $prefixes = bratonien_tools_nc_connector_owned_db_prefixes($connection); + if (!$prefixes) return array('sites'=>0, 'images'=>0); + + $where = array(); + foreach ($prefixes as $prefix) + { + $escaped = pwg_db_real_escape_string(addcslashes($prefix, '_%\\')); + $where[] = "path LIKE '".$escaped."%' ESCAPE '\\\\'"; + } - $escaped = pwg_db_real_escape_string(addcslashes($db_prefix, '_%\\')); $image_rows = array(); $result = pwg_query( - "SELECT id, path, representative_ext FROM ".IMAGES_TABLE. - " WHERE path LIKE '".$escaped."%' ESCAPE '\\\\'" + 'SELECT id, path, representative_ext FROM '.IMAGES_TABLE. + ' WHERE '.implode(' OR ', $where) ); while ($row = pwg_db_fetch_assoc($result)) { - if (strpos((string)$row['path'], $db_prefix) !== 0) continue; + $path = (string)$row['path']; + $owned = false; + foreach ($prefixes as $prefix) + { + if (strpos($path, $prefix) === 0) + { + $owned = true; + break; + } + } + if (!$owned) continue; + $row['id'] = (int)$row['id']; $image_rows[$row['id']] = $row; } @@ -77,13 +103,23 @@ function bratonien_tools_nc_connector_remove_webdav_piwigo_content(array $connec delete_elements(array_map('intval', array_keys($image_rows)), false); } - if ($site_id > 0) + $site_count = 0; + foreach (bratonien_tools_nc_connector_owned_site_urls($connection) as $site_url) { - delete_site($site_id); + $result = pwg_query( + "SELECT id FROM ".SITES_TABLE. + " WHERE galleries_url='".pwg_db_real_escape_string($site_url)."'" + ); + while ($row = pwg_db_fetch_assoc($result)) + { + delete_site((int)$row['id']); + $site_count++; + } } + update_category('all'); invalidate_user_cache(true); - return array('site_id'=>$site_id, 'images'=>count($image_rows)); + return array('sites'=>$site_count, 'images'=>count($image_rows)); } /** @@ -99,7 +135,7 @@ function bratonien_tools_nc_connector_delete_safe() throw new RuntimeException('Connector-Verbindung wurde nicht gefunden.'); } - $cleanup = array('site_id'=>0, 'images'=>0); + $cleanup = array('sites'=>0, 'images'=>0); if ( (string)($connection['adapter'] ?? '') === 'remote' && (string)($connection['config']['source_mode'] ?? '') === 'webdav-placeholder' From e58d2c700a8a591177ed29aacf4cc9c7e86d672c Mon Sep 17 00:00:00 2001 From: Terranom674 Date: Wed, 19 Aug 2026 23:54:35 +0200 Subject: [PATCH 15/19] Clean missing images across all connection path generations --- runtime/cleanup-missing-webdav-images.php | 52 +++++++++++++++-------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/runtime/cleanup-missing-webdav-images.php b/runtime/cleanup-missing-webdav-images.php index 25c71d2..dea0d50 100644 --- a/runtime/cleanup-missing-webdav-images.php +++ b/runtime/cleanup-missing-webdav-images.php @@ -56,19 +56,30 @@ if (!is_array($config) || (string)($config['source_mode'] ?? '') !== 'webdav-pla exit(0); } -$gallery_root = rtrim((string)($config['parallel_gallery_root'] ?? ''), '/'); -if ($gallery_root === '' || strpos($gallery_root, $piwigo_root.'/') !== 0) +$prefixes = array( + './_data/bratonien-tools/nc-webdav-gallery/connection-'.$connection_id.'/', + './_data/bratonien-tools/nc-webdav-source/connection-'.$connection_id.'/', + './galleries/bratonien-webdav-'.$connection_id.'/', +); + +$gallery_root = rtrim(str_replace('\\', '/', (string)($config['parallel_gallery_root'] ?? '')), '/'); +$normalized_piwigo_root = rtrim(str_replace('\\', '/', $piwigo_root), '/'); +if ($gallery_root !== '' && strpos($gallery_root, $normalized_piwigo_root.'/') === 0) { - fwrite(STDERR, "Verbindungseigene Gallery-Root ist ungueltig.\n"); - exit(1); + $relative = ltrim(substr($gallery_root, strlen($normalized_piwigo_root)), '/'); + if ($relative !== '') $prefixes[] = './'.rtrim($relative, '/').'/'; +} +$prefixes = array_values(array_unique($prefixes)); + +$where = array(); +foreach ($prefixes as $prefix) +{ + $escaped = $db->real_escape_string(addcslashes($prefix, "_%\\")); + $where[] = "path LIKE '{$escaped}%' ESCAPE '\\\\'"; } -$relative = ltrim(substr($gallery_root, strlen($piwigo_root)), '/'); -$db_prefix = './'.rtrim($relative, '/').'/'; -$escaped_prefix = $db->real_escape_string(addcslashes($db_prefix, "_%\\")); $images_table = $prefixeTable.'images'; -$query = "SELECT id, path FROM `{$images_table}` WHERE path LIKE '{$escaped_prefix}%' ESCAPE '\\\\'"; -$rows = $db->query($query); +$rows = $db->query("SELECT id, path FROM `{$images_table}` WHERE ".implode(' OR ', $where)); if (!$rows) { fwrite(STDERR, "Connector-Bilder konnten nicht gelesen werden: ".$db->error."\n"); @@ -79,12 +90,18 @@ $missing_ids = array(); while ($row = $rows->fetch_assoc()) { $path = (string)$row['path']; - if (strpos($path, $db_prefix) !== 0) + $owned = false; + foreach ($prefixes as $prefix) { - continue; + if (strpos($path, $prefix) === 0) + { + $owned = true; + break; + } } + if (!$owned) continue; - $absolute = $piwigo_root.'/'.ltrim(preg_replace('#^\\./#', '', $path), '/'); + $absolute = $piwigo_root.'/'.ltrim(preg_replace('#^\./#', '', $path), '/'); if (!is_file($absolute)) { $missing_ids[] = (int)$row['id']; @@ -98,9 +115,10 @@ if (!$missing_ids) exit(0); } -// Erst jetzt Piwigo bootstrappen. Dieser Prozess wird nur nach einem erfolgreich -// abgeschlossenen WebDAV-Abruf gestartet. Eine nicht erreichbare Verbindung kann -// daher niemals ueber diesen Pfad Bilder loeschen. +// Dieser Prozess wird ausschliesslich nach einem erfolgreich abgeschlossenen +// WebDAV-Aufbau und erfolgreicher Piwigo-Synchronisierung gestartet. Eine +// nicht erreichbare Verbindung kann deshalb niemals ueber diesen Pfad Bilder +// entfernen. define('PHPWG_ROOT_PATH', $piwigo_root.'/'); $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; $_SERVER['SERVER_ADDR'] = '127.0.0.1'; @@ -117,8 +135,8 @@ $_SERVER['HTTPS'] = 'off'; require_once(PHPWG_ROOT_PATH.'include/common.inc.php'); include_once(PHPWG_ROOT_PATH.'admin/include/functions.php'); -delete_elements($missing_ids, false); +$deleted = delete_elements(array_values(array_unique(array_map('intval', $missing_ids))), false); update_category('all'); invalidate_user_cache(true); -echo 'WebDAV-Bereinigung: '.count($missing_ids)." nicht mehr freigegebene/ vorhandene Bilder aus Piwigo entfernt.\n"; +echo 'WebDAV-Bereinigung: '.(int)$deleted." nicht mehr freigegebene/vorhandene Bilder aus Piwigo entfernt.\n"; From 65b068814fda151183beb747d5f38c269fd90597 Mon Sep 17 00:00:00 2001 From: Terranom674 Date: Wed, 19 Aug 2026 23:55:04 +0200 Subject: [PATCH 16/19] Unify stale WebDAV cleanup across historical connector paths --- runtime/cleanup-webdav-piwigo.php | 129 +++++++++++++++++------------- 1 file changed, 74 insertions(+), 55 deletions(-) diff --git a/runtime/cleanup-webdav-piwigo.php b/runtime/cleanup-webdav-piwigo.php index f51e51c..c8f0a4c 100644 --- a/runtime/cleanup-webdav-piwigo.php +++ b/runtime/cleanup-webdav-piwigo.php @@ -47,24 +47,31 @@ function bratonien_cleanup_tree($path, $allowed_root) @rmdir($path); } -function bratonien_webdav_site_images($site_id) +function bratonien_connection_id_from_owned_path($path) { - $rows = array(); - $query = ' -SELECT DISTINCT i.id, i.path, i.representative_ext - FROM '.IMAGES_TABLE.' AS i - LEFT JOIN '.CATEGORIES_TABLE.' AS sc ON sc.id = i.storage_category_id - LEFT JOIN '.IMAGE_CATEGORY_TABLE.' AS ic ON ic.image_id = i.id - LEFT JOIN '.CATEGORIES_TABLE.' AS vc ON vc.id = ic.category_id - WHERE sc.site_id = '.(int)$site_id.' OR vc.site_id = '.(int)$site_id.' -;'; - $result = pwg_query($query); - while ($row = pwg_db_fetch_assoc($result)) + $path = (string)$path; + foreach (array( + '#^\./_data/bratonien-tools/nc-webdav-gallery/connection-([0-9]+)/#', + '#^\./_data/bratonien-tools/nc-webdav-source/connection-([0-9]+)/#', + '#^\./galleries/bratonien-webdav-([0-9]+)/#', + ) as $pattern) { - $row['id'] = (int)$row['id']; - $rows[$row['id']] = $row; + if (preg_match($pattern, $path, $match)) return (int)$match[1]; } - return $rows; + return 0; +} + +function bratonien_connection_id_from_site_url($url) +{ + $url = (string)$url; + foreach (array( + '#^\./_data/bratonien-tools/nc-webdav-gallery/connection-([0-9]+)/$#', + '#^\./galleries/bratonien-webdav-([0-9]+)/$#', + ) as $pattern) + { + if (preg_match($pattern, $url, $match)) return (int)$match[1]; + } + return 0; } try @@ -84,24 +91,24 @@ try } } - $site_prefix = './_data/bratonien-tools/nc-webdav-gallery/connection-'; - $result = pwg_query( - "SELECT id, galleries_url FROM ".SITES_TABLE. - " WHERE galleries_url LIKE '".pwg_db_real_escape_string($site_prefix)."%'" - ); - - $removed_sites = 0; $removed_images = 0; - while ($site = pwg_db_fetch_assoc($result)) + $stale_by_connection = array(); + $result = pwg_query( + "SELECT id, path, representative_ext FROM ".IMAGES_TABLE. + " WHERE path LIKE './_data/bratonien-tools/nc-webdav-gallery/connection-%'". + " OR path LIKE './_data/bratonien-tools/nc-webdav-source/connection-%'". + " OR path LIKE './galleries/bratonien-webdav-%'" + ); + while ($row = pwg_db_fetch_assoc($result)) { - $site_id = (int)$site['id']; - $site_url = (string)$site['galleries_url']; - if (!preg_match('#^\./_data/bratonien-tools/nc-webdav-gallery/connection-([0-9]+)/$#', $site_url, $match)) continue; - - $connection_id = (int)$match[1]; + $connection_id = bratonien_connection_id_from_owned_path($row['path'] ?? ''); if ($connection_id < 1 || isset($active[$connection_id])) continue; + $row['id'] = (int)$row['id']; + $stale_by_connection[$connection_id][$row['id']] = $row; + } - $images = bratonien_webdav_site_images($site_id); + foreach ($stale_by_connection as $connection_id=>$images) + { foreach ($images as $row) { try @@ -114,48 +121,60 @@ try } } - delete_site($site_id); - - if ($images) - { - $ids = array_keys($images); - $remaining = query2array( - 'SELECT id FROM '.IMAGES_TABLE.' WHERE id IN ('.implode(',', array_map('intval', $ids)).')', - null, - 'id' - ); - if ($remaining) - { - delete_elements(array_map('intval', $remaining), false); - } - } - - $removed_sites++; + delete_elements(array_map('intval', array_keys($images)), false); $removed_images += count($images); - echo 'NC WebDAV: entferne verwaiste Piwigo-Site '.$site_id.' für gelöschte Verbindung '.$connection_id.".\n"; + echo 'NC WebDAV: entferne '.count($images).' verwaiste Bilder der gelöschten Verbindung '.$connection_id.".\n"; + } - $gallery_root = PHPWG_ROOT_PATH.'_data/bratonien-tools/nc-webdav-gallery'; - $source_root = PHPWG_ROOT_PATH.'_data/bratonien-tools/nc-webdav-source'; - $preview_root = PHPWG_ROOT_PATH.'_data/bratonien-tools/nc-webdav-preview'; + $removed_sites = 0; + $result = pwg_query('SELECT id, galleries_url FROM '.SITES_TABLE); + $stale_sites = array(); + while ($site = pwg_db_fetch_assoc($result)) + { + $connection_id = bratonien_connection_id_from_site_url($site['galleries_url'] ?? ''); + if ($connection_id < 1 || isset($active[$connection_id])) continue; + $stale_sites[] = array('id'=>(int)$site['id'], 'connection_id'=>$connection_id); + } + + foreach ($stale_sites as $site) + { + delete_site($site['id']); + $removed_sites++; + echo 'NC WebDAV: entferne verwaiste Piwigo-Site '.$site['id'].' der gelöschten Verbindung '.$site['connection_id'].".\n"; + } + + $stale_connection_ids = array_unique(array_merge( + array_map('intval', array_keys($stale_by_connection)), + array_map(function($site) { return (int)$site['connection_id']; }, $stale_sites) + )); + + $gallery_root = PHPWG_ROOT_PATH.'_data/bratonien-tools/nc-webdav-gallery'; + $source_root = PHPWG_ROOT_PATH.'_data/bratonien-tools/nc-webdav-source'; + $preview_root = PHPWG_ROOT_PATH.'_data/bratonien-tools/nc-webdav-preview'; + $legacy_root = PHPWG_ROOT_PATH.'galleries'; + $state_root = '/var/lib/bratonien-tools/nc-connector'; + $status_root = PHPWG_ROOT_PATH.'_data/bratonien-tools/nc-connector-status'; + + foreach ($stale_connection_ids as $connection_id) + { + if ($connection_id < 1) continue; bratonien_cleanup_tree($gallery_root.'/connection-'.$connection_id, $gallery_root); bratonien_cleanup_tree($source_root.'/connection-'.$connection_id, $source_root); bratonien_cleanup_tree($preview_root.'/connection-'.$connection_id, $preview_root); - - $state_root = '/var/lib/bratonien-tools/nc-connector'; + bratonien_cleanup_tree($legacy_root.'/bratonien-webdav-'.$connection_id, $legacy_root); bratonien_cleanup_tree($state_root.'/connection-'.$connection_id, $state_root); foreach (glob('/etc/bratonien-tools/nc-connector/webdav-connection-'.$connection_id.'.*') ?: array() as $file) { @unlink($file); } - - $status_root = PHPWG_ROOT_PATH.'_data/bratonien-tools/nc-connector-status'; @unlink($status_root.'/connection-'.$connection_id.'.json'); @unlink($status_root.'/deleted-'.$connection_id); } - if ($removed_sites > 0) + if ($removed_sites > 0 || $removed_images > 0) { + update_category('all'); invalidate_user_cache(true); } From 6f64cb9fe65ef663ebbec02191e9ea25fb1165b6 Mon Sep 17 00:00:00 2001 From: Terranom674 Date: Wed, 19 Aug 2026 23:57:24 +0200 Subject: [PATCH 17/19] Restore stable admin tab behavior --- js/admin_tabs.js | 97 +----------------------------------------------- 1 file changed, 2 insertions(+), 95 deletions(-) diff --git a/js/admin_tabs.js b/js/admin_tabs.js index f2361ab..77444a0 100644 --- a/js/admin_tabs.js +++ b/js/admin_tabs.js @@ -1,94 +1,6 @@ (function () { 'use strict'; - function initNcRouteStatus() { - var section = document.getElementById('nc-connector'); - if (!section) return; - - var statusCard = section.querySelector('.bratonien-grid .bratonien-card'); - if (!statusCard) return; - - var grid = statusCard.querySelector('.bratonien-form-grid'); - if (!grid) return; - - var routeLabel = document.createElement('span'); - routeLabel.className = 'bratonien-label'; - routeLabel.textContent = 'Aktiver Datenweg'; - - var routeValue = document.createElement('strong'); - routeValue.setAttribute('data-nc-route-status', ''); - routeValue.textContent = 'Noch kein Lauf erfasst'; - - var routeTimeLabel = document.createElement('span'); - routeTimeLabel.className = 'bratonien-label'; - routeTimeLabel.textContent = 'Datenweg zuletzt'; - - var routeTimeValue = document.createElement('strong'); - routeTimeValue.setAttribute('data-nc-route-time', ''); - routeTimeValue.textContent = 'Nicht verfügbar'; - - grid.appendChild(routeLabel); - grid.appendChild(routeValue); - grid.appendChild(routeTimeLabel); - grid.appendChild(routeTimeValue); - - var detail = document.createElement('p'); - detail.className = 'bratonien-base-note'; - detail.setAttribute('data-nc-route-detail', ''); - detail.textContent = 'Der Datenweg wird nach dem nächsten Connector-Lauf angezeigt.'; - statusCard.appendChild(detail); - - var basePath = window.location.pathname.replace(/admin\.php.*$/, ''); - var statusUrl = basePath + '_data/bratonien-tools/nc-connector-status/route-status.json'; - - function formatTime(timestamp) { - if (!timestamp) return 'Nicht verfügbar'; - var date = new Date(Number(timestamp) * 1000); - if (Number.isNaN(date.getTime())) return 'Nicht verfügbar'; - return date.toLocaleString('de-DE'); - } - - function render(data) { - var label = data && data.label ? String(data.label) : 'Unbekannt'; - var route = data && data.route ? String(data.route) : ''; - routeValue.textContent = label; - routeTimeValue.textContent = formatTime(data && data.timestamp); - - if (route === 'webdav') { - routeValue.textContent = 'WEBDAV PRIMÄR'; - detail.textContent = 'Portierung läuft über WebDAV. Legacy wurde in diesem Lauf nicht benutzt.'; - } else if (route === 'legacy_fallback') { - routeValue.textContent = 'LEGACY-FALLBACK AKTIV'; - detail.textContent = 'WebDAV war nicht erfolgreich. Dieser Lauf wurde über die alte Struktur abgefangen.'; - } else if (route === 'failed') { - routeValue.textContent = 'FEHLER - KEIN ERFOLGREICHER DATENWEG'; - detail.textContent = data && data.detail ? String(data.detail) : 'WebDAV und Fallback sind fehlgeschlagen.'; - } else { - detail.textContent = data && data.detail ? String(data.detail) : 'Unbekannter Datenweg.'; - } - } - - function refresh() { - fetch(statusUrl + '?t=' + Date.now(), { - cache: 'no-store', - credentials: 'same-origin' - }) - .then(function (response) { - if (!response.ok) throw new Error('status unavailable'); - return response.json(); - }) - .then(render) - .catch(function () { - routeValue.textContent = 'Noch kein Lauf erfasst'; - routeTimeValue.textContent = 'Nicht verfügbar'; - detail.textContent = 'Nach dem nächsten Connector-Lauf wird hier WebDAV oder Legacy-Fallback angezeigt.'; - }); - } - - refresh(); - window.setInterval(refresh, 10000); - } - function initBratonienTabs() { var admin = document.querySelector('.bratonien-admin'); if (!admin) return; @@ -201,14 +113,9 @@ activate(initial, false); } - function init() { - initBratonienTabs(); - initNcRouteStatus(); - } - if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', init); + document.addEventListener('DOMContentLoaded', initBratonienTabs); } else { - init(); + initBratonienTabs(); } })(); From bdfea105cfcbee228a880628e0c56e76df8a33f3 Mon Sep 17 00:00:00 2001 From: Terranom674 Date: Wed, 19 Aug 2026 23:58:13 +0200 Subject: [PATCH 18/19] Keep request-local watermark performance cache --- include/watermark_engine.inc.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/watermark_engine.inc.php b/include/watermark_engine.inc.php index 3826954..0cfcc0d 100644 --- a/include/watermark_engine.inc.php +++ b/include/watermark_engine.inc.php @@ -8,8 +8,17 @@ require_once(BRATONIEN_TOOLS_PATH . 'include/watermark_base.inc.php'); function bratonien_tools_watermark_engine_enabled() { + static $initialized = false; + static $enabled = false; + + if ($initialized) + { + return $enabled; + } + $config = bratonien_tools_get_watermark_engine_config(); $enabled = !empty($config['enabled']); + $initialized = true; // Piwigo recalculates use_watermark for custom derivatives from the native // watermark file itself. Therefore an active Bratonien engine must keep the From 61988db337a63c25ebde8977730b576e341bda69 Mon Sep 17 00:00:00 2001 From: Terranom674 Date: Wed, 19 Aug 2026 23:59:56 +0200 Subject: [PATCH 19/19] Restore single-path NC admin UI --- template/admin_tabs.tpl | 126 ++++++---------------------------------- 1 file changed, 19 insertions(+), 107 deletions(-) diff --git a/template/admin_tabs.tpl b/template/admin_tabs.tpl index 74722dc..d5c6da0 100644 --- a/template/admin_tabs.tpl +++ b/template/admin_tabs.tpl @@ -9,13 +9,7 @@ .bratonien-tab-panel { width:100%; max-width:none; margin-left:0 !important; margin-right:0 !important; } .bratonien-tab-panel[hidden] { display:none !important; } .bratonien-card { margin-left:0; margin-right:0; } -.bratonien-edit-dialog { width:min(920px,calc(100vw - 3rem)); max-height:88vh; overflow:auto; background:#444; color:inherit; border:1px solid #777; border-radius:4px; padding:0; box-shadow:0 18px 60px rgba(0,0,0,.55); } -.bratonien-edit-dialog::backdrop { background:rgba(0,0,0,.55); } -.bratonien-edit-dialog__body { padding:1.25rem 1.5rem; } -.bratonien-storage-row { display:grid; grid-template-columns:minmax(120px,.7fr) minmax(150px,1fr) minmax(240px,1.5fr) auto; gap:.5rem; align-items:end; margin:.5rem 0; } -.bratonien-storage-row label { display:flex; flex-direction:column; gap:.25rem; } -.bratonien-storage-row input { width:100%; box-sizing:border-box; } -@media (max-width:760px) { .bratonien-tabs{gap:5px}.bratonien-tab{border-bottom:1px solid rgba(255,255,255,.16);border-radius:4px;margin-bottom:0;padding:9px 11px}.bratonien-storage-row{grid-template-columns:1fr} } +@media (max-width:760px) { .bratonien-tabs{gap:5px}.bratonien-tab{border-bottom:1px solid rgba(255,255,255,.16);border-radius:4px;margin-bottom:0;padding:9px 11px} }