diff --git a/.htaccess b/.htaccess
index 6e783e20..d8888a8a 100644
--- a/.htaccess
+++ b/.htaccess
@@ -16,6 +16,7 @@ RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-d
RewriteRule ^/?wiki(/.*)?$ %{DOCUMENT_ROOT}/wiki/index.php [L]
+RewriteRule ^wiki-([^/]*)\.php$ /wiki.php?page=$1 [QSA,L]
#remove php file extension-e.g. https://example.com/file.php will become https://example.com/file
RewriteCond %{REQUEST_FILENAME} !-d
diff --git a/header.php b/header.php
index 46c01caf..088fe643 100644
--- a/header.php
+++ b/header.php
@@ -84,17 +84,17 @@
@@ -102,14 +102,14 @@
+
+ 128) ? 'light' : 'dark';
+}
+
+function updateStylesForLightBackground($xpath) {
+ foreach ($xpath->query('//*[@style]') as $node) {
+ $style = $node->getAttribute('style');
+ if (preg_match('/background\s*:\s*([^;]+);?/i', $style, $matches)) {
+ $bgColor = trim($matches[1]);
+ if (preg_match('/^#([a-f0-9]{3}|[a-f0-9]{6})$/i', $bgColor) && isLightOrDark($bgColor) == 'light') {
+ if (!str_contains($style, 'color')) {
+ $style .= ' color: #202122;';
+ }
+ foreach ($xpath->query('.//a', $node) as $link) {
+ $linkStyle = $link->getAttribute('style') ?: '';
+ if (!str_contains($linkStyle, 'color')) {
+ $link->setAttribute('style', $linkStyle . ' color: #202122;');
+ }
+ }
+ }
+ }
+
+ $node->setAttribute('style', $style);
+ }
+}
+
+function createDomAndXpath($html) {
+ $dom = new DOMDocument();
+ libxml_use_internal_errors(true);
+ $dom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'), LIBXML_HTML_NODEFDTD | LIBXML_HTML_NOIMPLIED);
+ libxml_clear_errors();
+ return array($dom, new DOMXPath($dom));
+}
+
+function removeNodesByXPath($xpath, $xpathQuery) {
+ $nodes = $xpath->query($xpathQuery);
+ foreach ($nodes as $node) {
+ $node->parentNode->removeChild($node);
+ }
+}
+
+function removeDivsByClass($xpath, $classes) {
+ $xpathQuery = "//div[contains(@class, '" . implode("') or contains(@class, '", $classes) . "')]";
+ removeNodesByXPath($xpath, $xpathQuery);
+}
+
+function removeDivsById($xpath, $ids) {
+ $xpathQuery = "//div[@id='" . implode("' or @id='", $ids) . "']";
+ removeNodesByXPath($xpath, $xpathQuery);
+}
+
+function convertRelativeLinksToWikiFormat($xpath, $lang = 'en') {
+ foreach ($xpath->query("//a[@href]") as $node) {
+ $href = $node->getAttribute('href');
+ if (strpos($href, '#') === 0) {
+ continue;
+ }
+ if (!preg_match('/^(http|https):\/\//', $href)) {
+ $hashPart = '';
+ if (strpos($href, '#') !== false) {
+ list($href, $hashPart) = explode('#', $href, 2);
+ $hashPart = '#' . $hashPart;
+ }
+ $href = trim($href, "/");
+ if (preg_match('/\/([a-z]{2})$/i', $href, $matches)) {
+ $langCode = $matches[1];
+ $newHref = "/wiki-" . trim($href, '/' . $langCode) . ".php?lang=" . $langCode;
+ } else {
+ $newHref = "/wiki-" . trim($href, '/') . ".php";
+ if ($lang != "en") {
+ $newHref .= "?lang=" . $lang;
+ }
+ }
+ $newHref .= $hashPart;
+ $node->setAttribute('href', $newHref);
+ }
+ }
+}
+
+function removeFileLinks($xpath) {
+ foreach ($xpath->query("//a[contains(@href, 'File:')]") as $node) {
+ $parent = $node->parentNode;
+ while ($node->firstChild) {
+ $parent->insertBefore($node->firstChild, $node);
+ }
+ $parent->removeChild($node);
+ }
+}
+
+function removeInlineStylesByClasses($xpath, $classNames) {
+ foreach ($classNames as $className) {
+ foreach ($xpath->query("//*[contains(@class, '$className')]") as $node) {
+ $node->removeAttribute('style');
+ }
+ }
+}
+
+function addPrefixToImageSrc($xpath, $baseImageUrl) {
+ foreach ($xpath->query("//img[@src]") as $node) {
+ $src = $node->getAttribute('src');
+ if (!preg_match('/^(http|https):\/\//', $src)) {
+ $newSrc = rtrim($baseImageUrl, '/') . '/' . ltrim($src, '/');
+ $node->setAttribute('src', $newSrc);
+ }
+ }
+}
+
+function wrapTablesInDiv($xpath, $divClasses = array(), $dom) {
+ foreach ($xpath->query("//table") as $table) {
+ $wrapperDiv = $dom->createElement('div');
+ if (!empty($divClasses)) {
+ $wrapperDiv->setAttribute('class', implode(' ', $divClasses));
+ }
+ $table->parentNode->replaceChild($wrapperDiv, $table);
+ $wrapperDiv->appendChild($table);
+ }
+}
+
+function fetchWikiContent($page, $lang = 'en') {
+ if (isset($lang)) {
+ $page .= "/" . $lang;
+ }
+
+ $url = "https://wiki.freecad.org/api.php?action=parse&page=" . $page . "&format=json&disableeditsection=true&disabletoc=true";
+ $ch = curl_init();
+ curl_setopt($ch, CURLOPT_URL, $url);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ $response = curl_exec($ch);
+ curl_close($ch);
+
+ return json_decode($response, true);
+}
+
+function convertWidthToMaxWidth($xpath) {
+ foreach ($xpath->query('//*[@style]') as $node) {
+ $style = $node->getAttribute('style');
+
+ $updatedStyle = preg_replace_callback('/\bwidth\s*:\s*[^;]+;?/i', function ($matches) {
+ return str_replace('width', 'max-width', $matches[0]);
+ }, $style);
+
+ $node->setAttribute('style', $updatedStyle);
+ }
+}
+
+function removeWidthFromFloatRightDivs($xpath) {
+ $query = "//div[contains(@style, 'float: right')]";
+ $nodes = $xpath->query($query);
+ foreach ($nodes as $node) {
+ $style = $node->getAttribute('style');
+ $updatedStyle = preg_replace('/\bwidth\s*:\s*[^;]+;?/', '', $style);
+ if (trim($updatedStyle)) {
+ $node->setAttribute('style', $updatedStyle);
+ } else {
+ $node->removeAttribute('style');
+ }
+ }
+}
+
+function removeSrcsetAttributes($xpath) {
+ foreach ($xpath->query("//img[@srcset]") as $node) {
+ $node->removeAttribute('srcset');
+ }
+}
+
+function fetchWikiContentWithCache($page, $lang = 'en', $cacheDuration = 86400) { //60 sec x 60 min x 24 hour = 86400 = 1 day
+ $cacheDir = __DIR__ . '/cache/';
+ if (!is_dir($cacheDir)) {
+ mkdir($cacheDir, 0777, true);
+ }
+ $cacheFile = $cacheDir . md5($page . $lang) . '.json.cache';
+
+ if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < $cacheDuration)) {
+ $cachedData = file_get_contents($cacheFile);
+ return json_decode($cachedData, true);
+ }
+
+ $data = fetchWikiContent($page, $lang);
+
+ file_put_contents($cacheFile, json_encode($data));
+
+ return $data;
+}
+
+
+function processWikiContent($page, $lang = 'en', $cacheDuration = 86400) { //60 sec x 60 min x 24 hour = 86400 = 1 day
+ $data = fetchWikiContentWithCache($page, $lang, $cacheDuration);
+ $htmlContent = $data['parse']['text']['*'];
+
+ list($dom, $xpath) = createDomAndXpath($htmlContent);
+
+ addPrefixToImageSrc($xpath, "https://wiki.freecad.org");
+ removeDivsByClass($xpath, array('NavFrame', 'mw-pt-languages'));
+ removeDivsById($xpath, array('itsstillfree', 'itsfree'));
+ removeInlineStylesByClasses($xpath, array('docnav'));
+ removeFileLinks($xpath);
+ convertRelativeLinksToWikiFormat($xpath, $lang);
+ wrapTablesInDiv($xpath, array('responsive-wiki-table'),$dom);
+ removeWidthFromFloatRightDivs($xpath);
+ convertWidthToMaxWidth($xpath);
+ removeSrcsetAttributes($xpath);
+ updateStylesForLightBackground($xpath);
+
+
+ return $dom->saveHTML();
+}
+
+echo processWikiContent($page, $lang, $cacheDuration);
+
+?>
+
+
+
+ ">
+
+
+