3declare(strict_types=1);
21 public function __construct(?
object $db =
null, ?
object $dd =
null)
23 $this->db = $db ??
dbx()->get_system_obj(
'dbxDB');
24 $this->dd = $dd ??
dbx()->get_system_obj(
'dbxDD');
26 if (!is_object($this->db) || !is_object($this->dd)) {
27 throw new RuntimeException(
'dbxDB/dbxDD stehen fuer die Installation nicht zur Verfuegung.');
45 $moduleFilter = array();
49 $moduleFilter[strtolower(
$module)] =
true;
55 foreach (glob(
$root .
'*/dd/*.dd.php') ?: array() as
$file) {
58 || preg_match(
'#/dbx/modules/([^/]+)/dd/([^/]+)\.dd\.php$#',
$normalized, $match) !== 1
64 $name = (string)$match[2];
65 if ($name ===
'' || str_starts_with($name,
'.')) {
68 if ($moduleFilter !== array()
69 && !isset($moduleFilter[strtolower(
$module)])
75 $records[
$module .
'|' . $name] = array(
80 'declared_server' => trim((
string)(
$definition[
'server'] ??
'')),
81 'table' => trim((
string)(
$definition[
'table'] ??
'')),
85 uksort($records,
static function (
string $left,
string $right):
int {
86 $leftCore = str_starts_with($left,
'dbx|') ? 0 : 1;
87 $rightCore = str_starts_with($right,
'dbx|') ? 0 : 1;
88 return $leftCore <=> $rightCore ?: strcasecmp($left, $right);
91 return array_values($records);
99 private function readDDTableDefinition(
string $file): array
105 $definition = (
static function (
string $ddFile): array {
128 throw new RuntimeException(
'Der Zielserver fuer die DD-Bindungen fehlt.');
133 $bindings[$record[
'dd']] =
$server;
137 'dd_server_bindings' => $bindings,
139 throw new RuntimeException(
'Lokale DD-Serverbindungen konnten nicht gespeichert werden.');
156 foreach ($records as $record) {
157 $binding = $this->db->get_dd_server_binding_info($record[
'dd']);
158 if (empty(
$binding[
'valid']) || (
$binding[
'resolved_server'] ??
'') ===
'') {
159 $errors[] = $record[
'dd'] .
': ungueltige DD-Serverbindung';
163 $this->dd->sync_dd_to_db(
170 for ($step = 0; $step < 1000; $step++) {
171 $state = $this->dd->sync_dd_to_db(
176 $status = (string)(
$state[
'status'] ??
'');
177 if (in_array($status, array(
'finished',
'error',
'cancelled'),
true)) {
183 'server' => (
string)
$binding[
'resolved_server'],
184 'status' => (
string)(
$state[
'status'] ??
'error'),
185 'message' => (
string)(
$state[
'message'] ??
''),
187 if ((
$state[
'status'] ??
'') !==
'finished') {
188 $errors[] = $record[
'dd'] .
': '
189 . (string)(
$state[
'message'] ??
'Schema-Synchronisation fehlgeschlagen');
195 'total' => count($records),
213 if (!method_exists($this->dd,
'get_table_exist')) {
214 throw new RuntimeException(
'Die lesende DD-Strukturprüfung steht nicht zur Verfügung.');
221 foreach ($records as $record) {
222 $binding = $this->db->get_dd_server_binding_info($record[
'dd']);
224 $table = method_exists($this->db,
'get_dd_table')
225 ? trim((
string)$this->db->get_dd_table($record[
'dd']))
228 $table = trim((
string)($record[
'table'] ??
''));
235 'status' =>
$valid ?
'verified' :
'missing',
243 .
': ausgelieferte DB3-Tabelle fehlt oder ist nicht erreichbar';
249 'total' => count($records),
250 'verified' => $verified,
268 string $targetServer,
271 $targetServer = trim($targetServer);
272 if ($targetServer ===
'') {
273 throw new RuntimeException(
'Der Zielserver für die Datenübertragung fehlt.');
275 if (!method_exists($this->dd,
'get_table_exist')
276 || !method_exists($this->dd,
'transfer_table')
278 throw new RuntimeException(
'Die DD-Datenübertragung steht nicht zur Verfügung.');
287 foreach ($records as $record) {
288 $ddRef = (string)$record[
'dd'];
289 $sourceServer = trim((
string)($record[
'declared_server'] ??
''));
290 $table = trim((
string)($record[
'table'] ??
''));
291 if ($sourceServer ===
''
293 || strcasecmp($sourceServer, $targetServer) === 0
294 || !$this->dd->get_table_exist($sourceServer,
$table)
297 'status' =>
'skipped',
298 'source' => $sourceServer,
299 'target' => $targetServer,
306 $this->dd->transfer_table(
316 for ($step = 0; $step < 100000; $step++) {
317 $state = $this->dd->transfer_table(
327 (
string)(
$state[
'status'] ??
''),
328 array(
'finished',
'error',
'cancelled'),
335 $status = (string)(
$state[
'status'] ??
'error');
338 'source' => $sourceServer,
339 'target' => $targetServer,
341 'message' => (
string)(
$state[
'message'] ??
''),
343 if ($status ===
'finished') {
347 . (string)(
$state[
'message'] ??
'Datenübertragung fehlgeschlagen');
353 'total' => count($records),
354 'transferred' => $transferred,
368 'guest' =>
'Nicht angemeldete Benutzer',
369 'member' =>
'Bestaetigte Benutzer',
370 'admin' =>
'Systemadministratoren',
372 $result = array(
'created' => array(),
'existing' => array());
374 foreach (
$groups as $name => $description) {
375 $existing = $this->db->select1(
376 'dbx|dbxUser_groups',
377 array(
'name' => $name),
381 if ((
int)($existing[
'id'] ?? 0) > 0) {
386 $id = $this->db->insert(
387 'dbx|dbxUser_groups',
390 'description' => $description,
399 throw new RuntimeException(
'Systemgruppe konnte nicht angelegt werden: ' . $name);
417 $existing = $this->db->select1(
419 array(
'uname' =>
'admin'),
420 array(
'id',
'uname',
'pass',
'email',
'settings'),
423 if ((
int)($existing[
'id'] ?? 0) <= 0) {
428 'default_password' =>
false,
429 'password_reset_required' =>
false,
435 $settings = json_decode((
string)($existing[
'settings'] ??
''),
true);
438 'id' => (
int)$existing[
'id'],
439 'email' => (
string)($existing[
'email'] ??
''),
440 'default_password' => password_verify(
442 (
string)($existing[
'pass'] ??
'')
444 'password_reset_required' => is_array($settings)
445 && !empty($settings[
'password_reset_required']),
460 bool $createIfMissing,
463 bool $passwordResetRequired =
false
466 throw new RuntimeException(
'Das Admin-Passwort muss zwischen 6 und 128 Zeichen lang sein.');
469 $status = $this->inspectInitialAdmin();
470 if (!$status[
'exists'] && !$createIfMissing) {
474 if (!$status[
'exists']) {
475 $id = $this->db->insert(
479 'pass' => password_hash(
$password, PASSWORD_DEFAULT),
482 'language' => preg_match(
'/^[a-z]{2,3}$/', $language)
487 'settings' => json_encode(
488 $passwordResetRequired
489 ? array(
'password_reset_required' => 1)
490 : array(
'password_changed_at' => date(DATE_ATOM)),
491 JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
500 throw new RuntimeException(
'Der initiale Admin-Benutzer konnte nicht angelegt werden.');
503 $status = $this->inspectInitialAdmin();
504 $status[
'created'] =
true;
505 $status[
'reset'] =
true;
509 $existing = $this->db->select1(
511 array(
'uname' =>
'admin'),
512 array(
'id',
'settings'),
515 $settings = json_decode((
string)($existing[
'settings'] ??
''),
true);
516 $settings = is_array($settings) ? $settings : array();
517 if ($passwordResetRequired) {
518 $settings[
'password_reset_required'] = 1;
519 unset($settings[
'password_changed_at']);
521 unset($settings[
'password_reset_required']);
522 $settings[
'password_changed_at'] = date(DATE_ATOM);
528 'pass' => password_hash(
$password, PASSWORD_DEFAULT),
532 'settings' => json_encode(
534 JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
544 throw new RuntimeException(
'Der vorhandene Admin-Benutzer konnte nicht auf den Installationsstandard zurückgesetzt werden.');
547 $status = $this->inspectInitialAdmin();
548 $status[
'reset'] =
true;
561 string $language =
'de'
563 $existing = $this->db->select1(
565 array(
'uname' =>
'admin'),
566 array(
'id',
'uname'),
569 if ((
int)($existing[
'id'] ?? 0) > 0) {
572 'id' => (
int)$existing[
'id'],
573 'reason' =>
'admin_exists',
578 throw new RuntimeException(
'Das Admin-Passwort muss mindestens 12 Zeichen lang sein.');
580 if (filter_var($email, FILTER_VALIDATE_EMAIL) ===
false) {
581 throw new RuntimeException(
'Die Admin-E-Mail-Adresse ist ungueltig.');
584 $id = $this->db->insert(
588 'pass' => password_hash(
$password, PASSWORD_DEFAULT),
591 'language' => preg_match(
'/^[a-z]{2,3}$/', $language)
604 throw new RuntimeException(
'Der Admin-Benutzer konnte nicht angelegt werden.');
607 return array(
'created' =>
true,
'id' => (
int)$id,
'reason' =>
'');
if(!is_array( $before)||(int)( $before[ 'id'] ?? 0) !==$mediaId) if((string)($before['media_type'] ?? '') !=='video') $updated
Reproduzierbare Erstinstallation aus DDs und idempotenten Seeds.
bindAllToServer(string $server, array $modules=array())
Bindet alle gefundenen DDs auf einen konfigurierten Server.
discoverDDs(array $modules=array())
Liefert alle installierbaren DDs, optional begrenzt auf Module.
transferDeclaredDataToServer(string $targetServer, array $modules=array())
Überträgt vorhandene Daten aus den in den DDs deklarierten Quellspeichern auf einen neu konfigurierte...
inspectInitialAdmin()
Liefert den Zustand des initialen Administrators ohne Datenänderung.
ensureInitialAdmin(bool $createIfMissing, string $language, string $password, bool $passwordResetRequired=false)
Stellt bei der Installation den persönlichen Administratorzugang bereit.
createAdmin(string $password, string $email, string $language='de')
Erstellt den Admin nur bei einer echten Erstinstallation.
verifyBundledSchema(array $modules=array())
Prüft die mitgelieferten DB3-Tabellen ausschließlich lesend.
seedCoreGroups()
Legt die verbindlichen Core-Gruppen an, ohne bestehende Werte zu ueberschreiben.
__construct(?object $db=null, ?object $dd=null)
provisionSchema(array $modules=array())
Erstellt beziehungsweise ergaenzt die DD-Struktur ohne Force-Rebuild.
get_base_dir(int $cutData=0)
Liefert das Basisverzeichnis der Installation.
patch_local_config(string $modul, array $patch)
Fuehrt einen lokalen Konfigurationsausschnitt rekursiv zusammen.
if(!defined( 'IMG_WEBP')) define( 'IMG_WEBP'
DBX schema administration.