mikuli.cz

:)
git clone https://git.sr.ht/~ashymad/mikuli.cz
Log | Files | Refs

commit fabd68c66a4dbc1a971bdfd03be63ac2ba31e51b
parent 80db30ad71f2fe47697b61cacf10dcacf4cb1aed
Author: markseu <mark2011@mayberg.se>
Date:   Fri, 17 Feb 2017 14:52:55 +0100

System update (better software update)

Diffstat:
Msystem/config/config.ini | 23+++++++++++------------
Msystem/plugins/commandline.php | 66++++++++++++++++++++++++++++++++----------------------------------
Msystem/plugins/core.php | 514+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Msystem/plugins/image.php | 7++++---
Msystem/plugins/markdown.php | 7+++----
Msystem/plugins/update.php | 311++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Msystem/plugins/webinterface.css | 2+-
Msystem/plugins/webinterface.js | 44++++++++++++++++++++++++++++----------------
Msystem/plugins/webinterface.php | 289+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Asystem/themes/assets/flatsite.css | 189+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asystem/themes/assets/flatsite.php | 13+++++++++++++
Asystem/themes/assets/opensans-bold.woff | 0
Asystem/themes/assets/opensans-light.woff | 0
Asystem/themes/assets/opensans-regular.woff | 0
Dsystem/themes/flatsite.css | 172-------------------------------------------------------------------------------
15 files changed, 952 insertions(+), 685 deletions(-)

