Consolidate NC wizard state, validation and API commit

This commit is contained in:
Terranom674
2026-08-18 08:44:41 +02:00
parent 43d01d0450
commit b0f5c3d571

View File

@@ -24,7 +24,7 @@ function bratonien_tools_nc_wizard_state()
'showcase_user' => '', 'showcase_user' => '',
'connection_name' => '', 'connection_name' => '',
'scan_message' => '', 'scan_message' => '',
'technical_source' => '', 'technical_source' => 'Noch nicht geprüft',
'technical_complete' => false, 'technical_complete' => false,
'db_host' => '', 'db_host' => '',
'db_port' => '5432', 'db_port' => '5432',
@@ -39,6 +39,12 @@ function bratonien_tools_nc_wizard_state()
'api_status' => 'pending', 'api_status' => 'pending',
'api_username' => '', 'api_username' => '',
'api_error' => '', 'api_error' => '',
'_password' => '',
'_db_password' => '',
'_api_key_id' => '',
'_api_key_secret' => '',
'_fallback_user' => '',
'_fallback_password' => '',
), $state); ), $state);
} }
@@ -60,34 +66,23 @@ function bratonien_tools_nc_wizard_normalize_url($host)
{ {
throw new RuntimeException('Nextcloud-Host fehlt.'); throw new RuntimeException('Nextcloud-Host fehlt.');
} }
if (!preg_match('#^https?://#i', $host)) if (!preg_match('#^https?://#i', $host))
{ {
$host = 'https://'.$host; $host = 'https://'.$host;
} }
$parts = parse_url($host); $parts = parse_url($host);
if (!is_array($parts) || empty($parts['host'])) if (!is_array($parts) || empty($parts['host']))
{ {
throw new RuntimeException('Nextcloud-Host ist ungültig.'); throw new RuntimeException('Nextcloud-Host ist ungültig.');
} }
$scheme = strtolower((string)($parts['scheme'] ?? 'https')); $scheme = strtolower((string)($parts['scheme'] ?? 'https'));
if (!in_array($scheme, array('http','https'), true)) if (!in_array($scheme, array('http','https'), true))
{ {
throw new RuntimeException('Nextcloud muss per HTTP oder HTTPS erreichbar sein.'); throw new RuntimeException('Nextcloud muss per HTTP oder HTTPS erreichbar sein.');
} }
$url = $scheme.'://'.$parts['host']; $url = $scheme.'://'.$parts['host'];
if (!empty($parts['port'])) if (!empty($parts['port'])) $url .= ':'.(int)$parts['port'];
{ if (!empty($parts['path']) && $parts['path'] !== '/') $url .= '/'.trim($parts['path'], '/');
$url .= ':'.(int)$parts['port'];
}
if (!empty($parts['path']) && $parts['path'] !== '/')
{
$url .= '/'.trim($parts['path'], '/');
}
return rtrim($url, '/'); return rtrim($url, '/');
} }
@@ -98,12 +93,10 @@ function bratonien_tools_nc_wizard_candidate_urls($host)
{ {
throw new RuntimeException('Bitte die Adresse der Nextcloud angeben.'); throw new RuntimeException('Bitte die Adresse der Nextcloud angeben.');
} }
if (preg_match('#^https?://#i', $host)) if (preg_match('#^https?://#i', $host))
{ {
return array(bratonien_tools_nc_wizard_normalize_url($host)); return array(bratonien_tools_nc_wizard_normalize_url($host));
} }
return array( return array(
bratonien_tools_nc_wizard_normalize_url('https://'.$host), bratonien_tools_nc_wizard_normalize_url('https://'.$host),
bratonien_tools_nc_wizard_normalize_url('http://'.$host), bratonien_tools_nc_wizard_normalize_url('http://'.$host),
@@ -116,16 +109,14 @@ function bratonien_tools_nc_wizard_http($url, $username = '', $password = '', ar
{ {
throw new RuntimeException('cURL ist in PHP nicht verfügbar. Der Nextcloud-Scan kann nicht ausgeführt werden.'); throw new RuntimeException('cURL ist in PHP nicht verfügbar. Der Nextcloud-Scan kann nicht ausgeführt werden.');
} }
$ch = curl_init($url); $ch = curl_init($url);
$http_headers = array_merge(array('Accept: application/json'), $headers);
$options = array( $options = array(
CURLOPT_RETURNTRANSFER => true, CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true, CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 3, CURLOPT_MAXREDIRS => 3,
CURLOPT_CONNECTTIMEOUT => 8, CURLOPT_CONNECTTIMEOUT => 8,
CURLOPT_TIMEOUT => 15, CURLOPT_TIMEOUT => 15,
CURLOPT_HTTPHEADER => $http_headers, CURLOPT_HTTPHEADER => array_merge(array('Accept: application/json'), $headers),
CURLOPT_USERAGENT => 'Bratonien-Tools-NC-Wizard/'.(function_exists('bratonien_tools_current_version') ? bratonien_tools_current_version() : 'dev'), CURLOPT_USERAGENT => 'Bratonien-Tools-NC-Wizard/'.(function_exists('bratonien_tools_current_version') ? bratonien_tools_current_version() : 'dev'),
); );
if ($username !== '') if ($username !== '')
@@ -134,18 +125,15 @@ function bratonien_tools_nc_wizard_http($url, $username = '', $password = '', ar
$options[CURLOPT_USERPWD] = $username.':'.$password; $options[CURLOPT_USERPWD] = $username.':'.$password;
} }
curl_setopt_array($ch, $options); curl_setopt_array($ch, $options);
$body = curl_exec($ch); $body = curl_exec($ch);
$errno = curl_errno($ch); $errno = curl_errno($ch);
$error = curl_error($ch); $error = curl_error($ch);
$status = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE); $status = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch); curl_close($ch);
if ($body === false || $errno !== 0) if ($body === false || $errno !== 0)
{ {
throw new RuntimeException($error !== '' ? $error : 'Verbindung fehlgeschlagen.'); throw new RuntimeException($error !== '' ? $error : 'Verbindung fehlgeschlagen.');
} }
return array('status'=>$status, 'body'=>(string)$body); return array('status'=>$status, 'body'=>(string)$body);
} }
@@ -156,28 +144,15 @@ function bratonien_tools_nc_wizard_ocs_data($body)
{ {
throw new RuntimeException('Nextcloud hat keine gültige OCS-Antwort geliefert.'); throw new RuntimeException('Nextcloud hat keine gültige OCS-Antwort geliefert.');
} }
$meta = isset($decoded['ocs']['meta']) && is_array($decoded['ocs']['meta']) ? $decoded['ocs']['meta'] : array();
$meta = isset($decoded['ocs']['meta']) && is_array($decoded['ocs']['meta'])
? $decoded['ocs']['meta']
: array();
$status = strtolower(trim((string)($meta['status'] ?? ''))); $status = strtolower(trim((string)($meta['status'] ?? '')));
$status_code = isset($meta['statuscode']) ? (int)$meta['statuscode'] : 0; $status_code = isset($meta['statuscode']) ? (int)$meta['statuscode'] : 0;
if ($status !== 'ok' && !in_array($status_code, array(100, 200), true))
// OCS API v1 commonly reports 100 for success, OCS API v2 commonly 200.
// Some Nextcloud versions additionally expose meta.status="ok". Treat all
// documented success forms as success instead of turning message "OK" into
// an error.
$success = $status === 'ok' || in_array($status_code, array(100, 200), true);
if (!$success)
{ {
$message = trim((string)($meta['message'] ?? '')); $message = trim((string)($meta['message'] ?? ''));
if ($message === '' || strtolower($message) === 'ok') if ($message === '' || strtolower($message) === 'ok') $message = 'Nextcloud hat die Anfrage abgelehnt.';
{
$message = 'Nextcloud hat die Anfrage abgelehnt.';
}
throw new RuntimeException($message); throw new RuntimeException($message);
} }
return isset($decoded['ocs']['data']) && is_array($decoded['ocs']['data']) ? $decoded['ocs']['data'] : array(); return isset($decoded['ocs']['data']) && is_array($decoded['ocs']['data']) ? $decoded['ocs']['data'] : array();
} }
@@ -190,7 +165,8 @@ function bratonien_tools_nc_wizard_fill_profile(array &$state)
$state['source_view'] = 'piwigo_showcase_sources'; $state['source_view'] = 'piwigo_showcase_sources';
$state['activity_view'] = 'piwigo_showcase_activity'; $state['activity_view'] = 'piwigo_showcase_activity';
$state['gallery_root'] = rtrim(PHPWG_ROOT_PATH, '/').'/galleries/nextcloud'; $state['gallery_root'] = rtrim(PHPWG_ROOT_PATH, '/').'/galleries/nextcloud';
$state['technical_source'] = 'Standardwerte vorbereitet'; $state['technical_source'] = 'Noch nicht geprüft';
$state['technical_complete'] = false;
foreach (bratonien_tools_nc_connector_connections() as $candidate) foreach (bratonien_tools_nc_connector_connections() as $candidate)
{ {
@@ -198,34 +174,17 @@ function bratonien_tools_nc_wizard_fill_profile(array &$state)
$candidate_url = trim((string)($config['nextcloud_url'] ?? '')); $candidate_url = trim((string)($config['nextcloud_url'] ?? ''));
$candidate_host = strtolower(trim((string)($config['host'] ?? ''))); $candidate_host = strtolower(trim((string)($config['host'] ?? '')));
$match = false; $match = false;
if ($candidate_url !== '') if ($candidate_url !== '')
{ {
try try { $match = bratonien_tools_nc_wizard_normalize_url($candidate_url) === $state['base_url']; }
{ catch (Throwable $ignored) { $match = false; }
$match = bratonien_tools_nc_wizard_normalize_url($candidate_url) === $state['base_url'];
}
catch (Throwable $ignored)
{
$match = false;
}
}
if (!$match && $candidate_host !== '' && $candidate_host === $url_host)
{
$match = true;
}
if (!$match)
{
continue;
} }
if (!$match && $candidate_host !== '' && $candidate_host === $url_host) $match = true;
if (!$match) continue;
$full = bratonien_tools_nc_connector_connection((int)$candidate['id'], true); $full = bratonien_tools_nc_connector_connection((int)$candidate['id'], true);
if (!$full) if (!$full) continue;
{
continue;
}
$credentials = bratonien_tools_nc_connector_credentials_from_blob($full['secret_blob'] ?? ''); $credentials = bratonien_tools_nc_connector_credentials_from_blob($full['secret_blob'] ?? '');
$state['db_host'] = (string)($config['host'] ?? $state['db_host']); $state['db_host'] = (string)($config['host'] ?? $state['db_host']);
$state['db_port'] = (string)($config['port'] ?? $state['db_port']); $state['db_port'] = (string)($config['port'] ?? $state['db_port']);
$state['db_database'] = (string)($config['database'] ?? $state['db_database']); $state['db_database'] = (string)($config['database'] ?? $state['db_database']);
@@ -236,26 +195,26 @@ function bratonien_tools_nc_wizard_fill_profile(array &$state)
$state['activity_view'] = (string)($config['activity_view'] ?? $state['activity_view']); $state['activity_view'] = (string)($config['activity_view'] ?? $state['activity_view']);
$state['gallery_root'] = (string)($config['gallery_root'] ?? $state['gallery_root']); $state['gallery_root'] = (string)($config['gallery_root'] ?? $state['gallery_root']);
$state['storages'] = isset($config['storages']) && is_array($config['storages']) ? $config['storages'] : array(); $state['storages'] = isset($config['storages']) && is_array($config['storages']) ? $config['storages'] : array();
$state['technical_source'] = 'Passende vorhandene Connector-Konfiguration erkannt'; $state['technical_source'] = 'Vorhandene passende Verbindung erkannt';
$state['technical_complete'] = $state['db_host'] !== '' && $state['db_port'] !== '' && $state['db_database'] !== ''
&& $state['db_user'] !== '' && $state['db_password_set'] && $state['source_view'] !== ''
&& $state['activity_view'] !== '' && $state['gallery_root'] !== '' && !empty($state['storages']);
break; break;
} }
$state['technical_complete'] = $state['db_host'] !== ''
&& $state['db_port'] !== ''
&& $state['db_database'] !== ''
&& $state['db_user'] !== ''
&& !empty($state['db_password_set'])
&& $state['source_view'] !== ''
&& $state['activity_view'] !== ''
&& $state['gallery_root'] !== ''
&& !empty($state['storages']);
} }
function bratonien_tools_nc_wizard_scan() function bratonien_tools_nc_wizard_scan()
{ {
$host_input = trim((string)($_POST['nc_wizard_host'] ?? '')); $state = bratonien_tools_nc_wizard_state();
$username = trim((string)($_POST['nc_wizard_user'] ?? '')); $host_input = trim((string)($_POST['nc_wizard_host'] ?? $state['host_input']));
$password = (string)($_POST['nc_wizard_password'] ?? ''); $username = trim((string)($_POST['nc_wizard_user'] ?? $state['username']));
$password = array_key_exists('nc_wizard_password', $_POST) ? (string)$_POST['nc_wizard_password'] : (string)$state['_password'];
$state['step'] = 1;
$state['host_input'] = $host_input;
$state['username'] = $username;
$state['_password'] = $password;
bratonien_tools_nc_wizard_store($state);
if ($username === '' || $password === '') if ($username === '' || $password === '')
{ {
throw new RuntimeException('Nextcloud-Benutzer und Passwort werden für den Scan benötigt.'); throw new RuntimeException('Nextcloud-Benutzer und Passwort werden für den Scan benötigt.');
@@ -267,37 +226,22 @@ function bratonien_tools_nc_wizard_scan()
{ {
try try
{ {
$status_response = bratonien_tools_nc_wizard_http($candidate_url.'/status.php'); $response = bratonien_tools_nc_wizard_http($candidate_url.'/status.php');
if ($status_response['status'] < 200 || $status_response['status'] >= 300) if ($response['status'] < 200 || $response['status'] >= 300) continue;
{ $candidate_status = json_decode($response['body'], true);
continue; if (!is_array($candidate_status) || empty($candidate_status['installed'])) continue;
}
$candidate_status = json_decode($status_response['body'], true);
if (!is_array($candidate_status) || empty($candidate_status['installed']))
{
continue;
}
$base_url = $candidate_url; $base_url = $candidate_url;
$status_data = $candidate_status; $status_data = $candidate_status;
break; break;
} }
catch (Throwable $ignored) catch (Throwable $ignored) {}
{
continue;
} }
}
if ($base_url === '' || !is_array($status_data)) if ($base_url === '' || !is_array($status_data))
{ {
throw new RuntimeException('Unter dieser Adresse konnte keine Nextcloud erreicht werden. Der Assistent hat die möglichen Web-Zugänge automatisch geprüft.'); throw new RuntimeException('Unter dieser Adresse konnte keine Nextcloud erreicht werden. HTTP und HTTPS wurden automatisch geprüft.');
} }
$user_response = bratonien_tools_nc_wizard_http( $user_response = bratonien_tools_nc_wizard_http($base_url.'/ocs/v2.php/cloud/user?format=json', $username, $password, array('OCS-APIRequest: true'));
$base_url.'/ocs/v2.php/cloud/user?format=json',
$username,
$password,
array('OCS-APIRequest: true')
);
if ($user_response['status'] === 401 || $user_response['status'] === 403) if ($user_response['status'] === 401 || $user_response['status'] === 403)
{ {
throw new RuntimeException('Nextcloud hat Benutzername oder Passwort abgelehnt.'); throw new RuntimeException('Nextcloud hat Benutzername oder Passwort abgelehnt.');
@@ -312,12 +256,7 @@ function bratonien_tools_nc_wizard_scan()
$can_list_users = false; $can_list_users = false;
try try
{ {
$users_response = bratonien_tools_nc_wizard_http( $users_response = bratonien_tools_nc_wizard_http($base_url.'/ocs/v2.php/cloud/users?format=json', $username, $password, array('OCS-APIRequest: true'));
$base_url.'/ocs/v2.php/cloud/users?format=json',
$username,
$password,
array('OCS-APIRequest: true')
);
if ($users_response['status'] >= 200 && $users_response['status'] < 300) if ($users_response['status'] >= 200 && $users_response['status'] < 300)
{ {
$users_data = bratonien_tools_nc_wizard_ocs_data($users_response['body']); $users_data = bratonien_tools_nc_wizard_ocs_data($users_response['body']);
@@ -329,83 +268,53 @@ function bratonien_tools_nc_wizard_scan()
} }
} }
} }
catch (Throwable $ignored) catch (Throwable $ignored) {}
{
$users = array();
$can_list_users = false;
}
$url_host = (string)parse_url($base_url, PHP_URL_HOST); $url_host = (string)parse_url($base_url, PHP_URL_HOST);
$state = array( $state = array_merge($state, array(
'step' => 2, 'step'=>2, 'scan_ok'=>true, 'base_url'=>$base_url, 'host_input'=>$host_input,
'scan_ok' => true, 'username'=>(string)($user_data['id'] ?? $username),
'base_url' => $base_url, 'display_name'=>(string)($user_data['display-name'] ?? $user_data['displayname'] ?? ''),
'host_input' => $host_input, 'version'=>(string)($status_data['versionstring'] ?? $status_data['version'] ?? ''),
'username' => (string)($user_data['id'] ?? $username), 'product'=>(string)($status_data['productname'] ?? 'Nextcloud'),
'display_name' => (string)($user_data['display-name'] ?? $user_data['displayname'] ?? ''), 'users'=>$users, 'can_list_users'=>$can_list_users, 'showcase_user'=>'',
'version' => (string)($status_data['versionstring'] ?? $status_data['version'] ?? ''), 'connection_name'=>$url_host !== '' ? $url_host : 'Nextcloud',
'product' => (string)($status_data['productname'] ?? 'Nextcloud'), 'scan_message'=>'Nextcloud wurde erkannt und der Benutzerzugriff wurde bestätigt.',
'users' => $users, '_password'=>$password, 'api_status'=>'pending', 'api_username'=>'', 'api_error'=>'',
'can_list_users' => $can_list_users, ));
'showcase_user' => '',
'connection_name' => $url_host !== '' ? $url_host : 'Nextcloud',
'scan_message' => 'Nextcloud wurde erkannt und der Benutzerzugriff wurde bestätigt.',
'_password' => $password,
'api_status' => 'pending',
'api_username' => '',
'api_error' => '',
);
bratonien_tools_nc_wizard_fill_profile($state); bratonien_tools_nc_wizard_fill_profile($state);
bratonien_tools_nc_wizard_store($state); bratonien_tools_nc_wizard_store($state);
return array('message'=>'Nextcloud wurde gefunden und der Zugang bestätigt.');
return array('message'=>'Nextcloud wurde gefunden und der Zugang bestätigt. Fehlende Angaben werden jetzt nur noch bei Bedarf abgefragt.');
} }
function bratonien_tools_nc_wizard_save_technical() function bratonien_tools_nc_wizard_save_technical()
{ {
$state = bratonien_tools_nc_wizard_state(); $state = bratonien_tools_nc_wizard_state();
if (empty($state['scan_ok'])) if (empty($state['scan_ok'])) throw new RuntimeException('Bitte zuerst Nextcloud erfolgreich scannen.');
{
throw new RuntimeException('Bitte zuerst Nextcloud erfolgreich scannen.');
}
$state['connection_name'] = trim((string)($_POST['nc_wizard_connection_name'] ?? $state['connection_name'])); $state['connection_name'] = trim((string)($_POST['nc_wizard_connection_name'] ?? $state['connection_name']));
$state['db_host'] = trim((string)($_POST['nc_wizard_db_host'] ?? $state['db_host'])); $state['db_host'] = trim((string)($_POST['nc_wizard_db_host'] ?? $state['db_host']));
$state['db_port'] = (string)max(1, min(65535, (int)($_POST['nc_wizard_db_port'] ?? $state['db_port']))); $state['db_port'] = (string)max(1, min(65535, (int)($_POST['nc_wizard_db_port'] ?? $state['db_port'])));
$state['db_database'] = trim((string)($_POST['nc_wizard_db_database'] ?? $state['db_database'])); $state['db_database'] = trim((string)($_POST['nc_wizard_db_database'] ?? $state['db_database']));
$state['db_user'] = trim((string)($_POST['nc_wizard_db_user'] ?? $state['db_user'])); $state['db_user'] = trim((string)($_POST['nc_wizard_db_user'] ?? $state['db_user']));
$db_password = (string)($_POST['nc_wizard_db_password'] ?? ''); $db_password = array_key_exists('nc_wizard_db_password', $_POST) ? (string)$_POST['nc_wizard_db_password'] : '';
if ($db_password !== '') if ($db_password !== '') { $state['_db_password'] = $db_password; $state['db_password_set'] = true; }
{ bratonien_tools_nc_wizard_store($state);
$state['_db_password'] = $db_password;
$state['db_password_set'] = true;
}
if ($state['connection_name'] === '' || $state['db_host'] === '' || $state['db_database'] === '' || $state['db_user'] === '' || empty($state['db_password_set'])) if ($state['connection_name'] === '' || $state['db_host'] === '' || $state['db_database'] === '' || $state['db_user'] === '' || empty($state['db_password_set']))
{ {
bratonien_tools_nc_wizard_store($state);
throw new RuntimeException('Die noch fehlenden Datenbankangaben müssen vollständig angegeben werden.'); throw new RuntimeException('Die noch fehlenden Datenbankangaben müssen vollständig angegeben werden.');
} }
$config = array( $config = array('host'=>$state['db_host'], 'port'=>$state['db_port'], 'database'=>$state['db_database'], 'user'=>$state['db_user']);
'host'=>$state['db_host'], $password = (string)$state['_db_password'];
'port'=>$state['db_port'],
'database'=>$state['db_database'],
'user'=>$state['db_user'],
);
$password = (string)($state['_db_password'] ?? '');
bratonien_tools_nc_connector_psql($config, $password, 'SELECT 1'); bratonien_tools_nc_connector_psql($config, $password, 'SELECT 1');
$source_view = bratonien_tools_nc_connector_view_name($state['source_view']); $source_view = bratonien_tools_nc_connector_view_name($state['source_view']);
$activity_view = bratonien_tools_nc_connector_view_name($state['activity_view']); $activity_view = bratonien_tools_nc_connector_view_name($state['activity_view']);
bratonien_tools_nc_connector_psql($config, $password, 'SELECT COUNT(*) FROM '.$source_view); bratonien_tools_nc_connector_psql($config, $password, 'SELECT COUNT(*) FROM '.$source_view);
bratonien_tools_nc_connector_psql($config, $password, 'SELECT 1 FROM '.$activity_view.' LIMIT 1'); bratonien_tools_nc_connector_psql($config, $password, 'SELECT 1 FROM '.$activity_view.' LIMIT 1');
$rows = bratonien_tools_nc_connector_psql( $rows = bratonien_tools_nc_connector_psql($config, $password, "SELECT DISTINCT storage_id::text || E'\\t' || source_path FROM ".$source_view." ORDER BY 1");
$config,
$password,
"SELECT DISTINCT storage_id::text || E'\\t' || source_path FROM ".$source_view." ORDER BY 1"
);
$candidates = array(); $candidates = array();
foreach (preg_split('/\r\n|\r|\n/', trim($rows)) as $line) foreach (preg_split('/\r\n|\r|\n/', trim($rows)) as $line)
{ {
@@ -414,22 +323,13 @@ function bratonien_tools_nc_wizard_save_technical()
if (count($parts) !== 2) continue; if (count($parts) !== 2) continue;
$storage_id = trim($parts[0]); $storage_id = trim($parts[0]);
$source_path = trim($parts[1], '/'); $source_path = trim($parts[1], '/');
$prefix = $source_path; $prefix = strpos($source_path, '/') !== false ? substr($source_path, 0, strpos($source_path, '/')) : $source_path;
if (strpos($source_path, '/') !== false) if ($storage_id !== '' && !isset($candidates[$storage_id]))
{
$prefix = substr($source_path, 0, strpos($source_path, '/'));
}
if ($storage_id === '') continue;
if (!isset($candidates[$storage_id]))
{ {
$candidates[$storage_id] = array('storage_id'=>$storage_id, 'source_prefix'=>$prefix, 'local_mount'=>''); $candidates[$storage_id] = array('storage_id'=>$storage_id, 'source_prefix'=>$prefix, 'local_mount'=>'');
} }
} }
if (!$candidates) throw new RuntimeException('Die Datenquelle ist erreichbar, liefert aber keine verwendbare Speicherzuordnung.');
if (!$candidates)
{
throw new RuntimeException('Die Source-View ist erreichbar, liefert aber keine Storage-Zuordnung.');
}
$known = array(); $known = array();
foreach (bratonien_tools_nc_connector_connections() as $connection) foreach (bratonien_tools_nc_connector_connections() as $connection)
@@ -443,143 +343,100 @@ function bratonien_tools_nc_wizard_save_technical()
foreach ($candidates as &$candidate) foreach ($candidates as &$candidate)
{ {
$key = $candidate['storage_id'].'|'.$candidate['source_prefix']; $key = $candidate['storage_id'].'|'.$candidate['source_prefix'];
if (!empty($known[$key])) if (!empty($known[$key])) $candidate['local_mount'] = $known[$key];
{
$candidate['local_mount'] = $known[$key];
}
} }
unset($candidate); unset($candidate);
$state['storage_candidates'] = array_values($candidates); $state['storage_candidates'] = array_values($candidates);
$state['storages'] = array_values(array_filter($state['storage_candidates'], function($storage) $state['storages'] = array_values(array_filter($state['storage_candidates'], function($storage) { return trim((string)($storage['local_mount'] ?? '')) !== ''; }));
{ $state['technical_source'] = 'Datenbank und Datenquelle erfolgreich geprüft';
return trim((string)($storage['local_mount'] ?? '')) !== '';
}));
$state['technical_source'] = 'Datenbank und Views erfolgreich geprüft';
$state['technical_complete'] = count($state['storages']) === count($state['storage_candidates']); $state['technical_complete'] = count($state['storages']) === count($state['storage_candidates']);
bratonien_tools_nc_wizard_store($state); bratonien_tools_nc_wizard_store($state);
return array('message'=>$state['technical_complete'] ? 'Technische Verbindung wurde erfolgreich geprüft.' : 'Die Verbindung wurde geprüft. Nur nicht erkannte Speicherorte müssen noch ergänzt werden.');
return array('message'=>$state['technical_complete']
? 'Technische Verbindung wurde automatisch vervollständigt.'
: 'Datenbank und Views wurden erkannt. Für nicht erkannte Storages wird nur noch der lokale Mount abgefragt.');
} }
function bratonien_tools_nc_wizard_save_mounts() function bratonien_tools_nc_wizard_save_mounts()
{ {
$state = bratonien_tools_nc_wizard_state(); $state = bratonien_tools_nc_wizard_state();
$candidates = isset($state['storage_candidates']) && is_array($state['storage_candidates']) ? $state['storage_candidates'] : array(); $candidates = is_array($state['storage_candidates']) ? $state['storage_candidates'] : array();
$mounts = isset($_POST['nc_wizard_storage_mount']) && is_array($_POST['nc_wizard_storage_mount']) ? $_POST['nc_wizard_storage_mount'] : array(); $mounts = isset($_POST['nc_wizard_storage_mount']) && is_array($_POST['nc_wizard_storage_mount']) ? $_POST['nc_wizard_storage_mount'] : array();
if (!$candidates) if (!$candidates) throw new RuntimeException('Es wurden noch keine Speicherorte erkannt.');
{
throw new RuntimeException('Es wurden noch keine Storages erkannt.');
}
$storages = array(); $storages = array();
foreach ($candidates as $index => $candidate) foreach ($candidates as $index => $candidate)
{ {
$mount = trim((string)($candidate['local_mount'] ?? '')); $mount = trim((string)($candidate['local_mount'] ?? ''));
if ($mount === '') if ($mount === '') $mount = trim((string)($mounts[$index] ?? ''));
{ if ($mount === '' || $mount[0] !== '/') throw new RuntimeException('Für einen Speicherort fehlt ein gültiger absoluter Pfad.');
$mount = trim((string)($mounts[$index] ?? ''));
}
if ($mount === '' || $mount[0] !== '/')
{
throw new RuntimeException('Für Storage '.(string)$candidate['storage_id'].' fehlt ein absoluter lokaler Mount-Pfad.');
}
$mount = rtrim($mount, '/'); $mount = rtrim($mount, '/');
if (!is_dir($mount) || !is_readable($mount)) if (!is_dir($mount) || !is_readable($mount)) throw new RuntimeException('Der angegebene Speicherort ist nicht vorhanden oder nicht lesbar: '.$mount);
{
throw new RuntimeException('Storage-Mount '.$mount.' ist nicht vorhanden oder nicht lesbar.');
}
$candidate['local_mount'] = $mount; $candidate['local_mount'] = $mount;
$storages[] = $candidate; $storages[] = $candidate;
} }
$state['storages'] = $storages; $state['storages'] = $storages;
$state['storage_candidates'] = $storages; $state['storage_candidates'] = $storages;
$state['technical_complete'] = true; $state['technical_complete'] = true;
bratonien_tools_nc_wizard_store($state); bratonien_tools_nc_wizard_store($state);
return array('message'=>'Storage-Zuordnung ist vollständig.'); return array('message'=>'Speicherzuordnung ist vollständig.');
} }
function bratonien_tools_nc_wizard_select_user() function bratonien_tools_nc_wizard_select_user()
{ {
$state = bratonien_tools_nc_wizard_state(); $state = bratonien_tools_nc_wizard_state();
if (empty($state['scan_ok'])) if (empty($state['scan_ok'])) throw new RuntimeException('Bitte zuerst Nextcloud erfolgreich scannen.');
{ if (empty($state['technical_complete'])) throw new RuntimeException('Die Verbindung ist noch nicht vollständig geprüft.');
throw new RuntimeException('Bitte zuerst Nextcloud erfolgreich scannen.');
}
if (empty($state['technical_complete']))
{
throw new RuntimeException('Die automatische technische Erkennung ist noch nicht vollständig. Bitte nur die noch fehlenden Angaben ergänzen.');
}
$showcase_user = trim((string)($_POST['nc_wizard_showcase_user'] ?? '')); $showcase_user = trim((string)($_POST['nc_wizard_showcase_user'] ?? ''));
if ($showcase_user === '')
{
throw new RuntimeException('Bitte einen Nextcloud-Benutzer für die Showcase-Freigaben auswählen.');
}
$connection_name = trim((string)($_POST['nc_wizard_connection_name'] ?? $state['connection_name'])); $connection_name = trim((string)($_POST['nc_wizard_connection_name'] ?? $state['connection_name']));
if ($connection_name === '') if ($showcase_user === '') throw new RuntimeException('Bitte den Benutzer auswählen, der die Showcase-Freigaben bereitstellt.');
{ if ($connection_name === '') throw new RuntimeException('Bitte einen Namen für die Verbindung angeben.');
throw new RuntimeException('Bitte einen Namen für die Verbindung angeben.');
}
$state['connection_name'] = $connection_name; $state['connection_name'] = $connection_name;
$state['showcase_user'] = $showcase_user; $state['showcase_user'] = $showcase_user;
$state['step'] = 3; $state['step'] = 3;
$state['api_error'] = ''; $state['api_error'] = '';
bratonien_tools_nc_wizard_store($state); bratonien_tools_nc_wizard_store($state);
return array('message'=>'Showcase-Benutzer übernommen. Jetzt folgt der Piwigo-API-Zugang.'); return array('message'=>'Showcase-Benutzer übernommen. Jetzt folgt der Piwigo-API-Zugang.');
} }
function bratonien_tools_nc_wizard_api_test() function bratonien_tools_nc_wizard_api_test()
{ {
$state = bratonien_tools_nc_wizard_state(); $state = bratonien_tools_nc_wizard_state();
if ((int)$state['step'] !== 3) if ((int)$state['step'] !== 3) throw new RuntimeException('Der API-Test ist in diesem Assistentenschritt nicht verfügbar.');
{
throw new RuntimeException('Der API-Test ist in diesem Assistentenschritt nicht verfügbar.');
}
$key_id = trim((string)($_POST['nc_wizard_api_key_id'] ?? '')); $posted_id = trim((string)($_POST['nc_wizard_api_key_id'] ?? ''));
$secret = trim((string)($_POST['nc_wizard_api_key_secret'] ?? '')); $posted_secret = trim((string)($_POST['nc_wizard_api_key_secret'] ?? ''));
if ($posted_id !== '') $state['_api_key_id'] = $posted_id;
if ($posted_secret !== '') $state['_api_key_secret'] = $posted_secret;
bratonien_tools_nc_wizard_store($state);
$key_id = trim((string)$state['_api_key_id']);
$secret = trim((string)$state['_api_key_secret']);
if ($key_id === '' && $secret === '') if ($key_id === '' && $secret === '')
{ {
$stored = bratonien_tools_nc_api_credentials(); $stored = bratonien_tools_nc_api_credentials();
$key_id = trim((string)($stored['key_id'] ?? '')); $key_id = trim((string)($stored['key_id'] ?? ''));
$secret = trim((string)($stored['key_secret'] ?? '')); $secret = trim((string)($stored['key_secret'] ?? ''));
} }
if ($key_id === '' || $secret === '') if ($key_id === '' || $secret === '') throw new RuntimeException('API-Schlüssel-ID und API-Geheimnis fehlen.');
{
throw new RuntimeException('API-Schlüssel-ID und API-Geheimnis fehlen.');
}
try try
{ {
$status = bratonien_tools_nc_connector_piwigo_api_request($key_id, $secret, 'pwg.session.getStatus'); $status = bratonien_tools_nc_connector_piwigo_api_request($key_id, $secret, 'pwg.session.getStatus');
$user_status = strtolower((string)($status['status'] ?? '')); $user_status = strtolower((string)($status['status'] ?? ''));
if (!in_array($user_status, array('admin','webmaster'), true)) if (!in_array($user_status, array('admin','webmaster'), true)) throw new RuntimeException('Der API-Key gehört keinem Piwigo-Administrator/Webmaster.');
{
throw new RuntimeException('Der API-Key gehört keinem Piwigo-Administrator/Webmaster.');
}
$method_result = bratonien_tools_nc_connector_piwigo_api_request($key_id, $secret, 'reflection.getMethodList'); $method_result = bratonien_tools_nc_connector_piwigo_api_request($key_id, $secret, 'reflection.getMethodList');
$method_map = array(); $method_map = array();
bratonien_tools_nc_connector_collect_method_names($method_result, $method_map); bratonien_tools_nc_connector_collect_method_names($method_result, $method_map);
$missing = array_values(array_diff(array('bratonien.nc.syncProductive','bratonien.nc.syncOrphans'), array_keys($method_map))); $missing = array_values(array_diff(array('bratonien.nc.syncProductive','bratonien.nc.syncOrphans'), array_keys($method_map)));
if ($missing) if ($missing) throw new RuntimeException('Benötigte Bratonien-Sync-Methoden fehlen: '.implode(', ', $missing).'.');
{
throw new RuntimeException('Benötigte Bratonien-Sync-Methoden fehlen: '.implode(', ', $missing).'.');
}
bratonien_tools_nc_api_credentials_store($key_id, $secret); $state['_api_key_id'] = $key_id;
$state['_api_key_secret'] = $secret;
$state['api_status'] = 'ok'; $state['api_status'] = 'ok';
$state['api_username'] = (string)($status['username'] ?? $status['user'] ?? ''); $state['api_username'] = (string)($status['username'] ?? $status['user'] ?? '');
$state['api_error'] = ''; $state['api_error'] = '';
$state['step'] = 4; $state['step'] = 4;
bratonien_tools_nc_wizard_store($state); bratonien_tools_nc_wizard_store($state);
return array('message'=>'Piwigo-API erfolgreich geprüft. Zum Abschluss kann jetzt der Fallback festgelegt werden.'); return array('message'=>'Piwigo-API erfolgreich geprüft. Gespeichert wird sie erst beim Abschluss des Assistenten.');
} }
catch (Throwable $e) catch (Throwable $e)
{ {
@@ -593,10 +450,7 @@ function bratonien_tools_nc_wizard_api_test()
function bratonien_tools_nc_wizard_api_skip() function bratonien_tools_nc_wizard_api_skip()
{ {
$state = bratonien_tools_nc_wizard_state(); $state = bratonien_tools_nc_wizard_state();
if ((int)$state['step'] !== 3) if ((int)$state['step'] !== 3) throw new RuntimeException('Die API kann in diesem Assistentenschritt nicht übersprungen werden.');
{
throw new RuntimeException('Die API kann in diesem Assistentenschritt nicht übersprungen werden.');
}
$state['api_status'] = 'skipped'; $state['api_status'] = 'skipped';
$state['api_error'] = ''; $state['api_error'] = '';
$state['step'] = 4; $state['step'] = 4;
@@ -612,29 +466,25 @@ function bratonien_tools_nc_wizard_finish()
throw new RuntimeException('Der Assistent ist noch nicht vollständig.'); throw new RuntimeException('Der Assistent ist noch nicht vollständig.');
} }
$fallback_user = trim((string)($_POST['nc_wizard_fallback_user'] ?? '')); if (array_key_exists('nc_wizard_fallback_user', $_POST)) $state['_fallback_user'] = trim((string)$_POST['nc_wizard_fallback_user']);
$fallback_password = (string)($_POST['nc_wizard_fallback_password'] ?? ''); if (array_key_exists('nc_wizard_fallback_password', $_POST)) $state['_fallback_password'] = (string)$_POST['nc_wizard_fallback_password'];
if (($fallback_user === '') !== ($fallback_password === '')) bratonien_tools_nc_wizard_store($state);
{ $fallback_user = trim((string)$state['_fallback_user'];
throw new RuntimeException('Fallback-Benutzer und Fallback-Passwort müssen entweder beide angegeben oder beide leer gelassen werden.'); $fallback_password = (string)$state['_fallback_password'];
} if (($fallback_user === '') !== ($fallback_password === '')) throw new RuntimeException('Fallback-Benutzer und Fallback-Passwort müssen entweder beide angegeben oder beide leer gelassen werden.');
if ($state['api_status'] !== 'ok' && $fallback_user === '') if ($state['api_status'] !== 'ok' && $fallback_user === '') throw new RuntimeException('Da die Piwigo-API übersprungen wurde, ist ein Fallback-Zugang erforderlich.');
{
throw new RuntimeException('Da die Piwigo-API übersprungen wurde, ist ein Fallback-Zugang erforderlich.');
}
$storage_lines = array(); $storage_lines = array();
foreach ($state['storages'] as $storage) foreach ($state['storages'] as $storage)
{ {
$storage_lines[] = (string)$storage['storage_id'].' | '.(string)$storage['source_prefix'].' | '.(string)$storage['local_mount']; $storage_lines[] = (string)$storage['storage_id'].' | '.(string)$storage['source_prefix'].' | '.(string)$storage['local_mount'];
} }
$_POST['nc_name'] = (string)$state['connection_name']; $_POST['nc_name'] = (string)$state['connection_name'];
$_POST['nc_host'] = (string)$state['db_host']; $_POST['nc_host'] = (string)$state['db_host'];
$_POST['nc_port'] = (string)$state['db_port']; $_POST['nc_port'] = (string)$state['db_port'];
$_POST['nc_database'] = (string)$state['db_database']; $_POST['nc_database'] = (string)$state['db_database'];
$_POST['nc_user'] = (string)$state['db_user']; $_POST['nc_user'] = (string)$state['db_user'];
$_POST['nc_db_password'] = (string)($state['_db_password'] ?? ''); $_POST['nc_db_password'] = (string)$state['_db_password'];
$_POST['nc_source_view'] = (string)$state['source_view']; $_POST['nc_source_view'] = (string)$state['source_view'];
$_POST['nc_activity_view'] = (string)$state['activity_view']; $_POST['nc_activity_view'] = (string)$state['activity_view'];
$_POST['nc_gallery_root'] = (string)$state['gallery_root']; $_POST['nc_gallery_root'] = (string)$state['gallery_root'];
@@ -649,9 +499,29 @@ function bratonien_tools_nc_wizard_finish()
$_POST['nc_access_user'] = (string)$state['username']; $_POST['nc_access_user'] = (string)$state['username'];
$_POST['nc_product'] = (string)$state['product']; $_POST['nc_product'] = (string)$state['product'];
$_POST['nc_version'] = (string)$state['version']; $_POST['nc_version'] = (string)$state['version'];
$_POST['nc_api_validated'] = $state['api_status'] === 'ok' ? '1' : '0';
$result = bratonien_tools_nc_connector_create_local_api_first(); $result = bratonien_tools_nc_connector_create_local_api_first();
$connection_id = (int)($result['connection_id'] ?? 0);
try
{
if ($state['api_status'] === 'ok')
{
bratonien_tools_nc_api_credentials_store((string)$state['_api_key_id'], (string)$state['_api_key_secret']);
}
}
catch (Throwable $e)
{
if ($connection_id > 0)
{
$table = bratonien_tools_nc_connector_table();
pwg_query("DELETE FROM `$table` WHERE id=".$connection_id." LIMIT 1");
}
throw new RuntimeException('Die Verbindung wurde nicht übernommen, weil der geprüfte API-Zugang nicht gespeichert werden konnte: '.$e->getMessage());
}
unset($_SESSION['bratonien_nc_wizard']); unset($_SESSION['bratonien_nc_wizard']);
unset($result['connection_id']);
$result['message'] = 'Verbindung wurde vollständig durch den Assistenten angelegt. '.$result['message']; $result['message'] = 'Verbindung wurde vollständig durch den Assistenten angelegt. '.$result['message'];
return $result; return $result;
} }
@@ -660,21 +530,12 @@ function bratonien_tools_nc_connector_update_name()
{ {
$id = (int)($_POST['connection_id'] ?? 0); $id = (int)($_POST['connection_id'] ?? 0);
$name = trim((string)($_POST['connection_name'] ?? '')); $name = trim((string)($_POST['connection_name'] ?? ''));
if ($id < 1 || $name === '') if ($id < 1 || $name === '') throw new RuntimeException('Verbindung oder Name fehlt.');
{
throw new RuntimeException('Verbindung oder Name fehlt.');
}
$connection = bratonien_tools_nc_connector_connection($id, false); $connection = bratonien_tools_nc_connector_connection($id, false);
if (!$connection) if (!$connection) throw new RuntimeException('Connector-Verbindung wurde nicht gefunden.');
{
throw new RuntimeException('Connector-Verbindung wurde nicht gefunden.');
}
$table = bratonien_tools_nc_connector_table(); $table = bratonien_tools_nc_connector_table();
$now = date('Y-m-d H:i:s'); $now = date('Y-m-d H:i:s');
pwg_query("UPDATE `$table` SET name='".pwg_db_real_escape_string($name)."', updated='".pwg_db_real_escape_string($now)."' WHERE id=".$id." LIMIT 1"); pwg_query("UPDATE `$table` SET name='".pwg_db_real_escape_string($name)."', updated='".pwg_db_real_escape_string($now)."' WHERE id=".$id." LIMIT 1");
return array('message'=>'Verbindungsname wurde aktualisiert.'); return array('message'=>'Verbindungsname wurde aktualisiert.');
} }
@@ -682,27 +543,14 @@ function bratonien_tools_nc_connector_update_technical()
{ {
$id = (int)($_POST['connection_id'] ?? 0); $id = (int)($_POST['connection_id'] ?? 0);
$connection = bratonien_tools_nc_connector_connection($id, true); $connection = bratonien_tools_nc_connector_connection($id, true);
if (!$connection) if (!$connection) throw new RuntimeException('Connector-Verbindung wurde nicht gefunden.');
{ if (!empty($connection['enabled']) || (string)$connection['takeover_state'] === 'active') throw new RuntimeException('Technische Einstellungen einer aktiven Verbindung können nicht geändert werden. Bitte zuerst deaktivieren.');
throw new RuntimeException('Connector-Verbindung wurde nicht gefunden.');
}
if (!empty($connection['enabled']) || (string)$connection['takeover_state'] === 'active')
{
throw new RuntimeException('Technische Einstellungen einer aktiven Verbindung können nicht geändert werden. Bitte zuerst deaktivieren.');
}
$config = $connection['config']; $config = $connection['config'];
$port = (int)($_POST['nc_port'] ?? ($config['port'] ?? 5432)); $port = (int)($_POST['nc_port'] ?? ($config['port'] ?? 5432));
if ($port < 1 || $port > 65535) if ($port < 1 || $port > 65535) throw new RuntimeException('Ungültiger PostgreSQL-Port.');
{
throw new RuntimeException('Ungültiger PostgreSQL-Port.');
}
$gallery_root = rtrim(trim((string)($_POST['nc_gallery_root'] ?? ($config['gallery_root'] ?? ''))), '/'); $gallery_root = rtrim(trim((string)($_POST['nc_gallery_root'] ?? ($config['gallery_root'] ?? ''))), '/');
if ($gallery_root === '' || $gallery_root[0] !== '/') if ($gallery_root === '' || $gallery_root[0] !== '/') throw new RuntimeException('Der Galerie-Pfad muss ein absoluter Pfad sein.');
{
throw new RuntimeException('Der Galerie-Pfad muss ein absoluter Pfad sein.');
}
$config['host'] = trim((string)($_POST['nc_host'] ?? ($config['host'] ?? ''))); $config['host'] = trim((string)($_POST['nc_host'] ?? ($config['host'] ?? '')));
$config['port'] = (string)$port; $config['port'] = (string)$port;
@@ -716,32 +564,19 @@ function bratonien_tools_nc_connector_update_technical()
$config['full_sync_seconds'] = max(300, (int)($_POST['nc_full_sync_seconds'] ?? ($config['full_sync_seconds'] ?? 86400))); $config['full_sync_seconds'] = max(300, (int)($_POST['nc_full_sync_seconds'] ?? ($config['full_sync_seconds'] ?? 86400)));
$config['storages'] = bratonien_tools_nc_connector_parse_storages($_POST['nc_storages'] ?? ''); $config['storages'] = bratonien_tools_nc_connector_parse_storages($_POST['nc_storages'] ?? '');
unset($config['verification']); unset($config['verification']);
bratonien_tools_nc_connector_view_name($config['source_view']); bratonien_tools_nc_connector_view_name($config['source_view']);
bratonien_tools_nc_connector_view_name($config['activity_view']); bratonien_tools_nc_connector_view_name($config['activity_view']);
$credentials = bratonien_tools_nc_connector_credentials_from_blob($connection['secret_blob'] ?? ''); $credentials = bratonien_tools_nc_connector_credentials_from_blob($connection['secret_blob'] ?? '');
$db_password = (string)($_POST['nc_db_password'] ?? ''); $db_password = (string)($_POST['nc_db_password'] ?? '');
if ($db_password === '') if ($db_password === '') $db_password = $credentials['db_password'];
{ if ($db_password === '') throw new RuntimeException('Datenbankpasswort fehlt.');
$db_password = $credentials['db_password']; $secret_blob = bratonien_tools_nc_connector_encrypt_credentials($db_password, $credentials['piwigo_user'], $credentials['piwigo_password']);
}
$secret_blob = bratonien_tools_nc_connector_encrypt_credentials(
$db_password,
$credentials['piwigo_user'],
$credentials['piwigo_password']
);
$config_json = json_encode($config, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); $config_json = json_encode($config, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
if (!is_string($config_json)) if (!is_string($config_json)) throw new RuntimeException('Connector-Konfiguration konnte nicht serialisiert werden.');
{
throw new RuntimeException('Connector-Konfiguration konnte nicht serialisiert werden.');
}
$table = bratonien_tools_nc_connector_table(); $table = bratonien_tools_nc_connector_table();
$now = date('Y-m-d H:i:s'); $now = date('Y-m-d H:i:s');
pwg_query("UPDATE `$table` SET takeover_state='disabled', enabled=0, config_json='".pwg_db_real_escape_string($config_json)."', secret_blob='".pwg_db_real_escape_string($secret_blob)."', updated='".pwg_db_real_escape_string($now)."' WHERE id=".$id." LIMIT 1"); pwg_query("UPDATE `$table` SET takeover_state='disabled', enabled=0, config_json='".pwg_db_real_escape_string($config_json)."', secret_blob='".pwg_db_real_escape_string($secret_blob)."', updated='".pwg_db_real_escape_string($now)."' WHERE id=".$id." LIMIT 1");
return array('message'=>'Technische Verbindungseinstellungen wurden gespeichert. Die Verbindung muss erneut geprüft werden.'); return array('message'=>'Technische Verbindungseinstellungen wurden gespeichert. Die Verbindung muss erneut geprüft werden.');
} }