index.php 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * Documentation viewer (Markdown -> HTML via the vendored marked.js).
  5. * Self-contained: no build step and no external requests, so it also works
  6. * from an unpacked ZIP.
  7. */
  8. $docsDir = __DIR__;
  9. $docMap = [];
  10. foreach (glob($docsDir . "/*.md") ?: [] as $path) {
  11. $base = basename($path, ".md");
  12. if (preg_match('/^[A-Z0-9_]+$/', $base) !== 1) {
  13. continue;
  14. }
  15. $docMap[$base] = $path;
  16. }
  17. ksort($docMap);
  18. function docTitle(string $key): string
  19. {
  20. // Drop a leading sort prefix like "01_" for display only.
  21. $key = preg_replace('/^\d+_/', "", $key) ?? $key;
  22. return ucwords(strtolower(str_replace("_", " ", $key)));
  23. }
  24. function docEscape(string $value): string
  25. {
  26. return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, "UTF-8");
  27. }
  28. $docsTitle = basename(dirname($docsDir)) === "client-package"
  29. ? "Manage Client"
  30. : "Manage Server";
  31. $requested = isset($_GET["doc"]) ? (string) $_GET["doc"] : "";
  32. $activeDoc = null;
  33. $markdown = null;
  34. $pageTitle = "Dokumentation";
  35. if ($requested !== "") {
  36. if (!isset($docMap[$requested])) {
  37. http_response_code(404);
  38. $pageTitle = "Nicht gefunden";
  39. } else {
  40. $activeDoc = $requested;
  41. $markdown = file_get_contents($docMap[$requested]);
  42. if ($markdown === false) {
  43. http_response_code(500);
  44. $pageTitle = "Fehler";
  45. $markdown = null;
  46. } else {
  47. $pageTitle = docTitle($activeDoc);
  48. }
  49. }
  50. }
  51. // Works both when docs/ is a subdirectory and when it is served as the web
  52. // root (php -S localhost:8080 -t docs), where dirname() yields "/".
  53. $baseHref = rtrim(str_replace("\\", "/", dirname($_SERVER["SCRIPT_NAME"] ?? "")), "/") . "/";
  54. if ($baseHref === "") {
  55. $baseHref = "/";
  56. }
  57. ?>
  58. <!DOCTYPE html>
  59. <html lang="de">
  60. <head>
  61. <meta charset="UTF-8">
  62. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  63. <title><?php echo docEscape($pageTitle); ?> – <?php echo docEscape($docsTitle); ?></title>
  64. <link rel="stylesheet" href="<?php echo docEscape($baseHref); ?>assets/docs.css">
  65. </head>
  66. <body>
  67. <header class="docs-header">
  68. <div class="docs-header-inner">
  69. <a class="docs-brand" href="<?php echo docEscape($baseHref); ?>index.php"><?php echo docEscape($docsTitle); ?></a>
  70. </div>
  71. </header>
  72. <div class="docs-layout">
  73. <nav class="docs-nav" aria-label="Dokumentation">
  74. <p class="docs-nav-title">Inhalt</p>
  75. <ul>
  76. <?php foreach ($docMap as $key => $_path): ?>
  77. <li>
  78. <a href="<?php echo docEscape($baseHref); ?>index.php?doc=<?php echo docEscape($key); ?>"
  79. <?php echo $key === $activeDoc ? 'aria-current="page"' : ""; ?>>
  80. <?php echo docEscape(docTitle($key)); ?>
  81. </a>
  82. </li>
  83. <?php endforeach; ?>
  84. </ul>
  85. </nav>
  86. <main class="docs-main">
  87. <?php if ($requested === ""): ?>
  88. <h1>Dokumentation</h1>
  89. <p>Alle Dokumente in empfohlener Lesereihenfolge.</p>
  90. <ul class="docs-index-list">
  91. <?php foreach ($docMap as $key => $_path): ?>
  92. <li>
  93. <a href="<?php echo docEscape($baseHref); ?>index.php?doc=<?php echo docEscape($key); ?>">
  94. <?php echo docEscape(docTitle($key)); ?>
  95. </a>
  96. </li>
  97. <?php endforeach; ?>
  98. </ul>
  99. <?php elseif ($markdown === null): ?>
  100. <h1><?php echo docEscape($pageTitle); ?></h1>
  101. <p>Das angeforderte Dokument ist nicht verfügbar.</p>
  102. <p><a href="<?php echo docEscape($baseHref); ?>index.php">Zur Übersicht</a></p>
  103. <?php else: ?>
  104. <article id="doc-content" class="markdown-body"></article>
  105. <script type="application/json" id="doc-source"><?php
  106. echo json_encode(
  107. $markdown,
  108. JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_UNESCAPED_UNICODE,
  109. );
  110. ?></script>
  111. <?php endif; ?>
  112. </main>
  113. </div>
  114. <?php if ($markdown !== null): ?>
  115. <script src="<?php echo docEscape($baseHref); ?>assets/marked.min.js"></script>
  116. <script>
  117. (function () {
  118. var source = document.getElementById('doc-source');
  119. var target = document.getElementById('doc-content');
  120. if (!source || !target || typeof marked === 'undefined') {
  121. return;
  122. }
  123. var text = JSON.parse(source.textContent || '""');
  124. target.innerHTML = marked.parse(text, { gfm: true, breaks: false });
  125. target.querySelectorAll('a[href]').forEach(function (link) {
  126. var href = link.getAttribute('href');
  127. if (!href || /^[a-z]+:/i.test(href) || href.charAt(0) === '#') {
  128. return;
  129. }
  130. var name = href.split('/').pop().replace(/\.md$/i, '');
  131. if (/^[A-Z0-9_]+$/i.test(name)) {
  132. link.setAttribute('href', 'index.php?doc=' + encodeURIComponent(name.toUpperCase()));
  133. }
  134. });
  135. })();
  136. </script>
  137. <?php endif; ?>
  138. </body>
  139. </html>