<?php
	$locale = $lang . '_' . $country; // locale-String zur Formatierung von internationalen Datumsangaben
	setlocale(LC_ALL, "$locale.utf8"); // locale-String für die Sortierung von Listen setzen

	date_default_timezone_set($timezone);

	// Sprachdatei inkludieren
	if ($lang !== 'en' && file_exists("lib/lang-$lang.php"))
		include_once "lib/lang-$lang.php";
	else
		include_once "lib/lang-en.php";

	// Basis-URL ermitteln (ohne / am Ende!)
	// u.a. für Metadaten und Sitemap
	$baseUrl = GetBaseUrl();

	// Titel und Beschreibung für den HTML-Header
	$htmlTitle = get_title() . ' – ' . $websiteName;
	$htmlDescription = get_description();

/*
	// Titel und Beschreibung der Index-Seite
	if (preg_match('/^(\/|\/index|\/index\.php)($|\?|#)/', $_SERVER['REQUEST_URI']))
	{
		$htmlTitle = $websiteName;
		$htmlDescription = preg_replace('/[ ]+/', ' ', str_replace('|', '', $headerSubtitle));
	}
*/

	// Getter für Config-Variablen (zur Verwendung in Widgets etc.)
	function get_lang()					{ global $lang;			return $lang; }

	// Array mit CSS-Variablen einlesen
	$cssVars = [ '--ContentBgImage' => '', '--WebsiteIcon' => '' ];
	ReadCssVariables();

	// Getter für die Header-Variablen
	// -> Wenn die Variable fehlt oder leer ist, wird ein Defaultwert zurückgegeben.
	function get_type()					{ global $type;			return def_type($type); }
	function get_published()			{ global $published;	return def_published($published); }
	function get_title()				{ global $title;		return def_title($title); }
	function get_description()			{ global $description;	return def_description($description); }
	function get_image()				{ global $image;		return def_image($image); }
	function get_cats()					{ global $cats;			return def_cats($cats); }
	function get_tags()					{ global $tags;			return def_tags($tags); }
	function get_date()					{ global $date;			return def_date($date); }
	function get_listed()				{ global $listed;		return def_listed($listed, get_type()); }
	function get_pinned()				{ global $pinned;		return def_pinned($pinned); }
	function get_front()				{ global $front;		return def_front($front); }

	// Defaultwerte für die Header-Variablen
	// - Wenn der angegebene Wert fehlt oder leer ist, wird ein Defaultwert zurückgegeben.
	function def_type($value)			{ 						return !empty($value) ? $value : 'post'; }
	function def_published($value)		{ 						return !empty($value) ? $value : false; }
	function def_title($value)			{ 						return !empty($value) ? $value : GetString('InsertPageTitle'); }
	function def_description($value)	{ 						return !empty($value) ? $value : GetString('InsertPageDescription'); }
	function def_image($value)			{ global $cssVars;		return !empty($value) ? $value : $cssVars['--ContentBgImage']; }
	function def_cats($value)			{ 						return !empty($value) ? $value : ''; }
	function def_tags($value)			{ 						return !empty($value) ? $value : ''; }
	function def_date($value)			{ 						return !empty($value) ? $value : date('Y-m-d', getlastmod()); }
	function def_listed($value, $type)	{ 						return  isset($value) ? $value : ($type === 'post' ? true : false); }
	function def_pinned($value)			{ 						return  isset($value) ? $value : false; }
	function def_front($value)			{ 						return  isset($value) ? $value : false; }

	$indexFile = ''; // Beitragsübersicht

	// Dateinamen und Header-Variablen der PHP-Dateien (Artikel) im aktuellen Verzeichnis ermitteln 
	$fileTypes = array();
	$filePublished = array();
	$fileTitles = array();
	$fileDescriptions = array();
	$fileImages = array();
	$fileCats = array();
	$catSums = array();
	$fileTags = array();
	$tagSums = array();
	$fileDates = array();
	$fileListed = array();
	$filePinned = array();
	$fileFront = array();

	function GetFileData()
	{
		global $baseUrl;
		global $indexFile;
		global $fileTypes;
		global $filePublished;
		global $fileTitles;
		global $fileDescriptions;
		global $fileImages;
		global $fileCats;
		global $catSums;
		global $fileTags;
		global $tagSums;
		global $fileDates;
		global $fileListed;
		global $filePinned;
		global $fileFront;

		// Header-Variablen der PHP-Dateien (Artikel) einlesen
		$files = scandir('.');
		sort($files, SORT_LOCALE_STRING);

		// Zur Performance-Optimierung die Daten serverseitig cachen und nur neu einlesen, wenn geändert.

		// a) Dateiliste mit Dateinamen und Modifiziert-Zeitstempel erstellen:
		$FileList = '';
		$mtimes = [];
		foreach ($files as $file)
		{
			if (!IsValidFile($file)) continue;

			$mtime = filemtime($file);
			$FileList .= "$file|$mtime\n";
			$mtimes[$file] = $mtime;
			$fileDates[$file] = def_date(null); // Defaultwert für Datum setzen. Falls in der Datei ein Datum angegeben ist, wird dieser später damit überschrieben.
		}

		// b) Dateiliste vom Server laden, mit lokaler Dateiliste vergleichen und zurückschreiben:
		$FileListServer = file_exists('.filelist.txt') ? file_get_contents('.filelist.txt') : false;
		$filesChanged = !$FileListServer || ($FileListServer != $FileList);
		if ($filesChanged) file_put_contents('.filelist.txt', $FileList, LOCK_EX);

		// c) Wenn Dateien unverändert, Dateidaten vom Server laden:
		$FileDataLines = !$filesChanged && file_exists('.filedata.txt') ? file('.filedata.txt', FILE_IGNORE_NEW_LINES) : false;

		// d) Wenn Dateien verändert oder Fehler beim Laden der Dateidaten vom Server, dann Dateidaten neu ermitteln:
		if (!$FileDataLines)
		{
			$mediafiles = scandir('media/');
			sort($mediafiles, SORT_LOCALE_STRING);

			$FileDataLines = array();
			$MediaUsageLines = array();

			foreach ($files as $file)
			{
				if (is_dir($file)) continue;
				$lines = file($file, FILE_IGNORE_NEW_LINES);
				// Artikel
				if (IsValidFile($file))
				{
					$insidePhp = false;
					foreach ($lines as $line)
					{
						$line = trim($line);
						if ($insidePhp)
						{
							if ($line === '?>') break;
							if (preg_match('/^[\s]*\$[\w]+/', $line))
							{
								array_push($FileDataLines, "$file|$line");
							}
						}
						else if ($line === '<?php') $insidePhp = true;
					}
				}
				// Medienverwendung
				if (preg_match('/\.php$/', $file))
				{
					$line = implode(PHP_EOL, $lines);
					foreach ($mediafiles as $mediafile)
					{
						if (preg_match('/\b(media\/' . $mediafile . ')\b/', $line))
						{
							array_push($MediaUsageLines, "$file|$mediafile");
						}
					}
				}
			}
			// Ermittelte Dateidaten auf dem Server speichern:
			file_put_contents('.filedata.txt', implode(PHP_EOL, $FileDataLines), LOCK_EX);
			file_put_contents('.media-usage.txt', implode(PHP_EOL, $MediaUsageLines), LOCK_EX);
		}

		// Eingelesene Header-Variablen parsen
		// $FileDataLines enthält Zeilen wie z.B.:
		// template.php|$cats = 'Kategorie 1, Kategorie 2'; // kommaseparierte Liste von Beitragskategorien
		foreach ($FileDataLines as $line)
		{
			$parts = explode('|', $line);
			$file = trim($parts[0]); // template.php
			$name = ''; $value = '';
			ParseHeaderVariables($parts[1], $name, $value);

			if (empty($value))
				continue;

			if ($name === 'type')
				$fileTypes[$file] = $value;
			else if ($name === 'published')
				$filePublished[$file] = ($value === 'true');
			else if ($name === 'title')
				$fileTitles[$file] = $value;
			else if ($name === 'description')
				$fileDescriptions[$file] = $value;
			else if ($name === 'image')
				$fileImages[$file] = $value;
			else if ($name === 'cats')
			{
				$fileCats[$file] = $value;
				if ($filePublished[$file] ?? null)
				{
					$values = explode(',', $value);
					foreach ($values as $cat)
					{
						$cat = trim($cat);
						if (array_key_exists($cat, $catSums))
							$catSums[$cat]++;
						else
							$catSums[$cat] = 1;
					}
				}
			}
			else if ($name === 'tags')
			{
				$fileTags[$file] = $value;
				if ($filePublished[$file] ?? null)
				{
					$values = explode(',', $value);
					foreach ($values as $tag)
					{
						$tag = trim($tag);
						if (array_key_exists($tag, $tagSums))
							$tagSums[$tag]++;
						else
							$tagSums[$tag] = 1;
					}
				}
			}
			else if ($name === 'date')
				$fileDates[$file] = $value;
			else if ($name === 'listed')
				$fileListed[$file] = ($value === 'true');
			else if ($name === 'pinned')
				$filePinned[$file] = ($value === 'true');
			else if ($name === 'front')
				$fileFront[$file] = ($value === 'true');

			// Beitragsübersicht?
			if (empty($indexFile) && ($filePublished[$file] ?? null) && ($fileTypes[$file] ?? null) === 'index')
				$indexFile = $file;
		}
		arsort($fileDates, SORT_LOCALE_STRING);
		ksort($catSums, SORT_LOCALE_STRING);
		ksort($tagSums, SORT_LOCALE_STRING);

		$indexFile = MakeLink($indexFile);

		// If files were changed, create sitemap.xml and feed.rss
		if ($filesChanged)
		{
			// sitemap.xml
			$baseUrl = preg_replace('/\/lib$/', '', $baseUrl);
			$xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n";
			foreach ($fileDates as $file => $file_date)
			{
				$file_published = $filePublished[$file] ?? null;
				if ($file_published)
				{
					$loc = "$baseUrl/" . MakeLink($file);
					$lastmod = date('Y-m-d\TH:i:sP', $mtimes[$file]);
					$xml .= "\t<url>\n\t\t<loc>$loc</loc>\n\t\t<lastmod>$lastmod</lastmod>\n\t</url>\n";
				}
			}
			$xml .= "</urlset>";
			file_put_contents('sitemap.xml', $xml, LOCK_EX);

			// feed.rss
			global $websiteName;
			global $headerSubtitle;
			global $lang;
			global $cssVars;
			$rssLastBuildDate = (new DateTimeImmutable())->format(DateTimeInterface::RFC2822);
			$rssChannelPubDate = '';
			$rssItems = '';
			foreach ($fileDates as $file => $file_date)
			{
				$file_type = def_type($fileTypes[$file] ?? null);
				$file_published = def_published($filePublished[$file] ?? null);
				$file_title = def_title($fileTitles[$file] ?? null);
				$file_description = $fileDescriptions[$file] ?? '';
				$file_image = def_image($fileImages[$file] ?? null);

				if ($file_type === 'post' && $file_published)
				{
					$rssItemPubDate = (new DateTimeImmutable($file_date))->format(DateTimeInterface::RFC2822);
					if (empty($rssChannelPubDate))
						$rssChannelPubDate = $rssItemPubDate;
					$rssItemLink = "$baseUrl/" . MakeLink($file);
					$rssItemImageMimeType = mime_content_type($file_image);
					$rssItemImageLength = filesize($file_image);

					$rssItems .= "		<item>\n";
					$rssItems .= "			<title>$file_title</title>\n";
#					$rssItems .= "			<description>$file_description</description>\n";
					$rssItems .= "			<description><![CDATA[<p><a href=\"$rssItemLink\" target=\"_blank\"><img src=\"$baseUrl/$file_image\"></a></p><p>$file_description [<a href=\"$rssItemLink\" target=\"_blank\">mehr</a>]</p> ]]></description>\n";
					$rssItems .= "			<link>$rssItemLink</link>\n";
					$rssItems .= "			<guid>$rssItemLink</guid>\n";
					$rssItems .= "			<pubDate>" . $rssItemPubDate . "</pubDate>\n";
					$rssItems .= "			<media:content url=\"$baseUrl/$file_image\" type=\"$rssItemImageMimeType\" medium=\"image\" />\n";
					$rssItems .= "			<media:thumbnail url=\"$baseUrl/$file_image\" />\n";
					$rssItems .= "			<enclosure url=\"$baseUrl/$file_image\" length=\"$rssItemImageLength\" type=\"$rssItemImageMimeType\" />\n";
					$rssItems .= "			<content:encoded><![CDATA[<p><a href=\"$rssItemLink\"><img src=\"$baseUrl/$file_image\"></a></p><p>$file_description [<a href=\"$rssItemLink\">mehr</a>]</p> ]]></content:encoded>\n";
					$rssItems .= "		</item>\n";
				}
			}

			$xml = '<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
	<channel>
		<title>' . $websiteName . '</title>
		<link>' . $baseUrl . '</link>
		<description>' . preg_replace('/[ ]+/', ' ', str_replace('|', '', $headerSubtitle)) . '</description>
		<language>' . $lang . '</language>
		<lastBuildDate>' . $rssLastBuildDate . '</lastBuildDate>
		<pubDate>' . $rssChannelPubDate . '</pubDate>
		<image>
			<url>' . "$baseUrl/" . $cssVars['--WebsiteIcon'] . '</url>
			<title>' . $websiteName . '</title>
			<link>' . $baseUrl . '</link>
		</image>
' . $rssItems . '	</channel>
</rss>';
			file_put_contents('feed.rss', $xml, LOCK_EX);
		}
	}

	// Ermittelt, ob der angegebene Dateiname gültig ist für die Beitragsliste
	function IsValidFile($file)
	{
		if (is_dir($file)) return false; # Keine Verzeichnisse!
		if (!preg_match('/\.php$/', $file)) return false; # Nur PHP-Dateien!
		if (preg_match('/^[!#-\._~]/', $file)) return false; # Keine "versteckten" Dateien, die einem der Zeichen !#-._~ beginnen!
		return true;
	}

	function ParseHeaderVariables($line, &$name, &$value)
	{
		$assignment = trim($line); // 	$cats = 'Kategorie 1, Kategorie 2'; // kommaseparierte Liste von Beitragskategorien; optional
		$pos = strrpos($assignment, '//'); // 37
		if ($pos !== false) $assignment = substr($assignment, 0, $pos); // 	$cats = 'Kategorie 1, Kategorie 2'; 
		$pos = strrpos($assignment, ';'); // 35
		if ($pos !== false) $assignment = substr($assignment, 0, $pos); // 	$cats = 'Kategorie 1, Kategorie 2'
		$name = trim($assignment); // $cats = 'Kategorie 1, Kategorie 2'
		$value = '';
		$pos = strpos($name, '='); // 6
		if ($pos !== false)
		{
			$value = substr($name, $pos + 1); //  'Kategorie 1, Kategorie 2'
			$name = substr($name, 0, $pos); // $cats 
		}
		$name = trim(trim($name), '$'); // cats
		$value = trim(trim($value), '\''); // Kategorie 1, Kategorie 2

		// Sonderbehandung bestimmter Header-Variablen
		if ($name === 'title' || $name === 'description')
			$value = DecodeHtml($value);
	}

	function EncodeHtml($string, $eol = true)
	{
		$string = str_replace('&', '&amp;', $string);

		$string = str_replace('<', '&lt;', $string);
		$string = str_replace('>', '&gt;', $string);
		$string = str_replace('"', '&quot;', $string);

		if ($eol)
			$string = str_replace("\n", '<br>', $string);

		return $string;
	}

	function DecodeHtml($string)
	{
		$string = str_replace('<br>', "\n", $string);

		$string = str_replace('&lt;', '<', $string);
		$string = str_replace('&gt;', '>', $string);
		$string = str_replace('&quot;', '"', $string);

		$string = str_replace('&amp;', '&', $string);

		return $string;
	}

	// Bearbeitet einen HTML-String so, dass er bei | zuerst umgebrochen wird
	// und die Teile zwischen den | zuletzt umgebrochen werden.
	function NoWrap($string)
	{
		$result = $string;
		$arr = explode('|', $string);
		if (count($arr) > 0)
		{
			$result = '';
			foreach ($arr as $part)
			{
				$part = trim($part);
				if ($result !== '') $result .= ' ';
				$result .= '<span style="display: inline-block;">' . $part . '</span>';
			}
		}
		return $result;
	}

	// Kategorien und Schlagwörter unterhalb des Beitrags ausgeben
	function PrintCatsOrTags($csv, $class)
	{
		global $indexFile;

		if (empty(trim($csv))) return;
		$arr = explode(',', $csv);
		if (count($arr) > 0)
		{
			$items = array();
			foreach ($arr as $item) $items[trim($item)] = 1;
			ksort($items, SORT_LOCALE_STRING);
			echo "<p class=\"$class\">\n";
			foreach ($items as $item => $value)
			{
				echo "<a href=\"$indexFile\?$class=$item\">$item</a>";
			}
			echo "</p>\n";
		}
	}

	function FilterPassed($file, $filterCat, $filterTag, $filterMonth)
	{
		global $fileCats;
		global $fileTags;
		global $fileDates;

		if (!empty($filterCat))
		{
			if (array_key_exists($file, $fileCats))
			{
				$Cats = explode(',', $fileCats[$file]);
				foreach ($Cats as $cat) if ($filterCat === trim($cat)) return true;
			}
			return false;
		}

		if (!empty($filterTag))
		{
			if (array_key_exists($file, $fileTags))
			{
				$Tags = explode(',', $fileTags[$file]);
				foreach ($Tags as $tag) if ($filterTag === trim($tag)) return true;
			}
			return false;
		}

		if (!empty($filterMonth))
		{
			if (array_key_exists($file, $fileDates))
			{
				$file_date = $fileDates[$file];
				if (substr($file_date, 0, 7) === $filterMonth) return true;
			}
			return false;
		}

		return true;
	}

	function Search($file, $searchTerm)
	{
		if (empty($searchTerm))
			return true;

		$text = strip_tags(file_get_contents($file));
		return preg_match("/$searchTerm/i", $text);
	}

	function MakeDisplayDate($date)
	{
		global $locale;

		$displayDate = '';
		if (!empty($date))
		{
			$formatter = new IntlDateFormatter($locale, IntlDateFormatter::FULL, IntlDateFormatter::NONE);
			$displayDate = $formatter->format(new DateTime($date));
			$displayDate = "<time datetime=\"$date\">$displayDate</time>";
		}
		return $displayDate;
	}

	// Entfernt die Dateiendung ".php" vom Dateinamen. Diese soll im Browser nicht angezeigt werden.
	// In der .htaccess muss eine RewriteRule existieren, die Links ohne .php intern wieder auf .php umleitet.
	// Des weiteren wird der Dateiname "index" entfernt.
	function MakeLink($file)
	{
		$link = preg_replace('/\.php$/', '', $file);
		$link = preg_replace('/^index$/', '', $link);
		return $link;
	}

	// Ausgabe des Artikel-Headers (Überschriften)
	function DisplayEntryHeader($type, $title, $date, $description, $image, $file, $pinned, $front)
	{
		$displayDate = $type === 'post' ? MakeDisplayDate($date) : '';

		$classArticle = '';
		$styleArticle = '';

		if ($pinned)
			$classArticle .= 'pinned ';

		if ($front)
			$classArticle .= 'front ';

		if (!empty($image))
		{
			$classArticle .= 'image ';
			$styleArticle = "style=\"background-image: url('$image');\"";
		}

		echo "<article class=\"$classArticle\">\n<div class=\"article-header\" $styleArticle></div>\n<div class=\"article-content\">\n";

		$link = MakeLink($file);
		if (!empty($link))
		{
			echo "<h1><a href=\"$link\">$title</a></h1>\n";
		}
		else
		{
			echo "<h1>$title</h1>\n";
		}

		echo "<h6>$displayDate</h6>\n";

		if ($front)
		{
			$contents = file_get_contents($file);
		/*	$contents = preg_replace('/<\?php[^>]*\?>/s', '', $contents);	*/
			$contents = preg_replace('/<\?php.*?\?>/s', '', $contents);
			echo $contents;
		}
		else if (!empty($description))
		{
			echo "<p>$description</p>\n";
		}
	}

	// Ausgabe des Haupt-Inhalts der Seite zwischen <main> und </main>
	function DisplayMainContentBegin()
	{
		global $locale;

		global $fileTypes;
		global $filePublished;
		global $fileTitles;
		global $fileDescriptions;
		global $fileImages;
		global $fileCats;
		global $catSums;
		global $fileTags;
		global $tagSums;
		global $fileDates;
		global $fileListed;
		global $filePinned;
		global $fileFront;

		// Artikel-Übersicht (Beiträge, Seiten)
		if (get_type() === 'index')
		{
			// Filter-Hinweis oben auf der Index-Seite
			$filtered = false;

			$filterCat = htmlspecialchars($_GET['cats'] ?? '');
			if (!empty($filterCat))
			{
				$filtered = true;
				echo "<h1 class=\"filter\">" . GetString('ArticlesWithCategory') . ": $filterCat</h1>";
			}

			$filterTag = htmlspecialchars($_GET['tags'] ?? '');
			if (!empty($filterTag))
			{
				$filtered = true;
				echo "<h1 class=\"filter\">" . GetString('ArticlesWithTag') . ": $filterTag</h1>";
			}

			$filterMonth = htmlspecialchars($_GET['month'] ?? '');
			if (!empty($filterMonth))
			{
				$filtered = true;
				$displayMonth = $filterMonth;
				$formatter = new IntlDateFormatter($locale, IntlDateFormatter::NONE, IntlDateFormatter::NONE);
				$formatter->setPattern("MMMM yyyy");
				$displayMonth = $formatter->format(new DateTime("$filterMonth-01"));
				echo "<h1 class=\"filter\">" . GetString('ArticlesFrom') . " $displayMonth</h1>";
			}

			$searchTerm = htmlspecialchars($_GET['search'] ?? '');
			if (!empty($searchTerm))
			{
				$filtered = true;
				echo "<h1 class=\"filter\">" . GetString('ArticlesWithSearchTerm') . ": $searchTerm</h1>";
			}

			$maxItems = PHP_INT_MAX; // TODO: Anzahl der Artikel pro Seite begrenzen und Blätterfunktion einbauen.
			$numItems = 0;

			// Auflistung der angepinnten Artikel
			foreach ($fileDates as $file => $file_date)
			{
				$file_type = def_type($fileTypes[$file] ?? null);
				$file_published = def_published($filePublished[$file] ?? null);
				$file_title = def_title($fileTitles[$file] ?? null);
				$file_description = def_description($fileDescriptions[$file] ?? null);
				$file_image = def_image($fileImages[$file] ?? null);
				$file_listed = def_listed($fileListed[$file] ?? null, $file_type);
				$file_pinned = def_pinned($filePinned[$file] ?? null);
				$file_front = def_front($fileFront[$file] ?? null);

				if ($file_published && $file_pinned 
				&& ($file_type === 'page' || $file_type === 'post')
				&& ($file_listed || $filtered)
				&& FilterPassed($file, $filterCat, $filterTag, $filterMonth)
				&& Search($file, $searchTerm))
				{
					if ($numItems > 0) echo "</div>\n</article>\n";
					DisplayEntryHeader($file_type, $file_title, $file_date, $file_description, $file_image, $file, $file_pinned, $file_front);
					if (++$numItems >= $maxItems) break;
				}
			}

			if ($numItems >= $maxItems) return;

			// Auflistung der nicht angepinnten Artikel
			foreach ($fileDates as $file => $file_date)
			{
				$file_type = def_type($fileTypes[$file] ?? null);
				$file_published = def_published($filePublished[$file] ?? null);
				$file_title = def_title($fileTitles[$file] ?? null);
				$file_description = def_description($fileDescriptions[$file] ?? null);
				$file_image = def_image($fileImages[$file] ?? null);
				$file_listed = def_listed($fileListed[$file] ?? null, $file_type);
				$file_pinned = def_pinned($filePinned[$file] ?? null);
				$file_front = def_front($fileFront[$file] ?? null);

				if ($file_published && !$file_pinned
				&& ($file_type === 'page' || $file_type === 'post')
				&& ($file_listed || $filtered)
				&& FilterPassed($file, $filterCat, $filterTag, $filterMonth)
				&& Search($file, $searchTerm))
				{
					if ($numItems > 0) echo "</div>\n</article>\n";
					DisplayEntryHeader($file_type, $file_title, $file_date, $file_description, $file_image, $file, $file_pinned, $file_front);
					if (++$numItems >= $maxItems) break;
				}
			}
			
			if ($numItems == 0)
			{
				echo "<article>\n<div class=\"article-header\"></div>\n<div class=\"article-content\">\n";
				echo "<p>" . GetString('NoArticlesFound') . ".</p>\n";
			}
		}
		// Einzelner Artikel (Beitrag, Seite)
		else
		{
			DisplayEntryHeader(get_type(), get_title(), get_date(), '', '', '', get_pinned(), false);
		}
	}

	// Ausgabe des Artikel-Footers (Kategorien und Tags)
	function DisplayMainContentEnd()
	{
		if (get_type() === 'post' && (!empty(get_cats()) || !empty(get_tags())))
		{
			echo '<div class="article-links">';
			PrintCatsOrTags(get_cats(), 'cats');
			PrintCatsOrTags(get_tags(), 'tags');
			echo "</div>\n";
		}
		echo "<div class=\"print\">\n";
		echo "	<p class=\"article-source\">Quelle: <script>document.write(window.location);</script></p>\n";
		echo "</div>\n";
		echo "</div>\n</article>\n";
	}

	// Ausgabe des Menüs
	function PrintMenu($xmlMenu = null)
	{
		if ($xmlMenu === null)
			$xmlMenu = file_get_contents('lib/menu.xml');
		if ($xmlMenu)
		{
			$menu = new SimpleXMLElement($xmlMenu);
			ProcessXmlMenuElement($menu);
		}
	}

	function ProcessXmlMenuElement($menu)
	{
		foreach ($menu->children() as $child)
		{
			if ($child->getName() == 'menu')
			{
				echo '<div>';
				ProcessXmlMenuElement($child);
				echo '</div>';
			}
			else if ($child->getName() == 'item')
			{
				if ($child['link'])
				{
					$target = ($child['newtab'] == 'true' ? 'target="_blank"' : '');
					echo '<a href="' . $child['link'] . '" ' . $target . '>' . (string) $child . '</a>';
				}
				else
				{
					echo '<a>' . (string) $child . '</a>';

					if ($child['type'])
					{
						echo '<div>';
						if ($child['type'] == 'recent-posts')
						{
							$max = 6; if ($child['max']) $max = $child['max'];
							PrintMenuItemsRecentPosts($max);
						}
						else if ($child['type'] == 'categories')
						{
							$max = 20; if ($child['max']) $max = $child['max'];
							PrintMenuItemsCategories($max);
						}
						else if ($child['type'] == 'design')
						{
							PrintMenuItemsDesign();
						}
						echo '</div>';
					}
				}
			}
		}
	}

	function PrintMenuItemsRecentPosts($maxItems)
	{
		global $fileTypes;
		global $filePublished;
		global $fileDates;
		global $fileTitles;

		$numItems = 0;

		if (count($fileDates) > 0)
		{
			foreach ($fileDates as $file => $date)
			{
				$type = def_type($fileTypes[$file] ?? null);
				$published = def_published($filePublished[$file] ?? null);
				$title = def_title($fileTitles[$file] ?? null);

				if ($type === 'post' && $published)
				{
					echo '<a href="' . MakeLink($file) . '">' . $title . '</a>';
					if (++$numItems >= $maxItems) break;
				}
			}
		}
	}

	function PrintMenuItemsCategories($maxItems)
	{
		global $indexFile;
		global $catSums;

		$numItems = 0;

		if (count($catSums) > 0)
		{
			foreach ($catSums as $cat => $n)
			{
				echo '<a href="./' . $indexFile . '?cats=' . $cat . '">' . $cat . ' (' . $n . ')</a>';
				if (++$numItems >= $maxItems) break;
			}
		}
	}

	function PrintMenuItemsDesign()
	{
		echo '<a href="javascript:SwitchDesign(\'auto\');">' . GetString('DesignAuto') . '<span id="icon-design-auto"></span></a>';
		echo '<a href="javascript:SwitchDesign(\'light\');">' . GetString('DesignLight') . '<span id="icon-design-light"></span></a>';
		echo '<a href="javascript:SwitchDesign(\'dark\');">' . GetString('DesignDark') . '<span id="icon-design-dark"></span></a>';
	}

	// Ausgabe der Widgets
	function PrintWidgets($xmlWidgets = null)
	{
		if ($xmlWidgets === null)
			$xmlWidgets = file_get_contents('widgets/widgets.xml');
		if ($xmlWidgets)
		{
			$widgets = new SimpleXMLElement($xmlWidgets);
			foreach ($widgets->item as $item)
			{
				echo '<div class="widget">';
				echo '<h1>' . (string) $item . '</h1>';
				include 'widgets/' . $item['filename'];
				echo '</div>';
			}
		}
	}

	// Ausgabe des Seiten-Footers
	function DisplayPageFooter()
	{
		global $timeStart;

		$timeEnd = microtime(true);
		$timeDiff = $timeEnd - $timeStart;
		$ms = sprintf("%.1f", $timeDiff * 1000);

		echo "<p title=\"page generated in $ms ms\">" . sprintf(GetString('WebsiteCreatedWith'), "<b><i><a href=\"https://www.stefanbion.de/blog4u\" target=\"_blank\">Blog4u</a></i></b>") . "</p>";
	}

	function ReadCssVariables()
	{
		global $cssVars;

		$preview = array_key_exists('preview', $_GET) || basename($_SERVER['PHP_SELF']) == 'preview.php';
		$filenames = [ GetStylesheet($preview), GetUserStylesheet($preview) ];
		foreach ($filenames as $filename)
		{
			if (!empty($filename) && file_exists("lib/$filename"))
			{
				$css = file_get_contents("lib/$filename");
				if ($css)
				{
					foreach ($cssVars as $name => $value)
					{
						if (preg_match("/$name:([^;]+);/", $css, $matches))
						{
							$value = trim($matches[1]);
							if (preg_match("/url\('([^']+)'\)/", $value, $matches))
								$value = trim($matches[1]);
							$cssVars[$name] = $value;
						}
					}
				}
			}
		}
	}

	function ExistsStylesheet($preview = false)
	{
		$filename = GetStylesheet($preview);
		return !empty($filename) && file_exists("lib/$filename");
	}

	function ExistsUserStylesheet($preview = false)
	{
		$filename = GetUserStylesheet($preview);
		return !empty($filename) && file_exists("lib/$filename");
	}

	function GetStylesheet($preview = false)
	{
		global $stylesheet;
		global $stylesheetPreview;

		if ($preview === false && array_key_exists('preview', $_GET)) $preview = true;
		return $preview ? $stylesheetPreview : $stylesheet;
	}

	function GetUserStylesheet($preview = false)
	{
		global $userStylesheet;
		global $userStylesheetPreview;

		if ($preview === false && array_key_exists('preview', $_GET)) $preview = true;
		return $preview ? $userStylesheetPreview : $userStylesheet;
	}

	function GetString($name)
	{
		global $LangStrings;
		if (array_key_exists($name, $LangStrings))
			return str_replace('|', '&#10;', $LangStrings[$name]);
		else
			return 'STRING MISSING';
	}

	function GetUrlParameter($key)
	{
		return array_key_exists($key, $_POST) ? $_POST[$key] : (array_key_exists($key, $_GET) ? $_GET[$key] : '');
	}

	function GetGetValue($key)
	{
		return array_key_exists($key, $_GET) ? $_GET[$key] : '';
	}

	function GetPostValue($key)
	{
		return array_key_exists($key, $_POST) ? $_POST[$key] : '';
	}

	function GetServerValue($key)
	{
		return array_key_exists($key, $_SERVER) ? $_SERVER[$key] : '';
	}

	function GetBaseUrl()
	{
		global $baseUrl;
		if (empty($baseUrl))
		{
			$baseUrl = '.';
			$url = $_SERVER['SCRIPT_URI'];
			if (empty($url))
			{
				$url = (empty($_SERVER['HTTPS']) ? 'http' : 'https') . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
				$url = explode('?', $url)[0];
			}
			if (!empty($url))
			{
				$indexLastSlash = strrpos($url, '/');
				if ($indexLastSlash !== false)
					$baseUrl = substr($url, 0, $indexLastSlash);
			}
		}
		return rtrim($baseUrl, '/');
	}

	function InitDefault()
	{
		GetFileData();
	}
?>
