Added interpreter for pseudocode for displaying exhibitions, events.

Added basic search.
Added object pages.
This commit is contained in:
Joshua Ramon Enslin 2018-06-15 11:26:25 +02:00 committed by Stefan Rohde-Enslin
parent a49746ab10
commit 80485a98ab
19 changed files with 1573 additions and 82 deletions

29
apiMirror.php Normal file
View File

@ -0,0 +1,29 @@
<?PHP
/**
* The main display file for standalone pages.
*
* @author Joshua Ramon Enslin <joshua@jrenslin.de>
*/
// Include functions and settings.
require_once __DIR__ . "/inc/functions.php";
// Check validity of request.
if (!isset($_GET['args']) || !isset($_GET['area'])) {
echo printErrorPage("File does not exist.");
return;
}
if (!in_array($_GET['area'], ['events', 'exhibitions'])) {
echo printErrorPage("Using a disallowed value for area.");
return;
}
// Ensure working environment for frontend.
ensureEnvironment();
echo queryCachePage($settings['mdVersion'] . "?" . urldecode($_GET['args']), $_GET['area'], $settings);

View File

@ -36,7 +36,7 @@ function printBackendHead(string $page = "home", string $title = "Home", string
$output .= '
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script type="text/javascript" src="./js/newToolTip.js"></script>
<script type="text/javascript" src="./js/newToolTip.js" defer></script>
<script type="text/javascript" src="./js/main.js"></script>
<script type="text/javascript" src="../js/main.js"></script>

View File

@ -5,7 +5,7 @@
*
* @return {void}
*/
function removeElement(elem) {
async function removeElement(elem) {
while (elem.firstChild) {
elem.removeChild(elem.firstChild);
}
@ -32,12 +32,24 @@ document.addEventListener("DOMContentLoaded", function() {
"uploadFile" : "Upload file",
"submit" : "Submit",
"generate" : "Generate",
"embedCodeGenerator" : "Generator for embed code",
"helpEmbedCode" : "Here you can create embed code.",
"singleObjectTile" : "Single object (tile)",
"singleObjectDetails": "Single object (details)",
"singleCollection" : "Single collection",
"exhibitionCalendar" : "Exhibition calendar",
"eventCalendar" : "Event calendar",
},
"de" : {
"uploadFile" : "Eine Datei heraufladen",
"submit" : "Abschicken",
"generate" : "Generieren",
"embedCodeGenerator" : "Generator für Einbettungen",
"helpEmbedCode" : "Hier können sie den embed-code generieren.",
"singleObjectTile" : "Einzelobjekt (Kachel)",
"singleObjectDetails": "Einzelobjekt (Details)",
"singleCollection" : "Einzelsammlung",
"exhibitionCalendar" : "Ausstellungskalender",
"eventCalendar" : "Veranstaltungen (Kalender)",
}
};
@ -159,7 +171,7 @@ document.addEventListener("DOMContentLoaded", function() {
}
}
(function() {
(async function() {
let trigger = document.getElementById("uploadFile");
if (trigger === undefined || trigger === null) return;
@ -206,7 +218,7 @@ document.addEventListener("DOMContentLoaded", function() {
overlay.appendChild(uploadForm);
document.getElementsByTagName("body")[0].appendChild(overlay);
document.getElementsByTagName("body")[0].addEventListener('keydown', function(e) {
document.getElementsByTagName("body")[0].addEventListener('keydown', async function(e) {
if (e.keyCode != 27) return;
removeElement(overlay);
});
@ -214,7 +226,7 @@ document.addEventListener("DOMContentLoaded", function() {
queryPage(
encodeURI('./files.php'),
function (request) {
let allFiles = request.response;
let allFiles = JSON.parse(request.response);
for (let i = 0, max = allFiles.length; i < max; i++) {
console.log(allFiles[i]);
@ -231,14 +243,56 @@ document.addEventListener("DOMContentLoaded", function() {
*/
(function() {
function generateToolTip(toolTipID, toolTipText, toolTipTitle = "", triggerId = "") {
let trigger = document.createElement("span");
trigger.classList.add("newToolTipTag");
trigger.classList.add("helpToolTip");
trigger.setAttribute("data-for", toolTipID);
let toolTip = document.createElement("div");
toolTip.id = "tooltip_" + toolTipID;
toolTip.classList.add("newToolTip");
toolTip.setAttribute("data-title", toolTipTitle);
let toolTipCont = document.createElement("p");
toolTipCont.textContent = toolTipText;
toolTip.appendChild(toolTipCont);
trigger.appendChild(toolTip);
return trigger;
}
let generator = document.getElementById("embedGenerator");
if (generator === undefined || generator === null) return;
// Define generator types
let generatorTypes = [
["", "", false, "text"],
["eventCalendar", getTranslation(translations, "eventCalendar"), true, "number"],
["", "", false],
["singleObjectTile", getTranslation(translations, "singleObjectTile"), true],
["singleObjectDetails", getTranslation(translations, "singleObjectDetails"), true],
["singleCollectionTile", getTranslation(translations, "singleCollection"), true],
["exhibitionCalendar", getTranslation(translations, "exhibitionCalendar"), true],
["eventCalendar", getTranslation(translations, "eventCalendar"), true],
];
// Add help tooltip
let generatorLabelSpan = document.createElement("span");
generatorLabelSpan.classList.add("labelLine");
let generatorLabel = document.createElement("label");
generatorLabel.textContent = getTranslation(translations, "embedCodeGenerator");
generatorLabelSpan.appendChild(generatorLabel);
generatorLabelSpan.appendChild(generateToolTip("embedGeneratorToolTip", getTranslation(translations, "helpEmbedCode"), getTranslation(translations, "embedCodeGenerator")));
generator.appendChild(generatorLabelSpan);
let selectType = document.createElement("select");
for (let i = 0, max = generatorTypes.length; i < max; i++) {
@ -246,7 +300,6 @@ document.addEventListener("DOMContentLoaded", function() {
generatorOption.value = generatorTypes[i][0];
generatorOption.textContent = generatorTypes[i][1];
generatorOption.setAttribute("data-useSpecifier", generatorTypes[i][2]);
generatorOption.setAttribute("data-inputType", generatorTypes[i][3]);
selectType.appendChild(generatorOption);
}
@ -274,7 +327,6 @@ document.addEventListener("DOMContentLoaded", function() {
generatorSpecifier.value = "";
if (selectType.options[selectType.selectedIndex].getAttribute("data-useSpecifier") == "true") {
generatorSpecifier.classList.remove("invisible");
generatorSpecifier.type = selectType.options[selectType.selectedIndex].getAttribute("data-inputType");
}
else generatorSpecifier.classList.add("invisible");
@ -283,13 +335,20 @@ document.addEventListener("DOMContentLoaded", function() {
});
buttonGenerate.addEventListener('click', function(e) {
function runGenerator() {
if (selectType.value == "") return;
generatorFieldCont.textContent = "[" + selectType.value + "]";
if (generatorSpecifier.value != "") {
generatorFieldCont.textContent += "{" + generatorSpecifier.value + "}";
}
}
generatorSpecifier.addEventListener('keydown', function(e) {
if (e.keyCode != 13) return;
e.stopPropagation(); e.preventDefault();
runGenerator();
});
buttonGenerate.addEventListener('click', function() { runGenerator(); });
generator.appendChild(generatorField);

View File

@ -22,7 +22,7 @@ $pages = loadPages(); // Load overview of pages.
*/
// Check for vars.
loadHttpToGlobals(["task", "startPage", "pageTitle", "logo", "url", "mdVersion", "maxFileSize", "defaultLang"]);
loadHttpToGlobals(["task", "startPage", "pageTitle", "logo", "url", "mdVersion", "mdImgFolder", "cacheRefreshInterval", "maxFileSize", "defaultLang"]);
if (isset($task) and $task == "update") { // Adding new users.
@ -32,11 +32,10 @@ if (isset($task) and $task == "update") { // Adding new users.
}
// Ensure that URLs end with a trailing slash.
if (isset($mdVersion)) {
$mdVersion = rtrim($mdVersion, "/") . "/";
}
if (isset($mdVersion)) $mdVersion = rtrim($mdVersion, "/") . "/";
if (isset($mdImgFolder)) $mdImgFolder = rtrim($mdImgFolder, "/") . "/";
foreach (["startPage", "pageTitle", "logo", "url", "mdVersion", "maxFileSize", "defaultLang"] as $var) {
foreach (["startPage", "pageTitle", "logo", "url", "mdVersion", "mdImgFolder", "cacheRefreshInterval", "maxFileSize", "defaultLang"] as $var) {
if (isset($$var)) $settings[$var] = $$var;
}
@ -117,6 +116,20 @@ echo '
<td>' . generateHelpToolTip("helpMDVersion", $translations['mdVersion'], $translations['helpMDVersion']) . '</td>
</tr>
<!-- MD Image Folder -->
<tr>
<th><label for="mdImgFolder">' . $translations['mdImgFolder'] . '</label></th>
<td><input type="url" id="mdImgFolder" name="mdImgFolder" placeholder="' . $translations['mdImgFolder']. '" value="'.$settings['mdImgFolder'].'" required /></td>
<td>' . generateHelpToolTip("helpMDimgFolder", $translations['mdImgFolder'], $translations['helpMDimgFolder']) . '</td>
</tr>
<!-- Refresh Interval -->
<tr>
<th><label for="cacheRefreshInterval">' . $translations['cacheRefreshInterval'] . '</label></th>
<td><input type="number" id="cacheRefreshInterval" name="cacheRefreshInterval" placeholder="' . $translations['cacheRefreshInterval']. '" value="'.$settings['cacheRefreshInterval'].'" required /></td>
<td>' . generateHelpToolTip("helpCacheRefreshInterval", $translations['cacheRefreshInterval'], $translations['helpCacheRefreshInterval']) . '</td>
</tr>
<!-- Max Upload Size -->
<tr>
<th><label for="maxFileSize">' . $translations['maxFileSize'] . '</label></th>

View File

@ -222,22 +222,28 @@ main { padding: .5em 5em 3em 3em; }
*/
#staticPageOptions > * { display: block; padding: .5em 1rem; }
#staticPageOptions > .labelLine { display: table; width: 100%; }
#staticPageOptions .helpToolTip { width: 2em; text-align: center; }
#staticPageOptions > .labelLine > * { display: table-cell; }
#staticPageOptions label { font-weight: bold; }
#staticPageOptions select { display: block; width: 100%; }
#staticPageOptions button { display: block; width: 100%; background: #0277BD; color: #FFF; transition: background .4s; }
#staticPageOptions button:hover { background: #039BE5; }
#embedGenerator .helpToolTip,
#staticPageOptions .helpToolTip { width: 2em; text-align: center; }
#pageTools > #embedGenerator { display: block;
margin-bottom: 1em; padding-bottom: 1em;
box-sizing: content-box; border-bottom: 1px solid #D6D6D6; }
#embedGenerator > * { display: block; width: 100%; }
#embedGenerator .buttonLike { cursor: pointer; }
#embedGenerator > .labelLine { margin-bottom: .5em; }
#pageTools > * { padding: 0 1rem; }
#staticPageOptions > .labelLine,
#embedGenerator > .labelLine,
.labelLine { display: table; width: 100%; }
.labelLine > * { display: table-cell; }
.labelLine label { font-weight: bold; }
/************
* Login Page
*/

View File

@ -29,6 +29,8 @@ $translations = [
"helpURL" => "<p>URL of the page. Filling out this field helps with optimization for search engines.</p>",
"startPage" => "Start page",
"helpStartPage" => "<p>The start page of the public site.</p>",
"mdImgFolder" => "Image folder (md)",
"helpMDimgFolder" => "<p>The folder in which image files are stored in the given instance of museum-digital.</p>",
"logo" => "Logo",
"helpLogo" => "<p>The logo of the site. Is mainly used for the little icon you see in the browser, right next to the title of the tab.</p>",
"mdVersion" => "Version of Museum-Digital",
@ -41,6 +43,14 @@ $translations = [
"helpLanguage" => "<p>The default language of this instance of md:cms.</p>",
"maxFileSize" => "Maximum upload size",
"helpMaxFileSize" => "<p>The maximum file size of file uploads.</p>",
"cacheRefreshInterval" => "Cache Refresh Interval",
"helpCacheRefreshInterval" => "<p>This setting determines how often contents fetched from museum-digital should be refreshed (the number equals X seconds).
<ul>
<li>Setting it to <code>0</code> disables it</li>
<li>Using a low value, means more accurate data, but slower loading time.</li>
<li>Using a high value, means data could be slightly outdated, but makes loading much faster.</li>
</ul>
</p>",
"IDatMD" => "ID at museum-digital",
"embedFromMD" => "Embed from museum-digital",

View File

@ -73,7 +73,9 @@ function ensureEnvironment() {
"pageTitle" => "md:cms",
"logo" => "",
"url" => "",
"mdVersion" => "https://www.museum-digital.de/nat/",
"cacheRefreshInterval" => 0,
"mdVersion" => "https://rlp.museum-digital.de/",
"mdImgFolder" => "https://rlp.museum-digital.de/data/rlp/",
"maxFileSize" => 300000,
"defaultLang" => "en"
],
@ -109,6 +111,42 @@ function loadPublicPages() {
}
/**
* Query or cache pages.
*
* @param string $url URL to query.
* @param string $area The type of the queried page. If caching is enabled, renew cache every X seconds.
* @param array $settings Settings variable.
*
* @return array
*/
function queryCachePage(string $url, string $area = "", array $settings = ['cacheRefreshInterval' => 0]) {
// Ignore caching if cacheRefreshInterval equals zero.
if ($settings['cacheRefreshInterval'] == 0) {
return file_get_contents($url);
}
$fileDir = __DIR__ . "/../data/caches/$area";
ensureDir($fileDir);
$fileName = md5($url);
$filePath = "$fileDir/$fileName.json";
// Load from cache.
if (file_exists($filePath) && time() - filemtime($filePath) < $settings['cacheRefreshInterval']) {
return file_get_contents($filePath);
}
// Refresh cache.
$contents = file_get_contents($url);
file_put_contents($filePath, $contents, LOCK_EX);
return $contents;
}
/**
* Function scanDirConts is a wrapper around scandir(), which removes [".", ".."].
*
@ -320,4 +358,78 @@ function checkPreviewAccess($sessionStarted = false) {
}
/**
* Function checking if a string starts with another.
*
* @param string $haystack String to check.
* @param string $needle Potential start of $haystack.
*
* @return boolean
*/
function startswith(string $haystack, string $needle):bool {
if (substr($haystack, 0, strlen($needle)) == $needle) return (true);
else return (false);
}
/**
* Function checking if a string starts with any input from the input array.
*
* @param string $haystack String to check.
* @param string[] $needles Array containing potential start values of $haystack.
*
* @return boolean
*/
function startswithAny(string $haystack, array $needles):bool {
$output = false;
foreach ($needles as $needle) {
$output = startswith($haystack, $needle);
if ($output == true) return $output;
}
return $output;
}
/**
* Curling web pages.
* Function to check errors.
*
* @param string $url URL to query.
* @param string $host Authentication data. Optional.
*
* @return string
*/
function runCurl(string $url, string $host = ""):string {
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
// curl_setopt($curl, CURLOPT_RESOLVE, ["www.example.com:443:172.16.1.1"]);
curl_setopt($curl, CURLOPT_ENCODING, '');
if ($host) curl_setopt($ch, CURLOPT_HTTPHEADER, array("Host: $host"));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($curl);
curl_close($curl);
return $result;
}
/**
* This function cuts down a string and adds a period in case it's longer than length to create a snippet.
*
* @param string $string Input text to cut down
* @param int $length Length of the snippet to create
*
* @return string
*/
function createTextSnippet($string, $length) {
if (strlen($string) > $length) {
$string = substr($string, 0, $length);
$string = substr($string, 0, strrpos($string, ' '));
$string .= ' ...';
}
return $string;
}
?>

View File

@ -12,12 +12,17 @@
* for embedding from museum-digital.
*
* @param string $text Input string.
* @param array $settings General settings.
*
* @return string
*/
function checkForEmbeds(string $text):string {
function checkForEmbeds(string $text, array $settings):string {
$embedOptions = [
"singleObjectTile",
"singleObjectDetails",
"singleCollectionTile",
"exhibitionCalendar",
"eventCalendar"
];
@ -26,13 +31,32 @@ function checkForEmbeds(string $text):string {
if (strpos($text, $option) === false) continue;
$position = strpos($text, $option) - 1;
$nextTag = strpos($text, "<", $position);
$nextWhitespace = strpos($text, " ", $position);
$nextTag = $nextWhitespace = strlen($text);
if (strpos($text, "<", $position) !== false) $nextTag = strpos($text, "<", $position);
if (strpos($text, " ", $position) !== false) $nextWhitespace = strpos($text, " ", $position);
$end = min($nextTag, $nextWhitespace, strlen($text));
$end = min($nextTag, $nextWhitespace);
$pseudocode = substr($text, $position, $position + $end);
echo $pseudocode;
$pseudocode = substr($text, $position, $end - $position);
$command = substr($pseudocode, 1, strpos($pseudocode, "]") - 1);
$arguments = [];
if (strpos($pseudocode, "{") !== false) $arguments = explode("&", substr($pseudocode, strpos($pseudocode, "{") + 1, -1));
switch ($command) {
case "singleObjectTile":
$text = str_replace($pseudocode, embedObject($arguments, $settings), $text);
break;
case "singleObjectDetails":
$text = str_replace($pseudocode, embedObject($arguments, $settings, true), $text);
break;
case "exhibitionCalendar":
$text = str_replace($pseudocode, embedExhibitionCalendar($arguments), $text);
break;
case "eventCalendar":
$text = str_replace($pseudocode, embedEventCalendar($arguments), $text);
break;
}
}
@ -40,4 +64,302 @@ function checkForEmbeds(string $text):string {
}
/**
* Function drawObjectTile creates a tile with just the most basic information on an object.
*
* @param string[] $contents Input data fetched from the object API at museum-digital.
* @param array $settings Settings variable.
*
* @return string
*/
function drawObjectTile(array $contents, array $settings):string {
$output = '
<div class="objTile">
';
if (count($contents['object_images']) > 0) {
foreach ($contents['object_images'] as $image) {
if ($image['is_main'] != "j") continue;
$output .= '
<img src="' . $settings['mdImgFolder'] . $image['folder'] . '/' . $image['preview'] . '" />';
}
}
$output .= '
<div>
<h4>' . $contents['object_name'] . '</h4>
<div>
<a href="./object.php?id=' . $contents['object_id'] . '" class="toTranslate" data-content="More"></a>
<a href="' . $settings['mdVersion'] . '?t=objekt&oges=' . $contents['object_id'] . '" class="toTranslate" data-content="MoreAtMuseumDigital">' . $contents['object_name'] . '</a>
</div>
</div>
</div>
';
return $output;
}
/**
* Function drawObjectDetails creates a tile with just the most basic information on an object.
*
* @param string[] $contents Input data fetched from the object API at museum-digital.
* @param array $settings Settings variable.
*
* @return string
*/
function drawObjectDetails(array $contents, array $settings):string {
$output = '
<div class="objDetails">
<h2>' . $contents['object_name'] . '</h2>
';
if (count($contents['object_images']) > 0) {
foreach ($contents['object_images'] as $image) {
if ($image['is_main'] != "j") continue;
$output .= '
<img class="objMainImg" src="' . $settings['mdImgFolder'] . $image['folder'] . '/' . $image['filename_loc'] . '" />';
}
}
$output .= '
<div>
';
$output .= '
<p>
' . $contents['object_description'] . '
</p>';
$simpleDefinedConts = [
"object_material_technique",
"object_dimensions",
];
$output .= "
<dl>";
if (count($contents['object_collection']) > 0) {
$output .= '
<dt class="toTranslate" data-content="Collection"></dt>';
foreach ($contents['object_collection'] as $collection) {
$output .= '
<dd>' . $collection['collection_name'] . '</dd>
';
}
}
foreach ($simpleDefinedConts as $value) {
$output .= "
<dt class='toTranslate' data-content='" . $value . "'></dt>
<dd>" . $contents[$value] . "</dd>
";
}
if (count($contents['object_tags']) > 0) {
$output .= '
<dt class="toTranslate" data-content="Tags"></dt>';
foreach ($contents['object_tags'] as $tag) {
$output .= '
<dd>' . $tag['tag_name'] . '</dd>
';
}
}
if (count($contents['object_relation_places']) > 0) {
$output .= '
<dt class="toTranslate" data-content="Places"></dt>';
foreach ($contents['object_relation_places'] as $place) {
$output .= '
<dd>' . $place['place']['place_name'] . '</dd>
';
}
}
if (count($contents['object_relation_people']) > 0) {
$output .= '
<dt class="toTranslate" data-content="People"></dt>';
foreach ($contents['object_relation_people'] as $people) {
$output .= '
<dd>' . $people['people']['displayname'] . '</dd>
';
}
}
$output .= "
</dl>";
if (count($contents['object_events']) > 0) {
$output .= "
<div class='events'>";
foreach ($contents['object_events'] as $event) {
$output .= '
<div class="objevent">
<h5 class="toTranslate" data-content="eventType' . $event['event_type'] . '"></h5>
<table>
';
if (isset($event['people'])) $output .= '
<tr>
<th class="toTranslate" data-content="... who"></th>
<td class="vcard h-card">
<a rel="tag" class="u-url fn p-name url">' . $event['people']['displayname'] . '</a>
</td>
</tr>';
if (isset($event['time'])) $output .= '
<tr>
<th class="toTranslate" data-content="... when"></th>
<td>
<a>' . $event['time']['time_name'] . '</a>
</td>
</tr>';
if (isset($event['place'])) $output .= '
<tr>
<th class="toTranslate" data-content="... where"></th>
<td class="h-geo geo">
<a rel="tag">' . $event['place']['place_name'] . '</a>
</td>
</tr>';
$output .= '
</table>
</div>
';
}
$output .= '
</div>
';
}
$output .= '
<p class="metadataLine">
<span><span class="toTranslate" data-content="Metadata"></span></span>
<span><span class="toTranslate" data-content="LastUpdated"></span>: ' . $contents['object_last_updated'] . '</span>
<span><span class="toTranslate" data-content="Licence"></span>: ' . $contents['licence']['metadata_rights_status'] . '</span>
<span><a class="toTranslate" data-content="ObjectAtMuseumDigital" href="' . $settings['mdVersion'] . '?t=objekt&oges=' . $contents['object_id'] . '"></a></span>
</p>
</div>
';
$output .= '
</div>
';
return $output;
}
/**
* Function for displaying objects.
*
* @param array $arguments Arguments / GET parameters for urls to query.
* @param array $settings Settings variable.
* @param boolean $showDetails Optional. By default, only a tile with most basic information is displayed.
*
* @return string
*/
function embedObject(array $arguments, array $settings, bool $showDetails = false):string {
$toIgnore = ["t=", "output="];
$srcArgs = "t=objekt";
foreach ($arguments as $arg) {
if (startsWithAny($arg, $toIgnore)) continue;
$srcArgs .= "&" . $arg;
}
$srcArgs .= "&output=json";
$contents = json_decode(queryCachePage($settings['mdVersion'] . "?$srcArgs", "object", $settings), true);
if (!$showDetails) return drawObjectTile($contents, $settings);
else return drawObjectDetails($contents, $settings);
}
/**
* Function for embedding event calendar.
*
* @param array $arguments Arguments / GET parameters for urls to query.
*
* @return string
*/
function embedExhibitionCalendar(array $arguments):string {
$toIgnore = ["t=", "calendar=", "output="];
$srcArgs = "t=exhibitions_overview&calendar=1";
foreach ($arguments as $arg) {
if (startsWithAny($arg, $toIgnore)) continue;
$srcArgs .= "&" . $arg;
}
$srcArgs .= "&output=json";
$srcURL = "apiMirror.php?area=exhibitions&args=" . urlencode($srcArgs);
if (isset($_GET['y'])) $y = $_GET['y'];
else $y = date("Y");
if (isset($_GET['m'])) $m = $_GET['m'];
else $m = date("m");
$prevMonth = strtotime('-1 month', strtotime("$y-$m-01"));
$nextMonth = strtotime('+1 month', strtotime("$y-$m-01"));
$output = '
<section class="mdCalendar"
data-year="'.$y.'" data-month="'.$m.'" data-colored="1"
data-today="./index.php?id=' . $GLOBALS['id'] . '&' . date("Y").'&m='.date("m") . '"
data-prev="./index.php?id=' . $GLOBALS['id'] . '&y=' . date("Y", $prevMonth).'&m='.date("m", $prevMonth) . '"
data-next="./index.php?id=' . $GLOBALS['id'] . '&y=' . date("Y", $nextMonth).'&m='.date("m", $nextMonth) . '"
data-src="' . $srcURL . '&y='.date("Y", $nextMonth).'&m='.date("m", $nextMonth) . '">
</section>
';
return $output;
}
/**
* Function for embedding event calendar.
*
* @param array $arguments Arguments / GET parameters for urls to query.
*
* @return string
*/
function embedEventCalendar(array $arguments):string {
$toIgnore = ["t=", "calendar=", "output="];
$srcArgs = "t=events&calendar=1";
foreach ($arguments as $arg) {
if (startsWithAny($arg, $toIgnore)) continue;
$srcArgs .= "&" . $arg;
}
$srcArgs .= "&output=json";
$srcURL = "apiMirror.php?area=events&args=" . urlencode($srcArgs);
if (isset($_GET['y'])) $y = $_GET['y'];
else $y = date("Y");
if (isset($_GET['m'])) $m = $_GET['m'];
else $m = date("m");
$prevMonth = strtotime('-1 month', strtotime("$y-$m-01"));
$nextMonth = strtotime('+1 month', strtotime("$y-$m-01"));
$output = '
<section class="mdCalendar"
data-year="'.$y.'" data-month="'.$m.'" data-colored="1"
data-today="./index.php?id=' . $GLOBALS['id'] . '&' . date("Y").'&m='.date("m") . '"
data-prev="./index.php?id=' . $GLOBALS['id'] . '&y=' . date("Y", $prevMonth).'&m='.date("m", $prevMonth) . '"
data-next="./index.php?id=' . $GLOBALS['id'] . '&y=' . date("Y", $nextMonth).'&m='.date("m", $nextMonth) . '"
data-src="' . $srcURL . '&y='.date("Y", $nextMonth).'&m='.date("m", $nextMonth) . '">
</section>
';
return $output;
}
?>

52
inc/search.php Normal file
View File

@ -0,0 +1,52 @@
<?PHP
/**
* This file provides search functions.
*
* @file
*
* @author Joshua Ramon Enslin <joshua@jrenslin.de>
*/
/**
* Function for searching in all static pages.
*
* @param string $searchTerm Search term.
*
* @return array
*/
function searchInPages(string $searchTerm) {
$files = scanDirConts(__DIR__ . "/../data/static");
$results = [];
foreach ($files as $file) {
$curResults = [];
$contents = json_decode(file_get_contents(__DIR__ . "/../data/static/$file"), true);
if (!$contents['public']) continue; // Don't display non-public files.
$curResults['inTitle'] = substr_count($contents['title'], $searchTerm);
$curResults['inDescription'] = substr_count($contents['content'], $searchTerm);
$curResults['priority'] = $curResults['inTitle'] * 3 + $curResults['inDescription'];
if ($curResults['priority'] == 0) continue;
$curResults['title'] = $contents['title'];
// Sanitize content for snippets.
$snippet = preg_replace('/[\[{\(].*[\]}\)]/U' , '', strip_tags($contents['content']));
$curResults['snippet'] = createTextSnippet($snippet, 180);
$results[$file] = $curResults;
}
usort($results, function($a, $b) {
if ($a == $b) return 0;
return ($a > $b) ? -1 : 1;
});
return $results;
}
?>

View File

@ -10,25 +10,30 @@
/**
* Prints the head element of an HTML page in the public frontend.
*
* @param array $settings Settings variable.
* @param string $page ID of the current page.
* @param string $title Title of the page.
* @param string $icon The icon of the website.
* @param string $additional Additional HTML to inject.
*
* @return string
*/
function printPublicHead(string $page = "home", string $title = "Home", string $icon = ""):string {
function printPublicHead(array $settings, string $page = "home", string $title = "Home", string $icon = "", $additional = ""):string {
$output = '<!DOCTYPE html>
<html lang="en" id="' . $page . '">
<head>
<!-- Content Security policies -->
<meta http-equiv="Content-Security-Policy" content="default-src \'none\'; script-src \'self\'; connect-src \'self\'; img-src \'self\'; style-src \'self\' \'unsafe-inline\'; font-src \'self\';" />
<meta http-equiv="Content-Security-Policy" content="default-src \'none\'; script-src \'self\'; connect-src \'self\' ' . $settings['mdVersion'] . '; img-src \'self\' ' . $settings['mdVersion'] . '; style-src \'self\' \'unsafe-inline\'; font-src \'self\';" />
<title>' . $title . '</title>
<link rel="stylesheet" type="text/css" href="themes/default/default.css">
<link rel="stylesheet" type="text/css" href="themes/imports.css">
<meta http-equiv="content-type" content="text/html;charset=utf-8">';
$output .= $additional;
if ($icon) {
$output .= '
<link rel="shortcut icon" sizes="16x16 32x32" href="' . $icon . '" />
@ -39,6 +44,7 @@ function printPublicHead(string $page = "home", string $title = "Home", string $
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script type="text/javascript" src="./js/main.js"></script>
<script type="text/javascript" src="./js/mdCalendar.js" defer></script>
</head>
<body>
@ -151,4 +157,37 @@ function printErrorPage(string $content):string {
}
/**
* Function for generating the standard navigation of the public parts of the page.
*
* @param array $pages List of all pages.
*
* @return string
*/
function generatePublicNav($pages):string {
$output = '<nav>';
$output .= buildPageOrder(
$pages,
function() {
return "<ul>";
},
function() {
return "</ul>";
},
function($inputs, string $toAdd) {
$output = "
<li";
if (!$inputs['public']) $output .= " class='notPublic'";
$output .= ">
<a href='./?id=" . $inputs['id'] . "'>" . $inputs['title'] . "</a>
$toAdd
</li>
";
return $output;
}
);
$output .= '</nav>';
return $output;
}
?>

View File

@ -5,6 +5,10 @@
* @author Joshua Ramon Enslin <joshua@jrenslin.de>
*/
// Include functions and settings.
require_once __DIR__ . "/inc/functions.php";
// Check validity of request.
if (isset($_GET['id']) and !file_exists(__DIR__ . "/data/static/" . $_GET['id'] . ".json")) {
@ -12,10 +16,6 @@ if (isset($_GET['id']) and !file_exists(__DIR__ . "/data/static/" . $_GET['id']
return;
}
// Include functions and settings.
require __DIR__ . "/inc/functions.php";
// Ensure working environment for frontend.
ensureEnvironment();
@ -25,8 +25,6 @@ $pages = loadPublicPages(); // Load overview of pages.
* Load data.
*/
/*
* @var array $tPage The variable contains the main data on the displayed page.
*/
@ -53,32 +51,11 @@ if (!$tPage['public']) {
* Output
*/
echo printPublicHead($id, $settings['pageTitle'], $settings['logo']);
echo printPublicHead($settings, $id, $settings['pageTitle'], $settings['logo']);
echo printPublicHeader($settings['pageTitle']);
echo printStaticPagePart("banner", "header"); // Print aside (if need be)
echo '<nav>';
echo buildPageOrder(
$pages,
function() {
return "<ul>";
},
function() {
return "</ul>";
},
function($inputs, string $toAdd) {
$output = "
<li";
if (!$inputs['public']) $output .= " class='notPublic'";
$output .= ">
<a href='./?id=" . $inputs['id'] . "'>" . $inputs['title'] . "</a>
$toAdd
</li>
";
return $output;
}
);
echo '</nav>';
echo generatePublicNav($pages);
echo '
<div id="mainWrapper">
@ -89,7 +66,7 @@ echo '
<main>';
echo '<h1>' . $tPage['title'] . '</h1>';
echo checkForEmbeds($tPage['content']);
echo checkForEmbeds($tPage['content'], $settings);
echo '
</main>

View File

@ -14,12 +14,46 @@ document.addEventListener("DOMContentLoaded", function() {
let translations = {
"en" : {
"More" : "More",
"MoreAtMuseumDigital" : "museum-digital",
"ObjectAtMuseumDigital" : "Object entry at museum-digital",
"Collection" : "Collection",
"object_material_technique" : "Material / Technique",
"object_dimensions" : "Dimensions",
"Metadata" : "Metadata",
"LastUpdated" : "Last Updated",
"Licence" : "Licence",
"Tags" : "Tags",
"People" : "People",
"Places" : "Places",
"Times" : "Times",
"Search" : "Search",
"SearchingFor" : "Searching for",
"... who" : "... who",
"... where" : "... where",
"... when" : "... when",
"eventType1" : "Created",
},
"de" : {
},
"hu" : {
"More" : "Mehr",
"MoreAtMuseumDigital" : "museum-digital",
"ObjectAtMuseumDigital" : "Objekt bei museum-digital",
"Collection" : "Sammlung",
"object_material_technique" : "Material / Technik",
"object_dimensions" : "Ausmaße",
"Metadata" : "Metadaten",
"LastUpdated" : "Zuletzt geupdatet",
"Licence" : "Lizenz",
"Tags" : "Schlagworte",
"People" : "Personen",
"Places" : "Orte",
"Times" : "Zeiten",
"Search" : "Suche",
"SearchingFor" : "Suche nach",
"... who" : "... wer",
"... where" : "... wo",
"... when" : "... wann",
"eventType1" : "Hergestellt",
}
};
@ -108,6 +142,13 @@ document.addEventListener("DOMContentLoaded", function() {
}
}
(function() {
let toTranslate = document.getElementsByClassName("toTranslate");
for (let i = 0, max = toTranslate.length; i < max; i++) {
console.log(toTranslate[i].getAttribute("data-content"));
toTranslate[i].textContent = getTranslation(translations, toTranslate[i].getAttribute("data-content"));
}
})();
});

480
js/mdCalendar.js Normal file
View File

@ -0,0 +1,480 @@
/**
* mdCalendar.js
*/
document.addEventListener("DOMContentLoaded", function() {
/**
* @var {array} translations Array of all translations.
*/
let translations = {
"en" : {
"dow0" : "Sunday",
"dow1" : "Monday",
"dow2" : "Tuesday",
"dow3" : "Wednesday",
"dow4" : "Thursday",
"dow5" : "Friday",
"dow6" : "Saturday",
"mon0" : "January",
"mon1" : "February",
"mon2" : "March",
"mon3" : "April",
"mon4" : "May",
"mon5" : "June",
"mon6" : "July",
"mon7" : "August",
"mon8" : "September",
"mon9" : "October",
"mon10" : "November",
"mon11" : "December",
"Today" : "Today",
"Title" : "Title",
"Start" : "Start",
"End" : "End",
"Location" : "Location"
},
"de" : {
"dow0" : "Sonntag",
"dow1" : "Montag",
"dow2" : "Dienstag",
"dow3" : "Mittwoch",
"dow4" : "Donnerstag",
"dow5" : "Freitag",
"dow6" : "Samstag",
"mon0" : "Januar",
"mon1" : "Februar",
"mon2" : "März",
"mon3" : "April",
"mon4" : "Mai",
"mon5" : "Juni",
"mon6" : "Juli",
"mon7" : "August",
"mon8" : "September",
"mon9" : "Oktober",
"mon10" : "November",
"mon11" : "Dezember",
"Today" : "Jetzt",
"Title" : "Titel",
"Start" : "Beginn",
"End" : "Ende",
"Location" : "Ort"
},
"hu" : {
"dow0": "Vas\u00e1rnap",
"dow1": "H\u00e9tf\u0151",
"dow2": "Kedd",
"dow3": "Szerda",
"dow4": "Cs\u00fct\u00f6rt\u00f6k",
"dow5": "P\u00e9ntek",
"dow6": "Szombat",
"mon0": "Janu\u00e1r",
"mon1": "Febru\u00e1r",
"mon2": "M\u00e1rcius",
"mon3": "\u00c1prilis",
"mon4": "M\u00e1jus",
"mon5": "J\u00fanius",
"mon6": "J\u00falius",
"mon7": "Augusztus",
"mon8": "Szeptember",
"mon9": "Okt\u00f3ber",
"mon10": "November",
"mon11": "December",
"Today": "Ma",
"Title": "C\u00edm",
"Start": "Nyit",
"End": "Bez\u00e1r",
"Location": "Helysz\u00edn"
}
}
/**
* @var {boolean} Toggle debugging.
*/
let debugging = false;
/**
* Function queryPage queries a web page and runs the specified function over the output.
*
* @param {string} url URL to query.
* @param {function} func Callback function to run on the request after loading.
* @param {boolean} debug Enable / disable debug mode.
*
* @return {boolean}
*/
function queryPage(url, func, debug = false) {
let request = new XMLHttpRequest();
request.open('GET', url);
request.setRequestHeader("Cache-Control", "no-cache");
request.responseType = 'htm';
request.send();
request.onload = function() {
func(request, debug);
};
}
/**
* Removes all children of an element.
*
* @param {string} id ID of the element to tear down.
*
* @return {void}
*/
function emptyElement(element) {
while (element.firstChild) {
emptyElement(element.firstChild);
}
element.parentNode.removeChild(element);
if (debugging === true) {
console.log("Removed element:");
console.log(element);
}
}
function tearDownById(id) {
let target = document.getElementById(id);
if (target !== null) emptyElement(target);
}
/**
* Returns a requested translation from an array in the currently used language.
*
* @param {mixed[]} list Translation variable.
* @param {string} specifier Specifies which translation to get.
*
* @return {string}
*/
function getTranslation(list, specifier) {
let preferedLang = document.getElementsByTagName("html")[0].getAttribute("lang");
if (list[preferedLang] !== undefined && list[preferedLang][specifier] !== null) return list[preferedLang][specifier];
return list["en"][specifier];
}
/**
* Function to create calendar.
*/
function createCalendarTable(target, events) {
let startOfWeek = 1; // The week starts on Monday.
let endOfWeek = 0; // The week ends on Sunday.
let calLocale = document.getElementsByTagName("html")[0].getAttribute("lang");
if (calLocale === undefined || calLocale === null) calLocale = "en";
function removeToolTips() {
let toolTips = document.getElementsByClassName("mdCToolTip");
for (let i = 0, max = toolTips.length; i < max; i++) {
emptyElement(toolTips[i]);
}
}
/**
* Gets all days to display per month.
* Adapted version of function described in https://stackoverflow.com/a/13146828.
*
* @param {int} The month number, 0 based
* @param {int} The year, not zero based, required to account for leap years
*
* @return {Date[]} List with date objects for each day of the month
*/
function getDaysInMonth(month, year) {
var date = new Date(year, month, 1);
var days = [];
// If the weekday of the first of the month does not equal 1 (Monday),
// get days until the last monday before the month.
if (date.getDay() !== startOfWeek) {
while (date.getDay() !== startOfWeek) {
date.setDate(date.getDate() - 1);
days.unshift(new Date(date));
}
}
date = new Date(year, month, 1);
// Get days of the month
while (date.getMonth() === month) {
days.push(new Date(date));
date.setDate(date.getDate() + 1);
}
// If the weekday of the first of the month does not equal 1 (Monday),
// get days until the last monday before the month.
while (date.getDay() !== startOfWeek) {
days.push(new Date(date));
date.setDate(date.getDate() + 1);
}
return days;
}
events = (function() {
let colorSchemeLength = 29;
for (var i = 0, max = events.length; i < max; i++) {
let hash = 0;
for (var j = 0, maxj = events[i].name.length; j < maxj; j++) {
hash = events[i].name.charCodeAt(j) + hash;
}
hash = hash % (colorSchemeLength) - 1;
events[i].color = hash;
}
return events;
})();
let d = new Date();
let year;
if (target.getAttribute("data-year") !== null) year = parseInt(target.getAttribute("data-year"));
else year = d.getFullYear();
let month;
if (target.getAttribute("data-month") !== null) month = parseInt(target.getAttribute("data-month") - 1);
else month = d.getMonth();
// Create outer div
let mdCalDiv = document.createElement("div");
mdCalDiv.classList.add("mdCalendar");
/**
* Creater header line of the calendar.
*/
(function() {
let mdCalTitleLine = document.createElement("header");
mdCalTitleLine.classList.add("mdCTitleLine");
let mdCalTitle = document.createElement("h3");
mdCalTitle.textContent = getTranslation(translations, "mon" + month.toString());
if (year != d.getFullYear()) mdCalTitle.textContent = mdCalTitle.textContent + " (" + year + ")";
mdCalTitleLine.appendChild(mdCalTitle);
let mdCalNav = document.createElement("div");
mdCalNav.classList.add("mdCNav");
mdCalTitleLine.appendChild(mdCalNav);
let mdCalNavPrev = document.createElement("a");
mdCalNavPrev.href = target.getAttribute("data-prev");
mdCalNavPrev.rel = "prev";
mdCalNavPrev.classList.add("mdCNavPrev");
mdCalNav.appendChild(mdCalNavPrev);
let mdCalNavNext = document.createElement("a");
mdCalNavNext.href = target.getAttribute("data-next");
mdCalNavNext.rel = "next";
mdCalNavNext.classList.add("mdCNavNext");
mdCalNav.appendChild(mdCalNavNext);
let mdCalNavToday = document.createElement("a");
mdCalNavToday.href = target.getAttribute("data-today");
mdCalNavToday.textContent = getTranslation(translations, "Today");
mdCalNav.appendChild(mdCalNavToday);
mdCalTitleLine.appendChild(mdCalNav);
mdCalDiv.appendChild(mdCalTitleLine);
})();
/**
* Create table.
*/
let table = document.createElement("table");
table.classList.add("mdCTable");
let thead = document.createElement("thead");
let theadTr = document.createElement("tr");
for (let i = 1, max = 7; i < max; i++) {
let th = document.createElement("th");
th.textContent = getTranslation(translations, "dow" + i.toString());
theadTr.appendChild(th);
}
let th = document.createElement("th");
th.textContent = getTranslation(translations, "dow0");
theadTr.appendChild(th);
thead.appendChild(theadTr);
table.appendChild(thead);
let tbody = document.createElement("tbody");
let days = getDaysInMonth(month, year);
let tr;
let daysTDs = [];
function createSingleEventOverlay(e, parentElement, data) {
let toolTip = document.createElement("table");
toolTip.classList.add("mdCToolTip");
let tableData = [];
tableData.push(["Title", data["name"]]);
if (data["start"] !== undefined) tableData.push(["Start", data["start"]]);
if (data["end"] !== undefined) tableData.push(["End", data["end"]]);
if (data["place"] !== undefined) tableData.push(["Location", data["place"]]);
for (let i = 0, max= tableData.length; i < max; i++) {
let nameRow = document.createElement("tr");
let nameTh = document.createElement("th");
nameTh.textContent = getTranslation(translations, tableData[i][0]);
nameRow.appendChild(nameTh);
let nameTd = document.createElement("td");
nameTd.textContent = tableData[i][1];
nameRow.appendChild(nameTd);
toolTip.appendChild(nameRow);
}
parentElement.appendChild(toolTip);
}
for (let i = 0, max= days.length; i < max; i++) {
// Begin a new table row every Monday.
if (days[i].getDay() === startOfWeek) {
tr = document.createElement("tr");
tbody.appendChild(tr);
}
// Create new TD per day and add appropriate classes.
let td = document.createElement("td");
if (days[i].getMonth() !== month) td.classList.add("mdCOtherMonth");
if (days[i].getYear() == d.getYear() && days[i].getMonth() == d.getMonth() && days[i].getDate() == d.getDate()) td.classList.add("mdCToday");
tdTitle = document.createElement("time");
tdTitle.textContent = days[i].getDate();
td.appendChild(tdTitle);
tr.appendChild(td);
// Append dates.
let dayTime = days[i].getTime();
let dayTimeMinusDay = days[i].getTime() + 24 * 3600 * 1000;
let dayTimePlusDay = days[i].getTime() - 0 * 3600 * 1000;
for (let j = 0, maxj = events.length; j < maxj; j++) {
if (Date.parse(events[j].start) > dayTimeMinusDay || Date.parse(events[j].end) < dayTimePlusDay) continue;
let eventSignifier = document.createElement("a");
eventSignifier.classList.add("color" + events[j].color.toString());
if (events[j].link) eventSignifier.href = events[j].link;
let eventSignifierText = document.createElement("span");
eventSignifierText.textContent = events[j].name;
eventSignifier.appendChild(eventSignifierText);
eventSignifier.addEventListener('mouseover', function(e) {
createSingleEventOverlay(e, eventSignifier, events[j]);
});
eventSignifier.addEventListener('mouseout', function(e) {
removeToolTips();
});
td.appendChild(eventSignifier);
}
// Add posibility for overlay.
tdTitle.addEventListener('click', function(e) {
tearDownById("mdCOverlay");
let overlay = document.createElement("div");
overlay.id = "mdCOverlay";
let eventTitleBar = document.createElement("div");
eventTitleBar.classList.add("mdCOverlayTitleBar");
overlay.appendChild(eventTitleBar);
let title = document.createElement("span");
title.classList.add("mdCOverlayTitle");
title.textContent = days[i].toLocaleDateString(calLocale);
eventTitleBar.appendChild(title);
let closer = document.createElement("span");
closer.classList.add("mdCOverlayClose");
closer.addEventListener('click', function(e) {
if (debugging === true) console.log("Clicked close button: Tearing down daily agenda.");
tearDownById("mdCOverlay");
});
eventTitleBar.appendChild(closer);
let ul = document.createElement("ul");
// Append events.
for (let j = 0, maxj = events.length; j < maxj; j++) {
let start = new Date(events[j].start);
let end = new Date(events[j].end);
if (start.getTime() > dayTime || end.getTime() < dayTimePlusDay) continue;
let eventLi = document.createElement("li");
let eventSignifier = document.createElement("a");
eventSignifier.textContent = events[j].name;
if (events[j].link) eventSignifier.href = events[j].link;
eventLi.appendChild(eventSignifier);
let eventP = document.createElement("p");
eventP.textContent = start.toLocaleDateString(calLocale) + " - " + end.toLocaleDateString(calLocale);
if (events[j].place !== undefined) eventP.textContent = eventP.textContent + ", " + events[j].place;
eventLi.appendChild(eventP);
let eventDesc = document.createElement("p");
eventDesc.textContent = events[j].description;
eventLi.appendChild(eventDesc);
ul.appendChild(eventLi);
}
overlay.appendChild(ul);
document.getElementsByTagName("body")[0].appendChild(overlay);
});
if (td.childElementCount > 6) td.classList.add("mdCManyElements");
daysTDs[days[i].getFullYear() + "-" + days[i].getMonth() + "-" + days[i].getDate()] = td;
}
table.appendChild(tbody);
mdCalDiv.appendChild(table);
target.appendChild(mdCalDiv);
// Enable closing overlay by pressing escape.
document.addEventListener('keydown', function(e) {
if (e.keyCode !== 27) return;
if (debugging === true) console.log("Pressed escape: Tearing down daily agenda.");
tearDownById("mdCOverlay");
});
return daysTDs;
}
(function() {
let calendars = document.getElementsByClassName("mdCalendar");
for (let i = 0, max = calendars.length; i < max; i++) {
queryPage(
encodeURI(calendars[i].getAttribute("data-src")),
function (request) {
if (debugging === true) console.log("Loaded\n" + request.response);
let elements = JSON.parse(request.response);
let tCalendar = createCalendarTable(calendars[i], elements);
});
}
}
)();
});

66
object.php Normal file
View File

@ -0,0 +1,66 @@
<?PHP
/**
* This file displays object detail pages as fetched from museum-digital.
*
* @author Joshua Ramon Enslin <joshua@jrenslin.de>
*/
// Include functions and settings.
require_once __DIR__ . "/inc/functions.php";
// Check validity of request.
if (!isset($_GET['id']) or !is_numeric($_GET['id'])) {
echo printErrorPage("Object does not exist.");
return;
}
// Ensure working environment for frontend.
ensureEnvironment();
$pages = loadPublicPages(); // Load overview of pages.
$contents = json_decode(queryCachePage($settings['mdVersion'] . "?t=objekt&oges=" . urlencode($_GET['id']) . "&output=json", "object", $settings), true);
if (!$contents || (isset($contents[0]) and $contents[0] == "There is no object with this ID yet.")) {
echo printErrorPage("Temporarily unavailable.");
return;
}
/*
* Output
*/
$addToHead = '
<link rel="canonical" href="' . $settings['mdVersion'] . 'index.php?t=objekt&oges=' . $contents['object_id'] . '" />';
echo printPublicHead($settings, $_GET['id'], $settings['pageTitle'] . " - " . $contents['object_name'], $settings['logo'], $addToHead);
echo printPublicHeader($settings['pageTitle']);
echo printStaticPagePart("banner", "header"); // Print aside (if need be)
echo generatePublicNav($pages);
echo '
<div id="mainWrapper">
';
// Print main content
echo '
<main>';
echo drawObjectDetails($contents, $settings);
echo '
</main>
';
echo printStaticPagePart("aside", "aside"); // Print aside (if need be)
echo '
</div>';
echo printStaticPagePart("footer", "footer"); // Print footer (if need be)
echo printPublicEnd();
?>

73
search.php Normal file
View File

@ -0,0 +1,73 @@
<?PHP
/**
* This file runs the search.
*
* @file
*
* @author Joshua Ramon Enslin <joshua@jrenslin.de>
*/
// Include functions and settings.
require_once __DIR__ . "/inc/functions.php";
require_once __DIR__ . "/inc/search.php";
// Ensure working environment for frontend.
ensureEnvironment();
$pages = loadPublicPages(); // Load overview of pages.
/*
* Load data.
*/
/*
* Output
*/
echo printPublicHead($settings, "search", $settings['pageTitle'], $settings['logo']);
echo printPublicHeader($settings['pageTitle']);
echo printStaticPagePart("banner", "header"); // Print aside (if need be)
echo generatePublicNav($pages);
echo '
<div id="mainWrapper">
';
// Print main content
echo '
<main>';
if (!isset($_GET['q'])) {
echo '
<h1><span class="toTranslate" data-content="Search"></span></h1>
<form action="" method="GET">
<input type="search" name="q" />
</form>
';
}
else {
echo '<h1><span class="toTranslate" data-content="SearchingFor"></span> "' . $_GET['q'] . '"</h1>';
$resultsInPages = searchInPages($_GET['q']);
print_r($resultsInPages);
}
echo '
</main>
';
echo printStaticPagePart("aside", "aside"); // Print aside (if need be)
echo '
</div>';
echo printStaticPagePart("footer", "footer"); // Print footer (if need be)
echo printPublicEnd();
?>

View File

@ -2,6 +2,12 @@
* Default theme for the backend of md:cms.
*/
/****************************
* Imports
*/
@import 'mdEmbeds.css';
/****************************
* Load fonts
*/
@ -82,7 +88,7 @@ aside > * { display: block; padding: 1em; border: 1px solid #D6D6D6; }
body > header:nth-child(2) { margin: 0; padding: 0; }
body > header:nth-child(2) p { margin: 0; padding: 0; }
body > header:nth-child(2) img { width: 100vw; height: 20vh; object-fit: cover; margin-bottom: -.4em; }
body > header:nth-child(2) img { width: 100%; height: 20vh; object-fit: cover; margin-bottom: -.4em; }
/************
* Main wrapper
@ -104,7 +110,7 @@ body > nav { display: block; background: #212121; color: #EEE;
border: 1px solid #D6D6D6; border-width: 0; padding: 0 5vw; }
body > nav ul { list-style: none; margin: 0; padding: 0 0; }
body > nav li { position: relative; }
body > nav > ul { display: block; border-bottom: 1px solid #D6D6D6; }
body > nav > ul { display: block; }
body > nav > ul > li { display: inline-block; }
body > nav > ul > li ul { position: absolute; left: 0; top: 100%;
background: #424242; color: #EEE; z-index: 2; animation: fade-in .4s; }

View File

@ -0,0 +1,54 @@
/**
* Default theme for the backend of md:cms:
* Styles for data embedded from museum-digital.
*/
/**********
* Simple tiles for displaying an object.
*/
div.objTile { display: inline-block; width: 200px; border: 1px solid #D6D6D6; text-align: center;
transition: background .4s, box-shadow .4s; }
div.objTile > div { position: relative; padding: .5em .5em 1.5em .5em; }
div.objTile h4 { margin: 0; padding: 0 .5em; }
div.objTile img { max-width: 198px; }
div.objTile:hover { background: #F2F2F2; box-shadow: 1px 1px 3px #D6D6D6, -1px -1px 3px #D6D6D6; }
div.objTile > div > div { color: #888; font-size: .8em; }
div.objTile > div > div > a { position: absolute; top: 100%; left: -1px;
display: none; max-width: 50%; padding: .3em;
background: #FFF; text-align: left; border: 1px solid #D6D6D6;
transition: background .4s, color .4s; }
div.objTile:hover > div > div > a { display: block; animation: fade-in .4s; }
div.objTile > div > div > a:last-child { left: initial; right: -1px; text-align: right; }
div.objTile > div > div > a:hover { background: #EEE; color: #212121; }
/**********
* Object details.
*/
div.objDetails { }
div.objDetails dt { font-weight: bold; margin-top: .5em; }
div.objDetails dd { margin-left: 0; padding-left: 0; }
div.objDetails .objMainImg { max-width: 200px; margin: .5em 1em 1em 0; float: left;
border: 1px solid #D6D6D6; transition: max-width .4s; }
div.objDetails .objMainImg:hover { max-width: 100vw; max-height: 80vh; float: initial; }
.metadataLine { margin-top: 2.5em; color: #666; border-top: 1px solid #D6D6D6; }
.metadataLine > span { margin-right: 2em; font-size: .95em; font-style: italic; }
/*****
* Events on object detail page.
*/
.events { display: block; max-width: 100%; width: auto; margin: 0 0 1em 0; }
.events > * { display: inline-block; margin: 0 1em 1em 0; padding: 0 1em; border: 1px solid #D6D6D6;
vertical-align: top; transition: box-shadow .4s; }
.events h5 { margin: 0; padding: .5em 1em; font-size: .9em; background: #F2F2F2; color: #646464; }
.events tr * { padding: .1em .5em; vertical-align: middle; font-size: .95em; }
.events th { font-weight: normal; color: #646464; }
.events table { margin: .5em 0; }
.events > *:hover { box-shadow: 1px 1px 3px #D6D6D6, -1px -1px 3px #D6D6D6; }
.events tr:hover { background: initial; }

1
themes/imports.css Normal file
View File

@ -0,0 +1 @@
@import 'mdCalendar.css';

151
themes/mdCalendar.css Normal file
View File

@ -0,0 +1,151 @@
/**
* CSS file for mdCalendar.
*/
.mdCalendar { display: block; }
.mdCTable { width: 100%; table-layout: fixed; font-size: .9em; }
/**
* Table
*/
.mdCTable thead tr { border: 1px solid #D6D6D6; }
.mdCTable th { color: #646464; font-weight: normal; text-align: center; }
.mdCTable td { padding: 0; height: 10em; background: #FFF; border: 1px solid #D6D6D6; vertical-align: top; }
.mdCTable *:hover { z-index: 2; }
.mdCTable time { display: block; color: #646464; border-bottom: 1px dashed #D6D6D6; font-weight: bold;
cursor: pointer; transition: background .4s, color .4s; }
.mdCTable time:hover { background: #03A9F4; color: #FFF; }
.mdCTable td > * { display: block; padding: .3em .5em; line-height: 1.2em; }
.mdCTable td > a { position: relative; margin: .2em; padding-left: .7em;
background: rgba(240, 240, 240, .2); color: #424242; border: 1px solid #D6D6D6;
cursor: pointer; transition: background .4s, color .4s; }
.mdCTable td > a:hover { background: #333; color: #FFF; z-index: 2; }
.mdCTable td > a:before { content: " "; position: absolute; left: 0; top: 0; width: .5em; height: 100%; }
.mdCTable .mdCToday { background: #FAFAFA; }
.mdCTable .mdCOtherMonth { color: #646464; background: #EEE; }
/**
* Table head.
*/
.mdCTitleLine { display: table; width: 100%; margin: .5em 0; padding: 0; }
.mdCTitleLine > * { display: table-cell; vertical-align: middle; }
.mdCTitleLine > h3 { font-weight: normal; color: #424242; }
.mdCNav { width: 200px; color: #424242; text-align: right; }
.mdCNav > * { transition: background .4s, color .4s; font-size: 1em; padding: .5em; }
.mdCNav > *:hover { background: #F2F2F2; color: #000; }
.mdCNavPrev:before { content: "<"; }
.mdCNavNext:before { content: ">"; }
/**
* Alternative display of events in case there are too many for a day.
*/
.mdCTable td.mdCManyElements > a { display: inline-block; height: 1.1em; width: 1.1em; }
.mdCTable td.mdCManyElements > a:before { width: 100%; }
.mdCTable td.mdCManyElements > a span { display: none; }
@media screen and (max-width: 75em) {
.mdCTable td > a { display: inline-block; height: 1.1em; width: 1.1em; }
.mdCTable td > a span { display: none; }
.mdCTable td > a:before { width: 100%; }
}
/**
* Tooltips
*/
.mdCToolTip { position: absolute; top: calc(100% + 22px); left: 50%; transform: translate(-50%, 0);
width: 300px; padding: 1em 0;
background: #FFF; border: 1px solid #646464;
z-index: 1000; animation: fade-in-tooltip .4s; }
.mdCToolTip:before,
.mdCToolTip:after { content: " "; position: absolute; bottom: calc(100% - 1px); left: 50%; transform: translate(-50%, 0);
display: block; width: 0; height: 0;
border-left: 21px solid transparent; border-right: 21px solid transparent;
border-bottom: 21px solid #FFF; z-index: 1002; }
.mdCToolTip:after { border-left: 22px solid transparent; border-right: 22px solid transparent;
border-bottom: 22px solid #646464; z-index: 1001; }
.mdCToolTip tr * { height: auto; padding: .5em; vertical-align: top; text-align: left; color: #424242; border: 0; }
.mdCToolTip tr:first-child * { padding-top: 1em; }
.mdCToolTip tr:last-child * { padding-bottom: 1em; }
/**
* Overlay
*/
#mdCOverlay { position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%);
max-height: 80vh; width: 600px; max-width: 80vw; background: #FFF;
box-shadow: 2px 2px 4px #D6D6D6, -2px -2px 4px #D6D6D6;
overflow-y: auto; z-index: 1003; }
.mdCOverlayTitleBar { display: table; width: 100%; }
.mdCOverlayTitleBar > * { display: table-cell; padding: .5rem 1.5rem; background: #EEE; color: #000; }
.mdCOverlayClose { width: 5em; text-align: right; }
.mdCOverlayClose:before { content: "x"; display: inline-block; padding: .1em .7em .3em .7em;
background: #03A9F4; color: #FFF; text-align: center;
border-radius: 100%; cursor: pointer; transition: background .4s; color .4s; }
.mdCOverlayClose:hover:before { background: #FFF; color: #03A9F4; }
#mdCOverlay ul { list-style: none; margin: 1em 1.5rem; padding: 0; }
#mdCOverlay li { margin: 0; padding: 1em 0; border-bottom: 1px solid #D6D6D6; }
#mdCOverlay li > a:first-child { color: #424242; font-weight: bold; }
/**
* @var {string[]} colorScheme Material design colors as taken from https://material.io/design/color/the-color-system.html#tools-for-picking-colors
*/
.color0:before { background: #F44336; }
.color1:before { background: #E53935; }
.color2:before { background: #D32F2F; }
.color3:before { background: #C62828; }
.color4:before { background: #B71C1C; }
.color5:before { background: #3F51B5; }
.color6:before { background: #3949AB; }
.color7:before { background: #303F9F; }
.color8:before { background: #283593; }
.color9:before { background: #1A237E; }
.color10:before { background: #2196F3; }
.color11:before { background: #1E88E5; }
.color12:before { background: #1976D2; }
.color13:before { background: #1565C0; }
.color14:before { background: #0D47A1; }
.color15:before { background: #03A9F4; }
.color16:before { background: #039BE5; }
.color17:before { background: #0288D1; }
.color18:before { background: #0277BD; }
.color19:before { background: #01579B; }
.color20:before { background: #00BCD4; }
.color21:before { background: #00ACC1; }
.color22:before { background: #0097A7; }
.color23:before { background: #00838F; }
.color24:before { background: #006064; }
.color25:before { background: #4CAF50; }
.color26:before { background: #43A047; }
.color27:before { background: #388E3C; }
.color28:before { background: #2E7D32; }
.color29:before { background: #1B5E20; }
/**
* Animations
*/
@keyframes fade-in-tooltip {
from { top: calc(100% + 19px); opacity: 0.1; }
to { top: calc(100% + 22px); opacity: 1.0; }
}