diff --git a/system/config/config.ini b/system/config/config.ini @@ -4,17 +4,20 @@ Sitename: Yellow Author: Yellow Email: webmaster Language: en +Timezone: UTC Theme: flatsite -# ServerScheme: http -# ServerName: your.domain.name -# ServerBase: -# ServerTime: UTC - +StaticUrl: +StaticDefaultFile: index.html +StaticErrorFile: 404.html +StaticDir: cache/ +MediaLocation: /media/ ImageLocation: /media/images/ PluginLocation: /media/plugins/ ThemeLocation: /media/themes/ AssetLocation: /media/themes/assets/ +MediaDir: media/ +ImageDir: media/images/ SystemDir: system/ ConfigDir: system/config/ PluginDir: system/plugins/ @@ -23,15 +26,10 @@ AssetDir: system/themes/assets/ SnippetDir: system/themes/snippets/ TemplateDir: system/themes/templates/ TrashDir: system/trash/ -MediaDir: media/ -ImageDir: media/images/ -StaticDir: cache/ -StaticDefaultFile: index.html -StaticErrorFile: 404.html -ContentPagination: page ContentDir: content/ ContentRootDir: default/ ContentHomeDir: home/ +ContentPagination: page ContentDefaultFile: page.txt ContentExtension: .txt ConfigExtension: .ini @@ -50,11 +48,12 @@ Parser: markdown ParserSafeMode: 0 MultiLanguageMode: 0 InstallationMode: 1 +StartupUpdateNotification: none UpdatePluginsUrl: https://github.com/datenstrom/yellow-plugins UpdateThemesUrl: https://github.com/datenstrom/yellow-themes UpdateInformationFile: update.ini UpdateVersionFile: version.ini -UpdateNotification: none +UpdateResourceFile: resource.ini WebinterfaceLocation: /edit/ WebinterfaceNewFile: page-new-(.*).txt WebinterfaceMetaFilePrefix: published diff --git a/system/plugins/commandline.php b/system/plugins/commandline.php @@ -5,7 +5,7 @@ // Command line plugin class YellowCommandline { - const VERSION = "0.6.17"; + const VERSION = "0.6.18"; var $yellow; //access to API var $files; //number of files var $errors; //number of errors @@ -65,8 +65,7 @@ class YellowCommandline $statusCode = 500; $this->files = 0; $this->errors = 1; $fileName = $this->yellow->config->get("configDir").$this->yellow->config->get("configFile"); - echo "ERROR building files: Please configure ServerScheme, ServerName, ServerBase, ServerTime in file '$fileName'!\n"; - echo "ERROR building files: Open your website in a web browser, if you want to see your server settings!\n"; + echo "ERROR building files: Please configure StaticUrl in file '$fileName'!\n"; } echo "Yellow $command: $this->files file".($this->files!=1 ? 's' : ''); echo ", $this->errors error".($this->errors!=1 ? 's' : ''); @@ -132,7 +131,9 @@ class YellowCommandline if(!is_readable($this->yellow->page->fileName)) { ob_start(); - $statusCode = $this->requestStaticFile($location); + $staticUrl = $this->yellow->config->get("staticUrl"); + list($scheme, $address, $base) = $this->yellow->lookup->getUrlInformation($staticUrl); + $statusCode = $this->requestStaticFile($scheme, $address, $base, $location); if($statusCode<400 || $error) { $fileData = ob_get_contents(); @@ -165,9 +166,9 @@ class YellowCommandline $this->yellow->page->set("pageError", "Can't write file '$fileName'!"); } } - if($statusCode==200 && $analyse) $this->analyseStaticFile($fileData); - if($statusCode==404 && $error) $statusCode = 200; + if($statusCode==200 && $analyse) $this->analyseStaticFile($scheme, $address, $base, $fileData); if($statusCode==404 && $probe) $statusCode = 100; + if($statusCode==404 && $error) $statusCode = 200; if($statusCode>=200) ++$this->files; if($statusCode>=400) { @@ -179,35 +180,39 @@ class YellowCommandline } // Request static file - function requestStaticFile($location) + function requestStaticFile($scheme, $address, $base, $location) { + list($serverName, $serverPort) = explode(':', $address); + if(is_null($serverPort)) $serverPort = $scheme=="https" ? 443 : 80; + $_SERVER["HTTPS"] = $scheme=="https" ? "on" : "off"; $_SERVER["SERVER_PROTOCOL"] = "HTTP/1.1"; - $_SERVER["SERVER_NAME"] = $this->yellow->config->get("serverName"); - $_SERVER["REQUEST_URI"] = $this->yellow->config->get("serverBase").$location; - $_SERVER["SCRIPT_NAME"] = $this->yellow->config->get("serverBase")."/yellow.php"; + $_SERVER["SERVER_NAME"] = $serverName; + $_SERVER["SERVER_PORT"] = $serverPort; + $_SERVER["REQUEST_URI"] = $base.$location; + $_SERVER["SCRIPT_NAME"] = $base."/yellow.php"; $_SERVER["REMOTE_ADDR"] = "127.0.0.1"; $_REQUEST = array(); return $this->yellow->request(); } // Analyse static file, detect locations with arguments - function analyseStaticFile($rawData) + function analyseStaticFile($scheme, $address, $base, $rawData) { - $serverName = $this->yellow->config->get("serverName"); - $serverBase = $this->yellow->config->get("serverBase"); $pagination = $this->yellow->config->get("contentPagination"); preg_match_all("/<(.*?)href=\"([^\"]+)\"(.*?)>/i", $rawData, $matches); foreach($matches[2] as $match) { - if(preg_match("/^(.*?)#(.*)$/", $match, $tokens)) $match = $tokens[1]; - if(preg_match("/^\w+:\/+(.*?)(\/.*)$/", $match, $tokens)) + $location = rawurldecode($match); + if(preg_match("/^(.*?)#(.*)$/", $location, $tokens)) $location = $tokens[1]; + if(preg_match("/^(\w+):\/\/([^\/]+)(.*)$/", $location, $tokens)) { - if($tokens[1]!=$serverName) continue; - $match = $tokens[2]; + if($tokens[1]!=$scheme) continue; + if($tokens[2]!=$address) continue; + $location = $tokens[3]; } - if(!$this->yellow->toolbox->isLocationArgs($match)) continue; - if(substru($match, 0, strlenu($serverBase))!=$serverBase) continue; - $location = rawurldecode(substru($match, strlenu($serverBase))); + if(substru($location, 0, strlenu($base))!=$base) continue; + $location = substru($location, strlenu($base)); + if(!$this->yellow->toolbox->isLocationArgs($location)) continue; if(!$this->yellow->toolbox->isLocationArgsPagination($location, $pagination)) { $location = rtrim($location, '/').'/'; @@ -308,9 +313,8 @@ class YellowCommandline // Show software version and updates function versionCommand($args) { - $statusCode = 0; - $serverSoftware = $this->yellow->toolbox->getServerSoftware(); - echo "Yellow ".YellowCore::VERSION.", PHP ".PHP_VERSION.", $serverSoftware\n"; + $serverVersion = $this->yellow->toolbox->getServerVersion(); + echo "Yellow ".YellowCore::VERSION.", PHP ".PHP_VERSION.", $serverVersion\n"; list($command) = $args; list($statusCode, $dataCurrent) = $this->getSoftwareVersion(); list($statusCode, $dataLatest) = $this->getSoftwareVersion(true); @@ -321,22 +325,17 @@ class YellowCommandline echo "$key $value\n"; } else { echo "$key $dataLatest[$key] - Update available\n"; - ++$updates; } } if($statusCode!=200) echo "ERROR checking updates: ".$this->yellow->page->get("pageError")."\n"; - if($updates) echo "Yellow $command: $updates update".($updates==1 ? "":"s")." available\n"; return $statusCode; } // Check static configuration function checkStaticConfig() { - $serverScheme = $this->yellow->config->get("serverScheme"); - $serverName = $this->yellow->config->get("serverName"); - $serverBase = $this->yellow->config->get("serverBase"); - return !empty($serverScheme) && !empty($serverName) && - $this->yellow->lookup->isValidLocation($serverBase) && $serverBase!="/"; + $staticUrl = $this->yellow->config->get("staticUrl"); + return !empty($staticUrl); } // Check static directory @@ -380,10 +379,9 @@ class YellowCommandline function getContentLocations() { $locations = array(); - $serverScheme = $this->yellow->config->get("serverScheme"); - $serverName = $this->yellow->config->get("serverName"); - $serverBase = $this->yellow->config->get("serverBase"); - $this->yellow->page->setRequestInformation($serverScheme, $serverName, $serverBase, "", ""); + $staticUrl = $this->yellow->config->get("staticUrl"); + list($scheme, $address, $base) = $this->yellow->lookup->getUrlInformation($staticUrl); + $this->yellow->page->setRequestInformation($scheme, $address, $base, "", ""); foreach($this->yellow->pages->index(true, true) as $page) { if($page->get("status")!="ignore" && $page->get("status")!="draft") diff --git a/system/plugins/core.php b/system/plugins/core.php @@ -5,7 +5,7 @@ // Yellow core class YellowCore { - const VERSION = "0.6.7"; + const VERSION = "0.6.8"; var $page; //current page var $pages; //pages from file system var $files; //files from file system @@ -31,15 +31,20 @@ class YellowCore $this->config->setDefault("author", "Yellow"); $this->config->setDefault("email", "webmaster"); $this->config->setDefault("language", "en"); + $this->config->setDefault("timezone", "UTC"); $this->config->setDefault("theme", "default"); - $this->config->setDefault("serverScheme", $this->toolbox->getServerScheme()); - $this->config->setDefault("serverName", $this->toolbox->getServerName()); - $this->config->setDefault("serverBase", $this->toolbox->getServerBase()); - $this->config->setDefault("serverTime", $this->toolbox->getServerTime()); + $this->config->setDefault("serverUrl", ""); + $this->config->setDefault("staticUrl", ""); + $this->config->setDefault("staticDefaultFile", "index.html"); + $this->config->setDefault("staticErrorFile", "404.html"); + $this->config->setDefault("staticDir", "cache/"); + $this->config->setDefault("mediaLocation", "/media/"); $this->config->setDefault("imageLocation", "/media/images/"); $this->config->setDefault("pluginLocation", "/media/plugins/"); $this->config->setDefault("themeLocation", "/media/themes/"); $this->config->setDefault("assetLocation", "/media/themes/assets/"); + $this->config->setDefault("mediaDir", "media/"); + $this->config->setDefault("imageDir", "media/images/"); $this->config->setDefault("systemDir", "system/"); $this->config->setDefault("configDir", "system/config/"); $this->config->setDefault("pluginDir", "system/plugins/"); @@ -48,15 +53,10 @@ class YellowCore $this->config->setDefault("snippetDir", "system/themes/snippets/"); $this->config->setDefault("templateDir", "system/themes/templates/"); $this->config->setDefault("trashDir", "system/trash/"); - $this->config->setDefault("mediaDir", "media/"); - $this->config->setDefault("imageDir", "media/images/"); - $this->config->setDefault("staticDir", "cache/"); - $this->config->setDefault("staticDefaultFile", "index.html"); - $this->config->setDefault("staticErrorFile", "404.html"); - $this->config->setDefault("contentPagination", "page"); $this->config->setDefault("contentDir", "content/"); $this->config->setDefault("contentRootDir", "default/"); $this->config->setDefault("contentHomeDir", "home/"); + $this->config->setDefault("contentPagination", "page"); $this->config->setDefault("contentDefaultFile", "page.txt"); $this->config->setDefault("contentExtension", ".txt"); $this->config->setDefault("configExtension", ".ini"); @@ -76,6 +76,12 @@ class YellowCore $this->config->setDefault("parserSafeMode", "0"); $this->config->setDefault("multiLanguageMode", "0"); $this->config->setDefault("installationMode", "0"); + $this->config->setDefault("startupUpdateNotification", "none"); + } + + function __destruct() + { + $this->shutdown(); } // Handle initialisation @@ -83,19 +89,35 @@ class YellowCore { if(defined("DEBUG") && DEBUG>=2) { - $serverSoftware = $this->toolbox->getServerSoftware(); - echo "Yellow ".YellowCore::VERSION.", PHP ".PHP_VERSION.", $serverSoftware<br>\n"; + $serverVersion = $this->toolbox->getServerVersion(); + echo "Yellow ".YellowCore::VERSION.", PHP ".PHP_VERSION.", $serverVersion<br/>\n"; } - $this->pages->requestHandler = "none"; $this->config->load($this->config->get("configDir").$this->config->get("configFile")); $this->text->load($this->config->get("pluginDir").$this->config->get("textFile")); $this->lookup->load(); - $this->plugins->load(); $this->themes->load(); + $this->plugins->load(); + $this->startup(); + } + + // Handle startup + function startup() + { + $tokens = explode(',', $this->config->get("startupUpdateNotification")); foreach($this->plugins->plugins as $key=>$value) { - if(method_exists($value["obj"], "onUpdate")) $value["obj"]->onUpdate(""); + if(method_exists($value["obj"], "onStartup")) $value["obj"]->onStartup(in_array($value["plugin"], $tokens)); + } + foreach($this->themes->themes as $key=>$value) + { + if(method_exists($value["obj"], "onStartup")) $value["obj"]->onStartup(in_array($value["theme"], $tokens)); } + if($this->config->get("startupUpdateNotification")!="none") + { + $fileNameConfig = $this->config->get("configDir").$this->config->get("configFile"); + $this->config->update($fileNameConfig, array("startupUpdateNotification" => "none")); + } + if(defined("DEBUG") && DEBUG>=2) echo "YellowCore::startup<br/>\n"; } // Handle request @@ -105,21 +127,21 @@ class YellowCore $statusCode = 0; $this->toolbox->timerStart($time); $this->toolbox->normaliseRequest(); - list($serverScheme, $serverName, $base, $location, $fileName) = $this->getRequestInformation(); - $this->page->setRequestInformation($serverScheme, $serverName, $base, $location, $fileName); + list($scheme, $address, $base, $location, $fileName) = $this->getRequestInformation(); + $this->page->setRequestInformation($scheme, $address, $base, $location, $fileName); foreach($this->plugins->plugins as $key=>$value) { if(method_exists($value["obj"], "onRequest")) { - $this->pages->requestHandler = $key; - $statusCode = $value["obj"]->onRequest($serverScheme, $serverName, $base, $location, $fileName); + $this->lookup->requestHandler = $key; + $statusCode = $value["obj"]->onRequest($scheme, $address, $base, $location, $fileName); if($statusCode!=0) break; } } if($statusCode==0) { - $this->pages->requestHandler = "core"; - $statusCode = $this->processRequest($serverScheme, $serverName, $base, $location, $fileName, true); + $this->lookup->requestHandler = "core"; + $statusCode = $this->processRequest($scheme, $address, $base, $location, $fileName, true); } if($this->page->isExisting("pageError")) $statusCode = $this->processRequestError(); $this->toolbox->timerStop($time); @@ -127,14 +149,13 @@ class YellowCore if(defined("DEBUG") && DEBUG>=1) { $handler = $this->getRequestHandler(); - echo "YellowCore::request status:$statusCode location:$location handler:$handler<br/>\n"; - echo "YellowCore::request time:$time ms<br/>\n"; + echo "YellowCore::request status:$statusCode handler:$handler time:$time ms<br/>\n"; } return $statusCode; } // Process request - function processRequest($serverScheme, $serverName, $base, $location, $fileName, $cacheable) + function processRequest($scheme, $address, $base, $location, $fileName, $cacheable) { $statusCode = 0; if(is_readable($fileName)) @@ -143,7 +164,7 @@ class YellowCore { $statusCode = 303; $location = $location.$this->getRequestLocationArgsClean(); - $location = $this->lookup->normaliseUrl($serverScheme, $serverName, $base, $location); + $location = $this->lookup->normaliseUrl($scheme, $address, $base, $location); $this->sendStatus($statusCode, $location); } } else { @@ -151,16 +172,16 @@ class YellowCore { $statusCode = 301; $location = $this->lookup->isFileLocation($location) ? "$location/" : "/".$this->getRequestLanguage()."/"; - $location = $this->lookup->normaliseUrl($serverScheme, $serverName, $base, $location); + $location = $this->lookup->normaliseUrl($scheme, $address, $base, $location); $this->sendStatus($statusCode, $location); } } if($statusCode==0) { - $fileName = $this->lookup->findFileStatic($location, $fileName, $cacheable); + $fileName = $this->lookup->findFileStatic($location, $fileName, $cacheable && !$this->isCommandLine()); if($this->lookup->isContentFile($fileName) || !is_readable($fileName)) { - $fileName = $this->readPage($serverScheme, $serverName, $base, $location, $fileName, $cacheable, + $fileName = $this->readPage($scheme, $address, $base, $location, $fileName, $cacheable, max(is_readable($fileName) ? 200 : 404, $this->page->statusCode), $this->page->get("pageError")); $statusCode = $this->sendPage(); } else { @@ -175,7 +196,7 @@ class YellowCore function processRequestError() { ob_clean(); - $fileName = $this->readPage($this->page->serverScheme, $this->page->serverName, $this->page->base, + $fileName = $this->readPage($this->page->scheme, $this->page->address, $this->page->base, $this->page->location, $this->page->fileName, $this->page->cacheable, $this->page->statusCode, $this->page->get("pageError")); $statusCode = $this->sendPage(); @@ -184,7 +205,7 @@ class YellowCore } // Read page - function readPage($serverScheme, $serverName, $base, $location, $fileName, $cacheable, $statusCode, $pageError) + function readPage($scheme, $address, $base, $location, $fileName, $cacheable, $statusCode, $pageError) { if($statusCode>=400) { @@ -193,7 +214,7 @@ class YellowCore $cacheable = false; } $this->page = new YellowPage($this); - $this->page->setRequestInformation($serverScheme, $serverName, $base, $location, $fileName); + $this->page->setRequestInformation($scheme, $address, $base, $location, $fileName); $this->page->parseData($this->toolbox->readFile($fileName), $cacheable, $statusCode, $pageError); $this->text->setLanguage($this->page->get("language")); $this->page->parseContent(); @@ -265,52 +286,71 @@ class YellowCore { if(method_exists($value["obj"], "onCommand")) { + $this->lookup->commandHandler = $key; $statusCode = $value["obj"]->onCommand(func_get_args()); if($statusCode!=0) break; } } if($statusCode==0) { + $this->lookup->commandHandler = "core"; $statusCode = 400; list($command) = func_get_args(); echo "Yellow $command: Command not found\n"; } $this->toolbox->timerStop($time); - if(defined("DEBUG") && DEBUG>=1) echo "YellowCore::command time:$time ms<br/>\n"; + if(defined("DEBUG") && DEBUG>=1) + { + $handler = $this->getCommandHandler(); + echo "YellowCore::command status:$statusCode handler:$handler time:$time ms<br/>\n"; + } return $statusCode; } + // Handle shutdown + function shutdown() + { + foreach($this->plugins->plugins as $key=>$value) + { + if(method_exists($value["obj"], "onShutdown")) $value["obj"]->onShutdown(); + } + foreach($this->themes->themes as $key=>$value) + { + if(method_exists($value["obj"], "onShutdown")) $value["obj"]->onShutdown(); + } + if(defined("DEBUG") && DEBUG>=2) echo "YellowCore::shutdown<br/>\n"; + } + // Parse snippet function snippet($name, $args = null) { - $this->pages->snippetArgs = func_get_args(); + $this->lookup->snippetArgs = func_get_args(); $this->page->parseSnippet($name); } + // Return snippet arguments + function getSnippetArgs() + { + return $this->lookup->snippetArgs; + } + // Return request information - function getRequestInformation($serverScheme = "", $serverName = "", $base = "") + function getRequestInformation($scheme = "", $address = "", $base = "") { - $serverScheme = empty($serverScheme) ? $this->config->get("serverScheme") : $serverScheme; - $serverName = empty($serverName) ? $this->config->get("serverName") : $serverName; - $base = empty($base) ? $this->config->get("serverBase") : $base; - $location = $this->toolbox->getLocation(); - $location = substru($location, strlenu($base)); - if(preg_match("/\.(css|ico|js|jpg|png|svg|txt|woff)$/", $location)) + if(empty($scheme) && empty($address) && empty($base)) { - $pluginLocationLength = strlenu($this->config->get("pluginLocation")); - $themeLocationLength = strlenu($this->config->get("themeLocation")); - if(substru($location, 0, $pluginLocationLength)==$this->config->get("pluginLocation")) { - $fileName = $this->config->get("pluginDir").substru($location, $pluginLocationLength); - } else if(substru($location, 0, $themeLocationLength)==$this->config->get("themeLocation")) { - $fileName = $this->config->get("themeDir").substru($location, $themeLocationLength); - } else if($location=="/".$this->config->get("robotsFile")) { - $fileName = $this->config->get("configDir").$this->config->get("robotsFile"); - } else if($location=="/".$this->config->get("faviconFile")) { - $fileName = $this->config->get("assetDir").$this->config->get("siteicon").".png"; - } + $url = $this->config->get("serverUrl"); + if(empty($url) || $this->isCommandLine()) $url = $this->toolbox->getServerUrl(); + list($scheme, $address, $base) = $this->lookup->getUrlInformation($url); + $this->config->set("serverScheme", $scheme); + $this->config->set("serverAddress", $address); + $this->config->set("serverBase", $base); } + $location = substru($this->toolbox->getLocation(), strlenu($base)); + if(empty($fileName)) $fileName = $this->lookup->findFileFromSystem($location); + if(empty($fileName)) $fileName = $this->lookup->findFileFromMedia($location); if(empty($fileName)) $fileName = $this->lookup->findFileFromLocation($location); - return array($serverScheme, $serverName, $base, $location, $fileName); + return array($scheme, $address, $base, $location, $fileName); } // Return request location @@ -328,13 +368,19 @@ class YellowCore // Return request handler function getRequestHandler() { - return $this->pages->requestHandler; + return $this->lookup->requestHandler; + } + + // Return command handler + function getCommandHandler() + { + return $this->lookup->commandHandler; } - // Return snippet arguments - function getSnippetArgs() + // Check if running at command line + function isCommandLine() { - return $this->pages->snippetArgs; + return !empty($this->lookup->commandHandler); } } @@ -342,8 +388,8 @@ class YellowCore class YellowPage { var $yellow; //access to API - var $serverScheme; //server scheme - var $serverName; //server name + var $scheme; //server scheme + var $address; //server address var $base; //base location var $location; //page location var $fileName; //content file name @@ -374,10 +420,10 @@ class YellowPage } // Set request information - function setRequestInformation($serverScheme, $serverName, $base, $location, $fileName) + function setRequestInformation($scheme, $address, $base, $location, $fileName) { - $this->serverScheme = $serverScheme; - $this->serverName = $serverName; + $this->scheme = $scheme; + $this->address = $address; $this->base = $base; $this->location = $location; $this->fileName = $fileName; @@ -420,7 +466,7 @@ class YellowPage $this->set("language", $this->yellow->lookup->findLanguageFromFile($this->fileName, $this->yellow->config->get("language"))); $this->set("theme", $this->yellow->lookup->findNameFromFile($this->fileName, - $this->yellow->config->get("themeDir"), $this->yellow->config->get("theme"), ".css")); + $this->yellow->config->get("assetDir"), $this->yellow->config->get("theme"), ".css")); $this->set("template", $this->yellow->lookup->findNameFromFile($this->fileName, $this->yellow->config->get("templateDir"), $this->yellow->config->get("template"), ".html")); $this->set("modified", date("Y-m-d H:i:s", $this->yellow->toolbox->getFileModified($this->fileName))); @@ -433,11 +479,11 @@ class YellowPage if($this->get("status")=="hidden") $this->available = false; $this->set("pageRead", $this->yellow->lookup->normaliseUrl( $this->yellow->config->get("serverScheme"), - $this->yellow->config->get("serverName"), + $this->yellow->config->get("serverAddress"), $this->yellow->config->get("serverBase"), $this->location)); $this->set("pageEdit", $this->yellow->lookup->normaliseUrl( - $this->yellow->config->get("webinterfaceServerScheme"), - $this->yellow->config->get("webinterfaceServerName"), + $this->yellow->config->get("serverScheme"), + $this->yellow->config->get("serverAddress"), $this->yellow->config->get("serverBase"), rtrim($this->yellow->config->get("webinterfaceLocation"), '/').$this->location)); $this->set("pageFile", $this->yellow->lookup->normaliseFile($this->fileName)); @@ -538,21 +584,12 @@ class YellowPage $output = "<span class=\"".htmlspecialchars($name)."\">\n"; if($text=="version") { - $serverSoftware = $this->yellow->toolbox->getServerSoftware(); - $output .= "Yellow ".YellowCore::VERSION.", PHP ".PHP_VERSION.", $serverSoftware<br />\n"; - foreach($this->yellow->plugins->getData() as $key=>$value) + $serverVersion = $this->yellow->toolbox->getServerVersion(); + $output .= "Yellow ".YellowCore::VERSION.", PHP ".PHP_VERSION.", $serverVersion<br />\n"; + foreach(array_merge($this->yellow->plugins->getData(), $this->yellow->themes->getData()) as $key=>$value) { $output .= htmlspecialchars("$key $value")."<br />\n"; } - foreach($this->yellow->themes->getData() as $key=>$value) - { - $output .= htmlspecialchars("$key $value")."<br />\n"; - } - } else { - foreach($this->yellow->config->getData($text) as $key=>$value) - { - $output .= htmlspecialchars(ucfirst($key).": ".$value)."<br />\n"; - } } $output .= "</span>\n"; if($this->parserSafeMode) $this->error(500, "Yellow '$text' is not available in safe mode!"); @@ -597,12 +634,15 @@ class YellowPage { $this->error(500, "Parser '".$this->get("parser")."' does not exist!"); } - if($this->yellow->lookup->isNestedLocation($this->location, true)) $this->error(500, "Home folder may not contain subfolders!"); + if($this->yellow->lookup->isNestedLocation($this->location, $this->fileName, true)) + { + $this->error(500, "Folder '".dirname($this->fileName)."' may not contain subfolders!"); + } if($this->yellow->toolbox->isRequestSelf()) $this->error(500, "Rewrite module not enabled on this server!"); if($this->yellow->getRequestHandler()=="core" && $this->isExisting("redirect") && $this->statusCode==200) { - $location = $this->yellow->lookup->normaliseLocation($this->get("redirect"), $this->base, $this->location); - $location = $this->yellow->lookup->normaliseUrl($this->serverScheme, $this->serverName, "", $location); + $location = $this->yellow->lookup->normaliseLocation($this->get("redirect"), $this->location); + $location = $this->yellow->lookup->normaliseUrl($this->scheme, $this->address, "", $location); $this->clean(301, $location); } if($this->yellow->getRequestHandler()=="core" && !$this->isAvailable() && $this->statusCode==200) @@ -759,10 +799,10 @@ class YellowPage return $absoluteLocation ? $this->base.$this->location : $this->location; } - // Return page URL with server scheme and server name + // Return page URL function getUrl() { - return $this->yellow->lookup->normaliseUrl($this->serverScheme, $this->serverName, $this->base, $this->location); + return $this->yellow->lookup->normaliseUrl($this->scheme, $this->address, $this->base, $this->location); } // Return page extra HTML data @@ -779,10 +819,10 @@ class YellowPage } if($name=="header") { - if(is_file($this->yellow->config->get("themeDir").$this->get("theme").".css")) + if(is_file($this->yellow->config->get("assetDir").$this->get("theme").".css")) { $location = $this->yellow->config->get("serverBase"). - $this->yellow->config->get("themeLocation").$this->get("theme").".css"; + $this->yellow->config->get("assetLocation").$this->get("theme").".css"; $output .= "<link rel=\"stylesheet\" type=\"text/css\" media=\"all\" href=\"".htmlspecialchars($location)."\" />\n"; } if(is_file($this->yellow->config->get("assetDir").$this->get("theme").".js")) @@ -1252,8 +1292,6 @@ class YellowPages { var $yellow; //access to API var $pages; //scanned pages - var $requestHandler; //request handler name - var $snippetArgs; //requested snippet arguments function __construct($yellow) { @@ -1268,8 +1306,8 @@ class YellowPages { if(defined("DEBUG") && DEBUG>=2) echo "YellowPages::scanLocation location:$location<br/>\n"; $this->pages[$location] = array(); - $serverScheme = $this->yellow->page->serverScheme; - $serverName = $this->yellow->page->serverName; + $scheme = $this->yellow->page->scheme; + $address = $this->yellow->page->address; $base = $this->yellow->page->base; if(empty($location)) { @@ -1278,7 +1316,7 @@ class YellowPages { list($rootLocation, $fileName) = explode(' ', $rootLocation, 2); $page = new YellowPage($this->yellow); - $page->setRequestInformation($serverScheme, $serverName, $base, $rootLocation, $fileName); + $page->setRequestInformation($scheme, $address, $base, $rootLocation, $fileName); $page->parseData("", false, 0); array_push($this->pages[$location], $page); } @@ -1287,7 +1325,7 @@ class YellowPages foreach($fileNames as $fileName) { $page = new YellowPage($this->yellow); - $page->setRequestInformation($serverScheme, $serverName, $base, + $page->setRequestInformation($scheme, $address, $base, $this->yellow->lookup->findLocationFromFile($fileName), $fileName); $page->parseData($this->yellow->toolbox->readFile($fileName, 4096), false, 0); if(strlenb($page->rawData)<4096) $page->statusCode = 200; @@ -1475,8 +1513,8 @@ class YellowFiles { if(defined("DEBUG") && DEBUG>=2) echo "YellowFiles::scanLocation location:$location<br/>\n"; $this->files[$location] = array(); - $serverScheme = $this->yellow->page->serverScheme; - $serverName = $this->yellow->page->serverName; + $scheme = $this->yellow->page->scheme; + $address = $this->yellow->page->address; $base = $this->yellow->config->get("serverBase"); if(empty($location)) { @@ -1496,7 +1534,7 @@ class YellowFiles foreach($fileNames as $fileName) { $file = new YellowPage($this->yellow); - $file->setRequestInformation($serverScheme, $serverName, $base, "/".$fileName, $fileName); + $file->setRequestInformation($scheme, $address, $base, "/".$fileName, $fileName); $file->parseData(null, false, 0); array_push($this->files[$location], $file); } @@ -1507,7 +1545,7 @@ class YellowFiles // Return page with media file information, null if not found function find($location, $absoluteLocation = false) { - if($absoluteLocation) $location = substru($location, strlenu($this->yellow->page->base)); + if($absoluteLocation) $location = substru($location, strlenu($this->yellow->config->get("serverBase"))); foreach($this->scanLocation($this->getParentLocation($location)) as $file) { if($file->location==$location) @@ -1566,13 +1604,13 @@ class YellowFiles // Return home location function getHomeLocation($location) { - return "/".$this->yellow->config->get("mediaDir"); + return $this->yellow->config->get("mediaLocation"); } // Return parent location function getParentLocation($location) { - $token = rtrim("/".$this->yellow->config->get("mediaDir"), '/'); + $token = rtrim($this->yellow->config->get("mediaLocation"), '/'); if(preg_match("#^($token.*\/).+?$#", $location, $matches)) { if($matches[1]!="$token/" || $this->yellow->lookup->isFileLocation($location)) $parentLocation = $matches[1]; @@ -1584,7 +1622,7 @@ class YellowFiles // Return top-level location function getParentTopLocation($location) { - $token = rtrim("/".$this->yellow->config->get("mediaDir"), '/'); + $token = rtrim($this->yellow->config->get("mediaLocation"), '/'); if(preg_match("#^($token.+?\/)#", $location, $matches)) $parentTopLocation = $matches[1]; if(empty($parentTopLocation)) $parentTopLocation = "$token/"; return $parentTopLocation; @@ -1606,16 +1644,17 @@ class YellowPlugins } // Load plugins - function load() + function load($path = "") { - if(empty($this->yellow->pages->requestHandler)) //TODO: remove later, guard for old version + if(count($this->yellow->config->config)==0) //TODO: remove later, backwards compability for old Yellow version { $this->yellow->load(); return; } - $path = $this->yellow->config->get("pluginDir"); + $path = empty($path) ? $this->yellow->config->get("pluginDir") : $path; foreach($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.php$/", true, false) as $entry) { + if(defined("DEBUG") && DEBUG>=3) echo "YellowPlugins::load file:$entry<br/>\n"; $this->modified = max($this->modified, filemtime($entry)); global $yellow; require_once($entry); @@ -1628,7 +1667,6 @@ class YellowPlugins foreach($this->plugins as $key=>$value) { $this->plugins[$key]["obj"] = new $value["plugin"]; - if(defined("DEBUG") && DEBUG>=3) echo "YellowPlugins::load $value[plugin]:$value[version]<br/>\n"; if(method_exists($this->plugins[$key]["obj"], "onLoad")) $this->plugins[$key]["obj"]->onLoad($yellow); } } @@ -1657,7 +1695,11 @@ class YellowPlugins { $data = array(); $data["YellowCore"] = YellowCore::VERSION; - foreach($this->plugins as $key=>$value) $data[$value["plugin"]] = $value["version"]; + foreach($this->plugins as $key=>$value) + { + if(empty($value["plugin"]) || empty($value["version"])) continue; + $data[$value["plugin"]] = $value["version"]; + } uksort($data, strnatcasecmp); return $data; } @@ -1690,29 +1732,45 @@ class YellowThemes } // Load themes - function load() + function load($path = "") { - $path = $this->yellow->config->get("themeDir"); + $path = empty($path) ? $this->yellow->config->get("assetDir") : $path; + foreach($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.php$/", true, false) as $entry) + { + if(defined("DEBUG") && DEBUG>=3) echo "YellowThemes::load file:$entry<br/>\n"; + $this->modified = max($this->modified, filemtime($entry)); + global $yellow; + require_once($entry); + } foreach($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.css$/", true, false) as $entry) { - $name = $this->yellow->lookup->normaliseName(basename($entry), true, true); - $theme = $version = ""; + if(defined("DEBUG") && DEBUG>=3) echo "YellowThemes::load file:$entry<br/>\n"; $this->modified = max($this->modified, filemtime($entry)); - $fileData = $this->yellow->toolbox->readFile($entry, 4096); - foreach($this->yellow->toolbox->getTextLines($fileData) as $line) - { - preg_match("/^\/\*\s*(.*?)\s*:\s*(.*?)\s*\*\/$/", $line, $matches); - if(lcfirst($matches[1])=="theme" && !strempty($matches[2])) $theme = $matches[2]; - if(lcfirst($matches[1])=="version" && !strempty($matches[2])) $version = $matches[2]; - if(!empty($line) && $line[0]!='/') break; - } - if(!empty($theme) && !empty($version)) - { - $this->themes[$name] = array(); - $this->themes[$name]["theme"] = $theme; - $this->themes[$name]["version"] = $version; - if(defined("DEBUG") && DEBUG>=3) echo "YellowThemes::load $theme:$version<br/>\n"; - } + $name = $this->yellow->lookup->normaliseName(basename($entry), true, true); + $this->register($name, "", ""); + } + $callback = function($a, $b) + { + return $a["priority"] - $b["priority"]; + }; + uasort($this->themes, $callback); + foreach($this->themes as $key=>$value) + { + $this->themes[$key]["obj"] = empty($value["theme"]) ? new stdClass : new $value["theme"]; + if(method_exists($this->themes[$key]["obj"], "onLoad")) $this->themes[$key]["obj"]->onLoad($yellow); + } + } + + // Register theme + function register($name, $theme, $version, $priority = 0) + { + if(!$this->isExisting($name)) + { + if($priority==0) $priority = count($this->themes) + 10; + $this->themes[$name] = array(); + $this->themes[$name]["theme"] = $theme; + $this->themes[$name]["version"] = $version; + $this->themes[$name]["priority"] = $priority; } } @@ -1720,7 +1778,11 @@ class YellowThemes function getData() { $data = array(); - foreach($this->themes as $key=>$value) $data[$value["theme"]] = $value["version"]; + foreach($this->themes as $key=>$value) + { + if(empty($value["theme"]) || empty($value["version"])) continue; + $data[$value["theme"]] = $value["version"]; + } uksort($data, strnatcasecmp); return $data; } @@ -2021,6 +2083,9 @@ class YellowText class YellowLookup { var $yellow; //access to API + var $requestHandler; //request handler name + var $commandHandler; //command handler name + var $snippetArgs; //snippet arguments function __construct($yellow) { @@ -2033,7 +2098,7 @@ class YellowLookup list($pathRoot, $pathHome) = $this->detectFileSystem(); $this->yellow->config->set("contentRootDir", $pathRoot); $this->yellow->config->set("contentHomeDir", $pathHome); - date_default_timezone_set($this->yellow->config->get("serverTime")); + date_default_timezone_set($this->yellow->config->get("timezone")); } // Detect file system @@ -2302,6 +2367,53 @@ class YellowLookup return dirname($fileName)."/".$fileNamePrefix.$fileNameText; } + // Return file path for media location + function findFileFromMedia($location) + { + if($this->isFileLocation($location)) + { + $mediaLocationLength = strlenu($this->yellow->config->get("mediaLocation")); + if(substru($location, 0, $mediaLocationLength)==$this->yellow->config->get("mediaLocation")) + { + $fileName = $this->yellow->config->get("mediaDir").substru($location, 7); + } + } + return $fileName; + } + + // Return file path for system location + function findFileFromSystem($location) + { + if(preg_match("/\.(css|ico|js|jpg|png|svg|txt|woff)$/", $location)) + { + $pluginLocationLength = strlenu($this->yellow->config->get("pluginLocation")); + $themeLocationLength = strlenu($this->yellow->config->get("themeLocation")); + if(substru($location, 0, $pluginLocationLength)==$this->yellow->config->get("pluginLocation")) { + $fileName = $this->yellow->config->get("pluginDir").substru($location, $pluginLocationLength); + } else if(substru($location, 0, $themeLocationLength)==$this->yellow->config->get("themeLocation")) { + $fileName = $this->yellow->config->get("themeDir").substru($location, $themeLocationLength); + } else if($location=="/".$this->yellow->config->get("robotsFile")) { + $fileName = $this->yellow->config->get("configDir").$this->yellow->config->get("robotsFile"); + } else if($location=="/".$this->yellow->config->get("faviconFile")) { + $fileName = $this->yellow->config->get("assetDir").$this->yellow->config->get("siteicon").".png"; + } + } + return $fileName; + } + + // Return file path for static file, if possible + function findFileStatic($location, $fileName, $cacheable) + { + if($cacheable) + { + $location .= $this->yellow->toolbox->getLocationArgs(); + $fileNameStatic = rtrim($this->yellow->config->get("staticDir"), '/').$location; + if(!$this->isFileLocation($location)) $fileNameStatic .= $this->yellow->config->get("staticDefaultFile"); + if(is_readable($fileNameStatic)) $fileName = $fileNameStatic; + } + return $fileName; + } + // Return file path for new page function findFilePageNew($fileName, $prefix = "") { @@ -2328,19 +2440,6 @@ class YellowLookup return $path; } - // Return file path for static file, if possible - function findFileStatic($location, $fileName, $cacheable) - { - if($cacheable && PHP_SAPI!="cli") - { - $location .= $this->yellow->toolbox->getLocationArgs(); - $fileNameStatic = rtrim($this->yellow->config->get("staticDir"), '/').$location; - if(!$this->isFileLocation($location)) $fileNameStatic .= $this->yellow->config->get("staticDefaultFile"); - if(is_readable($fileNameStatic)) $fileName = $fileNameStatic; - } - return $fileName; - } - // Normalise file/directory/other name function normaliseName($text, $removePrefix = true, $removeExtension = false, $filterStrict = false) { @@ -2362,23 +2461,36 @@ class YellowLookup return $fileName; } + // Normalise array, make keys with same upper/lower case + function normaliseUpperLower($input) + { + $array = array(); + foreach($input as $key=>$value) + { + if(empty($key) || strempty($value)) continue; + $keySearch = strtoloweru($key); + foreach($array as $keyNew=>$valueNew) if(strtoloweru($keyNew)==$keySearch) { $key = $keyNew; break; } + $array[$key] += $value; + } + return $array; + } + // Normalise location, make absolute location - function normaliseLocation($location, $pageBase, $pageLocation, $staticLocation = "", $filterStrict = true) + function normaliseLocation($location, $pageLocation, $filterStrict = true) { if(!preg_match("/^\w+:/", trim(html_entity_decode($location, ENT_QUOTES, "UTF-8")))) { - if(empty($staticLocation) || !preg_match("#^$staticLocation#", $location)) + $pageBase = $this->yellow->page->base; + $mediaBase = $this->yellow->config->get("serverBase").$this->yellow->config->get("mediaLocation"); + if(preg_match("/^\#/", $location)) { - if(preg_match("/^\#/", $location)) - { - $location = $pageBase.$pageLocation.$location; - } else if(!preg_match("/^\//", $location)) { - $location = $this->getDirectoryLocation($pageBase.$pageLocation).$location; - } else if(!preg_match("#^$pageBase#", $location)) { - $location = $pageBase.$location; - } - $location = strreplaceu(':', $this->yellow->toolbox->getLocationArgsSeparator(), $location); + $location = $pageBase.$pageLocation.$location; + } else if(!preg_match("/^\//", $location)) { + $location = $this->getDirectoryLocation($pageBase.$pageLocation).$location; + } else if(!preg_match("#^($pageBase|$mediaBase)#", $location)) { + $location = $pageBase.$location; } + $location = strreplaceu(':', $this->yellow->toolbox->getLocationArgsSeparator(), $location); } else { if($filterStrict && !preg_match("/^(http|https|ftp|mailto):/", $location)) $location = "error-xss-filter"; } @@ -2386,17 +2498,30 @@ class YellowLookup } // Normalise URL, make absolute URL - function normaliseUrl($serverScheme, $serverName, $base, $location) + function normaliseUrl($scheme, $address, $base, $location, $filterStrict = true) { if(!preg_match("/^\w+:/", $location)) { - $url = "$serverScheme://$serverName$base$location"; + $url = "$scheme://$address$base$location"; } else { + if($filterStrict && !preg_match("/^(http|https|ftp|mailto):/", $location)) $location = "error-xss-filter"; $url = $location; } return $url; } + // Return URL information + function getUrlInformation($url) + { + if(preg_match("#^(\w+)://([^/]+)(.*)$#", rtrim($url, '/'), $matches)) + { + $scheme = $matches[1]; + $address = $matches[2]; + $base = $matches[3]; + } + return array($scheme, $address, $base); + } + // Return directory location function getDirectoryLocation($location) { @@ -2429,12 +2554,12 @@ class YellowLookup } // Check if location contains nested directories - function isNestedLocation($location, $checkHomeLocation = false) + function isNestedLocation($location, $fileName, $checkHomeLocation = false) { $nested = false; if(!$checkHomeLocation || $location==$this->yellow->pages->getHomeLocation($location)) { - $path = $this->yellow->lookup->findFileFromLocation($location, true); + $path = dirname($fileName); if(count($this->yellow->toolbox->getDirectoryEntries($path, "/.*/", true, true, false))) $nested = true; } return $nested; @@ -2476,15 +2601,6 @@ class YellowLookup return $active; } - // Check if location is valid - function isValidLocation($location) - { - $string = ""; - $tokens = explode('/', $location); - for($i=1; $i<count($tokens); ++$i) $string .= '/'.$this->normaliseName($tokens[$i]); - return $location==$string; - } - // Check if file is valid function isValidFile($fileName) { @@ -2521,50 +2637,50 @@ class YellowLookup // Yellow toolbox with helpers class YellowToolbox { - // Return server software - function getServerSoftware() + // Return server version from current HTTP request + function getServerVersion() { - $serverSoftware = strtoupperu(PHP_SAPI); - if(preg_match("/^(\S+)/", $_SERVER["SERVER_SOFTWARE"], $matches)) $serverSoftware = $matches[1]; - return $serverSoftware." ".PHP_OS; + $serverVersion = strtoupperu(PHP_SAPI)." ".PHP_OS; + if(preg_match("/^(\S+)/", $_SERVER["SERVER_SOFTWARE"], $matches)) $serverVersion = $matches[1]." ".PHP_OS; + return $serverVersion; } // Return server URL from current HTTP request function getServerUrl() { - $serverScheme = $this->getServerScheme(); - $serverName = $this->getServerName(); - $serverBase = $this->getServerBase(); - return "$serverScheme://$serverName$serverBase/"; + $scheme = $this->getScheme(); + $address = $this->getAddress(); + $base = $this->getBase(); + return "$scheme://$address$base/"; } - // Return server scheme from current HTTP request - function getServerScheme() + // Return scheme from current HTTP request + function getScheme() { - $serverScheme = ""; + $scheme = ""; if(preg_match("/^HTTP\//", $_SERVER["SERVER_PROTOCOL"])) { $secure = isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"]!="off"; - $serverScheme = $secure ? "https" : "http"; + $scheme = $secure ? "https" : "http"; } - return $serverScheme; + return $scheme; } - // Return server name from current HTTP request - function getServerName() + // Return address from current HTTP request + function getAddress() { - $serverName = $_SERVER["SERVER_NAME"]; - $serverPort = $_SERVER["SERVER_PORT"]; - if($serverPort!=80 && $serverPort!=443) $serverName .= ":$serverPort"; - return $serverName; + $address = $_SERVER["SERVER_NAME"]; + $port = $_SERVER["SERVER_PORT"]; + if($port!=80 && $port!=443) $address .= ":$port"; + return $address; } - // Return server base from current HTTP request - function getServerBase() + // Return base from current HTTP request + function getBase() { - $serverBase = ""; - if(preg_match("/^(.*)\//", $_SERVER["SCRIPT_NAME"], $matches)) $serverBase = rtrim($matches[1], '/'); - return $serverBase; + $base = ""; + if(preg_match("/^(.*)\//", $_SERVER["SCRIPT_NAME"], $matches)) $base = rtrim($matches[1], '/'); + return $base; } // Return location from current HTTP request @@ -2747,30 +2863,16 @@ class YellowToolbox } return $text; } - - // Normalise array elements with different upper and lower case - function normaliseUpperLower($input) - { - $array = array(); - foreach($input as $key=>$value) - { - if(empty($key) || strempty($value)) continue; - $keySearch = strtoloweru($key); - foreach($array as $keyNew=>$valueNew) if(strtoloweru($keyNew)==$keySearch) { $key = $keyNew; break; } - $array[$key] += $value; - } - return $array; - } - // Return server time zone - function getServerTime() + // Return timezone + function getTimezone() { - $serverTime = @date_default_timezone_get(); - if(PHP_OS=="Darwin" && $serverTime=="UTC") + $timezone = @date_default_timezone_get(); + if(PHP_OS=="Darwin" && $timezone=="UTC") { - if(preg_match("#zoneinfo/(.*)#", @readlink("/etc/localtime"), $matches)) $serverTime = $matches[1]; + if(preg_match("#zoneinfo/(.*)#", @readlink("/etc/localtime"), $matches)) $timezone = $matches[1]; } - return $serverTime; + return $timezone; } // Return human readable HTTP server status diff --git a/system/plugins/image.php b/system/plugins/image.php @@ -1,11 +1,11 @@ <?php -// Copyright (c) 2013-2016 Datenstrom, http://datenstrom.se +// Copyright (c) 2013-2017 Datenstrom, http://datenstrom.se // This file may be used and distributed under the terms of the public license. // Image plugin class YellowImage { - const VERSION = "0.6.7"; + const VERSION = "0.6.8"; var $yellow; //access to API var $graphicsLibrary; //graphics library support? (boolean) @@ -39,7 +39,8 @@ class YellowImage if(empty($height)) $height = $width; list($src, $width, $height) = $this->getImageInfo($this->yellow->config->get("imageDir").$name, $width, $height); } else { - $src = $this->yellow->lookup->normaliseLocation($name, $page->base, $page->location); + if(empty($alt)) $alt = $this->yellow->config->get("imageAlt"); + $src = $this->yellow->lookup->normaliseUrl("", "", "", $name); $width = $height = 0; } $output = "<img src=\"".htmlspecialchars($src)."\""; diff --git a/system/plugins/markdown.php b/system/plugins/markdown.php @@ -1,11 +1,11 @@ <?php -// Copyright (c) 2013-2016 Datenstrom, http://datenstrom.se +// Copyright (c) 2013-2017 Datenstrom, http://datenstrom.se // This file may be used and distributed under the terms of the public license. // Markdown plugin class YellowMarkdown { - const VERSION = "0.6.4"; + const VERSION = "0.6.5"; var $yellow; //access to API // Handle initialisation @@ -37,8 +37,7 @@ class YellowMarkdownParser extends MarkdownExtraParser $this->no_markup = $page->parserSafeMode; $this->url_filter_func = function($url) use ($yellow, $page) { - return $yellow->lookup->normaliseLocation($url, $page->base, $page->location, - $yellow->config->get("serverBase").$yellow->config->get("imageLocation"), + return $yellow->lookup->normaliseLocation($url, $page->location, $page->parserSafeMode && $page->statusCode==200); }; parent::__construct(); diff --git a/system/plugins/update.php b/system/plugins/update.php @@ -5,8 +5,9 @@ // Update plugin class YellowUpdate { - const VERSION = "0.6.13"; + const VERSION = "0.6.14"; var $yellow; //access to API + var $updates; //number of updates // Handle initialisation function onLoad($yellow) @@ -16,24 +17,67 @@ class YellowUpdate $this->yellow->config->setDefault("updateThemesUrl", "https://github.com/datenstrom/yellow-themes"); $this->yellow->config->setDefault("updateInformationFile", "update.ini"); $this->yellow->config->setDefault("updateVersionFile", "version.ini"); - $this->yellow->config->setDefault("updateNotification", "none"); + $this->yellow->config->setDefault("updateResourceFile", "resource.ini"); } - // Handle update - function onUpdate($name) + // Handle startup + function onStartup($update) { - if(empty($name)) $this->processUpdateNotification(); + if($this->yellow->config->isExisting("updateNotification")) //TODO: remove later, cleans old config + { + $update = true; + $fileNameConfig = $this->yellow->config->get("configDir").$this->yellow->config->get("configFile"); + $fileData = $this->yellow->toolbox->readFile($fileNameConfig); + foreach($this->yellow->toolbox->getTextLines($fileData) as $line) + { + preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); + if(!empty($matches[1]) && is_null($this->yellow->config->configDefaults[$matches[1]]) && + $line[0]!='#' && substru($line, 0, 5)!="Login") + { + $fileDataNew .= "# $line"; + } else { + $fileDataNew .= $line; + } + } + $this->yellow->toolbox->createFile($fileNameConfig, $fileDataNew); + } + if($update) //TODO: remove later, converts old Yellow version + { + $fileNameScript = "yellow.php"; + if(filesize($fileNameScript)==591) + { + $fileData = $this->yellow->toolbox->readFile($fileNameScript); + $fileData = preg_replace("#yellow->plugins->load\(\)#", "yellow->load()", $fileData); + $this->yellow->toolbox->createFile($fileNameScript, $fileData); + } + } + if($update) //TODO: remove later, imports old file format + { + $path = $this->yellow->config->get("themeDir"); + foreach($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.css$/", true, false) as $entry) + { + $fileNameAsset = $this->yellow->config->get("assetDir").basename($entry); + if(!is_file($fileNameAsset)) + { + $fileData = $this->yellow->toolbox->readFile($entry); + $fileData = preg_replace("#url\(assets/(.*?)\)#", "url($1)", $fileData); + $this->yellow->toolbox->createFile($fileNameAsset, $fileData); + } + $this->yellow->toolbox->deleteFile($entry, $this->yellow->config->get("trashDir")); + $_GET["clean-url"] = "software-has-been-updated"; + } + } } // Handle request - function onRequest($serverScheme, $serverName, $base, $location, $fileName) + function onRequest($scheme, $address, $base, $location, $fileName) { $statusCode = 0; - if($this->isInstallationMode()) + if($this->yellow->config->get("installationMode")) { - $statusCode = $this->processRequestInstallationMode($serverScheme, $serverName, $base, $location, $fileName); + $statusCode = $this->processRequestInstallationMode($scheme, $address, $base, $location, $fileName); } else { - $statusCode = $this->processRequestInstallationPending($serverScheme, $serverName, $base, $location, $fileName); + $statusCode = $this->processRequestInstallationPending($scheme, $address, $base, $location, $fileName); } return $statusCode; } @@ -54,7 +98,7 @@ class YellowUpdate // Handle command help function onCommandHelp() { - return "update [FEATURE]"; + return "update [OPTION FEATURE]"; } // Clean downloads @@ -78,30 +122,82 @@ class YellowUpdate // Update website function updateCommand($args) { - list($command, $feature, $option) = $args; - list($statusCode, $data) = $this->getSoftwareUpdates($feature); - if(!empty($data)) + list($command, $option, $feature) = $args; + if(empty($option) || $option=="normal" || $option=="force") { - foreach($data as $key=>$value) + $force = $option=="force"; + list($statusCode, $data) = $this->detectSoftware($force, $feature); + if($statusCode!=200 || !empty($data)) { - list($version) = explode(',', $value); - echo "$key $version\n"; + $this->updates = 0; + if($statusCode==200) $statusCode = $this->downloadSoftware($data); + if($statusCode==200) $statusCode = $this->updateSoftware($force); + if($statusCode>=400) echo "ERROR updating files: ".$this->yellow->page->get("pageError")."\n"; + echo "Yellow $command: Website ".($statusCode!=200 ? "not " : "")."updated"; + echo ", $this->updates update".($this->updates!=1 ? 's' : '')." installed\n"; + } else { + echo "Your website is up to date\n"; } - if($statusCode==200) $statusCode = $this->downloadSoftware($data); - if($statusCode==200) $statusCode = $this->updateSoftware($option); - if($statusCode!=200) echo "ERROR updating files: ".$this->yellow->page->get("pageError")."\n"; - echo "Yellow $command: Website ".($statusCode!=200 ? "not " : "")."updated\n"; } else { - if($statusCode!=200) echo "ERROR updating files: ".$this->yellow->page->get("pageError")."\n"; - echo "Yellow $command: No updates available\n"; + $statusCode = 400; + echo "Yellow $command: Invalid arguments\n"; } return $statusCode; } + // Detect software + function detectSoftware($force, $feature) + { + $data = array(); + list($statusCodeCurrent, $dataCurrent) = $this->getSoftwareVersion(); + list($statusCodeLatest, $dataLatest) = $this->getSoftwareVersion(true, true); + list($statusCodeModified, $dataModified) = $this->getSoftwareModified(); + $statusCode = max($statusCodeCurrent, $statusCodeLatest, $statusCodeModified); + if(empty($feature)) + { + foreach($dataCurrent as $key=>$value) + { + list($version) = explode(',', $dataLatest[$key]); + if(strnatcasecmp($dataCurrent[$key], $version)<0) $data[$key] = $dataLatest[$key]; + if(!is_null($dataModified[$key]) && !empty($version) && $force) $data[$key] = $dataLatest[$key]; + } + } else { + foreach($dataCurrent as $key=>$value) + { + list($version) = explode(',', $dataLatest[$key]); + if(strtoloweru($key)==strtoloweru($feature) && !empty($version)) + { + $data[$key] = $dataLatest[$key]; + $dataModified = array_intersect_key($dataModified, $data); + break; + } + } + if(empty($data)) + { + $statusCode = 500; + $this->yellow->page->error($statusCode, "Can't find feature '$feature'!"); + } + } + if($statusCode==200) + { + foreach(array_merge($dataModified, $data) as $key=>$value) + { + list($version) = explode(',', $value); + if(is_null($dataModified[$key]) || $force) + { + echo "$key $version\n"; + } else { + echo "$key $version has been modified - Force update\n"; + } + } + } + return array($statusCode, $data); + } + // Download software function downloadSoftware($data) { - $statusCode = 0; + $statusCode = 200; $path = $this->yellow->config->get("pluginDir"); $fileExtension = $this->yellow->config->get("downloadExtension"); foreach($data as $key=>$value) @@ -132,31 +228,40 @@ class YellowUpdate } // Update software - function updateSoftware($option = "") + function updateSoftware($force = false) { - $statusCode = 0; + $statusCode = 200; $path = $this->yellow->config->get("pluginDir"); foreach($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.zip$/", true, false) as $entry) { - $statusCode = max($statusCode, $this->updateSoftwareArchive($entry, $option)); + $statusCode = max($statusCode, $this->updateSoftwareArchive($entry, $force)); + if(!$this->yellow->toolbox->deleteFile($entry)) + { + $statusCode = 500; + $this->yellow->page->error($statusCode, "Can't delete file '$entry'!"); + } } $path = $this->yellow->config->get("themeDir"); foreach($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.zip$/", true, false) as $entry) { - $statusCode = max($statusCode, $this->updateSoftwareArchive($entry, $option)); + $statusCode = max($statusCode, $this->updateSoftwareArchive($entry, $force)); + if(!$this->yellow->toolbox->deleteFile($entry)) + { + $statusCode = 500; + $this->yellow->page->error($statusCode, "Can't delete file '$entry'!"); + } } return $statusCode; } // Update software from archive - function updateSoftwareArchive($path, $option = "") + function updateSoftwareArchive($path, $force = false) { - $statusCode = 0; + $statusCode = 200; $zip = new ZipArchive(); if($zip->open($path)===true) { if(defined("DEBUG") && DEBUG>=2) echo "YellowUpdate::updateSoftwareArchive file:$path<br/>\n"; - if(strtoloweru($option)=="force") $force = true; if(preg_match("#^(.*\/).*?$#", $zip->getNameIndex(0), $matches)) $pathBase = $matches[1]; $fileData = $zip->getFromName($pathBase.$this->yellow->config->get("updateInformationFile")); foreach($this->yellow->toolbox->getTextLines($fileData) as $line) @@ -193,11 +298,10 @@ class YellowUpdate $zip->close(); if($statusCode==200) $statusCode = $this->updateSoftwareNew($software); if($statusCode==200) $statusCode = $this->updateSoftwareNotification($software); - } - if(!$this->yellow->toolbox->deleteFile($path)) - { + ++$this->updates; + } else { $statusCode = 500; - $this->yellow->page->error($statusCode, "Can't delete file '$path'!"); + $this->yellow->page->error(500, "Can't open file '$path'!"); } return $statusCode; } @@ -309,12 +413,12 @@ class YellowUpdate function updateSoftwareNotification($software) { $statusCode = 200; - $updateNotification = $this->yellow->config->get("updateNotification"); - if($updateNotification=="none") $updateNotification = ""; - if(!empty($updateNotification)) $updateNotification .= ","; - $updateNotification .= $software; + $startupUpdateNotification = $this->yellow->config->get("startupUpdateNotification"); + if($startupUpdateNotification=="none") $startupUpdateNotification = ""; + if(!empty($startupUpdateNotification)) $startupUpdateNotification .= ","; + $startupUpdateNotification .= $software; $fileNameConfig = $this->yellow->config->get("configDir").$this->yellow->config->get("configFile"); - if(!$this->yellow->config->update($fileNameConfig, array("updateNotification" => $updateNotification))) + if(!$this->yellow->config->update($fileNameConfig, array("startupUpdateNotification" => $startupUpdateNotification))) { $statusCode = 500; $this->yellow->page->error(500, "Can't write file '$fileNameConfig'!"); @@ -322,17 +426,21 @@ class YellowUpdate return $statusCode; } - // Update software features - function updateSoftwareFeatures($feature) + // Update installation features + function updateInstallationFeatures($feature) { $statusCode = 200; $path = $this->yellow->config->get("pluginDir"); $regex = "/^.*\\".$this->yellow->config->get("installationExtension")."$/"; foreach($this->yellow->toolbox->getDirectoryEntries($path, $regex, true, false) as $entry) { - if(stristr(basename($entry), $feature)) + if(preg_match("/^(.*?)-(.*?)\./", basename($entry), $matches)) { - $statusCode = max($statusCode, $this->updateSoftwareArchive($entry)); + if(strtoloweru($matches[2])==strtoloweru($feature)) + { + $statusCode = max($statusCode, $this->updateSoftwareArchive($entry)); + break; + } } } if($statusCode==200) @@ -345,21 +453,6 @@ class YellowUpdate return $statusCode; } - // Process update notification for recently installed software - function processUpdateNotification() - { - if($this->yellow->config->get("updateNotification")!="none") - { - $tokens = explode(',', $this->yellow->config->get("updateNotification")); - foreach($this->yellow->plugins->plugins as $key=>$value) - { - if(in_array($value["plugin"], $tokens) && method_exists($value["obj"], "onUpdate")) $value["obj"]->onUpdate($key); - } - $fileNameConfig = $this->yellow->config->get("configDir").$this->yellow->config->get("configFile"); - $this->yellow->config->update($fileNameConfig, array("updateNotification" => "none")); - } - } - // Process command to install pending software function processCommandInstallationPending($args) { @@ -367,26 +460,23 @@ class YellowUpdate if($this->isSoftwarePending()) { $statusCode = $this->updateSoftware(); - if($statusCode!=0) - { - if($statusCode!=200) echo "ERROR updating files: ".$this->yellow->page->get("pageError")."\n"; - echo "Yellow has ".($statusCode!=200 ? "not " : "")."been updated: Please run command again\n"; - } + if($statusCode!=200) echo "ERROR updating files: ".$this->yellow->page->get("pageError")."\n"; + echo "Yellow has ".($statusCode!=200 ? "not " : "")."been updated: Please run command again\n"; } return $statusCode; } // Process request to install pending software - function processRequestInstallationPending($serverScheme, $serverName, $base, $location, $fileName) + function processRequestInstallationPending($scheme, $address, $base, $location, $fileName) { $statusCode = 0; - if($this->yellow->lookup->isContentFile($fileName) && $this->isSoftwarePending()) + if($this->yellow->lookup->isContentFile($fileName) && !$this->yellow->isCommandLine() && $this->isSoftwarePending()) { $statusCode = $this->updateSoftware(); if($statusCode==200) { $statusCode = 303; - $location = $this->yellow->lookup->normaliseUrl($serverScheme, $serverName, $base, $location); + $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); $this->yellow->sendStatus($statusCode, $location); } } @@ -394,14 +484,14 @@ class YellowUpdate } // Process request to install website - function processRequestInstallationMode($serverScheme, $serverName, $base, $location, $fileName) + function processRequestInstallationMode($scheme, $address, $base, $location, $fileName) { $statusCode = 0; - if($this->yellow->lookup->isContentFile($fileName) && $this->isInstallationMode()) + if($this->yellow->lookup->isContentFile($fileName) && !$this->yellow->isCommandLine()) { $this->yellow->pages->pages["root/"] = array(); $this->yellow->page = new YellowPage($this->yellow); - $this->yellow->page->setRequestInformation($serverScheme, $serverName, $base, $location, $fileName); + $this->yellow->page->setRequestInformation($scheme, $address, $base, $location, $fileName); $this->yellow->page->parseData($this->getRawDataInstallation(), false, 404); $this->yellow->page->parserSafeMode = false; $this->yellow->page->parseContent(); @@ -435,7 +525,7 @@ class YellowUpdate { if(!empty($feature)) { - $status = $this->updateSoftwareFeatures($feature)==200 ? "ok" : "error"; + $status = $this->updateInstallationFeatures($feature)==200 ? "ok" : "error"; if($status=="error") $this->yellow->page->error(500, "Can't install feature '$feature'!"); } } @@ -449,7 +539,7 @@ class YellowUpdate if($status=="done") { $statusCode = 303; - $location = $this->yellow->lookup->normaliseUrl($serverScheme, $serverName, $base, $location); + $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); $this->yellow->sendStatus($statusCode, $location); } else { $statusCode = $this->yellow->sendPage(); @@ -482,10 +572,10 @@ class YellowUpdate } $rawData .= "</p>\n"; } - if(count($this->getSoftwareFeatures())>1) + if(count($this->getInstallationFeatures())>1) { $rawData .= "<p>".$this->yellow->text->get("webinterfaceInstallationFeature")."<p>"; - foreach($this->getSoftwareFeatures() as $feature) + foreach($this->getInstallationFeatures() as $feature) { $checked = $feature=="website" ? " checked=\"checked\"" : ""; $rawData .= "<label for=\"$feature\"><input type=\"radio\" name=\"feature\" id=\"$feature\" value=\"$feature\"$checked> ".ucfirst($feature)."</label><br />"; @@ -515,16 +605,14 @@ class YellowUpdate if(!$this->yellow->config->isExisting($key)) continue; $data[$key] = trim($value); } - $data["# serverScheme"] = $this->yellow->toolbox->getServerScheme(); - $data["# serverName"] = $this->yellow->toolbox->getServerName(); - $data["# serverBase"] = $this->yellow->toolbox->getServerBase(); - $data["# serverTime"] = $this->yellow->toolbox->getServerTime(); + $data["timezone"] = $this->yellow->toolbox->getTimezone(); + $data["staticUrl"] = $this->yellow->toolbox->getServerUrl(); $data["installationMode"] = "0"; return $data; } - // Return software features - function getSoftwareFeatures() + // Return installation features + function getInstallationFeatures() { $data = array("website"); $path = $this->yellow->config->get("pluginDir"); @@ -539,25 +627,6 @@ class YellowUpdate return $data; } - // Return software updates - function getSoftwareUpdates($feature) - { - $data = array(); - list($statusCode, $dataCurrent) = $this->getSoftwareVersion(); - list($statusCode, $dataLatest) = $this->getSoftwareVersion(true, true); - foreach($dataCurrent as $key=>$value) - { - list($version) = explode(',', $dataLatest[$key]); - if(empty($feature)) - { - if(strnatcasecmp($dataCurrent[$key], $version)<0) $data[$key] = $dataLatest[$key]; - } else { - if(stristr($key, $feature) && $version) $data[$key] = $dataLatest[$key]; - } - } - return array($statusCode, $data); - } - // Return software version function getSoftwareVersion($latest = false, $rawFormat = false) { @@ -587,6 +656,46 @@ class YellowUpdate } return array($statusCode, $data); } + + // Return software modification + function getSoftwareModified() + { + $data = array(); + $dataCurrent = array_merge($this->yellow->plugins->getData(), $this->yellow->themes->getData()); + $urlPlugins = $this->yellow->config->get("updatePluginsUrl")."/raw/master/".$this->yellow->config->get("updateResourceFile"); + $urlThemes = $this->yellow->config->get("updateThemesUrl")."/raw/master/".$this->yellow->config->get("updateResourceFile"); + list($statusCodePlugins, $fileDataPlugins) = $this->getSoftwareFile($urlPlugins); + list($statusCodeThemes, $fileDataThemes) = $this->getSoftwareFile($urlThemes); + $statusCode = max($statusCodePlugins, $statusCodeThemes); + if($statusCode==200) + { + foreach($this->yellow->toolbox->getTextLines($fileDataPlugins."\n".$fileDataThemes) as $line) + { + preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); + if(!empty($matches[1]) && !empty($matches[2])) + { + list($softwareNew) = explode('/', $matches[1]); + list($fileName, $flags) = explode(',', $matches[2], 2); + if($software!=$softwareNew) + { + $software = $softwareNew; + list($fileName, $flags) = explode(',', $matches[2], 2); + $lastPublished = $this->yellow->toolbox->getFileModified($fileName); + } + if($this->yellow->lookup->isValidFile($fileName) && !is_null($dataCurrent[$software])) + { + $lastModified = $this->yellow->toolbox->getFileModified($fileName); + if(preg_match("/update/i", $flags) && preg_match("/careful/i", $flags) && $lastModified!=$lastPublished) + { + $data[$software] = $dataCurrent[$software]; + if(defined("DEBUG") && DEBUG>=2) echo "YellowUpdate::getSoftwareModified detected file:$fileName<br/>\n"; + } + } + } + } + } + return array($statusCode, $data); + } // Return software file function getSoftwareFile($url) @@ -641,12 +750,6 @@ class YellowUpdate $data = array_merge($this->yellow->plugins->getData(), $this->yellow->themes->getData()); return !is_null($data[$software]); } - - // Check if installation mode - function isInstallationMode() - { - return $this->yellow->config->get("installationMode") && PHP_SAPI!="cli"; - } } $yellow->plugins->register("update", "YellowUpdate", YellowUpdate::VERSION, 1); diff --git a/system/plugins/webinterface.css b/system/plugins/webinterface.css @@ -1,4 +1,4 @@ -/* Yellow web interface 0.6.19 */ +/* Yellow web interface 0.6.20 */ .yellow-bar { position:relative; overflow:hidden; height:2em; margin-bottom:10px; } .yellow-bar-left { display:block; float:left; } diff --git a/system/plugins/webinterface.js b/system/plugins/webinterface.js @@ -4,8 +4,8 @@ // Yellow API var yellow = { - version: "0.6.19", - action: function(action) { yellow.webinterface.action(action, "none"); }, + version: "0.6.20", + action: function(action, status, args) { yellow.webinterface.action(action, status, args); }, onLoad: function() { yellow.webinterface.loadInterface(); }, onClick: function(e) { yellow.webinterface.hidePanesOnClick(yellow.toolbox.getEventElement(e)); }, onKeydown: function(e) { yellow.webinterface.hidePanesOnKeydown(yellow.toolbox.getEventKeycode(e)); }, @@ -22,9 +22,10 @@ yellow.webinterface = intervalId: 0, //timer interval ID // Handle action - action: function(action, status) + action: function(action, status, args) { - if(yellow.config.debug) console.log("yellow.webinterface.action action:"+action+" status:"+status); + status = status ? status : "none"; + args = args ? args : "none"; switch(action) { case "login": this.showPane("yellow-pane-login", action, status); break; @@ -37,7 +38,7 @@ yellow.webinterface = case "reconfirm": this.showPane("yellow-pane-settings", action, status); break; case "change": this.showPane("yellow-pane-settings", action, status); break; case "version": this.showPane("yellow-pane-version", action, status); break; - case "update": this.sendPane("yellow-pane-version", action); break; + case "update": this.sendPane("yellow-pane-update", action, status, args); break; case "create": this.showPane("yellow-pane-edit", action, status, true); break; case "edit": this.showPane("yellow-pane-edit", action, status, true); break; case "delete": this.showPane("yellow-pane-edit", action, status, true); break; @@ -430,7 +431,7 @@ yellow.webinterface = }, // Send pane - sendPane: function(paneId, paneAction) + sendPane: function(paneId, paneAction, paneStatus, paneArgs) { if(yellow.config.debug) console.log("yellow.webinterface.sendPane id:"+paneId); if(paneId=="yellow-pane-edit") @@ -438,16 +439,27 @@ yellow.webinterface = paneAction = this.getPaneAction(paneId, paneAction); if(paneAction) { - var params = {}; - params.action = paneAction; - params.rawdatasource = yellow.page.rawDataSource; - params.rawdataedit = document.getElementById("yellow-pane-edit-page").value; - yellow.toolbox.submitForm(params, true); + var args = {}; + args.action = paneAction; + args.rawdatasource = yellow.page.rawDataSource; + args.rawdataedit = document.getElementById("yellow-pane-edit-page").value; + yellow.toolbox.submitForm(args, true); } else { this.hidePane(paneId); } } else { - yellow.toolbox.submitForm({"action":paneAction}); + var args = {"action":paneAction}; + if(paneArgs) + { + var tokens = paneArgs.split('/'); + for(var i=0; i<tokens.length; i++) + { + var pair = tokens[i].split(/[:=]/); + if(!pair[0] || !pair[1]) continue; + args[pair[0]] = pair[1]; + } + } + yellow.toolbox.submitForm(args); } }, @@ -777,14 +789,14 @@ yellow.toolbox = }, // Submit form with post method - submitForm: function(params, encodeNewline) + submitForm: function(args, encodeNewline) { var elementForm = document.createElement("form"); elementForm.setAttribute("method", "post"); - for(var key in params) + for(var key in args) { - if(!params.hasOwnProperty(key)) continue; - var value = encodeNewline ? this.encodeNewline(params[key]) : params[key]; + if(!args.hasOwnProperty(key)) continue; + var value = encodeNewline ? this.encodeNewline(args[key]) : args[key]; var elementInput = document.createElement("input"); elementInput.setAttribute("type", "hidden"); elementInput.setAttribute("name", key); diff --git a/system/plugins/webinterface.php b/system/plugins/webinterface.php @@ -5,7 +5,7 @@ // Web interface plugin class YellowWebinterface { - const VERSION = "0.6.19"; + const VERSION = "0.6.20"; var $yellow; //access to API var $response; //web interface response var $users; //web interface users @@ -18,8 +18,6 @@ class YellowWebinterface $this->response = new YellowResponse($yellow); $this->users = new YellowUsers($yellow); $this->merge = new YellowMerge($yellow); - $this->yellow->config->setDefault("webinterfaceServerScheme", $this->yellow->config->get("serverScheme")); - $this->yellow->config->setDefault("webinterfaceServerName", $this->yellow->config->get("serverName")); $this->yellow->config->setDefault("webinterfaceLocation", "/edit/"); $this->yellow->config->setDefault("webinterfaceNewFile", "page-new-(.*).txt"); $this->yellow->config->setDefault("webinterfaceMetaFilePrefix", "published"); @@ -32,24 +30,24 @@ class YellowWebinterface $this->users->load($this->yellow->config->get("configDir").$this->yellow->config->get("webinterfaceUserFile")); } - // Handle update - function onUpdate($name) + // Handle startup + function onStartup($update) { - if($name=="webinterface") $this->cleanCommand(array("clean", "all")); + if($update) $this->cleanCommand(array("clean", "all")); } // Handle request - function onRequest($serverScheme, $serverName, $base, $location, $fileName) + function onRequest($scheme, $address, $base, $location, $fileName) { $statusCode = 0; if($this->checkRequest($location)) { - $serverScheme = $this->yellow->config->get("webinterfaceServerScheme"); - $serverName = $this->yellow->config->get("webinterfaceServerName"); + $scheme = $this->yellow->config->get("serverScheme"); + $address = $this->yellow->config->get("serverAddress"); $base = rtrim($this->yellow->config->get("serverBase").$this->yellow->config->get("webinterfaceLocation"), '/'); - list($serverScheme, $serverName, $base, $location, $fileName) = $this->yellow->getRequestInformation($serverScheme, $serverName, $base); - $this->yellow->page->setRequestInformation($serverScheme, $serverName, $base, $location, $fileName); - $statusCode = $this->processRequest($serverScheme, $serverName, $base, $location, $fileName); + list($scheme, $address, $base, $location, $fileName) = $this->yellow->getRequestInformation($scheme, $address, $base); + $this->yellow->page->setRequestInformation($scheme, $address, $base, $location, $fileName); + $statusCode = $this->processRequest($scheme, $address, $base, $location, $fileName); } return $statusCode; } @@ -91,9 +89,9 @@ class YellowWebinterface $output = null; if($name=="header" && $this->response->isActive()) { - $location = $this->yellow->config->get("serverBase").$this->yellow->config->get("pluginLocation")."webinterface"; - $output = "<link rel=\"stylesheet\" type=\"text/css\" media=\"all\" href=\"".htmlspecialchars($location).".css\" />\n"; - $output .= "<script type=\"text/javascript\" src=\"".htmlspecialchars($location).".js\"></script>\n"; + $pluginLocation = $this->yellow->config->get("serverBase").$this->yellow->config->get("pluginLocation"); + $output = "<link rel=\"stylesheet\" type=\"text/css\" media=\"all\" href=\"{$pluginLocation}webinterface.css\" />\n"; + $output .= "<script type=\"text/javascript\" src=\"{$pluginLocation}webinterface.js\"></script>\n"; $output .= "<script type=\"text/javascript\">\n"; $output .= "// <![CDATA[\n"; $output .= "yellow.page = ".json_encode($this->response->getPageData()).";\n"; @@ -176,40 +174,40 @@ class YellowWebinterface } // Process request - function processRequest($serverScheme, $serverName, $base, $location, $fileName) + function processRequest($scheme, $address, $base, $location, $fileName) { $statusCode = 0; - if($this->checkUser($serverScheme, $serverName, $base, $location, $fileName)) + if($this->checkUser($scheme, $address, $base, $location, $fileName)) { switch($_REQUEST["action"]) { - case "": $statusCode = $this->processRequestShow($serverScheme, $serverName, $base, $location, $fileName); break; - case "login": $statusCode = $this->processRequestLogin($serverScheme, $serverName, $base, $location, $fileName); break; - case "logout": $statusCode = $this->processRequestLogout($serverScheme, $serverName, $base, $location, $fileName); break; - case "signup": $statusCode = $this->processRequestSignup($serverScheme, $serverName, $base, $location, $fileName); break; - case "confirm": $statusCode = $this->processRequestConfirm($serverScheme, $serverName, $base, $location, $fileName); break; - case "approve": $statusCode = $this->processRequestApprove($serverScheme, $serverName, $base, $location, $fileName); break; - case "recover": $statusCode = $this->processRequestRecover($serverScheme, $serverName, $base, $location, $fileName); break; - case "settings": $statusCode = $this->processRequestSettings($serverScheme, $serverName, $base, $location, $fileName); break; - case "reconfirm": $statusCode = $this->processRequestReconfirm($serverScheme, $serverName, $base, $location, $fileName); break; - case "change": $statusCode = $this->processRequestChange($serverScheme, $serverName, $base, $location, $fileName); break; - case "version": $statusCode = $this->processRequestVersion($serverScheme, $serverName, $base, $location, $fileName); break; - case "update": $statusCode = $this->processRequestUpdate($serverScheme, $serverName, $base, $location, $fileName); break; - case "create": $statusCode = $this->processRequestCreate($serverScheme, $serverName, $base, $location, $fileName); break; - case "edit": $statusCode = $this->processRequestEdit($serverScheme, $serverName, $base, $location, $fileName); break; - case "delete": $statusCode = $this->processRequestDelete($serverScheme, $serverName, $base, $location, $fileName); break; + case "": $statusCode = $this->processRequestShow($scheme, $address, $base, $location, $fileName); break; + case "login": $statusCode = $this->processRequestLogin($scheme, $address, $base, $location, $fileName); break; + case "logout": $statusCode = $this->processRequestLogout($scheme, $address, $base, $location, $fileName); break; + case "signup": $statusCode = $this->processRequestSignup($scheme, $address, $base, $location, $fileName); break; + case "confirm": $statusCode = $this->processRequestConfirm($scheme, $address, $base, $location, $fileName); break; + case "approve": $statusCode = $this->processRequestApprove($scheme, $address, $base, $location, $fileName); break; + case "recover": $statusCode = $this->processRequestRecover($scheme, $address, $base, $location, $fileName); break; + case "settings": $statusCode = $this->processRequestSettings($scheme, $address, $base, $location, $fileName); break; + case "reconfirm": $statusCode = $this->processRequestReconfirm($scheme, $address, $base, $location, $fileName); break; + case "change": $statusCode = $this->processRequestChange($scheme, $address, $base, $location, $fileName); break; + case "version": $statusCode = $this->processRequestVersion($scheme, $address, $base, $location, $fileName); break; + case "update": $statusCode = $this->processRequestUpdate($scheme, $address, $base, $location, $fileName); break; + case "create": $statusCode = $this->processRequestCreate($scheme, $address, $base, $location, $fileName); break; + case "edit": $statusCode = $this->processRequestEdit($scheme, $address, $base, $location, $fileName); break; + case "delete": $statusCode = $this->processRequestDelete($scheme, $address, $base, $location, $fileName); break; } } else { - $this->yellow->pages->requestHandler = "core"; + $this->yellow->lookup->requestHandler = "core"; switch($_REQUEST["action"]) { - case "": $statusCode = $this->processRequestShow($serverScheme, $serverName, $base, $location, $fileName); break; - case "signup": $statusCode = $this->processRequestSignup($serverScheme, $serverName, $base, $location, $fileName); break; - case "confirm": $statusCode = $this->processRequestConfirm($serverScheme, $serverName, $base, $location, $fileName); break; - case "approve": $statusCode = $this->processRequestApprove($serverScheme, $serverName, $base, $location, $fileName); break; - case "recover": $statusCode = $this->processRequestRecover($serverScheme, $serverName, $base, $location, $fileName); break; - case "reconfirm": $statusCode = $this->processRequestReconfirm($serverScheme, $serverName, $base, $location, $fileName); break; - case "change": $statusCode = $this->processRequestChange($serverScheme, $serverName, $base, $location, $fileName); break; + case "": $statusCode = $this->processRequestShow($scheme, $address, $base, $location, $fileName); break; + case "signup": $statusCode = $this->processRequestSignup($scheme, $address, $base, $location, $fileName); break; + case "confirm": $statusCode = $this->processRequestConfirm($scheme, $address, $base, $location, $fileName); break; + case "approve": $statusCode = $this->processRequestApprove($scheme, $address, $base, $location, $fileName); break; + case "recover": $statusCode = $this->processRequestRecover($scheme, $address, $base, $location, $fileName); break; + case "reconfirm": $statusCode = $this->processRequestReconfirm($scheme, $address, $base, $location, $fileName); break; + case "change": $statusCode = $this->processRequestChange($scheme, $address, $base, $location, $fileName); break; } if($this->response->action=="fail") $this->yellow->page->error(500, "Login failed, [please log in](javascript:yellow.action('login');)!"); } @@ -217,22 +215,22 @@ class YellowWebinterface } // Process request to show file - function processRequestShow($serverScheme, $serverName, $base, $location, $fileName) + function processRequestShow($scheme, $address, $base, $location, $fileName) { $statusCode = 0; if(is_readable($fileName)) { - $statusCode = $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, false); + $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); } else { if($this->yellow->lookup->isRedirectLocation($location)) { $statusCode = 301; $location = $this->yellow->lookup->isFileLocation($location) ? "$location/" : "/".$this->yellow->getRequestLanguage()."/"; - $location = $this->yellow->lookup->normaliseUrl($serverScheme, $serverName, $base, $location); + $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); $this->yellow->sendStatus($statusCode, $location); } else { $statusCode = $this->response->isUserRestrictions() ? 404 : 424; - $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, false); + $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); $this->yellow->page->error($statusCode); } } @@ -240,39 +238,39 @@ class YellowWebinterface } // Process request for user login - function processRequestLogin($serverScheme, $serverName, $base, $location, $fileName) + function processRequestLogin($scheme, $address, $base, $location, $fileName) { $statusCode = 0; $home = $this->users->getHome($this->response->userEmail); if(substru($location, 0, strlenu($home))==$home) { $statusCode = 303; - $location = $this->yellow->lookup->normaliseUrl($serverScheme, $serverName, $base, $location); + $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); $this->yellow->sendStatus($statusCode, $location); } else { $statusCode = 302; - $location = $this->yellow->lookup->normaliseUrl($serverScheme, $serverName, $base, $home); + $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $home); $this->yellow->sendStatus($statusCode, $location); } return $statusCode; } // Process request for user logout - function processRequestLogout($serverScheme, $serverName, $base, $location, $fileName) + function processRequestLogout($scheme, $address, $base, $location, $fileName) { $statusCode = 302; $this->response->userEmail = ""; - $this->response->destroyCookie($serverScheme, $serverName, $base); + $this->response->destroyCookie($scheme, $address, $base); $location = $this->yellow->lookup->normaliseUrl( $this->yellow->config->get("serverScheme"), - $this->yellow->config->get("serverName"), + $this->yellow->config->get("serverAddress"), $this->yellow->config->get("serverBase"), $location); $this->yellow->sendStatus($statusCode, $location); return $statusCode; } // Process request for user signup - function processRequestSignup($serverScheme, $serverName, $base, $location, $fileName) + function processRequestSignup($scheme, $address, $base, $location, $fileName) { $this->response->action = "signup"; $this->response->status = "ok"; @@ -291,15 +289,15 @@ class YellowWebinterface } if($this->response->status=="ok") { - $this->response->status = $this->response->sendMail($serverScheme, $serverName, $base, $email, "confirm") ? "next" : "error"; + $this->response->status = $this->response->sendMail($scheme, $address, $base, $email, "confirm") ? "next" : "error"; if($this->response->status=="error") $this->yellow->page->error(500, "Can't send email on this server!"); } - $statusCode = $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, false); + $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); return $statusCode; } // Process request to confirm user signup - function processRequestConfirm($serverScheme, $serverName, $base, $location, $fileName) + function processRequestConfirm($scheme, $address, $base, $location, $fileName) { $this->response->action = "confirm"; $this->response->status = "ok"; @@ -313,15 +311,15 @@ class YellowWebinterface } if($this->response->status=="ok") { - $this->response->status = $this->response->sendMail($serverScheme, $serverName, $base, $email, "approve") ? "done" : "error"; + $this->response->status = $this->response->sendMail($scheme, $address, $base, $email, "approve") ? "done" : "error"; if($this->response->status=="error") $this->yellow->page->error(500, "Can't send email on this server!"); } - $statusCode = $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, false); + $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); return $statusCode; } // Process request to approve user signup - function processRequestApprove($serverScheme, $serverName, $base, $location, $fileName) + function processRequestApprove($scheme, $address, $base, $location, $fileName) { $this->response->action = "approve"; $this->response->status = "ok"; @@ -335,15 +333,15 @@ class YellowWebinterface } if($this->response->status=="ok") { - $this->response->status = $this->response->sendMail($serverScheme, $serverName, $base, $email, "welcome") ? "done" : "error"; + $this->response->status = $this->response->sendMail($scheme, $address, $base, $email, "welcome") ? "done" : "error"; if($this->response->status=="error") $this->yellow->page->error(500, "Can't send email on this server!"); } - $statusCode = $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, false); + $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); return $statusCode; } // Process request to recover password - function processRequestRecover($serverScheme, $serverName, $base, $location, $fileName) + function processRequestRecover($scheme, $address, $base, $location, $fileName) { $this->response->action = "recover"; $this->response->status = "ok"; @@ -356,7 +354,7 @@ class YellowWebinterface if($this->response->status=="ok" && !$this->users->isExisting($email)) $this->response->status = "next"; if($this->response->status=="ok") { - $this->response->status = $this->response->sendMail($serverScheme, $serverName, $base, $email, "recover") ? "next" : "error"; + $this->response->status = $this->response->sendMail($scheme, $address, $base, $email, "recover") ? "next" : "error"; if($this->response->status=="error") $this->yellow->page->error(500, "Can't send email on this server!"); } } else { @@ -374,18 +372,18 @@ class YellowWebinterface if($this->response->status=="ok") { $this->response->userEmail = ""; - $this->response->destroyCookie($serverScheme, $serverName, $base); - $this->response->status = $this->response->sendMail($serverScheme, $serverName, $base, $email, "information") ? "done" : "error"; + $this->response->destroyCookie($scheme, $address, $base); + $this->response->status = $this->response->sendMail($scheme, $address, $base, $email, "information") ? "done" : "error"; if($this->response->status=="error") $this->yellow->page->error(500, "Can't send email on this server!"); } } } - $statusCode = $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, false); + $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); return $statusCode; } // Process request to change settings - function processRequestSettings($serverScheme, $serverName, $base, $location, $fileName) + function processRequestSettings($scheme, $address, $base, $location, $fileName) { $this->response->action = "settings"; $this->response->status = "ok"; @@ -416,7 +414,7 @@ class YellowWebinterface if($this->response->status=="ok") { $action = $email!=$emailSource ? "reconfirm" : "change"; - $this->response->status = $this->response->sendMail($serverScheme, $serverName, $base, $email, $action) ? "next" : "error"; + $this->response->status = $this->response->sendMail($scheme, $address, $base, $email, $action) ? "next" : "error"; if($this->response->status=="error") $this->yellow->page->error(500, "Can't send email on this server!"); } } else { @@ -430,16 +428,16 @@ class YellowWebinterface if($this->response->status=="done") { $statusCode = 303; - $location = $this->yellow->lookup->normaliseUrl($serverScheme, $serverName, $base, $location); + $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); $this->yellow->sendStatus($statusCode, $location); } else { - $statusCode = $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, false); + $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); } return $statusCode; } // Process request to reconfirm email - function processRequestReconfirm($serverScheme, $serverName, $base, $location, $fileName) + function processRequestReconfirm($scheme, $address, $base, $location, $fileName) { $this->response->action = "reconfirm"; $this->response->status = "ok"; @@ -458,15 +456,15 @@ class YellowWebinterface } if($this->response->status=="ok") { - $this->response->status = $this->response->sendMail($serverScheme, $serverName, $base, $emailSource, "change") ? "done" : "error"; + $this->response->status = $this->response->sendMail($scheme, $address, $base, $emailSource, "change") ? "done" : "error"; if($this->response->status=="error") $this->yellow->page->error(500, "Can't send email on this server!"); } - $statusCode = $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, false); + $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); return $statusCode; } // Process request to change settings - function processRequestChange($serverScheme, $serverName, $base, $location, $fileName) + function processRequestChange($scheme, $address, $base, $location, $fileName) { $this->response->action = "change"; $this->response->status = "ok"; @@ -495,51 +493,75 @@ class YellowWebinterface if($this->response->status=="ok") { $this->response->userEmail = ""; - $this->response->destroyCookie($serverScheme, $serverName, $base); - $this->response->status = $this->response->sendMail($serverScheme, $serverName, $base, $email, "information") ? "done" : "error"; + $this->response->destroyCookie($scheme, $address, $base); + $this->response->status = $this->response->sendMail($scheme, $address, $base, $email, "information") ? "done" : "error"; if($this->response->status=="error") $this->yellow->page->error(500, "Can't send email on this server!"); } - $statusCode = $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, false); + $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); return $statusCode; } // Process request to show software version - function processRequestVersion($serverScheme, $serverName, $base, $location, $fileName) + function processRequestVersion($scheme, $address, $base, $location, $fileName) { $this->response->action = "version"; $this->response->status = "ok"; if($this->yellow->plugins->isExisting("update")) { - list($statusCode, $dataCurrent) = $this->yellow->plugins->get("update")->getSoftwareVersion(); - list($statusCode, $dataLatest) = $this->yellow->plugins->get("update")->getSoftwareVersion(true); - foreach($dataCurrent as $key=>$value) + list($statusCodeCurrent, $dataCurrent) = $this->yellow->plugins->get("update")->getSoftwareVersion(); + list($statusCodeLatest, $dataLatest) = $this->yellow->plugins->get("update")->getSoftwareVersion(true); + list($statusCodeModified, $dataModified) = $this->yellow->plugins->get("update")->getSoftwareModified(); + $statusCode = max($statusCodeCurrent, $statusCodeLatest, $statusCodeModified); + if($this->response->isUserWebmaster()) { - if(strnatcasecmp($dataCurrent[$key], $dataLatest[$key])<0) + foreach($dataCurrent as $key=>$value) { - if(!empty($this->response->rawDataOutput)) $this->response->rawDataOutput .= "<br />\n"; - $this->response->rawDataOutput .= "$key $dataLatest[$key]"; - ++$updates; - ++$count; if($count>=4) { $this->response->rawDataOutput .= "…"; break; } + if(strnatcasecmp($dataCurrent[$key], $dataLatest[$key])<0) + { + ++$updates; + if(!empty($this->response->rawDataOutput)) $this->response->rawDataOutput .= "<br />\n"; + $this->response->rawDataOutput .= htmlspecialchars("$key $dataLatest[$key]"); + } + } + if($updates==0) + { + foreach($dataCurrent as $key=>$value) + { + if(!is_null($dataModified[$key]) && !is_null($dataLatest[$key])) + { + $rawData = $this->yellow->text->getTextHtml("webinterfaceVersionUpdateModified", $this->response->language)." - <a href=\"#\" onclick=\"yellow.action('update','update','".$this->yellow->toolbox->normaliseArgs("option:force/feature:$key")."'); return false;\">".$this->yellow->text->getTextHtml("webinterfaceVersionUpdateForce", $this->response->language)."</a>"; + $rawData = preg_replace("/@software/i", htmlspecialchars("$key $dataLatest[$key]"), $rawData); + if(!empty($this->response->rawDataOutput)) $this->response->rawDataOutput .= "<br />\n"; + $this->response->rawDataOutput .= $rawData; + } + } + } + } else { + foreach($dataCurrent as $key=>$value) + { + if(strnatcasecmp($dataCurrent[$key], $dataLatest[$key])<0) ++$updates; } } $this->response->status = $updates ? "updates" : "done"; if($statusCode!=200) $this->response->status = "error"; } - $statusCode = $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, false); + $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); return $statusCode; } // Process request to update website - function processRequestUpdate($serverScheme, $serverName, $base, $location, $fileName) + function processRequestUpdate($scheme, $address, $base, $location, $fileName) { $statusCode = 0; if($this->yellow->plugins->isExisting("update") && $this->response->isUserWebmaster()) { - $statusCode = $this->yellow->command("update"); + $option = trim($_REQUEST["option"]); + $feature = trim($_REQUEST["feature"]); + $statusCode = $this->yellow->command("update", $option, $feature); if($statusCode==200) { $statusCode = 303; - $location = $this->yellow->lookup->normaliseUrl($serverScheme, $serverName, $base, $location); + $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); $this->yellow->sendStatus($statusCode, $location); } } @@ -547,29 +569,29 @@ class YellowWebinterface } // Process request to create page - function processRequestCreate($serverScheme, $serverName, $base, $location, $fileName) + function processRequestCreate($scheme, $address, $base, $location, $fileName) { $statusCode = 0; if(!$this->response->isUserRestrictions() && !empty($_POST["rawdataedit"])) { $this->response->rawDataSource = $this->response->rawDataEdit = rawurldecode($_POST["rawdatasource"]); $rawData = $this->response->normaliseText(rawurldecode($_POST["rawdataedit"])); - $page = $this->response->getPageNew($serverScheme, $serverName, $base, $location, $fileName, $rawData); + $page = $this->response->getPageNew($scheme, $address, $base, $location, $fileName, $rawData); if(!$page->isError()) { if($this->yellow->toolbox->createFile($page->fileName, $page->rawData, true)) { $statusCode = 303; - $location = $this->yellow->lookup->normaliseUrl($serverScheme, $serverName, $base, $page->location); + $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $page->location); $this->yellow->sendStatus($statusCode, $location); } else { $statusCode = 500; - $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, false); + $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); $this->yellow->page->error($statusCode, "Can't write file '$page->fileName'!"); } } else { $statusCode = 500; - $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, false); + $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); $this->yellow->page->error($statusCode, $page->get("pageError")); } } @@ -577,14 +599,14 @@ class YellowWebinterface } // Process request to edit page - function processRequestEdit($serverScheme, $serverName, $base, $location, $fileName) + function processRequestEdit($scheme, $address, $base, $location, $fileName) { $statusCode = 0; if(!$this->response->isUserRestrictions() && !empty($_POST["rawdataedit"])) { $this->response->rawDataSource = rawurldecode($_POST["rawdatasource"]); $this->response->rawDataEdit = $this->response->normaliseText(rawurldecode($_POST["rawdataedit"])); - $page = $this->response->getPageUpdate($serverScheme, $serverName, $base, $location, $fileName, + $page = $this->response->getPageUpdate($scheme, $address, $base, $location, $fileName, $this->response->rawDataSource, $this->response->rawDataEdit, $this->yellow->toolbox->readFile($fileName)); if(!$page->isError()) { @@ -592,16 +614,16 @@ class YellowWebinterface $this->yellow->toolbox->createFile($page->fileName, $page->rawData)) { $statusCode = 303; - $location = $this->yellow->lookup->normaliseUrl($serverScheme, $serverName, $base, $page->location); + $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $page->location); $this->yellow->sendStatus($statusCode, $location); } else { $statusCode = 500; - $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, false); + $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); $this->yellow->page->error($statusCode, "Can't write file '$page->fileName'!"); } } else { $statusCode = 500; - $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, false); + $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); $this->yellow->page->error($statusCode, $page->get("pageError")); } } @@ -609,7 +631,7 @@ class YellowWebinterface } // Process request to delete page - function processRequestDelete($serverScheme, $serverName, $base, $location, $fileName) + function processRequestDelete($scheme, $address, $base, $location, $fileName) { $statusCode = 0; if(!$this->response->isUserRestrictions() && is_file($fileName)) @@ -620,22 +642,22 @@ class YellowWebinterface if($this->yellow->toolbox->deleteFile($fileName, $this->yellow->config->get("trashDir"))) { $statusCode = 303; - $location = $this->yellow->lookup->normaliseUrl($serverScheme, $serverName, $base, $location); + $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); $this->yellow->sendStatus($statusCode, $location); } else { $statusCode = 500; - $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, false); + $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); $this->yellow->page->error($statusCode, "Can't delete file '$fileName'!"); } } else { if($this->yellow->toolbox->deleteDirectory(dirname($fileName), $this->yellow->config->get("trashDir"))) { $statusCode = 303; - $location = $this->yellow->lookup->normaliseUrl($serverScheme, $serverName, $base, $location); + $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); $this->yellow->sendStatus($statusCode, $location); } else { $statusCode = 500; - $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, false); + $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); $this->yellow->page->error($statusCode, "Can't delete file '$fileName'!"); } } @@ -646,17 +668,13 @@ class YellowWebinterface // Check web interface request function checkRequest($location) { - if($this->yellow->toolbox->getServerScheme()==$this->yellow->config->get("webinterfaceServerScheme") && - $this->yellow->toolbox->getServerName()==$this->yellow->config->get("webinterfaceServerName")) - { - $locationLength = strlenu($this->yellow->config->get("webinterfaceLocation")); - $this->response->active = substru($location, 0, $locationLength)==$this->yellow->config->get("webinterfaceLocation"); - } + $locationLength = strlenu($this->yellow->config->get("webinterfaceLocation")); + $this->response->active = substru($location, 0, $locationLength)==$this->yellow->config->get("webinterfaceLocation"); return $this->response->isActive(); } // Check web interface user - function checkUser($serverScheme, $serverName, $base, $location, $fileName) + function checkUser($scheme, $address, $base, $location, $fileName) { if($_POST["action"]=="login") { @@ -664,7 +682,7 @@ class YellowWebinterface $password = $_POST["password"]; if($this->users->checkUser($email, $password)) { - $this->response->createCookie($serverScheme, $serverName, $base, $email); + $this->response->createCookie($scheme, $address, $base, $email); $this->response->userEmail = $email; $this->response->userRestrictions = $this->getUserRestrictions($email, $location, $fileName); $this->response->language = $this->response->getLanguage($email); @@ -749,10 +767,10 @@ class YellowResponse } // Return new page - function getPageNew($serverScheme, $serverName, $base, $location, $fileName, $rawData) + function getPageNew($scheme, $address, $base, $location, $fileName, $rawData) { $page = new YellowPage($this->yellow); - $page->setRequestInformation($serverScheme, $serverName, $base, $location, $fileName); + $page->setRequestInformation($scheme, $address, $base, $location, $fileName); $page->parseData($rawData, false, 0); if($this->yellow->lookup->isFileLocation($location) || is_file($fileName)) { @@ -793,16 +811,16 @@ class YellowResponse } // Return modified page - function getPageUpdate($serverScheme, $serverName, $base, $location, $fileName, $rawDataSource, $rawDataEdit, $rawDataFile) + function getPageUpdate($scheme, $address, $base, $location, $fileName, $rawDataSource, $rawDataEdit, $rawDataFile) { $page = new YellowPage($this->yellow); - $page->setRequestInformation($serverScheme, $serverName, $base, $location, $fileName); + $page->setRequestInformation($scheme, $address, $base, $location, $fileName); $page->parseData($this->webinterface->merge->merge($rawDataSource, $rawDataEdit, $rawDataFile), false, 0); if(empty($page->rawData)) $page->error(500, "Page has been modified by someone else!"); if($this->yellow->lookup->isFileLocation($location) && !$page->isError()) { $pageSource = new YellowPage($this->yellow); - $pageSource->setRequestInformation($serverScheme, $serverName, $base, $location, $fileName); + $pageSource->setRequestInformation($scheme, $address, $base, $location, $fileName); $pageSource->parseData($rawDataSource, false, 0); $prefix = $this->yellow->config->get("webinterfaceMetaFilePrefix"); if($pageSource->get($prefix)!=$page->get($prefix) || $pageSource->get("title")!=$page->get("title")) @@ -840,7 +858,10 @@ class YellowResponse $data["rawDataEdit"] = $this->rawDataEdit; $data["rawDataNew"] = $this->getRawDataNew(); $data["rawDataOutput"] = strval($this->rawDataOutput); - $data["pageFile"] = $this->yellow->page->get("pageFile"); + $data["scheme"] = $this->yellow->page->scheme; + $data["address"] = $this->yellow->page->address; + $data["base"] = $this->yellow->page->base; + $data["location"] = $this->yellow->page->location; $data["parserSafeMode"] = $this->yellow->page->parserSafeMode; } if($this->action!="none") $data = array_merge($data, $this->getRequestData()); @@ -864,15 +885,14 @@ class YellowResponse $data["userRestrictions"] = intval($this->isUserRestrictions()); $data["userWebmaster"] = intval($this->isUserWebmaster()); $data["pluginUpdate"] = intval($this->yellow->plugins->isExisting("update")); - $data["serverScheme"] = $this->yellow->config->get("serverScheme"); - $data["serverName"] = $this->yellow->config->get("serverName"); - $data["serverBase"] = $this->yellow->config->get("serverBase"); - $data["serverTime"] = $this->yellow->config->get("serverTime"); $data["serverLanguages"] = array(); foreach($this->yellow->text->getLanguages() as $language) { $data["serverLanguages"][$language] = $this->yellow->text->getTextHtml("languageDescription", $language); } + $data["serverScheme"] = $this->yellow->config->get("serverScheme"); + $data["serverAddress"] = $this->yellow->config->get("serverAddress"); + $data["serverBase"] = $this->yellow->config->get("serverBase"); $data["serverVersion"] = "Yellow ".YellowCore::VERSION; } else { $data["loginEmail"] = $this->yellow->config->get("loginEmail"); @@ -898,9 +918,9 @@ class YellowResponse // Return text strings function getTextData() { - $textLanguage = array_merge($this->yellow->text->getData("language", $this->language)); - $textWebinterface = array_merge($this->yellow->text->getData("webinterface", $this->language)); - $textYellow = array_merge($this->yellow->text->getData("yellow", $this->language)); + $textLanguage = $this->yellow->text->getData("language", $this->language); + $textWebinterface = $this->yellow->text->getData("webinterface", $this->language); + $textYellow = $this->yellow->text->getData("yellow", $this->language); return array_merge($textLanguage, $textWebinterface, $textYellow); } @@ -968,28 +988,28 @@ class YellowResponse } // Create browser cookie - function createCookie($serverScheme, $serverName, $base, $email) + function createCookie($scheme, $address, $base, $email) { $session = $this->webinterface->users->createSession($email); - setcookie("login", "$email,$session", time()+60*60*24*365, "$base/", "", $serverScheme=="https"); + setcookie("login", "$email,$session", time()+60*60*24*365, "$base/", "", $scheme=="https"); } // Destroy browser cookie - function destroyCookie($serverScheme, $serverName, $base) + function destroyCookie($scheme, $address, $base) { - setcookie("login", "", time()-60*60, "$base/", "", $serverScheme=="https"); + setcookie("login", "", time()-60*60, "$base/", "", $scheme=="https"); } // Send mail to user - function sendMail($serverScheme, $serverName, $base, $email, $action) + function sendMail($scheme, $address, $base, $email, $action) { if($action=="welcome" || $action=="information") { - $url = "$serverScheme://$serverName$base/"; + $url = "$scheme://$address$base/"; } else { $expire = time()+60*60*24; $id = $this->webinterface->users->createRequestId($email, $action, $expire); - $url = "$serverScheme://$serverName$base"."/action:$action/email:$email/expire:$expire/id:$id/"; + $url = "$scheme://$address$base"."/action:$action/email:$email/expire:$expire/id:$id/"; } if($action=="approve") { @@ -1012,7 +1032,7 @@ class YellowResponse $mailTo = mb_encode_mimeheader("$name")." <$email>"; $mailSubject = mb_encode_mimeheader($this->yellow->text->getText("{$prefix}Subject", $language)); $mailHeaders = mb_encode_mimeheader("From: $sitename")." <noreply>\r\n"; - $mailHeaders .= mb_encode_mimeheader("X-Request-Url: $serverScheme://$serverName$base")."\r\n"; + $mailHeaders .= mb_encode_mimeheader("X-Request-Url: $scheme://$address$base")."\r\n"; $mailHeaders .= mb_encode_mimeheader("X-Remote-Addr: $_SERVER[REMOTE_ADDR]")."\r\n"; $mailHeaders .= "Mime-Version: 1.0\r\n"; $mailHeaders .= "Content-Type: text/plain; charset=utf-8\r\n"; @@ -1274,7 +1294,10 @@ class YellowUsers $data = array(); foreach($this->users as $key=>$value) { - $data[$key] = "$value[email] password $value[name] $value[language] $value[status]"; + $name = $value["name"]; if(preg_match("/\s/", $name)) $name = "\"$name\""; + $language = $value["language"]; if(preg_match("/\s/", $language)) $language = "\"$language\""; + $status = $value["status"]; if(preg_match("/\s/", $status)) $status = "\"$status\""; + $data[$key] = "$value[email] - $name $language $status"; if($value["home"]!="/") $data[$key] .= " restrictions"; } usort($data, strnatcasecmp); diff --git a/system/themes/assets/flatsite.css b/system/themes/assets/flatsite.css @@ -0,0 +1,188 @@ +/* Flatsite theme, https://github.com/datenstrom/yellow-themes/tree/master/flatsite */ +/* Copyright (c) 2013-2017 Datenstrom, http://datenstrom.se */ +/* This file may be used and distributed under the terms of the public license. */ + +html, body, div, form, pre, span, tr, th, td, img { margin:0; padding:0; border:0; vertical-align:baseline; } +@font-face { + font-family:'Open Sans'; + font-style:normal; + font-weight:300; + src:url(opensans-light.woff) format('woff'); +} +@font-face { + font-family:'Open Sans'; + font-style:normal; + font-weight:400; + src:url(opensans-regular.woff) format('woff'); +} +@font-face { + font-family:'Open Sans'; + font-style:normal; + font-weight:700; + src:url(opensans-bold.woff) format('woff'); +} +body { + margin:1em; + background-color:#fff; color:#717171; + font-family:'Open Sans',Helvetica,sans-serif; + font-size:1em; + font-weight:300; + line-height:1.5; +} +h1, h2, h3, h4, h5, h6 { color:#07d; font-weight:normal; } +h1 { font-size:2.0em; } +hr { height:1px; background:#ddd; border:0; } +strong { font-weight:bold; } +code { font-size:1.1em; } +a { color:#07d; text-decoration:none; } +a:hover { color:#07d; text-decoration:underline; } +.content h1:first-child, .content>*:first-child { margin-top:0; } +.content h1 a:hover { text-decoration:none; } +.content img { max-width:100%; height:auto; } +.content form { margin:1em 0; } +.content table { border-spacing:0; border-collapse:collapse; } +.content th { text-align:left; padding:0.3em; border-bottom:1px solid #ddd; } +.content td { text-align:left; padding:0.3em; border-top:1px solid #ddd; } +.content blockquote { margin-left:0; padding-left:1em; border-left:.5em solid #0a0; } +.content blockquote blockquote { margin-left:-1.5em; border-left:.5em solid #fb0; } +.content blockquote blockquote blockquote { border-color:#d00; } +.content code, pre { font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace; font-size:90%; } +.content code { padding:0.15em 0.4em; margin:0; background-color:#f7f7f7; border-radius:3px; } +.content pre>code { padding:0; margin:0; white-space:pre; background:transparent; border:0; font-size:inherit; } +.content pre { padding:1em; overflow:auto; line-height:1.45; background-color:#f7f7f7; border-radius:3px; } +.content .flexible { position:relative; padding-top:0; padding-bottom:56.25%; } +.content .flexible iframe { position:absolute; top:0; left:0; width:100%; height:100%; } +.content .stretchable ul { margin:0 -0.5em; padding:0; list-style:none; text-align:center; } +.content .stretchable li { margin:0; padding:1em 0; display:inline-block; text-align:center; vertical-align:top; } +.content .stretchable a { color:#717171; text-decoration:none; } +.content .toc { margin:0; padding:0; list-style:none; } +.content .links { margin:1em 0; } +.content .pagination { margin:1em 0; } +.content .left { float:left; margin:0 1em 0 0; } +.content .center { display:block; margin:0 auto; } +.content .right { float:right; margin:0 0 0 1em; } +.content .rounded { border-radius:4px; } + +/* Header */ + +.header .sitename { display:block; float:left; } +.header .sitename h1 { margin:0; } +.header .sitename h1 a { color:#111; text-decoration:none; } +.header .sitename h2 { margin-top:0; color:#717171; font-size:1.0em; font-weight:300; } + +/* Navigation */ + +.navigation { display:block; float:right; } +.navigation { margin-top:0.9em; margin-bottom:0.9em; line-height:2; } +.navigation a { padding:0 0.3em; } +.navigation ul { margin:0 -0.3em; padding:0; list-style:none; } +.navigation li { display:inline; } +.navigation-tree { display:block; float:right; } +.navigation-tree { margin-top:0.9em; margin-bottom:0.9em; line-height:2; } +.navigation-tree a { padding:0 0.3em; } +.navigation-tree ul { margin:0 -0.3em; padding:0; list-style:none; } +.navigation-tree li { display:inline; } +.navigation-tree ul li { display:inline-block; position:relative; cursor:pointer; margin:0; } +.navigation-tree ul li ul { padding:0.3em; position:absolute; width:13em; background:#fff; z-index:100; display:none; } +.navigation-tree ul li ul { border:1px solid #bbb; border-radius:4px; box-shadow:2px 4px 10px rgba(0, 0, 0, 0.2); } +.navigation-tree ul li ul li { display:block; } +.navigation-tree>ul>li:hover>ul { display:block; } +.navigation-banner { clear:both; } +.navigation-search { padding-bottom:1em; } +.navigation-search .search-form { position:relative; } +.navigation-search .search-text { font-family:inherit; font-size:inherit; font-weight:inherit; } +.navigation-search .search-text { padding:0.5em; border:1px solid #bbb; border-radius:4px; width:100%; box-sizing:border-box; } +.navigation-search .search-text { background-color:#fff; background-image:linear-gradient(to bottom, #fff, #fff); } +.navigation-search .search-button { position:absolute; top:0; right:0; } +.navigation-search .search-button { font-family:inherit; font-size:inherit; font-weight:inherit; } +.navigation-search .search-button { margin:5px; padding:0.3em; border:none; background-color:transparent; } + +/* Footer */ + +.footer { margin-top:2em; } +.footer .siteinfo a { color:#07d; } +.footer .siteinfo a:hover { color:#07d; text-decoration:underline; } +.footer .siteinfo a.socialmedia { + display:inline-block; + box-sizing:border-box; + border-radius:13px; + width:26px; + height:26px; + margin:-20px 0; + padding:1px 0; + text-align:center; + background-color:#07d; + color:#fff; +} +.footer .siteinfo a.socialmedia:hover { background-color:#039; color:#fff; } +.footer .siteinfo-left { float:left; } +.footer .siteinfo-right { float:right; } +.footer .siteinfo-banner { clear:both; } + +/* Sidebar */ + +.with-sidebar .main { margin-right:17em; } +.with-sidebar .sidebar { float:right; width:16em; margin-top:3.9em; padding:2px; overflow:hidden; font-size:0.9em; } +.with-sidebar .sidebar .search-form input { width:100%; box-sizing:border-box; } +.with-sidebar .content:after { content:""; display:table; clear:both; } + +/* Forms and buttons */ + +.form-control { + margin:0; padding:2px 4px; + display:inline-block; min-width:7em; + background-color:#fff; color:#555; + background-image:linear-gradient(to bottom, #fff, #fff); + border:1px solid #bbb; + border-radius:4px; + font-size:0.9em; font-family:inherit; font-weight:normal; line-height:normal; +} +.btn { + margin:0; padding:4px 22px; + display:inline-block; min-width:7em; + background-color:#eaeaea; color:#333333; + background-image:linear-gradient(to bottom, #f8f8f8, #e1e1e1); + border:1px solid #bbb; + border-color:#c1c1c1 #c1c1c1 #aaaaaa; + border-radius:4px; + outline-offset:-2px; + font-size:0.9em; font-family:inherit; font-weight:normal; line-height:1; + text-align:center; text-decoration:none; +} +.btn:hover, .btn:focus, .btn:active { + color:#333333; + background-image:none; + text-decoration:none; +} +.btn:active { box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.1); } + +/* Misc */ + +.highlight .kw1, .highlight .kw2, .highlight .kw3, .highlight .kw4 { color:#b0b; } +.highlight .st0, .highlight .st_h, .highlight .nu0 { color:#b0b; } +.highlight .re0, .highlight .re1, .highlight .re2, .highlight .re3, .css .nu0 { color:inherit; } +.highlight .co1, .highlight .coMULTI, .highlight .sc0, .highlight .sc-1 { color:inherit; } + +/* Responsive and print */ + +.page { margin:0 auto; max-width:1000px; } + +@media screen and (min-width:62em) { + body { width:60em; margin:1em auto; } + .page{ margin:0; max-width:none; } +} +@media screen and (max-width:32em) { + body { margin:0.5em; font-size:0.9em; } + .header .sitename h1, .content h1, .content h2 { font-size:1.3em; } + .header .sitename h1, .header .sitename h2, .footer, .page { margin:0; padding:0; } + .header .sitename, .navigation, .navigation-tree { float:none; } + .navigation { margin-top:0.5em; margin-bottom:0.5em; } + .navigation-search { padding-bottom:1em; } + .footer .siteinfo-right { display:none; } + .footer .siteinfo a.socialmedia { width:24px; height:24px; padding:1px 0; } + .with-sidebar .main { margin-right:0; } + .with-sidebar .sidebar { display:none; } +} +@media print { + .page { border:none !important; } +} +\ No newline at end of file diff --git a/system/themes/assets/flatsite.php b/system/themes/assets/flatsite.php @@ -0,0 +1,12 @@ +<?php +// Flatsite theme, https://github.com/datenstrom/yellow-themes/tree/master/flatsite +// Copyright (c) 2013-2017 Datenstrom, http://datenstrom.se +// This file may be used and distributed under the terms of the public license. + +class YellowThemeFlatsite +{ + const VERSION = "0.6.12"; +} + +$yellow->themes->register("flatsite", "YellowThemeFlatsite", YellowThemeFlatsite::VERSION); +?> +\ No newline at end of file diff --git a/system/themes/assets/opensans-bold.woff b/system/themes/assets/opensans-bold.woff Binary files differ. diff --git a/system/themes/assets/opensans-light.woff b/system/themes/assets/opensans-light.woff Binary files differ. diff --git a/system/themes/assets/opensans-regular.woff b/system/themes/assets/opensans-regular.woff Binary files differ. diff --git a/system/themes/flatsite.css b/system/themes/flatsite.css @@ -1,171 +0,0 @@ -/* Theme: Flatsite theme */ -/* Version: 0.6.11 */ -/* Designer: Mark Mayberg */ - -@import url(https://fonts.googleapis.com/css?family=Open+Sans:300,400,700); -html, body, div, form, pre, span, tr, th, td, img { margin:0; padding:0; border:0; vertical-align:baseline; } -body { - margin:1em; - background-color:#fff; color:#717171; - font-family:'Open Sans',Helvetica,sans-serif; - font-size:1em; - font-weight:300; - line-height:1.5; -} -h1, h2, h3, h4, h5, h6 { color:#07d; font-weight:normal; } -h1 { font-size:2.0em; } -hr { height:1px; background:#ddd; border:0; } -strong { font-weight:bold; } -code { font-size:1.1em; } -a { color:#07d; text-decoration:none; } -a:hover { color:#07d; text-decoration:underline; } -.content h1:first-child, .content>*:first-child { margin-top:0; } -.content h1 a:hover { text-decoration:none; } -.content img { max-width:100%; height:auto; } -.content form { margin:1em 0; } -.content table { border-spacing:0; border-collapse:collapse; } -.content th { text-align:left; padding:0.3em; border-bottom:1px solid #ddd; } -.content td { text-align:left; padding:0.3em; border-top:1px solid #ddd; } -.content blockquote { margin-left:0; padding-left:1em; border-left:.5em solid #0a0; } -.content blockquote blockquote { margin-left:-1.5em; border-left:.5em solid #fb0; } -.content blockquote blockquote blockquote { border-color:#d00; } -.content code, pre { font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace; font-size:90%; } -.content code { padding:0.15em 0.4em; margin:0; background-color:#f7f7f7; border-radius:3px; } -.content pre>code { padding:0; margin:0; white-space:pre; background:transparent; border:0; font-size:inherit; } -.content pre { padding:1em; overflow:auto; line-height:1.45; background-color:#f7f7f7; border-radius:3px; } -.content .flexible { position:relative; padding-top:0; padding-bottom:56.25%; } -.content .flexible iframe { position:absolute; top:0; left:0; width:100%; height:100%; } -.content .stretchable ul { margin:0 -0.5em; padding:0; list-style:none; text-align:center; } -.content .stretchable li { margin:0; padding:1em 0; display:inline-block; text-align:center; vertical-align:top; } -.content .stretchable a { color:#717171; text-decoration:none; } -.content .toc { margin:0; padding:0; list-style:none; } -.content .links { margin:1em 0; } -.content .pagination { margin:1em 0; } -.content .left { float:left; margin:0 1em 0 0; } -.content .center { display:block; margin:0 auto; } -.content .right { float:right; margin:0 0 0 1em; } -.content .rounded { border-radius:4px; } - -/* Header */ - -.header .sitename { display:block; float:left; } -.header .sitename h1 { margin:0; } -.header .sitename h1 a { color:#111; text-decoration:none; } -.header .sitename h2 { margin-top:0; color:#717171; font-size:1.0em; font-weight:300; } - -/* Navigation */ - -.navigation { display:block; float:right; } -.navigation { margin-top:0.9em; margin-bottom:0.9em; line-height:2; } -.navigation a { padding:0 0.3em; } -.navigation ul { margin:0 -0.3em; padding:0; list-style:none; } -.navigation li { display:inline; } -.navigation-tree { display:block; float:right; } -.navigation-tree { margin-top:0.9em; margin-bottom:0.9em; line-height:2; } -.navigation-tree a { padding:0 0.3em; } -.navigation-tree ul { margin:0 -0.3em; padding:0; list-style:none; } -.navigation-tree li { display:inline; } -.navigation-tree ul li { display:inline-block; position:relative; cursor:pointer; margin:0; } -.navigation-tree ul li ul { padding:0.3em; position:absolute; width:13em; background:#fff; z-index:100; display:none; } -.navigation-tree ul li ul { border:1px solid #bbb; border-radius:4px; box-shadow:2px 4px 10px rgba(0, 0, 0, 0.2); } -.navigation-tree ul li ul li { display:block; } -.navigation-tree>ul>li:hover>ul { display:block; } -.navigation-banner { clear:both; } -.navigation-search { padding-bottom:1em; } -.navigation-search .search-form { position:relative; } -.navigation-search .search-text { font-family:inherit; font-size:inherit; font-weight:inherit; } -.navigation-search .search-text { padding:0.5em; border:1px solid #bbb; border-radius:4px; width:100%; box-sizing:border-box; } -.navigation-search .search-text { background-color:#fff; background-image:linear-gradient(to bottom, #fff, #fff); } -.navigation-search .search-button { position:absolute; top:0; right:0; } -.navigation-search .search-button { font-family:inherit; font-size:inherit; font-weight:inherit; } -.navigation-search .search-button { margin:5px; padding:0.3em; border:none; background-color:transparent; } - -/* Footer */ - -.footer { margin-top:2em; } -.footer .siteinfo a { color:#07d; } -.footer .siteinfo a:hover { color:#07d; text-decoration:underline; } -.footer .siteinfo a.socialmedia { - display:inline-block; - box-sizing:border-box; - border-radius:13px; - width:26px; - height:26px; - margin:-20px 0; - padding:1px 0; - text-align:center; - background-color:#07d; - color:#fff; -} -.footer .siteinfo a.socialmedia:hover { background-color:#039; color:#fff; } -.footer .siteinfo-left { float:left; } -.footer .siteinfo-right { float:right; } -.footer .siteinfo-banner { clear:both; } - -/* Sidebar */ - -.with-sidebar .main { margin-right:17em; } -.with-sidebar .sidebar { float:right; width:16em; margin-top:3.9em; padding:2px; overflow:hidden; font-size:0.9em; } -.with-sidebar .sidebar .search-form input { width:100%; box-sizing:border-box; } -.with-sidebar .content:after { content:""; display:table; clear:both; } - -/* Forms and buttons */ - -.form-control { - margin:0; padding:2px 4px; - display:inline-block; min-width:7em; - background-color:#fff; color:#555; - background-image:linear-gradient(to bottom, #fff, #fff); - border:1px solid #bbb; - border-radius:4px; - font-size:0.9em; font-family:inherit; font-weight:normal; line-height:normal; -} -.btn { - margin:0; padding:4px 22px; - display:inline-block; min-width:7em; - background-color:#eaeaea; color:#333333; - background-image:linear-gradient(to bottom, #f8f8f8, #e1e1e1); - border:1px solid #bbb; - border-color:#c1c1c1 #c1c1c1 #aaaaaa; - border-radius:4px; - outline-offset:-2px; - font-size:0.9em; font-family:inherit; font-weight:normal; line-height:1; - text-align:center; text-decoration:none; -} -.btn:hover, .btn:focus, .btn:active { - color:#333333; - background-image:none; - text-decoration:none; -} -.btn:active { box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.1); } - -/* Misc */ - -.highlight .kw1, .highlight .kw2, .highlight .kw3, .highlight .kw4 { color:#b0b; } -.highlight .st0, .highlight .st_h, .highlight .nu0 { color:#b0b; } -.highlight .re0, .highlight .re1, .highlight .re2, .highlight .re3, .css .nu0 { color:inherit; } -.highlight .co1, .highlight .coMULTI, .highlight .sc0, .highlight .sc-1 { color:inherit; } - -/* Responsive and print */ - -.page { margin:0 auto; max-width:1000px; } - -@media screen and (min-width:62em) { - body { width:60em; margin:1em auto; } - .page{ margin:0; max-width:none; } -} -@media screen and (max-width:32em) { - body { margin:0.5em; font-size:0.9em; } - .header .sitename h1, .content h1, .content h2 { font-size:1.3em; } - .header .sitename h1, .header .sitename h2, .footer, .page { margin:0; padding:0; } - .header .sitename, .navigation, .navigation-tree { float:none; } - .navigation { margin-top:0.5em; margin-bottom:0.5em; } - .navigation-search { padding-bottom:1em; } - .footer .siteinfo-right { display:none; } - .footer .siteinfo a.socialmedia { width:24px; height:24px; padding:1px 0; } - .with-sidebar .main { margin-right:0; } - .with-sidebar .sidebar { display:none; } -} -@media print { - .page { border:none !important; } -} -\ No newline at end of file