From 95a64fabc5fd4d532579cd704cd6a3898f2a4254 Mon Sep 17 00:00:00 2001 From: Terranom674 Date: Tue, 18 Aug 2026 09:01:17 +0200 Subject: [PATCH] Consolidate intelligent NC connection wizard flow --- include/nc_connector_wizard.inc.php | 343 +++++++++++++++++++++------- 1 file changed, 258 insertions(+), 85 deletions(-) diff --git a/include/nc_connector_wizard.inc.php b/include/nc_connector_wizard.inc.php index 32b9fea..7772afa 100644 --- a/include/nc_connector_wizard.inc.php +++ b/include/nc_connector_wizard.inc.php @@ -9,8 +9,9 @@ function bratonien_tools_nc_wizard_state() $state = isset($_SESSION['bratonien_nc_wizard']) && is_array($_SESSION['bratonien_nc_wizard']) ? $_SESSION['bratonien_nc_wizard'] : array(); return array_merge(array( 'step'=>1,'scan_ok'=>false,'base_url'=>'','host_input'=>'','username'=>'','display_name'=>'','version'=>'','product'=>'Nextcloud', - 'users'=>array(),'can_list_users'=>false,'showcase_user'=>'','connection_name'=>'','scan_message'=>'','technical_source'=>'Noch nicht geprüft', - 'technical_complete'=>false,'db_host'=>'','db_port'=>'5432','db_database'=>'nextcloud','db_user'=>'','db_password_set'=>false, + 'users'=>array(),'can_list_users'=>false,'showcase_user'=>'','connection_name'=>'','scan_message'=>'', + 'technical_stage'=>'reader_credentials','technical_source'=>'Noch nicht geprüft','technical_error'=>'','technical_complete'=>false, + 'db_host'=>'','db_port'=>'5432','db_database'=>'nextcloud','db_user'=>'','db_password_set'=>false, 'source_view'=>'piwigo_showcase_sources','activity_view'=>'piwigo_showcase_activity','gallery_root'=>'','storages'=>array(),'storage_candidates'=>array(), 'api_status'=>'pending','api_username'=>'','api_error'=>'','_password'=>'','_db_password'=>'','_api_key_id'=>'','_api_key_secret'=>'', '_fallback_user'=>'','_fallback_password'=>'', @@ -22,52 +23,54 @@ function bratonien_tools_nc_wizard_reset() { unset($_SESSION['bratonien_nc_wizar function bratonien_tools_nc_wizard_normalize_url($host) { - $host = trim((string)$host); - if ($host === '') throw new RuntimeException('Nextcloud-Host fehlt.'); - if (!preg_match('#^https?://#i', $host)) $host = 'https://'.$host; - $parts = parse_url($host); - if (!is_array($parts) || empty($parts['host'])) throw new RuntimeException('Nextcloud-Host ist ungültig.'); - $scheme = strtolower((string)($parts['scheme'] ?? 'https')); - if (!in_array($scheme, array('http','https'), true)) throw new RuntimeException('Nextcloud muss per HTTP oder HTTPS erreichbar sein.'); - $url = $scheme.'://'.$parts['host']; - if (!empty($parts['port'])) $url .= ':'.(int)$parts['port']; - if (!empty($parts['path']) && $parts['path'] !== '/') $url .= '/'.trim($parts['path'], '/'); - return rtrim($url, '/'); + $host=trim((string)$host); + if($host==='') throw new RuntimeException('Nextcloud-Adresse fehlt.'); + if(!preg_match('#^https?://#i',$host)) $host='https://'.$host; + $parts=parse_url($host); + if(!is_array($parts)||empty($parts['host'])) throw new RuntimeException('Die Nextcloud-Adresse ist ungültig.'); + $scheme=strtolower((string)($parts['scheme']??'https')); + if(!in_array($scheme,array('http','https'),true)) throw new RuntimeException('Nextcloud muss per HTTP oder HTTPS erreichbar sein.'); + $url=$scheme.'://'.$parts['host']; + if(!empty($parts['port'])) $url.=':'.(int)$parts['port']; + if(!empty($parts['path'])&&$parts['path']!=='/') $url.='/'.trim($parts['path'],'/'); + return rtrim($url,'/'); } function bratonien_tools_nc_wizard_candidate_urls($host) { - $host = trim((string)$host); - if ($host === '') throw new RuntimeException('Bitte die Adresse der Nextcloud angeben.'); - if (preg_match('#^https?://#i', $host)) return array(bratonien_tools_nc_wizard_normalize_url($host)); - return array(bratonien_tools_nc_wizard_normalize_url('https://'.$host), bratonien_tools_nc_wizard_normalize_url('http://'.$host)); + $host=trim((string)$host); + if($host==='') throw new RuntimeException('Bitte die Adresse der Nextcloud angeben.'); + if(preg_match('#^https?://#i',$host)) return array(bratonien_tools_nc_wizard_normalize_url($host)); + return array(bratonien_tools_nc_wizard_normalize_url('https://'.$host),bratonien_tools_nc_wizard_normalize_url('http://'.$host)); } -function bratonien_tools_nc_wizard_http($url, $username = '', $password = '', array $headers = array()) +function bratonien_tools_nc_wizard_http($url,$username='',$password='',array $headers=array()) { - if (!function_exists('curl_init')) throw new RuntimeException('cURL ist in PHP nicht verfügbar. Der Nextcloud-Scan kann nicht ausgeführt werden.'); - $ch = curl_init($url); - $options = array( + if(!function_exists('curl_init')) throw new RuntimeException('Der Server kann Nextcloud derzeit nicht per HTTP prüfen.'); + $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-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 !== '') { $options[CURLOPT_HTTPAUTH]=CURLAUTH_BASIC; $options[CURLOPT_USERPWD]=$username.':'.$password; } + 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); $status=(int)curl_getinfo($ch,CURLINFO_HTTP_CODE); curl_close($ch); - if ($body===false || $errno!==0) throw new RuntimeException($error!==''?$error:'Verbindung fehlgeschlagen.'); + $body=curl_exec($ch);$errno=curl_errno($ch);$status=(int)curl_getinfo($ch,CURLINFO_HTTP_CODE);curl_close($ch); + if($body===false||$errno!==0) throw new RuntimeException('Verbindung fehlgeschlagen.'); return array('status'=>$status,'body'=>(string)$body); } function bratonien_tools_nc_wizard_ocs_data($body) { $decoded=json_decode((string)$body,true); - if (!is_array($decoded)||!isset($decoded['ocs'])||!is_array($decoded['ocs'])) throw new RuntimeException('Nextcloud hat keine gültige OCS-Antwort geliefert.'); + if(!is_array($decoded)||!isset($decoded['ocs'])||!is_array($decoded['ocs'])) throw new RuntimeException('Nextcloud hat keine gültige Antwort geliefert.'); $meta=isset($decoded['ocs']['meta'])&&is_array($decoded['ocs']['meta'])?$decoded['ocs']['meta']:array(); - $status=strtolower(trim((string)($meta['status']??''))); $status_code=isset($meta['statuscode'])?(int)$meta['statuscode']:0; - if ($status!=='ok'&&!in_array($status_code,array(100,200),true)) + $status=strtolower(trim((string)($meta['status']??'')));$status_code=isset($meta['statuscode'])?(int)$meta['statuscode']:0; + if($status!=='ok'&&!in_array($status_code,array(100,200),true)) { - $message=trim((string)($meta['message']??'')); if ($message===''||strtolower($message)==='ok') $message='Nextcloud hat die Anfrage abgelehnt.'; throw new RuntimeException($message); + $message=trim((string)($meta['message']??'')); + if($message===''||strtolower($message)==='ok') $message='Nextcloud hat die Anfrage abgelehnt.'; + throw new RuntimeException($message); } return isset($decoded['ocs']['data'])&&is_array($decoded['ocs']['data'])?$decoded['ocs']['data']:array(); } @@ -75,41 +78,72 @@ function bratonien_tools_nc_wizard_ocs_data($body) function bratonien_tools_nc_wizard_fill_profile(array &$state) { $url_host=strtolower((string)parse_url($state['base_url'],PHP_URL_HOST)); - $state['db_host']=$url_host; $state['db_port']='5432'; $state['db_database']='nextcloud'; $state['source_view']='piwigo_showcase_sources'; - $state['activity_view']='piwigo_showcase_activity'; $state['gallery_root']=rtrim(PHPWG_ROOT_PATH,'/').'/galleries/nextcloud'; - $state['technical_source']='Noch nicht geprüft'; $state['technical_complete']=false; - foreach (bratonien_tools_nc_connector_connections() as $candidate) + $state['db_host']=''; + $state['db_port']='5432'; + $state['db_database']='nextcloud'; + $state['source_view']='piwigo_showcase_sources'; + $state['activity_view']='piwigo_showcase_activity'; + $state['gallery_root']=rtrim(PHPWG_ROOT_PATH,'/').'/galleries/nextcloud'; + $state['technical_stage']='reader_credentials'; + $state['technical_source']='Noch nicht geprüft'; + $state['technical_error']=''; + $state['technical_complete']=false; + + foreach(bratonien_tools_nc_connector_connections() as $candidate) { $config=isset($candidate['config'])&&is_array($candidate['config'])?$candidate['config']:array(); - $candidate_url=trim((string)($config['nextcloud_url']??'')); $candidate_host=strtolower(trim((string)($config['host']??''))); $match=false; - if ($candidate_url!=='') { try{$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; - $full=bratonien_tools_nc_connector_connection((int)$candidate['id'],true); if(!$full) continue; + $candidate_url=trim((string)($config['nextcloud_url']??'')); + $match=false; + if($candidate_url!=='') + { + try{$match=bratonien_tools_nc_wizard_normalize_url($candidate_url)===$state['base_url'];}catch(Throwable $ignored){$match=false;} + } + if(!$match) continue; + + $full=bratonien_tools_nc_connector_connection((int)$candidate['id'],true); + if(!$full) continue; $credentials=bratonien_tools_nc_connector_credentials_from_blob($full['secret_blob']??''); - $state['db_host']=(string)($config['host']??$state['db_host']); $state['db_port']=(string)($config['port']??$state['db_port']); - $state['db_database']=(string)($config['database']??$state['db_database']); $state['db_user']=(string)($config['user']??''); - $state['_db_password']=(string)($credentials['db_password']??''); $state['db_password_set']=$state['_db_password']!==''; - $state['source_view']=(string)($config['source_view']??$state['source_view']); $state['activity_view']=(string)($config['activity_view']??$state['activity_view']); - $state['gallery_root']=(string)($config['gallery_root']??$state['gallery_root']); $state['storages']=isset($config['storages'])&&is_array($config['storages'])?$config['storages']:array(); - $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']); + $state['db_host']=(string)($config['host']??''); + $state['db_port']=(string)($config['port']??'5432'); + $state['db_database']=(string)($config['database']??'nextcloud'); + $state['db_user']=(string)($config['user']??''); + $state['_db_password']=(string)($credentials['db_password']??''); + $state['db_password_set']=$state['_db_password']!==''; + $state['source_view']=(string)($config['source_view']??$state['source_view']); + $state['activity_view']=(string)($config['activity_view']??$state['activity_view']); + $state['gallery_root']=(string)($config['gallery_root']??$state['gallery_root']); + $state['storages']=isset($config['storages'])&&is_array($config['storages'])?$config['storages']:array(); + $state['storage_candidates']=$state['storages']; + $state['technical_complete']=$state['db_host']!==''&&$state['db_user']!==''&&$state['db_password_set']&&!empty($state['storages']); + $state['technical_stage']=$state['technical_complete']?'ready':'reader_credentials'; + $state['technical_source']='Passende vorhandene Verbindung übernommen'; break; } + + if($state['db_host']==='') $state['db_host']=$url_host; } function bratonien_tools_nc_wizard_scan() { $state=bratonien_tools_nc_wizard_state(); - $host_input=trim((string)($_POST['nc_wizard_host']??$state['host_input'])); $username=trim((string)($_POST['nc_wizard_user']??$state['username'])); + $host_input=trim((string)($_POST['nc_wizard_host']??$state['host_input'])); + $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); + $state['step']=1;$state['host_input']=$host_input;$state['username']=$username;$state['_password']=$password;bratonien_tools_nc_wizard_store($state); if($username===''||$password==='') throw new RuntimeException('Nextcloud-Benutzer und Passwort werden für den Scan benötigt.'); - $base_url=''; $status_data=null; + $base_url='';$status_data=null; foreach(bratonien_tools_nc_wizard_candidate_urls($host_input) as $candidate_url) { - try { $response=bratonien_tools_nc_wizard_http($candidate_url.'/status.php'); if($response['status']<200||$response['status']>=300)continue; $candidate_status=json_decode($response['body'],true); if(!is_array($candidate_status)||empty($candidate_status['installed']))continue; $base_url=$candidate_url; $status_data=$candidate_status; break; } catch(Throwable $ignored) {} + try + { + $response=bratonien_tools_nc_wizard_http($candidate_url.'/status.php'); + if($response['status']<200||$response['status']>=300) continue; + $candidate_status=json_decode($response['body'],true); + if(!is_array($candidate_status)||empty($candidate_status['installed'])) continue; + $base_url=$candidate_url;$status_data=$candidate_status;break; + } + catch(Throwable $ignored){} } if($base_url===''||!is_array($status_data)) throw new RuntimeException('Unter dieser Adresse konnte keine Nextcloud erreicht werden. HTTP und HTTPS wurden automatisch geprüft.'); @@ -118,71 +152,210 @@ function bratonien_tools_nc_wizard_scan() if($user_response['status']<200||$user_response['status']>=300) throw new RuntimeException('Nextcloud ist erreichbar, aber die Anmeldung konnte nicht geprüft werden.'); $user_data=bratonien_tools_nc_wizard_ocs_data($user_response['body']); - $users=array(); $can_list_users=false; - try { $users_response=bratonien_tools_nc_wizard_http($base_url.'/ocs/v2.php/cloud/users?format=json',$username,$password,array('OCS-APIRequest: true')); if($users_response['status']>=200&&$users_response['status']<300){$users_data=bratonien_tools_nc_wizard_ocs_data($users_response['body']);if(isset($users_data['users'])&&is_array($users_data['users'])){$users=array_values(array_filter(array_map('strval',$users_data['users'])));sort($users,SORT_NATURAL|SORT_FLAG_CASE);$can_list_users=true;}} } catch(Throwable $ignored) {} + $users=array();$can_list_users=false; + try + { + $users_response=bratonien_tools_nc_wizard_http($base_url.'/ocs/v2.php/cloud/users?format=json',$username,$password,array('OCS-APIRequest: true')); + if($users_response['status']>=200&&$users_response['status']<300) + { + $users_data=bratonien_tools_nc_wizard_ocs_data($users_response['body']); + if(isset($users_data['users'])&&is_array($users_data['users'])) + { + $users=array_values(array_filter(array_map('strval',$users_data['users'])));sort($users,SORT_NATURAL|SORT_FLAG_CASE);$can_list_users=true; + } + } + } + catch(Throwable $ignored){} $url_host=(string)parse_url($base_url,PHP_URL_HOST); - $state=array_merge($state,array('step'=>2,'scan_ok'=>true,'base_url'=>$base_url,'host_input'=>$host_input,'username'=>(string)($user_data['id']??$username),'display_name'=>(string)($user_data['display-name']??$user_data['displayname']??''),'version'=>(string)($status_data['versionstring']??$status_data['version']??''),'product'=>(string)($status_data['productname']??'Nextcloud'),'users'=>$users,'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_store($state); + $state=array_merge($state,array( + 'step'=>2,'scan_ok'=>true,'base_url'=>$base_url,'host_input'=>$host_input, + 'username'=>(string)($user_data['id']??$username),'display_name'=>(string)($user_data['display-name']??$user_data['displayname']??''), + 'version'=>(string)($status_data['versionstring']??$status_data['version']??''),'product'=>(string)($status_data['productname']??'Nextcloud'), + 'users'=>$users,'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_store($state); return array('message'=>'Nextcloud wurde gefunden und der Zugang bestätigt.'); } +function bratonien_tools_nc_wizard_finish_data_access(array &$state) +{ + $config=array('host'=>$state['db_host'],'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'); + + $source_view=bratonien_tools_nc_connector_view_name($state['source_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 1 FROM '.$activity_view.' LIMIT 1'); + + $rows=bratonien_tools_nc_connector_psql($config,$password,"SELECT DISTINCT storage_id::text || E'\\t' || COALESCE(source_path, '') FROM ".$source_view." ORDER BY 1"); + $candidates=array(); + foreach(preg_split('/\r\n|\r|\n/',trim($rows)) as $line) + { + if($line==='') continue; + $parts=explode("\t",$line,2); + if(count($parts)!==2) continue; + $storage_id=trim($parts[0]);$source_path=trim($parts[1],'/'); + if($storage_id==='') continue; + $prefix=$source_path; + if(strpos($source_path,'/')!==false) $prefix=substr($source_path,0,strpos($source_path,'/')); + if(!isset($candidates[$storage_id])) $candidates[$storage_id]=array('storage_id'=>$storage_id,'source_prefix'=>$prefix,'local_mount'=>''); + } + if(!$candidates) throw new RuntimeException('Die Verbindung zur Datenquelle funktioniert, aber es wurden noch keine freigegebenen Speicherorte gefunden.'); + + $known=array(); + foreach(bratonien_tools_nc_connector_connections() as $connection) + { + foreach(($connection['config']['storages']??array()) as $storage) + { + $key=(string)($storage['storage_id']??'').'|'.trim((string)($storage['source_prefix']??''),'/'); + if($key!=='|') $known[$key]=(string)($storage['local_mount']??''); + } + } + foreach($candidates as &$candidate) + { + $key=$candidate['storage_id'].'|'.trim($candidate['source_prefix'],'/'); + if(!empty($known[$key])&&is_dir($known[$key])&&is_readable($known[$key])) $candidate['local_mount']=$known[$key]; + } + unset($candidate); + + $state['storage_candidates']=array_values($candidates); + $state['storages']=array_values(array_filter($state['storage_candidates'],function($storage){return trim((string)($storage['local_mount']??''))!=='';})); + $state['technical_error']=''; + $state['technical_source']='Datenzugriff erfolgreich geprüft'; + $state['technical_complete']=count($state['storages'])===count($state['storage_candidates']); + $state['technical_stage']=$state['technical_complete']?'ready':'mounts'; +} + function bratonien_tools_nc_wizard_save_technical() { - $state=bratonien_tools_nc_wizard_state(); if(empty($state['scan_ok'])) throw new RuntimeException('Bitte zuerst Nextcloud erfolgreich scannen.'); - $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_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_user']=trim((string)($_POST['nc_wizard_db_user']??$state['db_user'])); $db_password=array_key_exists('nc_wizard_db_password',$_POST)?(string)$_POST['nc_wizard_db_password']:''; - if($db_password!==''){$state['_db_password']=$db_password;$state['db_password_set']=true;} bratonien_tools_nc_wizard_store($state); - if($state['connection_name']===''||$state['db_host']===''||$state['db_database']===''||$state['db_user']===''||empty($state['db_password_set'])) throw new RuntimeException('Die noch fehlenden Datenbankangaben müssen vollständig angegeben werden.'); + $state=bratonien_tools_nc_wizard_state(); + if(empty($state['scan_ok'])) throw new RuntimeException('Bitte zuerst Nextcloud erfolgreich scannen.'); - $config=array('host'=>$state['db_host'],'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'); $source_view=bratonien_tools_nc_connector_view_name($state['source_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 1 FROM '.$activity_view.' LIMIT 1'); - $rows=bratonien_tools_nc_connector_psql($config,$password,"SELECT DISTINCT storage_id::text || E'\\t' || source_path FROM ".$source_view." ORDER BY 1"); - $candidates=array(); foreach(preg_split('/\r\n|\r|\n/',trim($rows)) as $line){if($line==='')continue;$parts=explode("\t",$line,2);if(count($parts)!==2)continue;$storage_id=trim($parts[0]);$source_path=trim($parts[1],'/');$prefix=strpos($source_path,'/')!==false?substr($source_path,0,strpos($source_path,'/')):$source_path;if($storage_id!==''&&!isset($candidates[$storage_id]))$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.'); - $known=array(); foreach(bratonien_tools_nc_connector_connections() as $connection){foreach(($connection['config']['storages']??array()) as $storage){$key=(string)($storage['storage_id']??'').'|'.(string)($storage['source_prefix']??'');if($key!=='|')$known[$key]=(string)($storage['local_mount']??'');}} - foreach($candidates as &$candidate){$key=$candidate['storage_id'].'|'.$candidate['source_prefix'];if(!empty($known[$key]))$candidate['local_mount']=$known[$key];}unset($candidate); - $state['storage_candidates']=array_values($candidates);$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';$state['technical_complete']=count($state['storages'])===count($state['storage_candidates']);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.'); + $state['connection_name']=trim((string)($_POST['nc_wizard_connection_name']??$state['connection_name'])); + $state['db_user']=trim((string)($_POST['nc_wizard_db_user']??$state['db_user'])); + $db_password=array_key_exists('nc_wizard_db_password',$_POST)?(string)$_POST['nc_wizard_db_password']:(string)$state['_db_password']; + if($db_password!==''){$state['_db_password']=$db_password;$state['db_password_set']=true;} + + $advanced=isset($_POST['nc_wizard_db_host'])||$state['technical_stage']==='database_details'; + if($advanced) + { + $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_database']=trim((string)($_POST['nc_wizard_db_database']??$state['db_database'])); + } + else + { + $state['db_host']=(string)parse_url($state['base_url'],PHP_URL_HOST); + $state['db_port']='5432'; + $state['db_database']='nextcloud'; + } + + bratonien_tools_nc_wizard_store($state); + if($state['db_user']===''||empty($state['db_password_set'])) throw new RuntimeException('Für den Datenzugriff werden Lese-Benutzer und Passwort benötigt.'); + if($state['db_host']===''||$state['db_database']==='') throw new RuntimeException('Die Adresse der Datenbank ist noch unvollständig.'); + + try + { + bratonien_tools_nc_wizard_finish_data_access($state); + bratonien_tools_nc_wizard_store($state); + return array('message'=>$state['technical_complete']?'Datenzugriff wurde erfolgreich geprüft.':'Datenzugriff funktioniert. Ein Speicherort muss noch bestätigt werden.'); + } + catch(Throwable $e) + { + $state['technical_error']=$e->getMessage(); + if(!$advanced) + { + $state['technical_stage']='database_details'; + bratonien_tools_nc_wizard_store($state); + throw new RuntimeException('Der übliche Datenbankzugang konnte nicht automatisch bestätigt werden. Bitte die Datenbank-Adresse prüfen.'); + } + bratonien_tools_nc_wizard_store($state); + throw $e; + } } function bratonien_tools_nc_wizard_save_mounts() { - $state=bratonien_tools_nc_wizard_state();$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();if(!$candidates)throw new RuntimeException('Es wurden noch keine Speicherorte erkannt.'); - $storages=array();foreach($candidates as $index=>$candidate){$mount=trim((string)($candidate['local_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=rtrim($mount,'/');if(!is_dir($mount)||!is_readable($mount))throw new RuntimeException('Der angegebene Speicherort ist nicht vorhanden oder nicht lesbar: '.$mount);$candidate['local_mount']=$mount;$storages[]=$candidate;} - $state['storages']=$storages;$state['storage_candidates']=$storages;$state['technical_complete']=true;bratonien_tools_nc_wizard_store($state);return array('message'=>'Speicherzuordnung ist vollständig.'); + $state=bratonien_tools_nc_wizard_state(); + $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(); + if(!$candidates) throw new RuntimeException('Es wurden noch keine Speicherorte erkannt.'); + $storages=array(); + foreach($candidates as $index=>$candidate) + { + $mount=trim((string)($candidate['local_mount']??'')); + if($mount==='') $mount=trim((string)($mounts[$index]??'')); + if($mount===''||$mount[0]!=='/') throw new RuntimeException('Für einen Speicherort fehlt ein gültiger lokaler Pfad.'); + $mount=rtrim($mount,'/'); + if(!is_dir($mount)||!is_readable($mount)) throw new RuntimeException('Der angegebene Speicherort ist nicht vorhanden oder nicht lesbar: '.$mount); + $candidate['local_mount']=$mount;$storages[]=$candidate; + } + $state['storages']=$storages;$state['storage_candidates']=$storages;$state['technical_complete']=true;$state['technical_stage']='ready';$state['technical_error']='';bratonien_tools_nc_wizard_store($state); + return array('message'=>'Speicherzuordnung wurde erfolgreich geprüft.'); } function bratonien_tools_nc_wizard_select_user() { - $state=bratonien_tools_nc_wizard_state();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.'); - $showcase_user=trim((string)($_POST['nc_wizard_showcase_user']??''));$connection_name=trim((string)($_POST['nc_wizard_connection_name']??$state['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.'); - $state['connection_name']=$connection_name;$state['showcase_user']=$showcase_user;$state['step']=3;$state['api_error']='';bratonien_tools_nc_wizard_store($state);return array('message'=>'Showcase-Benutzer übernommen. Jetzt folgt der Piwigo-API-Zugang.'); + $state=bratonien_tools_nc_wizard_state(); + 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.'); + $showcase_user=trim((string)($_POST['nc_wizard_showcase_user']??'')); + $connection_name=trim((string)($_POST['nc_wizard_connection_name']??$state['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.'); + $state['connection_name']=$connection_name;$state['showcase_user']=$showcase_user;$state['step']=3;$state['api_error']='';bratonien_tools_nc_wizard_store($state); + return array('message'=>'Showcase-Benutzer übernommen. Jetzt folgt der Piwigo-API-Zugang.'); } function bratonien_tools_nc_wizard_api_test() { - $state=bratonien_tools_nc_wizard_state();if((int)$state['step']!==3)throw new RuntimeException('Der API-Test ist in diesem Assistentenschritt nicht verfügbar.'); - $posted_id=trim((string)($_POST['nc_wizard_api_key_id']??''));$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===''){$stored=bratonien_tools_nc_api_credentials();$key_id=trim((string)($stored['key_id']??''));$secret=trim((string)($stored['key_secret']??''));}if($key_id===''||$secret==='')throw new RuntimeException('API-Schlüssel-ID und API-Geheimnis fehlen.'); - try{$status=bratonien_tools_nc_connector_piwigo_api_request($key_id,$secret,'pwg.session.getStatus');$user_status=strtolower((string)($status['status']??''));if(!in_array($user_status,array('admin','webmaster'),true))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_map=array();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)));if($missing)throw new RuntimeException('Benötigte Bratonien-Sync-Methoden fehlen: '.implode(', ',$missing).'.');$state['_api_key_id']=$key_id;$state['_api_key_secret']=$secret;$state['api_status']='ok';$state['api_username']=(string)($status['username']??$status['user']??'');$state['api_error']='';$state['step']=4;bratonien_tools_nc_wizard_store($state);return array('message'=>'Piwigo-API erfolgreich geprüft. Gespeichert wird sie erst beim Abschluss des Assistenten.');} + $state=bratonien_tools_nc_wizard_state(); + if((int)$state['step']!==3) throw new RuntimeException('Der API-Test ist in diesem Assistentenschritt nicht verfügbar.'); + $posted_id=trim((string)($_POST['nc_wizard_api_key_id']??''));$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===''){$stored=bratonien_tools_nc_api_credentials();$key_id=trim((string)($stored['key_id']??''));$secret=trim((string)($stored['key_secret']??''));} + if($key_id===''||$secret==='') throw new RuntimeException('API-Schlüssel-ID und API-Geheimnis fehlen.'); + try + { + $status=bratonien_tools_nc_connector_piwigo_api_request($key_id,$secret,'pwg.session.getStatus'); + $user_status=strtolower((string)($status['status']??'')); + if(!in_array($user_status,array('admin','webmaster'),true)) 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_map=array();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))); + if($missing) throw new RuntimeException('Benötigte Bratonien-Sync-Methoden fehlen: '.implode(', ',$missing).'.'); + $state['_api_key_id']=$key_id;$state['_api_key_secret']=$secret;$state['api_status']='ok';$state['api_username']=(string)($status['username']??$status['user']??'');$state['api_error']='';$state['step']=4;bratonien_tools_nc_wizard_store($state); + return array('message'=>'Piwigo-API erfolgreich geprüft. Gespeichert wird sie erst beim Abschluss des Assistenten.'); + } catch(Throwable $e){$state['api_status']='error';$state['api_error']=$e->getMessage();bratonien_tools_nc_wizard_store($state);throw $e;} } function bratonien_tools_nc_wizard_api_skip() { - $state=bratonien_tools_nc_wizard_state();if((int)$state['step']!==3)throw new RuntimeException('Die API kann in diesem Assistentenschritt nicht übersprungen werden.');$state['api_status']='skipped';$state['api_error']='';$state['step']=4;bratonien_tools_nc_wizard_store($state);return array('message'=>'Piwigo-API wurde übersprungen. Für diese Verbindung ist deshalb ein Fallback-Zugang erforderlich.'); + $state=bratonien_tools_nc_wizard_state();if((int)$state['step']!==3)throw new RuntimeException('Die API kann in diesem Assistentenschritt nicht übersprungen werden.'); + $state['api_status']='skipped';$state['api_error']='';$state['step']=4;bratonien_tools_nc_wizard_store($state); + return array('message'=>'Piwigo-API wurde übersprungen. Für diese Verbindung ist deshalb ein Fallback-Zugang erforderlich.'); } function bratonien_tools_nc_wizard_finish() { - $state=bratonien_tools_nc_wizard_state();if((int)$state['step']!==4||empty($state['technical_complete'])||trim((string)$state['showcase_user'])==='')throw new RuntimeException('Der Assistent ist noch nicht vollständig.'); - if(array_key_exists('nc_wizard_fallback_user',$_POST))$state['_fallback_user']=trim((string)$_POST['nc_wizard_fallback_user']);if(array_key_exists('nc_wizard_fallback_password',$_POST))$state['_fallback_password']=(string)$_POST['nc_wizard_fallback_password'];bratonien_tools_nc_wizard_store($state); - $fallback_user=trim((string)$state['_fallback_user']);$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==='')throw new RuntimeException('Da die Piwigo-API übersprungen wurde, ist ein Fallback-Zugang erforderlich.'); - $storage_lines=array();foreach($state['storages'] as $storage)$storage_lines[]=(string)$storage['storage_id'].' | '.(string)$storage['source_prefix'].' | '.(string)$storage['local_mount']; + $state=bratonien_tools_nc_wizard_state(); + if((int)$state['step']!==4||empty($state['technical_complete'])||trim((string)$state['showcase_user'])==='') throw new RuntimeException('Der Assistent ist noch nicht vollständig.'); + if(array_key_exists('nc_wizard_fallback_user',$_POST))$state['_fallback_user']=trim((string)$_POST['nc_wizard_fallback_user']; + if(array_key_exists('nc_wizard_fallback_password',$_POST))$state['_fallback_password']=(string)$_POST['nc_wizard_fallback_password']; + bratonien_tools_nc_wizard_store($state); + $fallback_user=trim((string)$state['_fallback_user']);$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==='') throw new RuntimeException('Da die Piwigo-API übersprungen wurde, ist ein Fallback-Zugang erforderlich.'); + + $storage_lines=array(); + foreach($state['storages'] as $storage) + { + $storage_lines[]=(string)$storage['storage_id'].' | '.trim((string)$storage['source_prefix'],'/').' | '.(string)$storage['local_mount']; + } $_POST['nc_name']=(string)$state['connection_name'];$_POST['nc_host']=(string)$state['db_host'];$_POST['nc_port']=(string)$state['db_port'];$_POST['nc_database']=(string)$state['db_database'];$_POST['nc_user']=(string)$state['db_user'];$_POST['nc_db_password']=(string)$state['_db_password'];$_POST['nc_source_view']=(string)$state['source_view'];$_POST['nc_activity_view']=(string)$state['activity_view'];$_POST['nc_gallery_root']=(string)$state['gallery_root'];$_POST['nc_storages']=implode("\n",$storage_lines);$_POST['nc_quiet_seconds']='120';$_POST['nc_max_wait_seconds']='900';$_POST['nc_full_sync_seconds']='86400';$_POST['nc_piwigo_user']=$fallback_user;$_POST['nc_piwigo_password']=$fallback_password;$_POST['nc_nextcloud_url']=(string)$state['base_url'];$_POST['nc_showcase_user']=(string)$state['showcase_user'];$_POST['nc_access_user']=(string)$state['username'];$_POST['nc_product']=(string)$state['product'];$_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();$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']);}