settings.php 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716
  1. <?php
  2. require_once __DIR__ . "/../config.php";
  3. require_once __DIR__ . "/../includes/functions.php";
  4. require_once __DIR__ . "/../includes/version.php";
  5. require_once __DIR__ . "/../manage-client/lib/client.php";
  6. function settingsFormatBackupDate(string $date): string
  7. {
  8. $timestamp = strtotime($date);
  9. if ($timestamp === false) {
  10. return $date;
  11. }
  12. return date("d.m.Y H:i", $timestamp);
  13. }
  14. function settingsIsSuperAdmin(): bool
  15. {
  16. return normalizeAdminUsername($_SESSION['admin_username'] ?? "") === "admin";
  17. }
  18. function settingsGetLocalConfigPath(): string
  19. {
  20. return dirname(__DIR__) . "/config.php";
  21. }
  22. function settingsReadLocalConfig(): string
  23. {
  24. $path = settingsGetLocalConfigPath();
  25. if (!is_file($path) || !is_readable($path)) {
  26. return "";
  27. }
  28. $content = file_get_contents($path);
  29. return $content === false ? "" : $content;
  30. }
  31. function settingsWriteLocalConfig(string $content): void
  32. {
  33. $content = str_replace("\r\n", "\n", $content);
  34. if (!str_starts_with(ltrim($content), "<?php")) {
  35. throw new RuntimeException('Die Konfiguration muss mit "<?php" beginnen.');
  36. }
  37. try {
  38. token_get_all($content, TOKEN_PARSE);
  39. } catch (ParseError $parseError) {
  40. throw new RuntimeException("PHP-Syntaxfehler: " . $parseError->getMessage());
  41. }
  42. $path = settingsGetLocalConfigPath();
  43. $tmpFile = $path . ".tmp";
  44. if (file_put_contents($tmpFile, $content, LOCK_EX) === false) {
  45. throw new RuntimeException("Konfiguration konnte nicht geschrieben werden.");
  46. }
  47. $backupDir = DATA_DIR . "backups/config/";
  48. if (!is_dir($backupDir) && !mkdir($backupDir, 0775, true) && !is_dir($backupDir)) {
  49. @unlink($tmpFile);
  50. throw new RuntimeException("Sicherungsverzeichnis konnte nicht angelegt werden.");
  51. }
  52. if (is_file($path)) {
  53. $backupPath = $backupDir . "config-" . date("Ymd-His") . ".php.bak";
  54. if (!copy($path, $backupPath)) {
  55. @unlink($tmpFile);
  56. throw new RuntimeException("Sicherung der bestehenden Konfiguration ist fehlgeschlagen.");
  57. }
  58. }
  59. @chmod($tmpFile, 0664);
  60. if (!rename($tmpFile, $path)) {
  61. @unlink($tmpFile);
  62. throw new RuntimeException("Konfiguration konnte nicht gespeichert werden.");
  63. }
  64. if (function_exists("opcache_invalidate")) {
  65. opcache_invalidate($path, true);
  66. }
  67. }
  68. if (empty($_SESSION['admin_logged_in'])) {
  69. header("Location: login.php");
  70. exit();
  71. }
  72. $pageTitle = "Einstellungen";
  73. $message = "";
  74. $messageType = "";
  75. $isSuperAdmin = settingsIsSuperAdmin();
  76. if ($_SERVER['REQUEST_METHOD'] === "POST" && isset($_POST['save_settings'])) {
  77. // Validate CSRF token
  78. if (!validateCsrfToken($_POST['csrf_token'] ?? "")) {
  79. $message = "Ungültiges Token. Bitte versuchen Sie es erneut.";
  80. $messageType = "error";
  81. } else {
  82. $settings = array_merge(getSystemSettings(), [
  83. "order_recipient_email" => $_POST['order_recipient_email'] ?? "",
  84. "attach_order_pdf_to_admin_email" => isset(
  85. $_POST['attach_order_pdf_to_admin_email'],
  86. ),
  87. "nametag_product_name" => $_POST['nametag_product_name'] ?? "",
  88. ]);
  89. if (saveSystemSettings($settings)) {
  90. logAccess("Admin updated system settings");
  91. $message = "Einstellungen wurden gespeichert.";
  92. $messageType = "success";
  93. } else {
  94. $message = "Einstellungen konnten nicht gespeichert werden.";
  95. $messageType = "error";
  96. }
  97. }
  98. } elseif ($_SERVER['REQUEST_METHOD'] === "POST" && isset($_POST['save_local_config'])) {
  99. if (!$isSuperAdmin) {
  100. http_response_code(403);
  101. $message = "Keine Berechtigung für diese Aktion.";
  102. $messageType = "error";
  103. } elseif (!validateCsrfToken($_POST['csrf_token'] ?? "")) {
  104. $message = "Ungültiges Token. Bitte versuchen Sie es erneut.";
  105. $messageType = "error";
  106. } else {
  107. try {
  108. settingsWriteLocalConfig((string) ($_POST['local_config_content'] ?? ""));
  109. logAccess("Admin updated local config.php");
  110. $message = "Lokale Konfiguration wurde gespeichert.";
  111. $messageType = "success";
  112. } catch (Throwable $exception) {
  113. $message = "Konfiguration konnte nicht gespeichert werden: " . $exception->getMessage();
  114. $messageType = "error";
  115. }
  116. }
  117. } elseif ($_SERVER['REQUEST_METHOD'] === "POST" && isset($_POST['add_category'])) {
  118. if (!validateCsrfToken($_POST['csrf_token'] ?? "")) {
  119. $message = "Ungültiges Token. Bitte versuchen Sie es erneut.";
  120. $messageType = "error";
  121. } else {
  122. $categories = getCategories();
  123. $label = normalizeCategoryLabel($_POST['category_label'] ?? "");
  124. if (!isValidCategoryLabel($label)) {
  125. $message = "Bitte geben Sie einen Kategorienamen mit maximal 80 Zeichen ein.";
  126. $messageType = "error";
  127. } else {
  128. $categoryId = generateCategoryIdFromLabel($label, $categories);
  129. $categories[] = [
  130. "id" => $categoryId,
  131. "label" => $label,
  132. ];
  133. if (saveCategories($categories)) {
  134. logAccess("Admin added category", [
  135. "category_id" => $categoryId,
  136. "label" => $label,
  137. ]);
  138. $message = "Kategorie wurde erfolgreich angelegt.";
  139. $messageType = "success";
  140. } else {
  141. $message = "Kategorie konnte nicht gespeichert werden.";
  142. $messageType = "error";
  143. }
  144. }
  145. }
  146. } elseif ($_SERVER['REQUEST_METHOD'] === "POST" && isset($_POST['update_category'])) {
  147. if (!validateCsrfToken($_POST['csrf_token'] ?? "")) {
  148. $message = "Ungültiges Token. Bitte versuchen Sie es erneut.";
  149. $messageType = "error";
  150. } else {
  151. $categories = getCategories();
  152. $categoryId = normalizeCategoryId($_POST['category_id'] ?? "");
  153. $label = normalizeCategoryLabel($_POST['category_label'] ?? "");
  154. $found = false;
  155. if (!isValidCategoryLabel($label)) {
  156. $message = "Bitte geben Sie einen Kategorienamen mit maximal 80 Zeichen ein.";
  157. $messageType = "error";
  158. } else {
  159. foreach ($categories as &$category) {
  160. if ($category["id"] === $categoryId) {
  161. $category["label"] = $label;
  162. $found = true;
  163. break;
  164. }
  165. }
  166. unset($category);
  167. if (!$found) {
  168. $message = "Kategorie nicht gefunden.";
  169. $messageType = "error";
  170. } elseif (saveCategories($categories)) {
  171. logAccess("Admin updated category", [
  172. "category_id" => $categoryId,
  173. "label" => $label,
  174. ]);
  175. $message = "Kategorie wurde erfolgreich aktualisiert.";
  176. $messageType = "success";
  177. } else {
  178. $message = "Kategorie konnte nicht gespeichert werden.";
  179. $messageType = "error";
  180. }
  181. }
  182. }
  183. } elseif ($_SERVER['REQUEST_METHOD'] === "POST" && isset($_POST['delete_category'])) {
  184. if (!validateCsrfToken($_POST['csrf_token'] ?? "")) {
  185. $message = "Ungültiges Token. Bitte versuchen Sie es erneut.";
  186. $messageType = "error";
  187. } else {
  188. $categoryId = normalizeCategoryId($_POST['category_id'] ?? "");
  189. $label = "";
  190. $found = false;
  191. if (isCategoryInUse($categoryId)) {
  192. $message =
  193. "Diese Kategorie wird noch von Produkten verwendet und kann nicht gelöscht werden.";
  194. $messageType = "error";
  195. } else {
  196. $categories = array_values(
  197. array_filter(getCategories(), function ($category) use (
  198. $categoryId,
  199. &$found,
  200. &$label,
  201. ) {
  202. if ($category["id"] === $categoryId) {
  203. $found = true;
  204. $label = $category["label"];
  205. return false;
  206. }
  207. return true;
  208. }),
  209. );
  210. if (!$found) {
  211. $message = "Kategorie nicht gefunden.";
  212. $messageType = "error";
  213. } elseif (saveCategories($categories)) {
  214. logAccess("Admin deleted category", [
  215. "category_id" => $categoryId,
  216. "label" => $label,
  217. ]);
  218. $message = "Kategorie wurde gelöscht.";
  219. $messageType = "success";
  220. } else {
  221. $message = "Kategorie konnte nicht gelöscht werden.";
  222. $messageType = "error";
  223. }
  224. }
  225. }
  226. } elseif ($_SERVER['REQUEST_METHOD'] === "POST" && isset($_POST['save_faq'])) {
  227. if (!validateCsrfToken($_POST['csrf_token'] ?? "")) {
  228. $message = "Ungültiges Token. Bitte versuchen Sie es erneut.";
  229. $messageType = "error";
  230. } else {
  231. $content = isset($_POST['content']) ? (string) $_POST['content'] : "";
  232. $faqSettings = array_merge(getSystemSettings(), [
  233. "startpage_intro_text" => isset($_POST['startpage_intro_text'])
  234. ? (string) $_POST['startpage_intro_text']
  235. : "",
  236. ]);
  237. if (saveFaqContent($content) && saveSystemSettings($faqSettings)) {
  238. logAccess("Admin updated FAQ content");
  239. $message = "FAQ-Inhalt und Startseitentext wurden gespeichert.";
  240. $messageType = "success";
  241. } else {
  242. $message = "FAQ-Inhalt und/oder Startseitentext konnten nicht gespeichert werden.";
  243. $messageType = "error";
  244. }
  245. }
  246. } elseif ($_SERVER['REQUEST_METHOD'] === "POST" && isset($_POST['add_organization'])) {
  247. if (!validateCsrfToken($_POST['csrf_token'] ?? "")) {
  248. $message = "Ungültiges Token. Bitte versuchen Sie es erneut.";
  249. $messageType = "error";
  250. } else {
  251. $organizations = getOrganizations(false);
  252. $label = normalizeOrganizationLabel($_POST['organization_label'] ?? "");
  253. $sortOrder = (int) ($_POST['sort_order'] ?? 0);
  254. $active = isset($_POST['active']);
  255. if (!isValidOrganizationLabel($label)) {
  256. $message = "Bitte einen Organisationsnamen mit maximal 120 Zeichen eingeben.";
  257. $messageType = "error";
  258. } else {
  259. $orgId = generateOrganizationIdFromLabel($label, $organizations);
  260. $organizations[] = [
  261. "id" => $orgId,
  262. "label" => $label,
  263. "sort_order" => $sortOrder,
  264. "active" => $active,
  265. ];
  266. if (saveOrganizations($organizations)) {
  267. logAccess("Admin added organization", [
  268. "org_id" => $orgId,
  269. "label" => $label,
  270. ]);
  271. $message = "Organisation wurde angelegt.";
  272. $messageType = "success";
  273. } else {
  274. $message = "Organisation konnte nicht gespeichert werden.";
  275. $messageType = "error";
  276. }
  277. }
  278. }
  279. } elseif ($_SERVER['REQUEST_METHOD'] === "POST" && isset($_POST['update_organization'])) {
  280. if (!validateCsrfToken($_POST['csrf_token'] ?? "")) {
  281. $message = "Ungültiges Token. Bitte versuchen Sie es erneut.";
  282. $messageType = "error";
  283. } else {
  284. $organizations = getOrganizations(false);
  285. $organizationId = normalizeOrganizationId($_POST['organization_id'] ?? "");
  286. $label = normalizeOrganizationLabel($_POST['organization_label'] ?? "");
  287. $sortOrder = (int) ($_POST['sort_order'] ?? 0);
  288. $active = isset($_POST['active']);
  289. $updated = false;
  290. if (!isValidOrganizationLabel($label)) {
  291. $message = "Bitte einen Organisationsnamen mit maximal 120 Zeichen eingeben.";
  292. $messageType = "error";
  293. } else {
  294. foreach ($organizations as &$organization) {
  295. if ($organization["id"] !== $organizationId) {
  296. continue;
  297. }
  298. $organization["label"] = $label;
  299. $organization["sort_order"] = $sortOrder;
  300. $organization["active"] = $active;
  301. $updated = true;
  302. break;
  303. }
  304. unset($organization);
  305. if (!$updated) {
  306. $message = "Organisation nicht gefunden.";
  307. $messageType = "error";
  308. } elseif (saveOrganizations($organizations)) {
  309. logAccess("Admin updated organization", [
  310. "org_id" => $organizationId,
  311. "label" => $label,
  312. ]);
  313. $message = "Organisation wurde aktualisiert.";
  314. $messageType = "success";
  315. } else {
  316. $message = "Organisation konnte nicht gespeichert werden.";
  317. $messageType = "error";
  318. }
  319. }
  320. }
  321. } elseif ($_SERVER['REQUEST_METHOD'] === "POST" && isset($_POST['delete_organization'])) {
  322. if (!validateCsrfToken($_POST['csrf_token'] ?? "")) {
  323. $message = "Ungültiges Token. Bitte versuchen Sie es erneut.";
  324. $messageType = "error";
  325. } else {
  326. $organizationId = normalizeOrganizationId($_POST['organization_id'] ?? "");
  327. $orgLabel = "";
  328. foreach (getOrganizations(false) as $organization) {
  329. if ($organization["id"] === $organizationId) {
  330. $orgLabel = $organization["label"];
  331. break;
  332. }
  333. }
  334. $organizations = array_values(
  335. array_filter(getOrganizations(false), function ($organization) use (
  336. $organizationId,
  337. ) {
  338. return $organization["id"] !== $organizationId;
  339. }),
  340. );
  341. if (saveOrganizations($organizations)) {
  342. logAccess("Admin deleted organization", [
  343. "org_id" => $organizationId,
  344. "label" => $orgLabel,
  345. ]);
  346. $message = "Organisation wurde gelöscht.";
  347. $messageType = "success";
  348. } else {
  349. $message = "Organisation konnte nicht gelöscht werden.";
  350. $messageType = "error";
  351. }
  352. }
  353. }
  354. $settings = getSystemSettings();
  355. $manageStatus = manageClientStatus();
  356. $localConfigContent = $isSuperAdmin ? settingsReadLocalConfig() : "";
  357. $categories = getCategories();
  358. $products = getProducts();
  359. $organizations = getOrganizations(false);
  360. $faqContent = getFaqContent();
  361. $startpageIntroText = (string) ($settings["startpage_intro_text"] ?? "");
  362. $editCategoryId = normalizeCategoryId($_GET['edit_category'] ?? "");
  363. $editingCategory = null;
  364. if ($editCategoryId !== "") {
  365. $editingCategory = getCategoryById($editCategoryId);
  366. if ($editingCategory === null && $message === "") {
  367. $message = "Ausgewählte Kategorie wurde nicht gefunden.";
  368. $messageType = "error";
  369. }
  370. }
  371. $editingOrganization = isset($_GET['edit_organization'])
  372. ? getOrganizationById($_GET['edit_organization'])
  373. : null;
  374. $bodyClass = "admin-page";
  375. include __DIR__ . "/../includes/header.php";
  376. ?>
  377. <div class="admin-header">
  378. <h2>Einstellungen</h2>
  379. <div>
  380. <a href="index.php" class="btn btn-secondary">Zurück zum Dashboard</a>
  381. </div>
  382. </div>
  383. <?php if ($message !== ""): ?>
  384. <div class="alert alert-<?php echo escape($messageType); ?>">
  385. <?php echo escape($message); ?>
  386. </div>
  387. <?php endif; ?>
  388. <div class="panel panel-lg" id="kategorien">
  389. <h3>Kategorien</h3>
  390. <?php if ($editingCategory !== null): ?>
  391. <h4>Kategorie bearbeiten</h4>
  392. <form method="POST" action="settings.php#kategorien">
  393. <?php echo csrfField(); ?>
  394. <input type="hidden" name="category_id" value="<?php echo escape(
  395. $editingCategory["id"],
  396. ); ?>">
  397. <div class="form-group">
  398. <label for="category_id_display">ID</label>
  399. <input type="text" id="category_id_display" value="<?php echo escape(
  400. $editingCategory["id"],
  401. ); ?>" readonly>
  402. <small>Die ID sollte gleichbleiben, damit Produktzuordnungen und Filter-URLs gültig bleiben.</small>
  403. </div>
  404. <div class="form-group">
  405. <label for="category_label_edit">Name *</label>
  406. <input type="text" id="category_label_edit" name="category_label" maxlength="80" required value="<?php echo escape(
  407. $editingCategory["label"],
  408. ); ?>">
  409. </div>
  410. <button type="submit" name="update_category" class="btn">Kategorie aktualisieren</button>
  411. <a href="settings.php#kategorien" class="btn btn-secondary">Abbrechen</a>
  412. </form>
  413. <?php else: ?>
  414. <h4>Neue Kategorie anlegen</h4>
  415. <form method="POST" action="settings.php#kategorien">
  416. <?php echo csrfField(); ?>
  417. <div class="form-group">
  418. <label for="category_label">Name *</label>
  419. <input type="text" id="category_label" name="category_label" maxlength="80" required placeholder="z.B. Accessoires">
  420. <small>Die technische ID wird einmalig automatisch aus dem Namen erzeugt.</small>
  421. </div>
  422. <button type="submit" name="add_category" class="btn">Kategorie anlegen</button>
  423. </form>
  424. <?php endif; ?>
  425. <div class="table-responsive mt-4">
  426. <table class="responsive-table">
  427. <thead>
  428. <tr>
  429. <th>Name</th>
  430. <th>ID</th>
  431. <th>Produkte</th>
  432. <th>Aktionen</th>
  433. </tr>
  434. </thead>
  435. <tbody>
  436. <?php foreach ($categories as $category): ?>
  437. <?php
  438. $productCount = 0;
  439. foreach ($products as $product) {
  440. if (productHasCategory($product, $category["id"])) {
  441. $productCount++;
  442. }
  443. }
  444. ?>
  445. <tr>
  446. <td data-label="Name"><?php echo escape(
  447. $category["label"],
  448. ); ?></td>
  449. <td data-label="ID"><code><?php echo escape(
  450. $category["id"],
  451. ); ?></code></td>
  452. <td data-label="Produkte"><?php echo $productCount; ?></td>
  453. <td data-label="Aktionen">
  454. <a href="settings.php?edit_category=<?php echo urlencode(
  455. $category["id"],
  456. ); ?>#kategorien" class="btn btn-small btn-secondary">Bearbeiten</a>
  457. <form method="POST" action="settings.php#kategorien" class="inline-form" onsubmit="return confirm('Kategorie wirklich löschen?');">
  458. <?php echo csrfField(); ?>
  459. <input type="hidden" name="category_id" value="<?php echo escape(
  460. $category["id"],
  461. ); ?>">
  462. <button type="submit" name="delete_category" class="btn btn-small">Löschen</button>
  463. </form>
  464. </td>
  465. </tr>
  466. <?php endforeach; ?>
  467. </tbody>
  468. </table>
  469. </div>
  470. </div>
  471. <div class="panel panel-lg mt-4" id="faq">
  472. <h3>FAQ</h3>
  473. <p class="mb-2">
  474. Unterstützte Markdown-Syntax: <code>#</code>, <code>##</code>, <code>###</code>, <code>**fett**</code>, <code>*kursiv*</code>, Listen mit <code>-</code> oder <code>1.</code>, Links mit <code>[Text](https://example.com)</code>
  475. </p>
  476. <form method="POST" action="settings.php#faq">
  477. <?php echo csrfField(); ?>
  478. <div class="form-group">
  479. <label for="content">FAQ-Inhalt (Markdown)</label>
  480. <textarea id="content" name="content" rows="18"><?php echo escape(
  481. $faqContent,
  482. ); ?></textarea>
  483. </div>
  484. <div class="form-group">
  485. <label for="startpage_intro_text">Startseitentext</label>
  486. <textarea id="startpage_intro_text" name="startpage_intro_text" rows="6"><?php echo escape(
  487. $startpageIntroText,
  488. ); ?></textarea>
  489. </div>
  490. <button type="submit" name="save_faq" class="btn">Speichern</button>
  491. </form>
  492. </div>
  493. <div class="panel panel-lg mt-4" id="organisationen">
  494. <h3>Organisationen</h3>
  495. <h4><?php echo $editingOrganization
  496. ? "Organisation bearbeiten"
  497. : "Neue Organisation"; ?></h4>
  498. <form method="POST" action="settings.php#organisationen">
  499. <?php echo csrfField(); ?>
  500. <?php if ($editingOrganization): ?>
  501. <input type="hidden" name="organization_id" value="<?php echo escape(
  502. $editingOrganization["id"],
  503. ); ?>">
  504. <?php endif; ?>
  505. <div class="form-group">
  506. <label for="organization_label">Name *</label>
  507. <input type="text" id="organization_label" name="organization_label" required maxlength="120" value="<?php echo escape(
  508. $editingOrganization["label"] ?? "",
  509. ); ?>">
  510. </div>
  511. <div class="form-group">
  512. <label for="sort_order">Sortierung</label>
  513. <input type="number" id="sort_order" name="sort_order" value="<?php echo escape(
  514. (string) ($editingOrganization["sort_order"] ?? 0),
  515. ); ?>">
  516. </div>
  517. <div class="form-group">
  518. <label>
  519. <input type="checkbox" name="active" value="1" <?php echo !isset(
  520. $editingOrganization["active"],
  521. ) || !empty($editingOrganization["active"])
  522. ? "checked"
  523. : ""; ?>>
  524. Organisation ist auswählbar
  525. </label>
  526. </div>
  527. <button type="submit" name="<?php echo $editingOrganization
  528. ? "update_organization"
  529. : "add_organization"; ?>" class="btn">
  530. <?php echo $editingOrganization
  531. ? "Speichern"
  532. : "Organisation anlegen"; ?>
  533. </button>
  534. <?php if ($editingOrganization): ?>
  535. <a href="settings.php#organisationen" class="btn btn-secondary">Abbrechen</a>
  536. <?php endif; ?>
  537. </form>
  538. <div class="table-responsive mt-4">
  539. <table class="responsive-table">
  540. <thead>
  541. <tr>
  542. <th>Name</th>
  543. <th>ID</th>
  544. <th>Sortierung</th>
  545. <th>Status</th>
  546. <th>Aktionen</th>
  547. </tr>
  548. </thead>
  549. <tbody>
  550. <?php foreach ($organizations as $organization): ?>
  551. <tr>
  552. <td data-label="Name"><?php echo escape(
  553. $organization["label"],
  554. ); ?></td>
  555. <td data-label="ID"><code><?php echo escape(
  556. $organization["id"],
  557. ); ?></code></td>
  558. <td data-label="Sortierung"><?php echo (int) $organization[
  559. "sort_order"
  560. ]; ?></td>
  561. <td data-label="Status">
  562. <span class="status <?php echo !empty(
  563. $organization["active"]
  564. )
  565. ? "status-open"
  566. : "status-cancelled"; ?>">
  567. <?php echo !empty($organization["active"])
  568. ? "Aktiv"
  569. : "Inaktiv"; ?>
  570. </span>
  571. </td>
  572. <td data-label="Aktionen">
  573. <a href="settings.php?edit_organization=<?php echo urlencode(
  574. $organization["id"],
  575. ); ?>#organisationen" class="btn btn-small">Bearbeiten</a>
  576. <form method="POST" action="settings.php#organisationen" class="inline-form" onsubmit="return confirm('Organisation wirklich löschen?');">
  577. <?php echo csrfField(); ?>
  578. <input type="hidden" name="organization_id" value="<?php echo escape(
  579. $organization["id"],
  580. ); ?>">
  581. <button type="submit" name="delete_organization" class="btn btn-secondary btn-small">Löschen</button>
  582. </form>
  583. </td>
  584. </tr>
  585. <?php endforeach; ?>
  586. </tbody>
  587. </table>
  588. </div>
  589. </div>
  590. <div class="panel panel-lg mt-4" id="allgemein">
  591. <h3>Allgemein</h3>
  592. <form method="POST" action="settings.php#allgemein">
  593. <?php echo csrfField(); ?>
  594. <div class="form-group">
  595. <label for="order_recipient_email">Empfängeradresse für interne Bestellungen *</label>
  596. <input type="email" id="order_recipient_email" name="order_recipient_email" required value="<?php echo escape(
  597. $settings["order_recipient_email"],
  598. ); ?>">
  599. </div>
  600. <div class="form-group">
  601. <label class="checkbox-label">
  602. <input type="checkbox" name="attach_order_pdf_to_admin_email" value="1" <?php echo !empty(
  603. $settings["attach_order_pdf_to_admin_email"]
  604. )
  605. ? "checked"
  606. : ""; ?>>
  607. PDF an interne Bestell-E-Mails anhängen
  608. </label>
  609. </div>
  610. <div class="form-group">
  611. <label for="nametag_product_name">Artikelname für Namensschilder *</label>
  612. <input type="text" id="nametag_product_name" name="nametag_product_name" required value="<?php echo escape(
  613. $settings["nametag_product_name"],
  614. ); ?>">
  615. <small>Muss exakt dem Produktnamen im Katalog entsprechen. Wird von der Namensschilder-Übersicht verwendet.</small>
  616. </div>
  617. <button type="submit" name="save_settings" class="btn">Speichern</button>
  618. </form>
  619. </div>
  620. <div class="panel panel-lg mt-4" id="update-backup">
  621. <h3>Update &amp; Backup</h3>
  622. <p>Installierte Version: <?php echo escape($manageStatus["version"] !== "" ? $manageStatus["version"] : "unbekannt"); ?></p>
  623. <?php if (!$manageStatus["configured"]): ?>
  624. <p>Der Manage-Client ist nicht konfiguriert. <code>manage-client/config.php</code> fehlt oder ist unvollständig.</p>
  625. <?php elseif ($manageStatus["update"] !== null && $manageStatus["update"]["available"]): ?>
  626. <p>Update verfügbar: <?php echo escape($manageStatus["update"]["latest"]); ?></p>
  627. <?php elseif ($manageStatus["update"] !== null): ?>
  628. <p>Kein Update verfügbar.</p>
  629. <?php else: ?>
  630. <p>Update-Status konnte nicht geladen werden<?php echo $manageStatus["update_error"] !== null
  631. ? ": " . escape($manageStatus["update_error"])
  632. : "."; ?></p>
  633. <?php endif; ?>
  634. <p>
  635. Letztes Backup:
  636. <?php echo $manageStatus["last_backup_at"] !== null
  637. ? escape(settingsFormatBackupDate($manageStatus["last_backup_at"]))
  638. : "noch keines"; ?>
  639. (<?php echo count($manageStatus["backups"]); ?> lokal)
  640. </p>
  641. <?php if ($manageStatus["pending_migrations"] !== []): ?>
  642. <p><?php echo count($manageStatus["pending_migrations"]); ?> offene Migration(en).</p>
  643. <?php endif; ?>
  644. <p><a href="manage.php" class="btn btn-secondary">Update &amp; Backup öffnen</a></p>
  645. </div>
  646. <?php if ($isSuperAdmin): ?>
  647. <div class="panel panel-lg mt-4" id="konfiguration">
  648. <h3>Lokale Konfiguration</h3>
  649. <p>Bearbeitet <code>config.php</code> direkt auf diesem Server. Diese Datei wird beim Update nicht überschrieben.</p>
  650. <div class="alert alert-warning">Vor dem Speichern wird die PHP-Syntax geprüft und die vorherige Version automatisch gesichert. Fehlerhafte Änderungen können die gesamte Anwendung lahmlegen.</div>
  651. <form method="POST" action="settings.php#konfiguration">
  652. <?php echo csrfField(); ?>
  653. <div class="form-group">
  654. <label for="local_config_content">Inhalt von config.php</label>
  655. <textarea id="local_config_content" name="local_config_content" rows="24" class="config-editor" spellcheck="false"><?php echo escape(
  656. $localConfigContent,
  657. ); ?></textarea>
  658. </div>
  659. <button type="submit" name="save_local_config" class="btn">Speichern</button>
  660. </form>
  661. </div>
  662. <?php endif; ?>
  663. <?php include __DIR__ . "/../includes/footer.php"; ?>