commit 0df028981075d78a20a885b7fe4e789f77ce894e parent 19e7150c282f27a0eccadc1ba5dfac47bfdaa14d Author: markseu <mark2011@mayberg.se> Date: Sun, 26 Jul 2020 16:51:07 +0200 Refactored code base, updated system files Diffstat:
25 files changed, 1113 insertions(+), 930 deletions(-)
diff --git a/system/extensions/bundle.php b/system/extensions/bundle.php @@ -2,8 +2,7 @@ // Bundle extension, https://github.com/datenstrom/yellow-extensions/tree/master/source/bundle class YellowBundle { - const VERSION = "0.8.13"; - const TYPE = "feature"; + const VERSION = "0.8.14"; public $yellow; // access to API // Handle initialisation @@ -33,7 +32,7 @@ class YellowBundle { public function processCommandClean($command, $text) { $statusCode = 0; if ($command=="clean" && $text=="all") { - $path = $this->yellow->system->get("coreResourceDirectory"); + $path = $this->yellow->system->get("coreExtensionDirectory"); foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/bundle-.*/", false, false) as $entry) { if (!$this->yellow->toolbox->deleteFile($entry)) $statusCode = 500; } @@ -96,8 +95,8 @@ class YellowBundle { if (!empty($fileNames)) { $autoVersioning = intval($modified/(60*60*24)); $id = substru(md5($autoVersioning.$base.implode($fileNames)), 0, 10); - $fileNameBundle = $this->yellow->system->get("coreResourceDirectory")."bundle-$id.min.$type"; - $locationBundle = $base.$this->yellow->system->get("coreResourceLocation")."bundle-$id.min.$type"; + $fileNameBundle = $this->yellow->system->get("coreExtensionDirectory")."bundle-$id.min.$type"; + $locationBundle = $base.$this->yellow->system->get("coreExtensionLocation")."bundle-$id.min.$type"; $rawDataAttribute = $attribute=="defer" ? "defer=\"defer\" " : ""; if ($type=="css") { $data[$locationBundle] = "<link rel=\"stylesheet\" type=\"text/css\" media=\"all\" href=\"".htmlspecialchars($locationBundle)."\" />\n"; @@ -129,11 +128,11 @@ class YellowBundle { // Process bundle, convert URLs public function processBundleConvert($scheme, $address, $base, $fileData, $fileName, $type) { if ($type=="css") { - $extensionDirectoryLength = strlenu($this->yellow->system->get("coreExtensionDirectory")); - if (substru($fileName, 0, $extensionDirectoryLength) == $this->yellow->system->get("coreExtensionDirectory")) { - $base .= $this->yellow->system->get("coreExtensionLocation"); + $themeDirectoryLength = strlenu($this->yellow->system->get("coreThemeDirectory")); + if (substru($fileName, 0, $themeDirectoryLength) == $this->yellow->system->get("coreThemeDirectory")) { + $base .= $this->yellow->system->get("coreThemeLocation"); } else { - $base .= $this->yellow->system->get("coreResourceLocation"); + $base .= $this->yellow->system->get("coreExtensionLocation"); } $thisCompatible = $this; $callback = function ($matches) use ($thisCompatible, $scheme, $address, $base) { @@ -1924,7 +1923,7 @@ class Converter implements ConverterInterface { } } -// Bundle extensions, Copyright Datenstrom, License GPLv2 +// Bundle extension, Copyright Datenstrom, License GPLv2 class MinifyCss extends CSS { } diff --git a/system/extensions/command.php b/system/extensions/command.php @@ -2,8 +2,7 @@ // Command extension, https://github.com/datenstrom/yellow-extensions/tree/master/source/command class YellowCommand { - const VERSION = "0.8.17"; - const TYPE = "feature"; + const VERSION = "0.8.18"; const PRIORITY = "3"; public $yellow; // access to API public $files; // number of files @@ -155,11 +154,11 @@ class YellowCommand { public function requestStaticFile($scheme, $address, $base, $location) { list($serverName, $serverPort) = $this->yellow->toolbox->getTextList($address, ":", 2); if (empty($serverPort)) $serverPort = $scheme=="https" ? 443 : 80; - $_SERVER["HTTPS"] = $scheme=="https" ? "on" : "off"; $_SERVER["SERVER_PROTOCOL"] = "HTTP/1.1"; $_SERVER["SERVER_NAME"] = $serverName; $_SERVER["SERVER_PORT"] = $serverPort; $_SERVER["REQUEST_METHOD"] = "GET"; + $_SERVER["REQUEST_SCHEME"] = $scheme; $_SERVER["REQUEST_URI"] = $base.$location; $_SERVER["SCRIPT_NAME"] = $base."/yellow.php"; $_SERVER["REMOTE_ADDR"] = "127.0.0.1"; @@ -437,7 +436,7 @@ class YellowCommand { // Broadcast command to other extensions public function broadcastCommand($command, $text) { $statusCode = 0; - foreach ($this->yellow->extensions->extensions as $key=>$value) { + foreach ($this->yellow->extension->data as $key=>$value) { if (method_exists($value["obj"], "onCommand") && $key!="command") { $statusCode = max($statusCode, $value["obj"]->onCommand($command, $text)); } @@ -489,7 +488,7 @@ class YellowCommand { // Return command help public function getCommandHelp() { $data = array(); - foreach ($this->yellow->extensions->extensions as $key=>$value) { + foreach ($this->yellow->extension->data as $key=>$value) { if (method_exists($value["obj"], "onCommandHelp")) { foreach (preg_split("/[\r\n]+/", $value["obj"]->onCommandHelp()) as $line) { list($command, $dummy) = $this->yellow->toolbox->getTextList($line, " ", 2); @@ -572,16 +571,16 @@ class YellowCommand { // Return system locations public function getSystemLocations() { $locations = array(); - $regex = "/\.(css|gif|ico|js|jpg|png|svg|txt|woff|woff2)$/"; + $regex = "/\.(css|gif|ico|js|jpg|png|svg|woff|woff2)$/"; $extensionDirectoryLength = strlenu($this->yellow->system->get("coreExtensionDirectory")); $fileNames = $this->yellow->toolbox->getDirectoryEntriesRecursive($this->yellow->system->get("coreExtensionDirectory"), $regex, false, false); foreach ($fileNames as $fileName) { array_push($locations, $this->yellow->system->get("coreExtensionLocation").substru($fileName, $extensionDirectoryLength)); } - $resourceDirectoryLength = strlenu($this->yellow->system->get("coreResourceDirectory")); - $fileNames = $this->yellow->toolbox->getDirectoryEntriesRecursive($this->yellow->system->get("coreResourceDirectory"), $regex, false, false); + $themeDirectoryLength = strlenu($this->yellow->system->get("coreThemeDirectory")); + $fileNames = $this->yellow->toolbox->getDirectoryEntriesRecursive($this->yellow->system->get("coreThemeDirectory"), $regex, false, false); foreach ($fileNames as $fileName) { - array_push($locations, $this->yellow->system->get("coreResourceLocation").substru($fileName, $resourceDirectoryLength)); + array_push($locations, $this->yellow->system->get("coreThemeLocation").substru($fileName, $themeDirectoryLength)); } return $locations; } diff --git a/system/extensions/core.php b/system/extensions/core.php @@ -2,16 +2,18 @@ // Core extension, https://github.com/datenstrom/yellow-extensions/tree/master/source/core class YellowCore { - const VERSION = "0.8.13"; - const TYPE = "feature"; + const VERSION = "0.8.14"; public $page; // current page - public $content; // content files from file system - public $media; // media files from file system + public $content; // content files + public $media; // media files public $system; // system settings - public $text; // text settings + public $user; // user settings + public $language; // language settings + public $extension; // extensions public $lookup; // location and file lookup - public $toolbox; // toolbox with helpers - public $extensions; // features and themes + public $toolbox; // toolbox with helper functions + public $text; // TODO: remove later, for backwards compatibility + public $extensions; // TODO: remove later, for backwards compatibility public function __construct() { $this->checkRequirements(); @@ -19,9 +21,12 @@ class YellowCore { $this->content = new YellowContent($this); $this->media = new YellowMedia($this); $this->system = new YellowSystem($this); - $this->text = new YellowText($this); + $this->user = new YellowUser($this); + $this->language = new YellowLanguage($this); + $this->extension = new YellowExtension($this); $this->lookup = new YellowLookup($this); $this->toolbox = new YellowToolbox(); + $this->text = new YellowText($this); $this->extensions = new YellowExtensions($this); $this->system->setDefault("sitename", "Yellow"); $this->system->setDefault("author", "Yellow"); @@ -44,14 +49,14 @@ class YellowCore { $this->system->setDefault("coreDownloadLocation", "/media/downloads/"); $this->system->setDefault("coreImageLocation", "/media/images/"); $this->system->setDefault("coreExtensionLocation", "/media/extensions/"); - $this->system->setDefault("coreResourceLocation", "/media/resources/"); + $this->system->setDefault("coreThemeLocation", "/media/themes/"); $this->system->setDefault("coreMediaDirectory", "media/"); $this->system->setDefault("coreDownloadDirectory", "media/downloads/"); $this->system->setDefault("coreImageDirectory", "media/images/"); $this->system->setDefault("coreSystemDirectory", "system/"); $this->system->setDefault("coreExtensionDirectory", "system/extensions/"); $this->system->setDefault("coreLayoutDirectory", "system/layouts/"); - $this->system->setDefault("coreResourceDirectory", "system/resources/"); + $this->system->setDefault("coreThemeDirectory", "system/themes/"); $this->system->setDefault("coreSettingDirectory", "system/settings/"); $this->system->setDefault("coreContentDirectory", "content/"); $this->system->setDefault("coreContentRootDirectory", "default/"); @@ -62,8 +67,12 @@ class YellowCore { $this->system->setDefault("coreContentExtension", ".md"); $this->system->setDefault("coreDownloadExtension", ".download"); $this->system->setDefault("coreSystemFile", "system.ini"); - $this->system->setDefault("coreTextFile", "text.ini"); + $this->system->setDefault("coreUserFile", "user.ini"); + $this->system->setDefault("coreLanguageFile", "language.ini"); $this->system->setDefault("coreLogFile", "yellow.log"); + $this->language->setDefault("coreDateFormatShort"); + $this->language->setDefault("coreDateFormatMedium"); + $this->language->setDefault("coreDateFormatLong"); } public function __destruct() { @@ -90,9 +99,10 @@ class YellowCore { // Handle initialisation public function load() { $this->system->load($this->system->get("coreSettingDirectory").$this->system->get("coreSystemFile")); - $this->extensions->load($this->system->get("coreExtensionDirectory")); - $this->text->load($this->system->get("coreExtensionDirectory")); - $this->text->load($this->system->get("coreSettingDirectory"), $this->system->get("coreTextFile"), $this->system->get("language")); + $this->user->load($this->system->get("coreSettingDirectory").$this->system->get("coreUserFile")); + $this->language->load($this->system->get("coreExtensionDirectory").".*\.txt"); + $this->language->load($this->system->get("coreSettingDirectory").$this->system->get("coreLanguageFile")); + $this->extension->load($this->system->get("coreExtensionDirectory")); $this->lookup->detectFileSystem(); $this->startup(); } @@ -104,7 +114,7 @@ class YellowCore { ob_start(); list($scheme, $address, $base, $location, $fileName) = $this->getRequestInformation(); $this->page->setRequestInformation($scheme, $address, $base, $location, $fileName); - foreach ($this->extensions->extensions as $key=>$value) { + foreach ($this->extension->data as $key=>$value) { if (method_exists($value["obj"], "onRequest")) { $this->lookup->requestHandler = $key; $statusCode = $value["obj"]->onRequest($scheme, $address, $base, $location, $fileName); @@ -177,8 +187,8 @@ class YellowCore { $rawData = $this->toolbox->readFile($fileNameError); } else { $language = $this->lookup->findLanguageFromFile($fileName, $this->system->get("language")); - $rawData = "---\nTitle: ".$this->text->getText("coreError${statusCode}Title", $language)."\n"; - $rawData .= "Layout: error\n---\n".$this->text->getText("coreError${statusCode}Text", $language); + $rawData = "---\nTitle: ".$this->language->getText("coreError${statusCode}Title", $language)."\n"; + $rawData .= "Layout: error\n---\n".$this->language->getText("coreError${statusCode}Text", $language); } $cacheable = false; } else { @@ -187,7 +197,7 @@ class YellowCore { $this->page = new YellowPage($this); $this->page->setRequestInformation($scheme, $address, $base, $location, $fileName); $this->page->parseData($rawData, $cacheable, $statusCode, $pageError); - $this->text->setLanguage($this->page->get("language")); + $this->language->set($this->page->get("language")); $this->page->parseContent(); return $fileName; } @@ -211,10 +221,11 @@ class YellowCore { foreach ($this->page->headerData as $key=>$value) { echo "YellowCore::sendPage $key: $value<br/>\n"; } + $language = $this->page->get("language"); $layout = $this->page->get("layout"); $theme = $this->page->get("theme"); $parser = $this->page->get("parser"); - echo "YellowCore::sendPage layout:$layout theme:$theme parser:$parser<br/>\n"; + echo "YellowCore::sendPage language:$language layout:$layout theme:$theme parser:$parser<br/>\n"; } return $statusCode; } @@ -265,7 +276,7 @@ class YellowCore { $statusCode = 0; $this->toolbox->timerStart($time); list($command, $text) = $this->getCommandInformation($line); - foreach ($this->extensions->extensions as $key=>$value) { + foreach ($this->extension->data as $key=>$value) { if (method_exists($value["obj"], "onCommand")) { $this->lookup->commandHandler = $key; $statusCode = $value["obj"]->onCommand($command, $text); @@ -287,10 +298,10 @@ class YellowCore { // Handle startup public function startup() { if ($this->isLoaded()) { - foreach ($this->extensions->extensions as $key=>$value) { + foreach ($this->extension->data as $key=>$value) { if (method_exists($value["obj"], "onStartup")) $value["obj"]->onStartup(); } - foreach ($this->extensions->extensions as $key=>$value) { + foreach ($this->extension->data as $key=>$value) { if (method_exists($value["obj"], "onUpdate")) $value["obj"]->onUpdate("startup"); } } @@ -299,7 +310,7 @@ class YellowCore { // Handle shutdown public function shutdown() { if ($this->isLoaded()) { - foreach ($this->extensions->extensions as $key=>$value) { + foreach ($this->extension->data as $key=>$value) { if (method_exists($value["obj"], "onShutdown")) $value["obj"]->onShutdown(); } } @@ -308,7 +319,7 @@ class YellowCore { // Handle logging public function log($action, $message) { $statusCode = 0; - foreach ($this->extensions->extensions as $key=>$value) { + foreach ($this->extension->data as $key=>$value) { if (method_exists($value["obj"], "onLog")) { $statusCode = $value["obj"]->onLog($action, $message); if ($statusCode!=0) break; @@ -379,7 +390,7 @@ class YellowCore { // Check if all extensions loaded public function isLoaded() { - return isset($this->extensions->extensions); + return isset($this->extension->data); } } @@ -477,7 +488,7 @@ class YellowPage { $this->set("modified", date("Y-m-d H:i:s", $this->yellow->toolbox->getFileModified($this->fileName))); } if (!empty($pageError)) $this->set("pageError", $pageError); - foreach ($this->yellow->extensions->extensions as $key=>$value) { + foreach ($this->yellow->extension->data as $key=>$value) { if (method_exists($value["obj"], "onParseMeta")) $value["obj"]->onParseMeta($this); } } @@ -504,8 +515,8 @@ class YellowPage { // Parse page content on demand public function parseContent($sizeMax = 0) { if (!is_object($this->parser)) { - if ($this->yellow->extensions->isExisting($this->get("parser"))) { - $value = $this->yellow->extensions->extensions[$this->get("parser")]; + if ($this->yellow->extension->isExisting($this->get("parser"))) { + $value = $this->yellow->extension->data[$this->get("parser")]; if (method_exists($value["obj"], "onParseContentRaw")) { $this->parser = $value["obj"]; $this->parserData = $this->getContent(true, $sizeMax); @@ -513,9 +524,9 @@ class YellowPage { $this->parserData = preg_replace("/@pageEdit/i", $this->get("pageEdit"), $this->parserData); $this->parserData = $this->parser->onParseContentRaw($this, $this->parserData); $this->parserData = $this->yellow->toolbox->normaliseData($this->parserData, "html"); - foreach ($this->yellow->extensions->extensions as $key=>$value) { - if (method_exists($value["obj"], "onParseContentText")) { - $output = $value["obj"]->onParseContentText($this, $this->parserData); + foreach ($this->yellow->extension->data as $key=>$value) { + if (method_exists($value["obj"], "onParseContentHtml")) { + $output = $value["obj"]->onParseContentHtml($this, $this->parserData); if (!is_null($output)) $this->parserData = $output; } } @@ -535,7 +546,7 @@ class YellowPage { // Parse page content shortcut public function parseContentShortcut($name, $text, $type) { $output = null; - foreach ($this->yellow->extensions->extensions as $key=>$value) { + foreach ($this->yellow->extension->data as $key=>$value) { if (method_exists($value["obj"], "onParseContentShortcut")) { $output = $value["obj"]->onParseContentShortcut($this, $name, $text, $type); if (!is_null($output)) break; @@ -572,14 +583,14 @@ class YellowPage { if (!$this->isHeader("Content-Type")) $this->setHeader("Content-Type", "text/html; charset=utf-8"); if (!$this->isHeader("Content-Modified")) $this->setHeader("Content-Modified", $this->getModified(true)); if (!$this->isHeader("Last-Modified")) $this->setHeader("Last-Modified", $this->getLastModified(true)); - $fileNameTheme = $this->yellow->system->get("coreResourceDirectory").$this->yellow->lookup->normaliseName($this->get("theme")).".css"; + $fileNameTheme = $this->yellow->system->get("coreThemeDirectory").$this->yellow->lookup->normaliseName($this->get("theme")).".css"; if (!is_file($fileNameTheme)) { $this->error(500, "Theme '".$this->get("theme")."' does not exist!"); } if (!is_object($this->parser)) { $this->error(500, "Parser '".$this->get("parser")."' does not exist!"); } - if (!$this->yellow->text->isLanguage($this->get("language"))) { + if (!$this->yellow->language->isExisting($this->get("language"))) { $this->error(500, "Language '".$this->get("language")."' does not exist!"); } if ($this->yellow->lookup->isNestedLocation($this->location, $this->fileName, true)) { @@ -594,7 +605,7 @@ class YellowPage { $this->error(404); } if ($this->isExisting("pageClean")) $this->outputData = null; - foreach ($this->yellow->extensions->extensions as $key=>$value) { + foreach ($this->yellow->extension->data as $key=>$value) { if (method_exists($value["obj"], "onParsePageOutput")) { $output = $value["obj"]->onParsePageOutput($this, $this->outputData); if (!is_null($output)) $this->outputData = $output; @@ -605,7 +616,7 @@ class YellowPage { // Parse page layout public function parsePageLayout($name) { $this->outputData = null; - foreach ($this->yellow->extensions->extensions as $key=>$value) { + foreach ($this->yellow->extension->data as $key=>$value) { if (method_exists($value["obj"], "onParsePageLayout")) { $value["obj"]->onParsePageLayout($this, $name); } @@ -652,42 +663,42 @@ class YellowPage { return htmlspecialchars($this->get($key)); } - // Return page setting as language specific date format + // Return page setting as language specific date public function getDate($key, $format = "") { if (!empty($format)) { - $format = $this->yellow->text->get($format); + $format = $this->yellow->language->getText($format); } else { - $format = $this->yellow->text->get("coreDateFormatMedium"); + $format = $this->yellow->language->getText("coreDateFormatMedium"); } - return $this->yellow->text->getDateFormatted(strtotime($this->get($key)), $format); + return $this->yellow->language->getDateFormatted(strtotime($this->get($key)), $format); } - // Return page setting as language specific date format, HTML encoded + // Return page setting as language specific date, HTML encoded public function getDateHtml($key, $format = "") { return htmlspecialchars($this->getDate($key, $format)); } - // Return page setting as language specific date format, relative to today + // Return page setting as language specific date, relative to today public function getDateRelative($key, $format = "", $daysLimit = 30) { if (!empty($format)) { - $format = $this->yellow->text->get($format); + $format = $this->yellow->language->getText($format); } else { - $format = $this->yellow->text->get("coreDateFormatMedium"); + $format = $this->yellow->language->getText("coreDateFormatMedium"); } - return $this->yellow->text->getDateRelative(strtotime($this->get($key)), $format, $daysLimit); + return $this->yellow->language->getDateRelative(strtotime($this->get($key)), $format, $daysLimit); } - // Return page setting as language specific date format, relative to today, HTML encoded + // Return page setting as language specific date, relative to today, HTML encoded public function getDateRelativeHtml($key, $format = "", $daysLimit = 30) { return htmlspecialchars($this->getDateRelative($key, $format, $daysLimit)); } - // Return page setting as custom date format + // Return page setting as date public function getDateFormatted($key, $format) { - return $this->yellow->text->getDateFormatted(strtotime($this->get($key)), $format); + return $this->yellow->language->getDateFormatted(strtotime($this->get($key)), $format); } - // Return page setting as custom date format, HTML encoded + // Return page setting as date, HTML encoded public function getDateFormattedHtml($key, $format) { return htmlspecialchars($this->getDateFormatted($key, $format)); } @@ -730,7 +741,7 @@ class YellowPage { return $this->yellow->content->getChildren($this->location, $showInvisible); } - // Return page collection with sub pages + // Return page collection with child pages recursively public function getChildrenRecursive($showInvisible = false, $levelMax = 0) { return $this->yellow->content->getChildrenRecursive($this->location, $showInvisible, $levelMax); } @@ -798,25 +809,31 @@ class YellowPage { // Return page extra data public function getExtra($name) { $output = ""; - foreach ($this->yellow->extensions->extensions as $key=>$value) { + foreach ($this->yellow->extension->data as $key=>$value) { if (method_exists($value["obj"], "onParsePageExtra")) { $outputExtension = $value["obj"]->onParsePageExtra($this, $name); if (!is_null($outputExtension)) $output .= $outputExtension; } } if ($name=="header") { - $fileNameTheme = $this->yellow->system->get("coreResourceDirectory").$this->yellow->lookup->normaliseName($this->get("theme")).".css"; + $fileNameTheme = $this->yellow->system->get("coreThemeDirectory").$this->yellow->lookup->normaliseName($this->get("theme")).".css"; if (is_file($fileNameTheme)) { $locationTheme = $this->yellow->system->get("coreServerBase"). - $this->yellow->system->get("coreResourceLocation").$this->yellow->lookup->normaliseName($this->get("theme")).".css"; + $this->yellow->system->get("coreThemeLocation").$this->yellow->lookup->normaliseName($this->get("theme")).".css"; $output .= "<link rel=\"stylesheet\" type=\"text/css\" media=\"all\" href=\"$locationTheme\" />\n"; } - $fileNameScript = $this->yellow->system->get("coreResourceDirectory").$this->yellow->lookup->normaliseName($this->get("theme")).".js"; + $fileNameScript = $this->yellow->system->get("coreThemeDirectory").$this->yellow->lookup->normaliseName($this->get("theme")).".js"; if (is_file($fileNameScript)) { $locationScript = $this->yellow->system->get("coreServerBase"). - $this->yellow->system->get("coreResourceLocation").$this->yellow->lookup->normaliseName($this->get("theme")).".js"; + $this->yellow->system->get("coreThemeLocation").$this->yellow->lookup->normaliseName($this->get("theme")).".js"; $output .= "<script type=\"text/javascript\" src=\"$locationScript\"></script>\n"; } + $fileNameFavicon = $this->yellow->system->get("coreThemeDirectory").$this->yellow->lookup->normaliseName($this->get("theme")).".png"; + if (is_file($fileNameFavicon)) { + $locationFavicon = $this->yellow->system->get("coreServerBase"). + $this->yellow->system->get("coreThemeLocation").$this->yellow->lookup->normaliseName($this->get("theme")).".png"; + $output .= "<link rel=\"icon\" type=\"image/png\" href=\"$locationFavicon\" />\n"; + } } return $output; } @@ -840,7 +857,7 @@ class YellowPage { // Return last modification date, Unix time or HTTP format public function getLastModified($httpFormat = false) { $lastModified = max($this->lastModified, $this->getModified(), $this->pageCollection->getModified(), - $this->yellow->system->getModified(), $this->yellow->text->getModified(), $this->yellow->extensions->getModified()); + $this->yellow->system->getModified(), $this->yellow->language->getModified(), $this->yellow->extension->getModified()); foreach ($this->pageRelations as $page) $lastModified = max($lastModified, $page->getModified()); return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($lastModified) : $lastModified; } @@ -964,7 +981,7 @@ class YellowPageCollection extends ArrayObject { $this->yellow = $yellow; } - // Filter page collection by setting + // Filter page collection by page setting public function filter($key, $value, $exactMatch = true) { $array = array(); $value = str_replace(" ", "-", strtoloweru($value)); @@ -996,7 +1013,7 @@ class YellowPageCollection extends ArrayObject { return $this; } - // Sort page collection by setting + // Sort page collection by page setting public function sort($key, $ascendingOrder = true) { $array = $this->getArrayCopy(); $sortIndex = 0; @@ -1233,7 +1250,7 @@ class YellowContent { return $this->pages[$location]; } - // Return page from file system, null if not found + // Return page from, null if not found public function find($location, $absoluteLocation = false) { $found = false; if ($absoluteLocation) $location = substru($location, strlenu($this->yellow->page->base)); @@ -1343,7 +1360,7 @@ class YellowContent { return $pages; } - // Return sub pages + // Return child pages recursively public function getChildrenRecursive($location, $showInvisible = false, $levelMax = 0) { --$levelMax; $pages = new YellowPageCollection($this->yellow); @@ -1472,7 +1489,7 @@ class YellowMedia { return $files; } - // Return sub files + // Return child files recursively public function getChildrenRecursive($location, $showInvisible = false, $levelMax = 0) { --$levelMax; $files = new YellowPageCollection($this->yellow); @@ -1513,9 +1530,9 @@ class YellowMedia { class YellowSystem { public $yellow; // access to API - public $modified; // settings modification date - public $settings; // settings - public $settingsDefaults; // settings defaults + public $modified; // system modification date + public $settings; // system settings + public $settingsDefaults; // system settings defaults public function __construct($yellow) { $this->yellow = $yellow; @@ -1594,7 +1611,7 @@ class YellowSystem { } // Return system settings - public function getData($filterStart = "", $filterEnd = "") { + public function getSettings($filterStart = "", $filterEnd = "") { $settings = array(); if (empty($filterStart) && empty($filterEnd)) { $settings = array_merge($this->settingsDefaults->getArrayCopy(), $this->settings->getArrayCopy()); @@ -1607,6 +1624,31 @@ class YellowSystem { return $settings; } + // Return supported values for system setting, empty if not known + public function getValues($key) { + $values = array(); + if ($key=="email") { + foreach ($this->yellow->user->settings as $userKey=>$userValue) { + array_push($values, $userKey); + } + } elseif ($key=="language") { + foreach ($this->yellow->language->settings as $languageKey=>$languageValue) { + array_push($values, $languageKey); + } + } elseif ($key=="layout") { + $path = $this->yellow->system->get("coreLayoutDirectory"); + foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.html$/", true, false, false) as $entry) { + array_push($values, lcfirst(substru($entry, 0, -5))); + } + } elseif ($key=="theme") { + $path = $this->yellow->system->get("coreThemeDirectory"); + foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.css$/", true, false, false) as $entry) { + array_push($values, lcfirst(substru($entry, 0, -4))); + } + } + return $values; + } + // Return system settings modification date, Unix time or HTTP format public function getModified($httpFormat = false) { return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($this->modified) : $this->modified; @@ -1618,33 +1660,193 @@ class YellowSystem { } } -class YellowText { +class YellowUser { public $yellow; // access to API - public $modified; // text modification date - public $text; // text - public $language; // current language + public $modified; // user modification date + public $settings; // user settings + public $email; // current email + + public function __construct($yellow) { + $this->yellow = $yellow; + $this->modified = 0; + $this->settings = array(); + $this->email = ""; + } + + // Load user settings from file + public function load($fileName) { + if (defined("DEBUG") && DEBUG>=2) echo "YellowUser::load file:$fileName<br/>\n"; + $email = ""; + $this->modified = $this->yellow->toolbox->getFileModified($fileName); + $fileData = $this->yellow->toolbox->readFile($fileName); + foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) { + if (preg_match("/^\#/", $line)) continue; + if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) { + if (lcfirst($matches[1])=="email" && !strempty($matches[2])) { + $email = $matches[2]; + if (defined("DEBUG") && DEBUG>=3) echo "YellowUser::load email:$email<br/>\n"; + } + if (!empty($email) && !empty($matches[1]) && !strempty($matches[2])) { + $this->setUser($matches[1], $matches[2], $email); + } + } + } + } + + // Save user settings to file + public function save($fileName, $email, $settings) { + $scan = false; + $fileData = $this->yellow->toolbox->readFile($fileName); + $fileDataStart = $fileDataMiddle = $fileDataEnd = ""; + foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) { + if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) { + if (lcfirst($matches[1])=="email" && !strempty($matches[2])) { + $scan = $matches[2]==$email; + } + } + if (!$scan && empty($fileDataMiddle)) { + $fileDataStart .= $line; + } elseif ($scan) { + $fileDataMiddle .= $line; + } else { + $fileDataEnd .= $line; + } + } + $settingsNew = new YellowDataCollection(); + $settingsNew["email"] = $email; + foreach ($settings as $key=>$value) { + if (!empty($key) && !strempty($value)) { + $this->setUser($key, $value, $email); + $settingsNew[$key] = $value; + } + } + $fileDataSettings = ""; + foreach ($this->yellow->toolbox->getTextLines($fileDataMiddle) as $line) { + if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) { + if (!empty($matches[1]) && isset($settingsNew[$matches[1]])) { + $fileDataSettings .= "$matches[1]: ".$settingsNew[$matches[1]]."\n"; + unset($settingsNew[$matches[1]]); + continue; + } + } + $fileDataSettings .= $line; + } + foreach ($settingsNew as $key=>$value) { + $fileDataSettings .= ucfirst($key).": $value\n"; + } + if (!empty($fileDataSettings)) { + $fileDataSettings = preg_replace("/\n+/", "\n", $fileDataSettings); + if (!empty($fileDataStart) && substr($fileDataStart, -2)!="\n\n") $fileDataSettings = "\n".$fileDataSettings; + if (!empty($fileDataEnd)) $fileDataSettings .= "\n"; + } + $fileDataNew = $fileDataStart.$fileDataSettings.$fileDataEnd; + return $this->yellow->toolbox->createFile($fileName, $fileDataNew); + } + + // Remove user from file + public function remove($fileName, $email) { + $scan = false; + $fileData = $this->yellow->toolbox->readFile($fileName); + $fileDataStart = $fileDataMiddle = $fileDataEnd = ""; + foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) { + if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) { + if (lcfirst($matches[1])=="email" && !strempty($matches[2])) { + $scan = $matches[2]==$email; + } + } + if (!$scan && empty($fileDataMiddle)) { + $fileDataStart .= $line; + } elseif ($scan) { + $fileDataMiddle .= $line; + } else { + $fileDataEnd .= $line; + } + } + if (isset($this->settings[$email])) unset($this->settings[$email]); + $fileDataNew = rtrim($fileDataStart.$fileDataEnd)."\n"; + return $this->yellow->toolbox->createFile($fileName, $fileDataNew); + } + + // Set current email + public function set($email) { + $this->email = $email; + } + + // Set user setting + public function setUser($key, $value, $email) { + if (!isset($this->settings[$email])) $this->settings[$email] = new YellowDataCollection(); + $this->settings[$email][$key] = $value; + } + + // Return user setting + public function getUser($key, $email = "") { + if (empty($email)) $email = $this->email; + return isset($this->settings[$email]) && isset($this->settings[$email][$key]) ? $this->settings[$email][$key] : ""; + } + + // Return user setting, HTML encoded + public function getUserHtml($key, $email = "") { + return htmlspecialchars($this->getUser($key, $email)); + } + + // Return user settings + public function getSettings($email = "") { + $settings = array(); + if (empty($email)) $email = $this->email; + if (isset($this->settings[$email])) $settings = $this->settings[$email]->getArrayCopy(); + return $settings; + } + + // Return user settings modification date, Unix time or HTTP format + public function getModified($httpFormat = false) { + return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($this->modified) : $this->modified; + } + + // Check if user setting exists + public function isUser($key, $email = "") { + if (empty($email)) $email = $this->email; + return isset($this->settings[$email]) && isset($this->settings[$email][$key]); + } + + // Check if user exists + public function isExisting($email) { + return isset($this->settings[$email]); + } +} + +class YellowLanguage { + public $yellow; // access to API + public $modified; // language modification date + public $settings; // language settings + public $settingsDefaults; // language settings defaults + public $language; // current language public function __construct($yellow) { $this->yellow = $yellow; $this->modified = 0; - $this->text = new YellowDataCollection(); + $this->settings = new YellowDataCollection(); + $this->settingsDefaults = new YellowDataCollection(); + $this->language = ""; } - // Load text settings - public function load($path, $fileName = "", $languageDefault = "") { - $regex = empty($fileName) ? "/^.*\.txt$/" : "/^$fileName$/"; + // Load language settings from file + public function load($fileName) { + $path = dirname($fileName); + $regex = "/^".basename($fileName)."$/"; foreach ($this->yellow->toolbox->getDirectoryEntries($path, $regex, true, false) as $entry) { - if (defined("DEBUG") && DEBUG>=2) echo "YellowText::load file:$entry<br/>\n"; - $language = $languageDefault; + if (defined("DEBUG") && DEBUG>=2) echo "YellowLanguage::load file:$entry<br/>\n"; + $language = ""; $this->modified = max($this->modified, filemtime($entry)); $fileData = $this->yellow->toolbox->readFile($entry); foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) { if (preg_match("/^\#/", $line)) continue; if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) { - if (lcfirst($matches[1])=="language" && !strempty($matches[2])) $language = $matches[2]; + if (lcfirst($matches[1])=="language" && !strempty($matches[2])) { + $language = $matches[2]; + if (defined("DEBUG") && DEBUG>=3) echo "YellowLanguage::load language:$language<br/>\n"; + } if (!empty($language) && !empty($matches[1]) && !strempty($matches[2])) { $this->setText($matches[1], $matches[2], $language); - if (defined("DEBUG") && DEBUG>=3) echo "YellowText::load $matches[1]:$matches[2]<br/>\n"; } } } @@ -1652,56 +1854,36 @@ class YellowText { } // Set current language - public function setLanguage($language) { + public function set($language) { $this->language = $language; } - // Set text settings for specific language - public function setText($key, $value, $language) { - if (!isset($this->text[$language])) $this->text[$language] = new YellowDataCollection(); - $this->text[$language][$key] = $value; - } - - // Return text setting - public function get($key) { - return $this->getText($key, $this->language); + // Set default language setting + public function setDefault($key) { + $this->settingsDefaults[$key] = true; } - // Return text setting, HTML encoded - public function getHtml($key) { - return htmlspecialchars($this->getText($key, $this->language)); + // Set language setting + public function setText($key, $value, $language) { + if (!isset($this->settings[$language])) $this->settings[$language] = new YellowDataCollection(); + $this->settings[$language][$key] = $value; } - // Return text setting for specific language - public function getText($key, $language) { - return $this->isExisting($key, $language) ? $this->text[$language][$key] : "[$key]"; + // Return language setting + public function getText($key, $language = "") { + if (empty($language)) $language = $this->language; + return $this->isText($key, $language) ? $this->settings[$language][$key] : "[$key]"; } - // Return text setting for specific language, HTML encoded - public function getTextHtml($key, $language) { + // Return language setting, HTML encoded + public function getTextHtml($key, $language = "") { return htmlspecialchars($this->getText($key, $language)); } - // Return text settings - public function getData($filterStart = "", $language = "") { - $text = array(); - if (empty($language)) $language = $this->language; - if ($this->isLanguage($language)) { - if (empty($filterStart)) { - $text = $this->text[$language]; - } else { - foreach ($this->text[$language] as $key=>$value) { - if (substru($key, 0, strlenu($filterStart))==$filterStart) $text[$key] = $value; - } - } - } - return $text; - } - - // Return human readable date, custom date format - public function getDateFormatted($timestamp, $format) { - $dateMonths = preg_split("/\s*,\s*/", $this->get("coreDateMonths")); - $dateWeekdays = preg_split("/\s*,\s*/", $this->get("coreDateWeekdays")); + // Return human readable date + public function getDateFormatted($timestamp, $format, $language = "") { + $dateMonths = preg_split("/\s*,\s*/", $this->getText("coreDateMonths", $language)); + $dateWeekdays = preg_split("/\s*,\s*/", $this->getText("coreDateWeekdays", $language)); $month = $dateMonths[date("n", $timestamp) - 1]; $weekday = $dateWeekdays[date("N", $timestamp) - 1]; $timeZone = $this->yellow->system->get("coreServerTimezone"); @@ -1717,11 +1899,11 @@ class YellowText { } // Return human readable date, relative to today - public function getDateRelative($timestamp, $format, $daysLimit) { + public function getDateRelative($timestamp, $format, $daysLimit, $language = "") { $timeDifference = time() - $timestamp; $days = abs(intval($timeDifference / 86400)); $key = $timeDifference>=0 ? "coreDatePast" : "coreDateFuture"; - $tokens = preg_split("/\s*,\s*/", $this->get($key)); + $tokens = preg_split("/\s*,\s*/", $this->getText($key, $language)); if (count($tokens)>=8) { if ($days<=$daysLimit || $daysLimit==0) { if ($days==0) { @@ -1740,7 +1922,7 @@ class YellowText { $output = preg_replace("/@x/i", intval($days/365.25), $tokens[6]); } } else { - $output = preg_replace("/@x/i", $this->getDateFormatted($timestamp, $format), $tokens[7]); + $output = preg_replace("/@x/i", $this->getDateFormatted($timestamp, $format, $language), $tokens[7]); } } else { $output = "[$key]"; @@ -1748,43 +1930,149 @@ class YellowText { return $output; } - // Return text settings modification date, Unix time or HTTP format + // Return language settings + public function getSettings($filterStart = "", $filterEnd = "", $language = "") { + $settings = array(); + if (empty($language)) $language = $this->language; + if (isset($this->settings[$language])) { + if (empty($filterStart) && empty($filterEnd)) { + $settings = $this->settings[$language]->getArrayCopy(); + } else { + foreach ($this->settings[$language] as $key=>$value) { + if (!empty($filterStart) && substru($key, 0, strlenu($filterStart))==$filterStart) $settings[$key] = $value; + if (!empty($filterEnd) && substru($key, -strlenu($filterEnd))==$filterEnd) $settings[$key] = $value; + } + } + } + return $settings; + } + + // Return language settings modification date, Unix time or HTTP format public function getModified($httpFormat = false) { return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($this->modified) : $this->modified; } - - // Return languages - public function getLanguages() { - $languages = array(); - foreach ($this->text as $key=>$value) { - array_push($languages, $key); - } - return $languages; - } // Normalise date into known format - public function normaliseDate($text) { + public function normaliseDate($text, $language = "") { if (preg_match("/^\d+\-\d+$/", $text)) { - $output = $this->getDateFormatted(strtotime($text), $this->get("coreDateFormatShort")); + $output = $this->getDateFormatted(strtotime($text), $this->getText("coreDateFormatShort", $language), $language); } elseif (preg_match("/^\d+\-\d+\-\d+$/", $text)) { - $output = $this->getDateFormatted(strtotime($text), $this->get("coreDateFormatMedium")); + $output = $this->getDateFormatted(strtotime($text), $this->getText("coreDateFormatMedium", $language), $language); } elseif (preg_match("/^\d+\-\d+\-\d+ \d+\:\d+$/", $text)) { - $output = $this->getDateFormatted(strtotime($text), $this->get("coreDateFormatLong")); + $output = $this->getDateFormatted(strtotime($text), $this->getText("coreDateFormatLong", $language), $language); } else { $output = $text; } return $output; } + // Check if language setting exists + public function isText($key, $language = "") { + if (empty($language)) $language = $this->language; + return isset($this->settings[$language]) && isset($this->settings[$language][$key]); + } + // Check if language exists - public function isLanguage($language) { - return isset($this->text[$language]); + public function isExisting($language) { + return isset($this->settings[$language]); + } +} + +class YellowExtension { + public $yellow; // access to API + public $modified; // extension modification date + public $data; // extension data + + public function __construct($yellow) { + $this->yellow = $yellow; + $this->modified = 0; + $this->data = array(); } - // Check if text setting exists - public function isExisting($key, $language = "") { - if (empty($language)) $language = $this->language; - return isset($this->text[$language]) && isset($this->text[$language][$key]); + // Load extensions + public function load($path) { + foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.php$/", true, false) as $entry) { + if (defined("DEBUG") && DEBUG>=2) echo "YellowExtension::load file:$entry<br/>\n"; + $this->modified = max($this->modified, filemtime($entry)); + require_once($entry); + $name = $this->yellow->lookup->normaliseName(basename($entry), true, true); + $this->register(lcfirst($name), "Yellow".ucfirst($name)); + } + $callback = function ($a, $b) { + return $a["priority"] - $b["priority"]; + }; + uasort($this->data, $callback); + foreach ($this->data as $key=>$value) { + if (method_exists($this->data[$key]["obj"], "onLoad")) $this->data[$key]["obj"]->onLoad($this->yellow); + } + $this->yellow->system->set("mediaLocation", "/media/"); // TODO: remove later, for backwards compatibility + $this->yellow->system->set("downloadLocation", "/media/downloads/"); + $this->yellow->system->set("imageLocation", "/media/images/"); + $this->yellow->system->set("extensionLocation", "/media/extensions/"); + $this->yellow->system->set("resourceLocation", "/media/themes/"); + $this->yellow->system->set("mediaDir", "media/"); + $this->yellow->system->set("downloadDir", "media/downloads/"); + $this->yellow->system->set("imageDir", "media/images/"); + $this->yellow->system->set("systemDir", "system/"); + $this->yellow->system->set("extensionDir", "system/extensions/"); + $this->yellow->system->set("layoutDir", "system/layouts/"); + $this->yellow->system->set("resourceDir", "system/themes/"); + $this->yellow->system->set("settingDir", "system/extensions/"); + $this->yellow->system->set("trashDir", "system/trash/"); + $this->yellow->system->set("contentDir", "content/"); + $this->yellow->system->set("contentPagination", "page"); + $this->yellow->system->set("coreStaticDir", "public/"); + $this->yellow->system->set("coreCacheDir", "cache/"); + $this->yellow->system->set("coreTrashDir", "system/trash/"); + $this->yellow->system->set("coreMediaDir", "media/"); + $this->yellow->system->set("coreDownloadDir", "media/downloads/"); + $this->yellow->system->set("coreImageDir", "media/images/"); + $this->yellow->system->set("coreSystemDir", "system/"); + $this->yellow->system->set("coreExtensionDir", "system/extensions/"); + $this->yellow->system->set("coreLayoutDir", "system/layouts/"); + $this->yellow->system->set("coreResourceDir", "system/themes/"); + $this->yellow->system->set("coreSettingDir", "system/extensions/"); + $this->yellow->system->set("coreContentDir", "content/"); + $this->yellow->system->set("coreContentRootDir", "default/"); + $this->yellow->system->set("coreContentHomeDir", "home/"); + $this->yellow->system->set("coreContentSharedDir", "shared/"); + $this->yellow->system->set("coreResourceLocation", "/media/themes/"); + $this->yellow->system->set("coreResourceDirectory", "system/themes/"); + } + + // Register extension + public function register($key, $class) { + if (!$this->isExisting($key) && class_exists($class)) { + $this->data[$key] = array(); + $this->data[$key]["obj"] = new $class; + $this->data[$key]["class"] = $class; + $this->data[$key]["version"] = defined("$class::VERSION") ? $class::VERSION : 0; + $this->data[$key]["priority"] = defined("$class::PRIORITY") ? $class::PRIORITY : count($this->data) + 10; + } + } + + // Return extension + public function get($key) { + return $this->data[$key]["obj"]; + } + + // Return extension + public function getExtensions() { + $extensions = array(); + foreach ($this->data as $key=>$value) { + array_push($extensions, $key); + } + return $extensions; + } + + // Return extensions modification date, Unix time or HTTP format + public function getModified($httpFormat = false) { + return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($this->modified) : $this->modified; + } + + // Check if extension exists + public function isExisting($key) { + return isset($this->data[$key]); } } @@ -2038,13 +2326,13 @@ class YellowLookup { // Return file path from system location public function findFileFromSystem($location) { $fileName = null; - if (preg_match("/\.(css|gif|ico|js|jpg|png|svg|txt|woff|woff2)$/", $location)) { + if (preg_match("/\.(css|gif|ico|js|jpg|png|svg|woff|woff2)$/", $location)) { $extensionLocationLength = strlenu($this->yellow->system->get("coreExtensionLocation")); - $resourceLocationLength = strlenu($this->yellow->system->get("coreResourceLocation")); + $themeLocationLength = strlenu($this->yellow->system->get("coreThemeLocation")); if (substru($location, 0, $extensionLocationLength)==$this->yellow->system->get("coreExtensionLocation")) { $fileName = $this->yellow->system->get("coreExtensionDirectory").substru($location, $extensionLocationLength); - } elseif (substru($location, 0, $resourceLocationLength)==$this->yellow->system->get("coreResourceLocation")) { - $fileName = $this->yellow->system->get("coreResourceDirectory").substru($location, $resourceLocationLength); + } elseif (substru($location, 0, $themeLocationLength)==$this->yellow->system->get("coreThemeLocation")) { + $fileName = $this->yellow->system->get("coreThemeDirectory").substru($location, $themeLocationLength); } } return $fileName; @@ -2318,139 +2606,32 @@ class YellowToolbox { return (strtoupperu(substru(PHP_OS, 0, 3))!="WIN") ? ":" : "="; } - // Normalise path or location, take care of relative path tokens - public function normaliseTokens($text, $prependSlash = false) { - $textFiltered = ""; - if ($prependSlash && substru($text, 0, 1)!="/") $textFiltered .= "/"; - $textLength = strlenb($text); - for ($pos=0; $pos<$textLength; ++$pos) { - if (($text[$pos]=="/" || $pos==0) && $pos+1<$textLength) { - if ($text[$pos+1]=="/") continue; - if ($text[$pos+1]==".") { - $posNew = $pos+1; - while ($text[$posNew]==".") { - ++$posNew; - } - if ($text[$posNew]=="/" || $text[$posNew]=="") { - $pos = $posNew-1; - continue; - } - } - } - $textFiltered .= $text[$pos]; - } - return $textFiltered; + // Return human readable HTTP date + public function getHttpDateFormatted($timestamp) { + return gmdate("D, d M Y H:i:s", $timestamp)." GMT"; } - // Normalise location arguments - public function normaliseArguments($text, $appendSlash = true, $filterStrict = true) { - if ($appendSlash) $text .= "/"; - if ($filterStrict) $text = str_replace(" ", "-", strtoloweru($text)); - $text = str_replace(":", $this->getLocationArgumentsSeparator(), $text); - return str_replace(array("%2F","%3A","%3D"), array("/",":","="), rawurlencode($text)); - } - - // Normalise elements and attributes in html/svg data - public function normaliseData($text, $type = "html", $filterStrict = true) { - $output = ""; - $elementsHtml = array( - "a", "abbr", "acronym", "address", "area", "article", "aside", "audio", "b", "bdi", "bdo", "big", "blink", "blockquote", "body", "br", "button", "canvas", "caption", "center", "cite", "code", "col", "colgroup", "content", "data", "datalist", "dd", "decorator", "del", "details", "dfn", "dir", "div", "dl", "dt", "element", "em", "fieldset", "figcaption", "figure", "font", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hgroup", "hr", "html", "i", "iframe", "image", "img", "input", "ins", "kbd", "label", "legend", "li", "main", "map", "mark", "marquee", "menu", "menuitem", "meta", "meter", "nav", "nobr", "ol", "optgroup", "option", "output", "p", "pre", "progress", "q", "rp", "rt", "ruby", "s", "samp", "section", "select", "shadow", "small", "source", "spacer", "span", "strike", "strong", "style", "sub", "summary", "sup", "table", "tbody", "td", "template", "textarea", "tfoot", "th", "thead", "time", "title", "tr", "track", "tt", "u", "ul", "var", "video", "wbr"); - $elementsSvg = array( - "svg", "altglyph", "altglyphdef", "altglyphitem", "animatecolor", "animatemotion", "animatetransform", "circle", "clippath", "defs", "desc", "ellipse", "feblend", "fecolormatrix", "fecomponenttransfer", "fecomposite", "feconvolvematrix", "fediffuselighting", "fedisplacementmap", "fedistantlight", "feflood", "fefunca", "fefuncb", "fefuncg", "fefuncr", "fegaussianblur", "femerge", "femergenode", "femorphology", "feoffset", "fepointlight", "fespecularlighting", "fespotlight", "fetile", "feturbulence", "filter", "font", "g", "glyph", "glyphref", "hkern", "image", "line", "lineargradient", "marker", "mask", "metadata", "mpath", "path", "pattern", "polygon", "polyline", "radialgradient", "rect", "stop", "switch", "symbol", "text", "textpath", "title", "tref", "tspan", "use", "view", "vkern"); - $attributesHtml = array( - "accept", "action", "align", "allowfullscreen", "alt", "autocomplete", "background", "bgcolor", "border", "cellpadding", "cellspacing", "charset", "checked", "cite", "class", "clear", "color", "cols", "colspan", "content", "controls", "coords", "crossorigin", "datetime", "default", "dir", "disabled", "download", "enctype", "face", "for", "frameborder", "headers", "height", "hidden", "high", "href", "hreflang", "id", "integrity", "ismap", "label", "lang", "list", "loop", "low", "max", "maxlength", "media", "method", "min", "multiple", "name", "noshade", "novalidate", "nowrap", "open", "optimum", "pattern", "placeholder", "poster", "prefix", "preload", "property", "pubdate", "radiogroup", "readonly", "rel", "required", "rev", "reversed", "role", "rows", "rowspan", "spellcheck", "scope", "selected", "shape", "size", "sizes", "span", "srclang", "start", "src", "srcset", "step", "style", "summary", "tabindex", "target", "title", "type", "usemap", "valign", "value", "width", "xmlns"); - $attributesSvg = array( - "accent-height", "accumulate", "additivive", "alignment-baseline", "ascent", "attributename", "attributetype", "azimuth", "basefrequency", "baseline-shift", "begin", "bias", "by", "class", "clip", "clip-path", "clip-rule", "color", "color-interpolation", "color-interpolation-filters", "color-profile", "color-rendering", "cx", "cy", "d", "datenstrom", "dx", "dy", "diffuseconstant", "direction", "display", "divisor", "dur", "edgemode", "elevation", "end", "fill", "fill-opacity", "fill-rule", "filter", "flood-color", "flood-opacity", "font-family", "font-size", "font-size-adjust", "font-stretch", "font-style", "font-variant", "font-weight", "fx", "fy", "g1", "g2", "glyph-name", "glyphref", "gradientunits", "gradienttransform", "height", "href", "id", "image-rendering", "in", "in2", "k", "k1", "k2", "k3", "k4", "kerning", "keypoints", "keysplines", "keytimes", "lang", "lengthadjust", "letter-spacing", "kernelmatrix", "kernelunitlength", "lighting-color", "local", "marker-end", "marker-mid", "marker-start", "markerheight", "markerunits", "markerwidth", "maskcontentunits", "maskunits", "max", "mask", "media", "method", "mode", "min", "name", "numoctaves", "offset", "operator", "opacity", "order", "orient", "orientation", "origin", "overflow", "paint-order", "path", "pathlength", "patterncontentunits", "patterntransform", "patternunits", "points", "preservealpha", "preserveaspectratio", "r", "rx", "ry", "radius", "refx", "refy", "repeatcount", "repeatdur", "restart", "result", "rotate", "scale", "seed", "shape-rendering", "specularconstant", "specularexponent", "spreadmethod", "stddeviation", "stitchtiles", "stop-color", "stop-opacity", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke", "stroke-width", "style", "surfacescale", "tabindex", "targetx", "targety", "transform", "text-anchor", "text-decoration", "text-rendering", "textlength", "type", "u1", "u2", "unicode", "values", "viewbox", "visibility", "vert-adv-y", "vert-origin-x", "vert-origin-y", "width", "word-spacing", "wrap", "writing-mode", "xchannelselector", "ychannelselector", "x", "x1", "x2", "xlink:href", "xml:id", "xml:space", "xmlns", "y", "y1", "y2", "z", "zoomandpan"); - $elementsSafe = $elementsHtml; - $attributesSafe = $attributesHtml; - if ($type=="svg") { - $elementsSafe = array_merge($elementsHtml, $elementsSvg); - $attributesSafe = array_merge($attributesHtml, $attributesSvg); - } - $offsetBytes = 0; - while (true) { - $elementFound = preg_match("/<(\/?)([\!\?\w]+)(.*?)(\/?)>/s", $text, $matches, PREG_OFFSET_CAPTURE, $offsetBytes); - $elementBefore = $elementFound ? substrb($text, $offsetBytes, $matches[0][1] - $offsetBytes) : substrb($text, $offsetBytes); - $elementStart = $elementFound ? $matches[1][0] : ""; - $elementName = $elementFound ? $matches[2][0]: ""; - $elementMiddle = $elementFound ? $matches[3][0]: ""; - $elementEnd = $elementFound ? $matches[4][0]: ""; - $output .= $elementBefore; - if (substrb($elementName, 0, 1)=="!") { - $output .= "<$elementName$elementMiddle>"; - } elseif (in_array(strtolower($elementName), $elementsSafe)) { - $elementAttributes = $this->getTextAttributes($elementMiddle); - foreach ($elementAttributes as $key=>$value) { - if (!in_array(strtolower($key), $attributesSafe) && !preg_match("/^(aria|data)-/i", $key)) { - unset($elementAttributes[$key]); - } - } - if ($filterStrict) { - $href = isset($elementAttributes["href"]) ? $elementAttributes["href"] : ""; - if (preg_match("/^\w+:/", $href) && !preg_match("/^(http|https|ftp|mailto):/", $href)) { - $elementAttributes["href"] = "error-xss-filter"; - } - $href = isset($elementAttributes["xlink:href"]) ? $elementAttributes["xlink:href"] : ""; - if (preg_match("/^\w+:/", $href) && !preg_match("/^(http|https|ftp|mailto):/", $href)) { - $elementAttributes["xlink:href"] = "error-xss-filter"; - } - } - $output .= "<$elementStart$elementName"; - foreach ($elementAttributes as $key=>$value) $output .= " $key=\"$value\""; - if (!empty($elementEnd)) $output .= " "; - $output .= "$elementEnd>"; - } - if (!$elementFound) break; - $offsetBytes = $matches[0][1] + strlenb($matches[0][0]); - } - return $output; - } - - // Normalise text lines, convert line endings - public function normaliseLines($text, $endOfLine = "lf") { - if ($endOfLine=="lf") { - $text = preg_replace("/\R/u", "\n", $text); - } else { - $text = preg_replace("/\R/u", "\r\n", $text); - } - return $text; - } - - // Normalise text into UTF-8 NFC - public function normaliseUnicode($text) { - if (PHP_OS=="Darwin" && !mb_check_encoding($text, "ASCII")) { - $utf8nfc = preg_match("//u", $text) && !preg_match("/[^\\x00-\\x{2FF}]/u", $text); - if (!$utf8nfc) $text = iconv("UTF-8-MAC", "UTF-8", $text); - } - return $text; - } - - // Return human readable HTTP date - public function getHttpDateFormatted($timestamp) { - return gmdate("D, d M Y H:i:s", $timestamp)." GMT"; - } - - // Return human readable HTTP server status - public function getHttpStatusFormatted($statusCode, $shortFormat = false) { - switch ($statusCode) { - case 0: $text = "No data"; break; - case 200: $text = "OK"; break; - case 301: $text = "Moved permanently"; break; - case 302: $text = "Moved temporarily"; break; - case 303: $text = "Reload please"; break; - case 304: $text = "Not modified"; break; - case 400: $text = "Bad request"; break; - case 403: $text = "Forbidden"; break; - case 404: $text = "Not found"; break; - case 430: $text = "Login failed"; break; - case 434: $text = "Not existing"; break; - case 500: $text = "Server error"; break; - case 503: $text = "Service unavailable"; break; - default: $text = "Error $statusCode"; - } - $serverProtocol = $this->getServer("SERVER_PROTOCOL"); - if (!preg_match("/^HTTP\//", $serverProtocol)) $serverProtocol = "HTTP/1.1"; - return $shortFormat ? $text : "$serverProtocol $statusCode $text"; + // Return human readable HTTP server status + public function getHttpStatusFormatted($statusCode, $shortFormat = false) { + switch ($statusCode) { + case 0: $text = "No data"; break; + case 200: $text = "OK"; break; + case 301: $text = "Moved permanently"; break; + case 302: $text = "Moved temporarily"; break; + case 303: $text = "Reload please"; break; + case 304: $text = "Not modified"; break; + case 400: $text = "Bad request"; break; + case 403: $text = "Forbidden"; break; + case 404: $text = "Not found"; break; + case 430: $text = "Login failed"; break; + case 434: $text = "Not existing"; break; + case 500: $text = "Server error"; break; + case 503: $text = "Service unavailable"; break; + default: $text = "Error $statusCode"; + } + $serverProtocol = $this->getServer("SERVER_PROTOCOL"); + if (!preg_match("/^HTTP\//", $serverProtocol)) $serverProtocol = "HTTP/1.1"; + return $shortFormat ? $text : "$serverProtocol $statusCode $text"; } // Return MIME content type @@ -2480,18 +2661,6 @@ class YellowToolbox { return $contentType; } - // Return file type - public function getFileType($fileName) { - return strtoloweru(($pos = strrposu($fileName, ".")) ? substru($fileName, $pos+1) : ""); - } - - // Return file group - public function getFileGroup($fileName, $path) { - $group = "none"; - if (preg_match("#^$path(.+?)\/#", $fileName, $matches)) $group = strtoloweru($matches[1]); - return $group; - } - // Return number of bytes public function getNumberBytes($string) { $bytes = intval($string); @@ -2668,7 +2837,19 @@ class YellowToolbox { return is_file($fileName) ? filemtime($fileName) : 0; } - // Return lines from text string, including newline + // Return file type + public function getFileType($fileName) { + return strtoloweru(($pos = strrposu($fileName, ".")) ? substru($fileName, $pos+1) : ""); + } + + // Return file group + public function getFileGroup($fileName, $path) { + $group = "none"; + if (preg_match("#^$path(.+?)\/#", $fileName, $matches)) $group = strtoloweru($matches[1]); + return $group; + } + + // Return lines from text, including newline public function getTextLines($text) { $lines = preg_split("/\n/", $text); foreach ($lines as &$line) { @@ -2678,7 +2859,7 @@ class YellowToolbox { return $lines; } - // Return attributes from text string + // Return attributes from text public function getTextAttributes($text) { $tokens = array(); $posStart = $posQuote = 0; @@ -2722,13 +2903,13 @@ class YellowToolbox { return $attributes; } - // Return array of specific size from text string + // Return array of specific size from text public function getTextList($text, $separator, $size) { $tokens = explode($separator, $text, $size); return array_pad($tokens, $size, null); } - // Return arguments from text string, space separated + // Return array from text, space separated public function getTextArguments($text, $optional = "-", $sizeMin = 9) { $text = preg_replace("/\s+/s", " ", trim($text)); $tokens = str_getcsv($text, " ", "\""); @@ -2738,7 +2919,7 @@ class YellowToolbox { return array_pad($tokens, $sizeMin, null); } - // Return text string from arguments, space separated + // Return text from array, space separated public function getTextString($tokens, $optional = "-") { $text = ""; foreach ($tokens as $token) { @@ -2750,14 +2931,14 @@ class YellowToolbox { return $text; } - // Return number of words in text string + // Return number of words in text public function getTextWords($text) { $text = preg_replace("/([\p{Han}\p{Hiragana}\p{Katakana}]{3})/u", "$1 ", $text); $text = preg_replace("/(\pL|\p{N})/u", "x", $text); return str_word_count($text); } - // Return text string truncated at word boundary + // Return text truncated at word boundary public function getTextTruncated($text, $lengthMax) { if (strlenu($text)>$lengthMax-1) { $text = substru($text, 0, $lengthMax); @@ -2767,7 +2948,7 @@ class YellowToolbox { return $text; } - // Create description from text string + // Create text description, with or without HTML public function createTextDescription($text, $lengthMax = 0, $removeHtml = true, $endMarker = "", $endMarkerText = "") { $output = ""; $elementsBlock = array("blockquote", "br", "div", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "li", "ol", "p", "pre", "ul"); @@ -2834,7 +3015,7 @@ class YellowToolbox { return trim($output); } - // Create title from text string + // Create title from text public function createTextTitle($text) { if (preg_match("/^.*\/([\pL\d\-\_]+)/u", $text, $matches)) $text = str_replace("-", " ", ucfirst($matches[1])); return $text; @@ -2902,7 +3083,7 @@ class YellowToolbox { return $this->verifyToken($hashCalculated, $hash); } - // Verify that token is not empty and identical, timing attack safe text string comparison + // Verify that token is not empty and identical, timing attack safe string comparison public function verifyToken($tokenExpected, $tokenReceived) { $ok = false; $lengthExpected = strlenb($tokenExpected); @@ -3099,6 +3280,113 @@ class YellowToolbox { return array($width, $height, $type); } + // Normalise location arguments + public function normaliseArguments($text, $appendSlash = true, $filterStrict = true) { + if ($appendSlash) $text .= "/"; + if ($filterStrict) $text = str_replace(" ", "-", strtoloweru($text)); + $text = str_replace(":", $this->getLocationArgumentsSeparator(), $text); + return str_replace(array("%2F","%3A","%3D"), array("/",":","="), rawurlencode($text)); + } + + // Normalise path or location, take care of relative path tokens + public function normaliseTokens($text, $prependSlash = false) { + $textFiltered = ""; + if ($prependSlash && substru($text, 0, 1)!="/") $textFiltered .= "/"; + $textLength = strlenb($text); + for ($pos=0; $pos<$textLength; ++$pos) { + if (($text[$pos]=="/" || $pos==0) && $pos+1<$textLength) { + if ($text[$pos+1]=="/") continue; + if ($text[$pos+1]==".") { + $posNew = $pos+1; + while ($text[$posNew]==".") { + ++$posNew; + } + if ($text[$posNew]=="/" || $text[$posNew]=="") { + $pos = $posNew-1; + continue; + } + } + } + $textFiltered .= $text[$pos]; + } + return $textFiltered; + } + + // Normalise elements and attributes in HTML/SVG data + public function normaliseData($text, $type = "html", $filterStrict = true) { + $output = ""; + $elementsHtml = array( + "a", "abbr", "acronym", "address", "area", "article", "aside", "audio", "b", "bdi", "bdo", "big", "blink", "blockquote", "body", "br", "button", "canvas", "caption", "center", "cite", "code", "col", "colgroup", "content", "data", "datalist", "dd", "decorator", "del", "details", "dfn", "dir", "div", "dl", "dt", "element", "em", "fieldset", "figcaption", "figure", "font", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hgroup", "hr", "html", "i", "iframe", "image", "img", "input", "ins", "kbd", "label", "legend", "li", "main", "map", "mark", "marquee", "menu", "menuitem", "meta", "meter", "nav", "nobr", "ol", "optgroup", "option", "output", "p", "pre", "progress", "q", "rp", "rt", "ruby", "s", "samp", "section", "select", "shadow", "small", "source", "spacer", "span", "strike", "strong", "style", "sub", "summary", "sup", "table", "tbody", "td", "template", "textarea", "tfoot", "th", "thead", "time", "title", "tr", "track", "tt", "u", "ul", "var", "video", "wbr"); + $elementsSvg = array( + "svg", "altglyph", "altglyphdef", "altglyphitem", "animatecolor", "animatemotion", "animatetransform", "circle", "clippath", "defs", "desc", "ellipse", "feblend", "fecolormatrix", "fecomponenttransfer", "fecomposite", "feconvolvematrix", "fediffuselighting", "fedisplacementmap", "fedistantlight", "feflood", "fefunca", "fefuncb", "fefuncg", "fefuncr", "fegaussianblur", "femerge", "femergenode", "femorphology", "feoffset", "fepointlight", "fespecularlighting", "fespotlight", "fetile", "feturbulence", "filter", "font", "g", "glyph", "glyphref", "hkern", "image", "line", "lineargradient", "marker", "mask", "metadata", "mpath", "path", "pattern", "polygon", "polyline", "radialgradient", "rect", "stop", "switch", "symbol", "text", "textpath", "title", "tref", "tspan", "use", "view", "vkern"); + $attributesHtml = array( + "accept", "action", "align", "allowfullscreen", "alt", "autocomplete", "background", "bgcolor", "border", "cellpadding", "cellspacing", "charset", "checked", "cite", "class", "clear", "color", "cols", "colspan", "content", "controls", "coords", "crossorigin", "datetime", "default", "dir", "disabled", "download", "enctype", "face", "for", "frameborder", "headers", "height", "hidden", "high", "href", "hreflang", "id", "integrity", "ismap", "label", "lang", "list", "loop", "low", "max", "maxlength", "media", "method", "min", "multiple", "name", "noshade", "novalidate", "nowrap", "open", "optimum", "pattern", "placeholder", "poster", "prefix", "preload", "property", "pubdate", "radiogroup", "readonly", "rel", "required", "rev", "reversed", "role", "rows", "rowspan", "spellcheck", "scope", "selected", "shape", "size", "sizes", "span", "srclang", "start", "src", "srcset", "step", "style", "summary", "tabindex", "target", "title", "type", "usemap", "valign", "value", "width", "xmlns"); + $attributesSvg = array( + "accent-height", "accumulate", "additivive", "alignment-baseline", "ascent", "attributename", "attributetype", "azimuth", "basefrequency", "baseline-shift", "begin", "bias", "by", "class", "clip", "clip-path", "clip-rule", "color", "color-interpolation", "color-interpolation-filters", "color-profile", "color-rendering", "cx", "cy", "d", "datenstrom", "dx", "dy", "diffuseconstant", "direction", "display", "divisor", "dur", "edgemode", "elevation", "end", "fill", "fill-opacity", "fill-rule", "filter", "flood-color", "flood-opacity", "font-family", "font-size", "font-size-adjust", "font-stretch", "font-style", "font-variant", "font-weight", "fx", "fy", "g1", "g2", "glyph-name", "glyphref", "gradientunits", "gradienttransform", "height", "href", "id", "image-rendering", "in", "in2", "k", "k1", "k2", "k3", "k4", "kerning", "keypoints", "keysplines", "keytimes", "lang", "lengthadjust", "letter-spacing", "kernelmatrix", "kernelunitlength", "lighting-color", "local", "marker-end", "marker-mid", "marker-start", "markerheight", "markerunits", "markerwidth", "maskcontentunits", "maskunits", "max", "mask", "media", "method", "mode", "min", "name", "numoctaves", "offset", "operator", "opacity", "order", "orient", "orientation", "origin", "overflow", "paint-order", "path", "pathlength", "patterncontentunits", "patterntransform", "patternunits", "points", "preservealpha", "preserveaspectratio", "r", "rx", "ry", "radius", "refx", "refy", "repeatcount", "repeatdur", "restart", "result", "rotate", "scale", "seed", "shape-rendering", "specularconstant", "specularexponent", "spreadmethod", "stddeviation", "stitchtiles", "stop-color", "stop-opacity", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke", "stroke-width", "style", "surfacescale", "tabindex", "targetx", "targety", "transform", "text-anchor", "text-decoration", "text-rendering", "textlength", "type", "u1", "u2", "unicode", "values", "viewbox", "visibility", "vert-adv-y", "vert-origin-x", "vert-origin-y", "width", "word-spacing", "wrap", "writing-mode", "xchannelselector", "ychannelselector", "x", "x1", "x2", "xlink:href", "xml:id", "xml:space", "xmlns", "y", "y1", "y2", "z", "zoomandpan"); + $elementsSafe = $elementsHtml; + $attributesSafe = $attributesHtml; + if ($type=="svg") { + $elementsSafe = array_merge($elementsHtml, $elementsSvg); + $attributesSafe = array_merge($attributesHtml, $attributesSvg); + } + $offsetBytes = 0; + while (true) { + $elementFound = preg_match("/<(\/?)([\!\?\w]+)(.*?)(\/?)>/s", $text, $matches, PREG_OFFSET_CAPTURE, $offsetBytes); + $elementBefore = $elementFound ? substrb($text, $offsetBytes, $matches[0][1] - $offsetBytes) : substrb($text, $offsetBytes); + $elementStart = $elementFound ? $matches[1][0] : ""; + $elementName = $elementFound ? $matches[2][0]: ""; + $elementMiddle = $elementFound ? $matches[3][0]: ""; + $elementEnd = $elementFound ? $matches[4][0]: ""; + $output .= $elementBefore; + if (substrb($elementName, 0, 1)=="!") { + $output .= "<$elementName$elementMiddle>"; + } elseif (in_array(strtolower($elementName), $elementsSafe)) { + $elementAttributes = $this->getTextAttributes($elementMiddle); + foreach ($elementAttributes as $key=>$value) { + if (!in_array(strtolower($key), $attributesSafe) && !preg_match("/^(aria|data)-/i", $key)) { + unset($elementAttributes[$key]); + } + } + if ($filterStrict) { + $href = isset($elementAttributes["href"]) ? $elementAttributes["href"] : ""; + if (preg_match("/^\w+:/", $href) && !preg_match("/^(http|https|ftp|mailto):/", $href)) { + $elementAttributes["href"] = "error-xss-filter"; + } + $href = isset($elementAttributes["xlink:href"]) ? $elementAttributes["xlink:href"] : ""; + if (preg_match("/^\w+:/", $href) && !preg_match("/^(http|https|ftp|mailto):/", $href)) { + $elementAttributes["xlink:href"] = "error-xss-filter"; + } + } + $output .= "<$elementStart$elementName"; + foreach ($elementAttributes as $key=>$value) $output .= " $key=\"$value\""; + if (!empty($elementEnd)) $output .= " "; + $output .= "$elementEnd>"; + } + if (!$elementFound) break; + $offsetBytes = $matches[0][1] + strlenb($matches[0][0]); + } + return $output; + } + + // Normalise text lines, convert line endings + public function normaliseLines($text, $endOfLine = "lf") { + if ($endOfLine=="lf") { + $text = preg_replace("/\R/u", "\n", $text); + } else { + $text = preg_replace("/\R/u", "\r\n", $text); + } + return $text; + } + + // Normalise text into UTF-8 NFC + public function normaliseUnicode($text) { + if (PHP_OS=="Darwin" && !mb_check_encoding($text, "ASCII")) { + $utf8nfc = preg_match("//u", $text) && !preg_match("/[^\\x00-\\x{2FF}]/u", $text); + if (!$utf8nfc) $text = iconv("UTF-8-MAC", "UTF-8", $text); + } + return $text; + } + // Start timer public function timerStart(&$time) { $time = microtime(true); @@ -3137,121 +3425,41 @@ class YellowToolbox { public function isLocationArgs($location = "") { return $this->isLocationArguments($location); } public function isLocationArgsPagination($location) { return $this->isLocationArgumentsPagination($location); } } - -class YellowExtensions { - public $yellow; // access to API - public $modified; // extension modification date - public $extensions; // registered extensions - public function __construct($yellow) { - $this->yellow = $yellow; - $this->modified = 0; - $this->extensions = array(); - } - - // Load extensions - public function load($path) { - foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.php$/", true, false) as $entry) { - if (defined("DEBUG") && DEBUG>=3) echo "YellowExtensions::load file:$entry<br/>\n"; - $this->modified = max($this->modified, filemtime($entry)); - require_once($entry); - $name = $this->yellow->lookup->normaliseName(basename($entry), true, true); - $this->register(lcfirst($name), "Yellow".ucfirst($name)); - } - $callback = function ($a, $b) { - return $a["priority"] - $b["priority"]; - }; - uasort($this->extensions, $callback); - foreach ($this->extensions as $key=>$value) { - if (method_exists($this->extensions[$key]["obj"], "onLoad")) $this->extensions[$key]["obj"]->onLoad($this->yellow); - } - $this->yellow->system->set("mediaLocation", "/media/"); // TODO: remove later, for backwards compatibility - $this->yellow->system->set("downloadLocation", "/media/downloads/"); - $this->yellow->system->set("imageLocation", "/media/images/"); - $this->yellow->system->set("extensionLocation", "/media/extensions/"); - $this->yellow->system->set("resourceLocation", "/media/resources/"); - $this->yellow->system->set("mediaDir", "media/"); - $this->yellow->system->set("downloadDir", "media/downloads/"); - $this->yellow->system->set("imageDir", "media/images/"); - $this->yellow->system->set("systemDir", "system/"); - $this->yellow->system->set("extensionDir", "system/extensions/"); - $this->yellow->system->set("layoutDir", "system/layouts/"); - $this->yellow->system->set("resourceDir", "system/resources/"); - $this->yellow->system->set("settingDir", "system/settings/"); - $this->yellow->system->set("trashDir", "system/trash/"); - $this->yellow->system->set("contentDir", "content/"); - $this->yellow->system->set("contentPagination", "page"); - $this->yellow->system->set("coreStaticDir", "public/"); - $this->yellow->system->set("coreCacheDir", "cache/"); - $this->yellow->system->set("coreTrashDir", "system/trash/"); - $this->yellow->system->set("coreMediaDir", "media/"); - $this->yellow->system->set("coreDownloadDir", "media/downloads/"); - $this->yellow->system->set("coreImageDir", "media/images/"); - $this->yellow->system->set("coreSystemDir", "system/"); - $this->yellow->system->set("coreExtensionDir", "system/extensions/"); - $this->yellow->system->set("coreLayoutDir", "system/layouts/"); - $this->yellow->system->set("coreResourceDir", "system/resources/"); - $this->yellow->system->set("coreSettingDir", "system/settings/"); - $this->yellow->system->set("coreContentDir", "content/"); - $this->yellow->system->set("coreContentRootDir", "default/"); - $this->yellow->system->set("coreContentHomeDir", "home/"); - $this->yellow->system->set("coreContentSharedDir", "shared/"); - } - - // Register extension - public function register($name, $class) { - if (!$this->isExisting($name) && class_exists($class)) { - $this->extensions[$name] = array(); - $this->extensions[$name]["obj"] = new $class; - $this->extensions[$name]["class"] = $class; - $this->extensions[$name]["version"] = defined("$class::VERSION") ? $class::VERSION : 0; - $this->extensions[$name]["type"] = defined("$class::TYPE") ? $class::TYPE : "feature"; - $this->extensions[$name]["priority"] = defined("$class::PRIORITY") ? $class::PRIORITY : count($this->extensions) + 10; - } - } - - // Return extension - public function get($name) { - return $this->extensions[$name]["obj"]; - } - - // Return extensions version - public function getData($type = "") { - $data = array(); - foreach ($this->extensions as $key=>$value) { - if (empty($type) || $value["type"]==$type) { - $data[$key] = $value["version"]; - } - } - uksort($data, "strnatcasecmp"); - return $data; - } - - // Return extensions modification date, Unix time or HTTP format - public function getModified($httpFormat = false) { - return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($this->modified) : $this->modified; - } +function strreplaceu() { // TODO: remove later, for backwards compatibility + return call_user_func_array("str_replace", func_get_args()); +} - // Return extensions - public function getExtensions($type = "") { - $extensions = array(); - foreach ($this->extensions as $key=>$value) { - if (empty($type) || $value["type"]==$type) { - array_push($extensions, $key); - } - } - return $extensions; - } - - // Check if extension exists - public function isExisting($name) { - return isset($this->extensions[$name]); - } +class YellowText { // TODO: remove later, for backwards compatibility + public $yellow; + public function __construct($yellow) { $this->yellow = $yellow; } + public function load($fileName) { $this->yellow->language->load($fileName); } + public function setLanguage($language) { $this->yellow->language->set($language); } + public function setText($key, $value, $language) { $this->yellow->language->setText($key, $value, $language); } + public function get($key) { return $this->yellow->language->getText($key); } + public function getHtml($key) { return $this->yellow->language->getTextHtml($key); } + public function getText($key, $language) { return $this->yellow->language->getText($key, $language); } + public function getTextHtml($key, $language) { return $this->yellow->language->getTextHtml($key, $language); } + public function getData($filterStart = "", $language = "") { return $this->yellow->language->getSettings($filterStart, "", $language); } + public function getDateFormatted($timestamp, $format) { return $this->yellow->language->getDateFormatted($timestamp, $format); } + public function getDateRelative($timestamp, $format, $daysLimit) { return $this->yellow->language->getDateRelative($timestamp, $format, $daysLimit); } + public function getModified($httpFormat = false) { return $this->yellow->language->getModified($httpFormat); } + public function getLanguages() { return $this->yellow->system->getValues("language"); } + public function normaliseDate($text) { return $this->yellow->language->normaliseDate($text); } + public function isLanguage($language) { return $this->yellow->language->isExisting($language); } + public function isExisting($key, $language = "") { return $this->yellow->language->isText($key, $language); } } -// Check if string is empty -function strempty($string) { - return is_null($string) || $string===""; +class YellowExtensions { // TODO: remove later, for backwards compatibility + public $yellow; + public function __construct($yellow) { $this->yellow = $yellow; } + public function load($path) { $this->yellow->extension->load($path); } + public function register($name, $class) { $this->yellow->extension->register($name, $class); } + public function get($name) { return $this->yellow->extension->get($name); } + public function getData($type = "") { return array(); } + public function getModified($httpFormat = false) { return $this->yellow->extension->getModified($httpFormat); } + public function getExtensions($type = "") { return array(); } + public function isExisting($name) { return $this->yellow->extension->isExisting($name); } } // Make string lowercase, UTF-8 compatible @@ -3264,47 +3472,47 @@ function strtoupperu() { return call_user_func_array("mb_strtoupper", func_get_args()); } -// Replace string, UTF-8 compatible -function strreplaceu() { - return call_user_func_array("str_replace", func_get_args()); -} - -// Return string length in characters, UTF-8 compatible +// Return string length, UTF-8 characters function strlenu() { return call_user_func_array("mb_strlen", func_get_args()); } -// Return string length in bytes, ASCII compatible +// Return string length, bytes function strlenb() { return call_user_func_array("strlen", func_get_args()); } -// Return string positon in characters, UTF-8 compatible +// Return string position of first match, UTF-8 characters function strposu() { return call_user_func_array("mb_strpos", func_get_args()); } -// Return string position in bytes, ASCII compatible +// Return string position of first match, bytes function strposb() { return call_user_func_array("strpos", func_get_args()); } -// Return reverse string position in characters, UTF-8 compatible +// Return string position of last match, UTF-8 characters function strrposu() { return call_user_func_array("mb_strrpos", func_get_args()); } -// Return reverse string position in bytes, ASCII compatible +// Return string position of last match, bytes function strrposb() { return call_user_func_array("strrpos", func_get_args()); } -// Return part of a string, UTF-8 compatible +// Return part of a string, UTF-8 characters function substru() { return call_user_func_array("mb_substr", func_get_args()); } -// Return part of a string, ASCII compatible +// Return part of a string, bytes function substrb() { return call_user_func_array("substr", func_get_args()); } + +// Check if string is empty +function strempty($string) { + return is_null($string) || $string===""; +} diff --git a/system/extensions/edit.js b/system/extensions/edit.js @@ -74,7 +74,7 @@ yellow.edit = { // Handle page cache pageShow: function(e) { - if (e.persisted && yellow.system.userEmail && !this.getCookie("csrftoken")) { + if (e.persisted && yellow.user.email && !this.getCookie("csrftoken")) { window.location.reload(); } }, @@ -92,7 +92,7 @@ yellow.edit = { } var elementDiv = document.createElement("div"); elementDiv.setAttribute("id", barId+"-content"); - if (yellow.system.userName) { + if (yellow.user.name) { elementDiv.innerHTML = "<div class=\"yellow-bar-left\">"+ this.getRawDataPaneAction("edit")+ @@ -100,7 +100,7 @@ yellow.edit = { "<div class=\"yellow-bar-right\">"+ this.getRawDataPaneAction("create")+ this.getRawDataPaneAction("delete")+ - this.getRawDataPaneAction("menu", yellow.system.userName, true)+ + this.getRawDataPaneAction("menu", yellow.user.name, true)+ "</div>"+ "<div class=\"yellow-bar-banner\"></div>"; } else { @@ -328,7 +328,7 @@ yellow.edit = { case "yellow-pane-menu": elementDiv.innerHTML = "<ul class=\"yellow-dropdown\">"+ - "<li><span>"+yellow.toolbox.encodeHtml(yellow.system.userEmail)+"</span></li>"+ + "<li><span>"+yellow.toolbox.encodeHtml(yellow.user.email)+"</span></li>"+ "<li><a href=\"#\" data-action=\"settings\">"+this.getText("MenuSettings")+"</a></li>" + "<li><a href=\"#\" data-action=\"help\">"+this.getText("MenuHelp")+"</a></li>" + "<li><a href=\"#\" data-action=\"submit\" data-arguments=\"action:logout\">"+this.getText("MenuLogout")+"</a></li>"+ @@ -374,10 +374,10 @@ yellow.edit = { } if (paneStatus=="none") { document.getElementById("yellow-pane-account-status").innerHTML = this.getText("AccountStatusNone"); - document.getElementById("yellow-pane-account-name").value = yellow.system.userName; - document.getElementById("yellow-pane-account-email").value = yellow.system.userEmail; + document.getElementById("yellow-pane-account-name").value = yellow.user.name; + document.getElementById("yellow-pane-account-email").value = yellow.user.email; document.getElementById("yellow-pane-account-password").value = ""; - document.getElementById("yellow-pane-account-"+yellow.system.userLanguage).checked = true; + document.getElementById("yellow-pane-account-"+yellow.user.language).checked = true; } break; case "yellow-pane-system": @@ -952,14 +952,14 @@ yellow.edit = { return rawDataButtons; }, - // Return request string + // Return request data getRequest: function(key, prefix) { if (!prefix) prefix = "request"; key = prefix + yellow.toolbox.toUpperFirst(key); return (key in yellow.page) ? yellow.page[key] : ""; }, - // Return shortcut string + // Return shortcut setting getShortcut: function(key) { var shortcut = ""; var tokens = yellow.system.editKeyboardShortcuts.split(/\s*,\s*/); @@ -970,7 +970,7 @@ yellow.edit = { break; } } - var labels = yellow.text.editKeyboardLabels.split(/\s*,\s*/); + var labels = yellow.language.editKeyboardLabels.split(/\s*,\s*/); if (navigator.platform.indexOf("Mac")==-1) { shortcut = shortcut.toUpperCase().replace("CTRL+", labels[0]).replace("ALT+", labels[1]).replace("SHIFT+", labels[2]); } else { @@ -980,12 +980,12 @@ yellow.edit = { return shortcut; }, - // Return text string + // Return text setting getText: function(key, prefix, postfix) { if (!prefix) prefix = "edit"; if (!postfix) postfix = ""; key = prefix + yellow.toolbox.toUpperFirst(key) + yellow.toolbox.toUpperFirst(postfix); - return (key in yellow.text) ? yellow.text[key] : "["+key+"]"; + return (key in yellow.language) ? yellow.language[key] : "["+key+"]"; }, // Return browser cookie @@ -995,8 +995,8 @@ yellow.edit = { // Check if user with access isUserAccess: function(action, location) { - var tokens = yellow.system.userAccess.split(/\s*,\s*/); - return tokens.indexOf(action)!=-1 && (!location || location.substring(0, yellow.system.userHome.length)==yellow.system.userHome); + var tokens = yellow.user.access.split(/\s*,\s*/); + return tokens.indexOf(action)!=-1 && (!location || location.substring(0, yellow.user.home.length)==yellow.user.home); }, // Check if element is expandable diff --git a/system/extensions/edit.php b/system/extensions/edit.php @@ -2,18 +2,15 @@ // Edit extension, https://github.com/datenstrom/yellow-extensions/tree/master/source/edit class YellowEdit { - const VERSION = "0.8.29"; - const TYPE = "feature"; + const VERSION = "0.8.30"; public $yellow; // access to API public $response; // web response - public $users; // user accounts public $merge; // text merge // Handle initialisation public function onLoad($yellow) { $this->yellow = $yellow; $this->response = new YellowEditResponse($yellow); - $this->users = new YellowEditUsers($yellow); $this->merge = new YellowEditMerge($yellow); $this->yellow->system->setDefault("editLocation", "/edit/"); $this->yellow->system->setDefault("editUploadNewLocation", "/media/@group/@filename"); @@ -22,7 +19,6 @@ class YellowEdit { $this->yellow->system->setDefault("editToolbarButtons", "auto"); $this->yellow->system->setDefault("editEndOfLine", "auto"); $this->yellow->system->setDefault("editNewFile", "page-new-(.*).md"); - $this->yellow->system->setDefault("editUserFile", "user.ini"); $this->yellow->system->setDefault("editUserPasswordMinLength", "8"); $this->yellow->system->setDefault("editUserHashAlgorithm", "bcrypt"); $this->yellow->system->setDefault("editUserHashCost", "10"); @@ -31,7 +27,7 @@ class YellowEdit { $this->yellow->system->setDefault("editLoginRestriction", "0"); $this->yellow->system->setDefault("editLoginSessionTimeout", "2592000"); $this->yellow->system->setDefault("editBruteForceProtection", "25"); - $this->users->load($this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("editUserFile")); + $this->yellow->language->setDefault("editMailFooter"); } // Handle request @@ -77,7 +73,8 @@ class YellowEdit { $output .= "// <![CDATA[\n"; $output .= "yellow.page = ".json_encode($this->response->getPageData($page)).";\n"; $output .= "yellow.system = ".json_encode($this->response->getSystemData()).";\n"; - $output .= "yellow.text = ".json_encode($this->response->getTextData()).";\n"; + $output .= "yellow.user = ".json_encode($this->response->getUserData()).";\n"; + $output .= "yellow.language = ".json_encode($this->response->getLanguageData()).";\n"; $output .= "// ]]>\n"; $output .= "</script>\n"; } @@ -102,13 +99,13 @@ class YellowEdit { public function onUpdate($action) { if ($action=="update") { $cleanup = false; - $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("editUserFile"); + $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("coreUserFile"); $fileData = $this->yellow->toolbox->readFile($fileNameUser); $fileDataNew = ""; foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) { if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) { if (lcfirst($matches[1])=="email" && !strempty($matches[2])) { - $status = $this->users->getUser($matches[2], "status"); + $status = $this->yellow->user->getUser("status", $matches[2]); $cleanup = !empty($status) && $status!="active" && $status!="inactive"; } } @@ -136,10 +133,15 @@ class YellowEdit { // Show user accounts public function userShow($command, $text) { - foreach ($this->users->getData() as $line) { - echo "$line\n"; + $data = array(); + foreach ($this->yellow->user->settings as $key=>$value) { + $name = $value["name"]; + if (preg_match("/\s/", $name)) $name = "\"$name\""; + $data[$key] = "$value[email] $name $value[status]"; } - if (!$this->users->getNumber()) echo "Yellow $command: No user accounts\n"; + uksort($data, "strnatcasecmp"); + foreach ($data as $line) echo "$line\n"; + if (count($data)==0) echo "Yellow $command: No user accounts\n"; return 200; } @@ -149,8 +151,8 @@ class YellowEdit { list($option, $email, $password, $name) = $this->yellow->toolbox->getTextArguments($text); if (empty($email) || empty($password)) $status = $this->response->status = "incomplete"; if (empty($name)) $name = $this->yellow->system->get("sitename"); - if ($status=="ok") $status = $this->getUserAccount($email, $password, "add"); - if ($status=="ok" && $this->users->isTaken($email)) $status = "taken"; + if ($status=="ok") $status = $this->getUserAccount("add", $email, $password); + if ($status=="ok" && $this->isUserAccountTaken($email)) $status = "taken"; switch ($status) { case "incomplete": echo "ERROR updating settings: Please enter email and password!\n"; break; case "invalid": echo "ERROR updating settings: Please enter a valid email!\n"; break; @@ -159,25 +161,25 @@ class YellowEdit { case "short": echo "ERROR updating settings: Please enter a longer password!\n"; break; } if ($status=="ok") { - $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("editUserFile"); + $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("coreUserFile"); $settings = array( "name" => $name, "language" => $this->yellow->system->get("language"), "home" => $this->yellow->system->get("editUserHome"), "access" => $this->yellow->system->get("editUserAccess"), - "hash" => $this->users->createHash($password), - "stamp" => $this->users->createStamp(), + "hash" => $this->response->createHash($password), + "stamp" => $this->response->createStamp(), "pending" => "none", "failed" => "0", "modified" => date("Y-m-d H:i:s", time()), "status" => "active"); - $status = $this->users->save($fileNameUser, $email, $settings) ? "ok" : "error"; + $status = $this->yellow->user->save($fileNameUser, $email, $settings) ? "ok" : "error"; if ($status=="error") echo "ERROR updating settings: Can't write file '$fileNameUser'!\n"; $this->yellow->log($status=="ok" ? "info" : "error", "Add user '".strtok($name, " ")."'"); } if ($status=="ok") { $algorithm = $this->yellow->system->get("editUserHashAlgorithm"); - $status = substru($this->users->getUser($email, "hash"), 0, 10)!="error-hash" ? "ok" : "error"; + $status = substru($this->yellow->user->getUser("hash", $email), 0, 10)!="error-hash" ? "ok" : "error"; if ($status=="error") echo "ERROR updating settings: Hash algorithm '$algorithm' not supported!\n"; } $statusCode = $status=="ok" ? 200 : 500; @@ -190,8 +192,8 @@ class YellowEdit { $status = "ok"; list($option, $email, $password, $name) = $this->yellow->toolbox->getTextArguments($text); if (empty($email)) $status = $this->response->status = "invalid"; - if ($status=="ok") $status = $this->getUserAccount($email, $password, "change"); - if ($status=="ok" && !$this->users->isExisting($email)) $status = "unknown"; + if ($status=="ok") $status = $this->getUserAccount("change", $email, $password); + if ($status=="ok" && !$this->yellow->user->isExisting($email)) $status = "unknown"; switch ($status) { case "invalid": echo "ERROR updating settings: Please enter a valid email!\n"; break; case "unknown": echo "ERROR updating settings: Can't find email '$email'!\n"; break; @@ -199,13 +201,13 @@ class YellowEdit { case "short": echo "ERROR updating settings: Please enter a longer password!\n"; break; } if ($status=="ok") { - $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("editUserFile"); + $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("coreUserFile"); $settings = array( - "name" => empty($name) ? $this->users->getUser($email, "name") : $name, - "hash" => empty($password) ? $this->users->getUser($email, "hash") : $this->users->createHash($password), + "name" => empty($name) ? $this->yellow->user->getUser("name", $email) : $name, + "hash" => empty($password) ? $this->yellow->user->getUser("hash", $email) : $this->response->createHash($password), "failed" => "0", "modified" => date("Y-m-d H:i:s", time())); - $status = $this->users->save($fileNameUser, $email, $settings) ? "ok" : "error"; + $status = $this->yellow->user->save($fileNameUser, $email, $settings) ? "ok" : "error"; if ($status=="error") echo "ERROR updating settings: Can't write file '$fileNameUser'!\n"; } $statusCode = $status=="ok" ? 200 : 500; @@ -217,18 +219,18 @@ class YellowEdit { public function userRemove($command, $text) { $status = "ok"; list($option, $email) = $this->yellow->toolbox->getTextArguments($text); - $name = $this->users->getUser($email, "name"); + $name = $this->yellow->user->getUser("name", $email); if (empty($email)) $status = $this->response->status = "invalid"; if (empty($name)) $name = $this->yellow->system->get("sitename"); - if ($status=="ok") $status = $this->getUserAccount($email, "", "remove"); - if ($status=="ok" && !$this->users->isExisting($email)) $status = "unknown"; + if ($status=="ok") $status = $this->getUserAccount("remove", $email, ""); + if ($status=="ok" && !$this->yellow->user->isExisting($email)) $status = "unknown"; switch ($status) { case "invalid": echo "ERROR updating settings: Please enter a valid email!\n"; break; case "unknown": echo "ERROR updating settings: Can't find email '$email'!\n"; break; } if ($status=="ok") { - $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("editUserFile"); - $status = $this->users->remove($fileNameUser, $email) ? "ok" : "error"; + $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("coreUserFile"); + $status = $this->yellow->user->remove($fileNameUser, $email) ? "ok" : "error"; if ($status=="error") echo "ERROR updating settings: Can't write file '$fileNameUser'!\n"; $this->yellow->log($status=="ok" ? "info" : "error", "Remove user '".strtok($name, " ")."'"); } @@ -295,10 +297,10 @@ class YellowEdit { // Process request for user login public function processRequestLogin($scheme, $address, $base, $location, $fileName) { - $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("editUserFile"); + $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("coreUserFile"); $settings = array("failed" => "0", "modified" => date("Y-m-d H:i:s", time())); - if ($this->users->save($fileNameUser, $this->response->userEmail, $settings)) { - $home = $this->users->getUser($this->response->userEmail, "home"); + if ($this->yellow->user->save($fileNameUser, $this->response->userEmail, $settings)) { + $home = $this->yellow->user->getUser("home", $this->response->userEmail); if (substru($location, 0, strlenu($home))==$home) { $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); $statusCode = $this->yellow->sendStatus(303, $location); @@ -335,28 +337,28 @@ class YellowEdit { $password = trim($this->yellow->page->getRequest("password")); $consent = trim($this->yellow->page->getRequest("consent")); if (empty($name) || empty($email) || empty($password) || empty($consent)) $this->response->status = "incomplete"; - if ($this->response->status=="ok") $this->response->status = $this->getUserAccount($email, $password, $this->response->action); + if ($this->response->status=="ok") $this->response->status = $this->getUserAccount($this->response->action, $email, $password); if ($this->response->status=="ok" && $this->response->isLoginRestriction()) $this->response->status = "next"; - if ($this->response->status=="ok" && $this->users->isTaken($email)) $this->response->status = "next"; + if ($this->response->status=="ok" && $this->isUserAccountTaken($email)) $this->response->status = "next"; if ($this->response->status=="ok") { - $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("editUserFile"); + $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("coreUserFile"); $settings = array( "name" => $name, "language" => $this->yellow->lookup->findLanguageFromFile($fileName, $this->yellow->system->get("language")), "home" => $this->yellow->system->get("editUserHome"), "access" => $this->yellow->system->get("editUserAccess"), - "hash" => $this->users->createHash($password), - "stamp" => $this->users->createStamp(), + "hash" => $this->response->createHash($password), + "stamp" => $this->response->createStamp(), "pending" => "none", "failed" => "0", "modified" => date("Y-m-d H:i:s", time()), "status" => "unconfirmed"); - $this->response->status = $this->users->save($fileNameUser, $email, $settings) ? "ok" : "error"; + $this->response->status = $this->yellow->user->save($fileNameUser, $email, $settings) ? "ok" : "error"; if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); } if ($this->response->status=="ok") { $algorithm = $this->yellow->system->get("editUserHashAlgorithm"); - $this->response->status = substru($this->users->getUser($email, "hash"), 0, 10)!="error-hash" ? "ok" : "error"; + $this->response->status = substru($this->yellow->user->getUser("hash", $email), 0, 10)!="error-hash" ? "ok" : "error"; if ($this->response->status=="error") $this->yellow->page->error(500, "Hash algorithm '$algorithm' not supported!"); } if ($this->response->status=="ok") { @@ -374,9 +376,9 @@ class YellowEdit { $email = $this->yellow->page->getRequest("email"); $this->response->status = $this->getUserStatus($email, $this->yellow->page->getRequest("action")); if ($this->response->status=="ok") { - $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("editUserFile"); + $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("coreUserFile"); $settings = array("failed" => "0", "modified" => date("Y-m-d H:i:s", time()), "status" => "unapproved"); - $this->response->status = $this->users->save($fileNameUser, $email, $settings) ? "ok" : "error"; + $this->response->status = $this->yellow->user->save($fileNameUser, $email, $settings) ? "ok" : "error"; if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); } if ($this->response->status=="ok") { @@ -394,11 +396,12 @@ class YellowEdit { $email = $this->yellow->page->getRequest("email"); $this->response->status = $this->getUserStatus($email, $this->yellow->page->getRequest("action")); if ($this->response->status=="ok") { - $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("editUserFile"); + $name = $this->yellow->user->getUser("name", $email); + $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("coreUserFile"); $settings = array("failed" => "0", "modified" => date("Y-m-d H:i:s", time()), "status" => "active"); - $this->response->status = $this->users->save($fileNameUser, $email, $settings) ? "ok" : "error"; + $this->response->status = $this->yellow->user->save($fileNameUser, $email, $settings) ? "ok" : "error"; if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); - $this->yellow->log($this->response->status=="ok" ? "info" : "error", "Add user '".strtok($this->users->getUser($email, "name"), " ")."'"); + $this->yellow->log($this->response->status=="ok" ? "info" : "error", "Add user '".strtok($name, " ")."'"); } if ($this->response->status=="ok") { $this->response->status = $this->response->sendMail($scheme, $address, $base, $email, "welcome") ? "done" : "error"; @@ -414,7 +417,7 @@ class YellowEdit { $this->response->status = "ok"; $email = trim($this->yellow->page->getRequest("email")); if (!filter_var($email, FILTER_VALIDATE_EMAIL)) $this->response->status = "invalid"; - if ($this->response->status=="ok" && !$this->users->isExisting($email)) $this->response->status = "next"; + if ($this->response->status=="ok" && !$this->yellow->user->isExisting($email)) $this->response->status = "next"; if ($this->response->status=="ok") { $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!"); @@ -432,11 +435,11 @@ class YellowEdit { $this->response->status = $this->getUserStatus($email, $this->yellow->page->getRequest("action")); if ($this->response->status=="ok") { if (empty($password)) $this->response->status = "password"; - if ($this->response->status=="ok") $this->response->status = $this->getUserAccount($email, $password, $this->response->action); + if ($this->response->status=="ok") $this->response->status = $this->getUserAccount($this->response->action, $email, $password); if ($this->response->status=="ok") { - $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("editUserFile"); - $settings = array("hash" => $this->users->createHash($password), "failed" => "0", "modified" => date("Y-m-d H:i:s", time())); - $this->response->status = $this->users->save($fileNameUser, $email, $settings) ? "ok" : "error"; + $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("coreUserFile"); + $settings = array("hash" => $this->response->createHash($password), "failed" => "0", "modified" => date("Y-m-d H:i:s", time())); + $this->response->status = $this->yellow->user->save($fileNameUser, $email, $settings) ? "ok" : "error"; if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); } if ($this->response->status=="ok") { @@ -455,9 +458,9 @@ class YellowEdit { $email = $this->yellow->page->getRequest("email"); $this->response->status = $this->getUserStatus($email, $this->yellow->page->getRequest("action")); if ($this->response->status=="ok") { - $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("editUserFile"); + $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("coreUserFile"); $settings = array("failed" => "0", "modified" => date("Y-m-d H:i:s", time()), "status" => "active"); - $this->response->status = $this->users->save($fileNameUser, $email, $settings) ? "done" : "error"; + $this->response->status = $this->yellow->user->save($fileNameUser, $email, $settings) ? "done" : "error"; if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); } $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); @@ -471,13 +474,13 @@ class YellowEdit { $email = $emailSource = $this->yellow->page->getRequest("email"); $this->response->status = $this->getUserStatus($email, $this->yellow->page->getRequest("action")); if ($this->response->status=="ok") { - $emailSource = $this->users->getUser($email, "pending"); - if ($this->users->getUser($emailSource, "status")!="active") $this->response->status = "done"; + $emailSource = $this->yellow->user->getUser("pending", $email); + if ($this->yellow->user->getUser("status", $emailSource)!="active") $this->response->status = "done"; } if ($this->response->status=="ok") { - $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("editUserFile"); + $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("coreUserFile"); $settings = array("failed" => "0", "modified" => date("Y-m-d H:i:s", time()), "status" => "unchanged"); - $this->response->status = $this->users->save($fileNameUser, $email, $settings) ? "ok" : "error"; + $this->response->status = $this->yellow->user->save($fileNameUser, $email, $settings) ? "ok" : "error"; if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); } if ($this->response->status=="ok") { @@ -495,23 +498,23 @@ class YellowEdit { $email = $emailSource = trim($this->yellow->page->getRequest("email")); $this->response->status = $this->getUserStatus($email, $this->yellow->page->getRequest("action")); if ($this->response->status=="ok") { - list($email, $hash) = $this->yellow->toolbox->getTextList($this->users->getUser($email, "pending"), ":", 2); - if (!$this->users->isExisting($email) || empty($hash)) $this->response->status = "done"; + list($email, $hash) = $this->yellow->toolbox->getTextList($this->yellow->user->getUser("pending", $email), ":", 2); + if (!$this->yellow->user->isExisting($email) || empty($hash)) $this->response->status = "done"; } if ($this->response->status=="ok") { - $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("editUserFile"); + $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("coreUserFile"); $settings = array( "hash" => $hash, "pending" => "none", "failed" => "0", "modified" => date("Y-m-d H:i:s", time()), "status" => "active"); - $this->response->status = $this->users->save($fileNameUser, $email, $settings) ? "ok" : "error"; + $this->response->status = $this->yellow->user->save($fileNameUser, $email, $settings) ? "ok" : "error"; if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); } if ($this->response->status=="ok" && $email!=$emailSource) { - $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("editUserFile"); - $this->response->status = $this->users->remove($fileNameUser, $emailSource) ? "ok" : "error"; + $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("coreUserFile"); + $this->response->status = $this->yellow->user->remove($fileNameUser, $emailSource) ? "ok" : "error"; if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); } if ($this->response->status=="ok") { @@ -529,8 +532,8 @@ class YellowEdit { $name = trim($this->yellow->page->getRequest("name")); $email = $this->response->userEmail; if (empty($name)) $this->response->status = "none"; - if ($this->response->status=="ok" && $name!=$this->users->getUser($email, "name")) $this->response->status = "mismatch"; - if ($this->response->status=="ok") $this->response->status = $this->getUserAccount($email, "", $this->response->action); + if ($this->response->status=="ok" && $name!=$this->yellow->user->getUser("name", $email)) $this->response->status = "mismatch"; + if ($this->response->status=="ok") $this->response->status = $this->getUserAccount($this->response->action, $email, ""); if ($this->response->status=="ok") { $this->response->status = $this->response->sendMail($scheme, $address, $base, $email, "remove") ? "next" : "error"; if ($this->response->status=="error") $this->yellow->page->error(500, "Can't send email on this server!"); @@ -546,19 +549,20 @@ class YellowEdit { $email = $this->yellow->page->getRequest("email"); $this->response->status = $this->getUserStatus($email, $this->yellow->page->getRequest("action")); if ($this->response->status=="ok") { - $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("editUserFile"); + $name = $this->yellow->user->getUser("name", $email); + $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("coreUserFile"); $settings = array("failed" => "0", "modified" => date("Y-m-d H:i:s", time()), "status" => "removed"); - $this->response->status = $this->users->save($fileNameUser, $email, $settings) ? "ok" : "error"; + $this->response->status = $this->yellow->user->save($fileNameUser, $email, $settings) ? "ok" : "error"; if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); - $this->yellow->log($this->response->status=="ok" ? "info" : "error", "Remove user '".strtok($this->users->getUser($email, "name"), " ")."'"); + $this->yellow->log($this->response->status=="ok" ? "info" : "error", "Remove user '".strtok($name, " ")."'"); } if ($this->response->status=="ok") { $this->response->status = $this->response->sendMail($scheme, $address, $base, $email, "goodbye") ? "ok" : "error"; if ($this->response->status=="error") $this->yellow->page->error(500, "Can't send email on this server!"); } if ($this->response->status=="ok") { - $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("editUserFile"); - $this->response->status = $this->users->remove($fileNameUser, $email) ? "ok" : "error"; + $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("coreUserFile"); + $this->response->status = $this->yellow->user->remove($fileNameUser, $email) ? "ok" : "error"; if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); } if ($this->response->status=="ok") { @@ -580,33 +584,33 @@ class YellowEdit { $language = trim($this->yellow->page->getRequest("language")); if ($email!=$emailSource || !empty($password)) { if (empty($email)) $this->response->status = "invalid"; - if ($this->response->status=="ok") $this->response->status = $this->getUserAccount($email, $password, $this->response->action); - if ($this->response->status=="ok" && $email!=$emailSource && $this->users->isTaken($email)) $this->response->status = "taken"; + if ($this->response->status=="ok") $this->response->status = $this->getUserAccount($this->response->action, $email, $password); + if ($this->response->status=="ok" && $email!=$emailSource && $this->isUserAccountTaken($email)) $this->response->status = "taken"; if ($this->response->status=="ok" && $email!=$emailSource) { - $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("editUserFile"); + $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("coreUserFile"); $settings = array( "name" => $name, "language" => $language, - "home" => $this->users->getUser($emailSource, "home"), - "access" => $this->users->getUser($emailSource, "access"), - "hash" => $this->users->createHash("none"), - "stamp" => $this->users->createStamp(), + "home" => $this->yellow->user->getUser("home", $emailSource), + "access" => $this->yellow->user->getUser("access", $emailSource), + "hash" => $this->response->createHash("none"), + "stamp" => $this->response->createStamp(), "pending" => $emailSource, "failed" => "0", "modified" => date("Y-m-d H:i:s", time()), "status" => "unverified"); - $this->response->status = $this->users->save($fileNameUser, $email, $settings) ? "ok" : "error"; + $this->response->status = $this->yellow->user->save($fileNameUser, $email, $settings) ? "ok" : "error"; if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); } if ($this->response->status=="ok") { - $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("editUserFile"); + $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("coreUserFile"); $settings = array( "name" => $name, "language" => $language, - "pending" => $email.":".(empty($password) ? $this->users->getUser($emailSource, "hash") : $this->users->createHash($password)), + "pending" => $email.":".(empty($password) ? $this->yellow->user->getUser("hash", $emailSource) : $this->response->createHash($password)), "failed" => "0", "modified" => date("Y-m-d H:i:s", time())); - $this->response->status = $this->users->save($fileNameUser, $emailSource, $settings) ? "ok" : "error"; + $this->response->status = $this->yellow->user->save($fileNameUser, $emailSource, $settings) ? "ok" : "error"; if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); } if ($this->response->status=="ok") { @@ -616,9 +620,9 @@ class YellowEdit { } } else { if ($this->response->status=="ok") { - $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("editUserFile"); + $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("coreUserFile"); $settings = array("name" => $name, "language" => $language, "failed" => "0", "modified" => date("Y-m-d H:i:s", time())); - $this->response->status = $this->users->save($fileNameUser, $email, $settings) ? "done" : "error"; + $this->response->status = $this->yellow->user->save($fileNameUser, $email, $settings) ? "done" : "error"; if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); } } @@ -794,10 +798,7 @@ class YellowEdit { $page = $this->response->getPagePreview($scheme, $address, $base, $location, $fileName, $this->yellow->page->getRequest("rawdataedit"), $this->yellow->page->getRequest("rawdataendofline")); $statusCode = $this->yellow->sendData(200, $page->outputData, "", false); - if (defined("DEBUG") && DEBUG>=1) { - $parser = $page->get("parser"); - echo "YellowEdit::processRequestPreview parser:$parser<br/>\n"; - } + if (defined("DEBUG") && DEBUG>=1) echo "YellowEdit::processRequestPreview file:$fileName<br/>\n"; return $statusCode; } @@ -840,7 +841,7 @@ class YellowEdit { if ($action=="login") { $email = $this->yellow->page->getRequest("email"); $password = $this->yellow->page->getRequest("password"); - if ($this->users->checkAuthLogin($email, $password)) { + if ($this->response->checkAuthLogin($email, $password)) { $this->response->createCookies($scheme, $address, $base, $email); $this->response->userEmail = $email; $this->response->language = $this->getUserLanguage($email); @@ -852,15 +853,16 @@ class YellowEdit { } elseif (!empty($authToken) && !empty($csrfToken)) { $csrfTokenReceived = isset($_POST["csrftoken"]) ? $_POST["csrftoken"] : ""; $csrfTokenIrrelevant = empty($action); - if ($this->users->checkAuthToken($authToken, $csrfToken, $csrfTokenReceived, $csrfTokenIrrelevant)) { - $this->response->userEmail = $email = $this->users->getAuthEmail($authToken); + if ($this->response->checkAuthToken($authToken, $csrfToken, $csrfTokenReceived, $csrfTokenIrrelevant)) { + $this->response->userEmail = $email = $this->response->getAuthEmail($authToken); $this->response->language = $this->getUserLanguage($email); } else { $this->response->userFailedError = "auth"; - $this->response->userFailedEmail = $this->users->getAuthEmail($authToken); - $this->response->userFailedExpire = $this->users->getAuthExpire($authToken); + $this->response->userFailedEmail = $this->response->getAuthEmail($authToken); + $this->response->userFailedExpire = $this->response->getAuthExpire($authToken); } } + $this->yellow->user->set($this->response->userEmail); } return $this->response->isUser(); } @@ -877,7 +879,7 @@ class YellowEdit { $action = $this->yellow->page->getRequest("action"); $expire = $this->yellow->page->getRequest("expire"); $language = $this->yellow->page->getRequest("language"); - if ($this->users->checkActionToken($actionToken, $email, $action, $expire)) { + if ($this->response->checkActionToken($actionToken, $email, $action, $expire)) { $ok = true; $this->response->language = $this->getActionLanguage($language); } else { @@ -892,17 +894,17 @@ class YellowEdit { // Check user failed public function checkUserFailed($scheme, $address, $base, $location, $fileName) { if (!empty($this->response->userFailedError)) { - if ($this->response->userFailedExpire>time() && $this->users->isExisting($this->response->userFailedEmail)) { + if ($this->response->userFailedExpire>time() && $this->yellow->user->isExisting($this->response->userFailedEmail)) { $email = $this->response->userFailedEmail; - $failed = $this->users->getUser($email, "failed")+1; - $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("editUserFile"); - $status = $this->users->save($fileNameUser, $email, array("failed" => $failed)) ? "ok" : "error"; + $failed = $this->yellow->user->getUser("failed", $email)+1; + $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("coreUserFile"); + $status = $this->yellow->user->save($fileNameUser, $email, array("failed" => $failed)) ? "ok" : "error"; if ($status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); if ($failed==$this->yellow->system->get("editBruteForceProtection")) { - $statusBeforeProtection = $this->users->getUser($email, "status"); + $statusBeforeProtection = $this->yellow->user->getUser("status", $email); $statusAfterProtection = ($statusBeforeProtection=="active" || $statusBeforeProtection=="inactive") ? "inactive" : "failed"; if ($status=="ok") { - $status = $this->users->save($fileNameUser, $email, array("status" => $statusAfterProtection)) ? "ok" : "error"; + $status = $this->yellow->user->save($fileNameUser, $email, array("status" => $statusAfterProtection)) ? "ok" : "error"; if ($status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); } if ($status=="ok" && $statusBeforeProtection=="active") { @@ -933,15 +935,15 @@ class YellowEdit { case "change": $statusExpected = "active"; break; case "remove": $statusExpected = "active"; break; } - return $this->users->getUser($email, "status")==$statusExpected ? "ok" : "done"; + return $this->yellow->user->getUser("status", $email)==$statusExpected ? "ok" : "done"; } // Return user account changes - public function getUserAccount($email, $password, $action) { + public function getUserAccount($action, $email, $password) { $status = null; - foreach ($this->yellow->extensions->extensions as $key=>$value) { + foreach ($this->yellow->extension->data as $key=>$value) { if (method_exists($value["obj"], "onEditUserAccount")) { - $status = $value["obj"]->onEditUserAccount($email, $password, $action, $this->users); + $status = $value["obj"]->onEditUserAccount($action, $email, $password); if (!is_null($status)) break; } } @@ -956,14 +958,14 @@ class YellowEdit { // Return user language public function getUserLanguage($email) { - $language = $this->users->getUser($email, "language"); - if (!$this->yellow->text->isLanguage($language)) $language = $this->yellow->system->get("language"); + $language = $this->yellow->user->getUser("language", $email); + if (!$this->yellow->language->isExisting($language)) $language = $this->yellow->system->get("language"); return $language; } // Return action language public function getActionLanguage($language) { - if (!$this->yellow->text->isLanguage($language)) $language = $this->yellow->system->get("language"); + if (!$this->yellow->language->isExisting($language)) $language = $this->yellow->system->get("language"); return $language; } @@ -974,6 +976,17 @@ class YellowEdit { if ($this->yellow->toolbox->getServer("HTTP_ORIGIN")) $origin = $this->yellow->toolbox->getServer("HTTP_ORIGIN"); return $this->yellow->toolbox->getServer("REQUEST_METHOD")==$method && $origin=="$scheme://$address"; } + + // Check if user account is taken + public function isUserAccountTaken($email) { + $taken = false; + if ($this->yellow->user->isExisting($email)) { + $status = $this->yellow->user->getUser("status", $email); + $reserved = strtotime($this->yellow->user->getUser("modified", $email)) + 60*60*24; + if ($status=="active" || $status=="inactive" || $reserved>time()) $taken = true; + } + return $taken; + } } class YellowEditResponse { @@ -995,7 +1008,7 @@ class YellowEditResponse { public function __construct($yellow) { $this->yellow = $yellow; - $this->extension = $yellow->extensions->get("edit"); + $this->extension = $yellow->extension->get("edit"); } // Process page data @@ -1025,7 +1038,7 @@ class YellowEditResponse { $page = new YellowPage($this->yellow); $page->setRequestInformation($scheme, $address, $base, $location, $fileName); $page->parseData($rawData, false, 0); - $this->editContentFile($page, "create"); + $this->editContentFile($page, "create", $this->userEmail); if ($this->yellow->content->find($page->location)) { $page->location = $this->getPageNewLocation($page->rawData, $page->location, $page->get("pageNewLocation")); $page->fileName = $this->getPageNewFile($page->location, $page->fileName, $page->get("published")); @@ -1060,7 +1073,7 @@ class YellowEditResponse { $pageSource = new YellowPage($this->yellow); $pageSource->setRequestInformation($scheme, $address, $base, $location, $fileName); $pageSource->parseData(($rawDataSource), false, 0); - $this->editContentFile($page, "edit"); + $this->editContentFile($page, "edit", $this->userEmail); if ($this->isMetaModified($pageSource, $page) && $page->location!=$this->yellow->content->getHomeLocation($page->location)) { $page->location = $this->getPageNewLocation($page->rawData, $page->location, $page->get("pageNewLocation"), true); $page->fileName = $this->getPageNewFile($page->location, $page->fileName, $page->get("published")); @@ -1082,7 +1095,7 @@ class YellowEditResponse { $page = new YellowPage($this->yellow); $page->setRequestInformation($scheme, $address, $base, $location, $fileName); $page->parseData($rawData, false, 0); - $this->editContentFile($page, "delete"); + $this->editContentFile($page, "delete", $this->userEmail); if (!$this->isUserAccess("delete", $page->location)) { $page->error(500, "Page '".$page->get("title")."' is restricted!"); } @@ -1095,7 +1108,7 @@ class YellowEditResponse { $page = new YellowPage($this->yellow); $page->setRequestInformation($scheme, $address, $base, $location, $fileName); $page->parseData($rawData, false, 200); - $this->yellow->text->setLanguage($page->get("language")); + $this->yellow->language->set($page->get("language")); $class = "page-preview layout-".$page->get("layout"); $output = "<div class=\"".htmlspecialchars($class)."\"><div class=\"content\"><div class=\"main\">"; if ($this->yellow->system->get("editToolbarButtons")!="none") $output .= "<h1>".$page->getHtml("titleContent")."</h1>\n"; @@ -1119,7 +1132,7 @@ class YellowEditResponse { $file->error(500, "Can't write file '$fileNameTemp'!"); } } - $this->editMediaFile($file, "upload"); + $this->editMediaFile($file, "upload", $this->userEmail); $file->location = $this->getFileNewLocation($fileNameShort, $pageLocation, $file->get("fileNewLocation")); $file->fileName = substru($file->location, 1); while (is_file($file->fileName)) { @@ -1138,7 +1151,7 @@ class YellowEditResponse { $file->setRequestInformation($scheme, $address, $base, "/".$fileName, $fileName); $file->parseData(null, false, 0); foreach ($settings as $key=>$value) $file->set($key, $value); - $this->editSystemFile($file, "system"); + $this->editSystemFile($file, "system", $this->userEmail); return $file; } @@ -1165,28 +1178,23 @@ class YellowEditResponse { return $data; } - // Return system data including user information + // Return system data public function getSystemData() { - $data = $this->yellow->system->getData("", "Location"); + $data = array(); + $data["coreServerScheme"] = $this->yellow->system->get("coreServerScheme"); + $data["coreServerAddress"] = $this->yellow->system->get("coreServerAddress"); + $data["coreServerBase"] = $this->yellow->system->get("coreServerBase"); + $data = array_merge($data, $this->yellow->system->getSettings("", "Location")); if ($this->isUser()) { - $data["userEmail"] = $this->userEmail; - $data["userName"] = $this->extension->users->getUser($this->userEmail, "name"); - $data["userLanguage"] = $this->extension->users->getUser($this->userEmail, "language"); - $data["userStatus"] = $this->extension->users->getUser($this->userEmail, "status"); - $data["userHome"] = $this->extension->users->getUser($this->userEmail, "home"); - $data["userAccess"] = $this->extension->users->getUser($this->userEmail, "access"); - $data["coreServerScheme"] = $this->yellow->system->get("coreServerScheme"); - $data["coreServerAddress"] = $this->yellow->system->get("coreServerAddress"); - $data["coreServerBase"] = $this->yellow->system->get("coreServerBase"); $data["coreFileSizeMax"] = $this->yellow->toolbox->getNumberBytes(ini_get("upload_max_filesize")); $data["coreVersion"] = "Datenstrom Yellow ".YellowCore::VERSION; $data["coreExtensions"] = array(); - foreach ($this->yellow->extensions->extensions as $key=>$value) { + foreach ($this->yellow->extension->data as $key=>$value) { $data["coreExtensions"][$key] = $value["type"]; } $data["coreLanguages"] = array(); - foreach ($this->yellow->text->getLanguages() as $language) { - $data["coreLanguages"][$language] = $this->yellow->text->getTextHtml("languageDescription", $language); + foreach ($this->yellow->system->getValues("language") as $language) { + $data["coreLanguages"][$language] = $this->yellow->language->getTextHtml("languageDescription", $language); } $data["editSettingsActions"] = $this->getSettingsActions(); $data["editUploadExtensions"] = $this->yellow->system->get("editUploadExtensions"); @@ -1209,7 +1217,28 @@ class YellowEditResponse { return $data; } - // Return request strings + // Return user data + public function getUserData() { + $data = array(); + if ($this->isUser()) { + $data["email"] = $this->userEmail; + $data["name"] = $this->yellow->user->getUser("name", $this->userEmail); + $data["language"] = $this->yellow->user->getUser("language", $this->userEmail); + $data["status"] = $this->yellow->user->getUser("status", $this->userEmail); + $data["home"] = $this->yellow->user->getUser("home", $this->userEmail); + $data["access"] = $this->yellow->user->getUser("access", $this->userEmail); + } + return $data; + } + + // Return language data + public function getLanguageData() { + $dataLanguage = $this->yellow->language->getSettings("language", "", $this->language); + $dataEdit = $this->yellow->language->getSettings("edit", "", $this->language); + return array_merge($dataLanguage, $dataEdit); + } + + // Return request data public function getRequestData() { $data = array(); foreach ($_REQUEST as $key=>$value) { @@ -1219,13 +1248,6 @@ class YellowEditResponse { return $data; } - // Return text strings - public function getTextData() { - $textLanguage = $this->yellow->text->getData("language", $this->language); - $textEdit = $this->yellow->text->getData("edit", $this->language); - return array_merge($textLanguage, $textEdit); - } - // Return settings actions public function getSettingsActions() { $settingsActions = "account"; @@ -1239,9 +1261,9 @@ class YellowEditResponse { $toolbarButtons = $this->yellow->system->get("editToolbarButtons"); if ($toolbarButtons=="auto") { $toolbarButtons = ""; - if ($this->yellow->extensions->isExisting("markdown")) $toolbarButtons = "format, bold, italic, strikethrough, code, separator, list, link, file"; - if ($this->yellow->extensions->isExisting("emojiawesome")) $toolbarButtons .= ", emojiawesome"; - if ($this->yellow->extensions->isExisting("fontawesome")) $toolbarButtons .= ", fontawesome"; + if ($this->yellow->extension->isExisting("markdown")) $toolbarButtons = "format, bold, italic, strikethrough, code, separator, list, link, file"; + if ($this->yellow->extension->isExisting("emojiawesome")) $toolbarButtons .= ", emojiawesome"; + if ($this->yellow->extension->isExisting("fontawesome")) $toolbarButtons .= ", fontawesome"; $toolbarButtons .= ", status, preview"; } return $toolbarButtons; @@ -1250,8 +1272,8 @@ class YellowEditResponse { // Return status values public function getStatusValues() { $statusValues = ""; - if ($this->yellow->extensions->isExisting("private")) $statusValues .= ", private"; - if ($this->yellow->extensions->isExisting("draft")) $statusValues .= ", draft"; + if ($this->yellow->extension->isExisting("private")) $statusValues .= ", private"; + if ($this->yellow->extension->isExisting("draft")) $statusValues .= ", draft"; $statusValues .= ", unlisted"; return ltrim($statusValues, ", "); } @@ -1271,10 +1293,10 @@ class YellowEditResponse { $statusCode = 200; $updates = 0; $rawData = ""; - if ($this->yellow->extensions->isExisting("update")) { - list($statusCodeCurrent, $dataCurrent) = $this->yellow->extensions->get("update")->getExtensionsVersion(); - list($statusCodeLatest, $dataLatest) = $this->yellow->extensions->get("update")->getExtensionsVersion(true); - list($statusCodeModified, $dataModified) = $this->yellow->extensions->get("update")->getExtensionsModified(); + if ($this->yellow->extension->isExisting("update")) { + list($statusCodeCurrent, $dataCurrent) = $this->yellow->extension->get("update")->getExtensionsVersion(); + list($statusCodeLatest, $dataLatest) = $this->yellow->extension->get("update")->getExtensionsVersion(true); + list($statusCodeModified, $dataModified) = $this->yellow->extension->get("update")->getExtensionsModified(); $statusCode = max($statusCodeCurrent, $statusCodeLatest, $statusCodeModified); foreach ($dataCurrent as $key=>$value) { if (strnatcasecmp($dataCurrent[$key], $dataLatest[$key])<0) { @@ -1285,7 +1307,7 @@ class YellowEditResponse { if ($updates==0) { foreach ($dataCurrent as $key=>$value) { if (isset($dataModified[$key]) && isset($dataLatest[$key])) { - $output = $this->yellow->text->getTextHtml("editUpdateModified", $this->language)." - <a href=\"#\" data-action=\"submit\" data-arguments=\"".$this->yellow->toolbox->normaliseArguments("action:update/extension:$key/option:force")."\">".$this->yellow->text->getTextHtml("editUpdateForce", $this->language)."</a><br />\n"; + $output = $this->yellow->language->getTextHtml("editUpdateModified", $this->language)." - <a href=\"#\" data-action=\"submit\" data-arguments=\"".$this->yellow->toolbox->normaliseArguments("action:update/extension:$key/option:force")."\">".$this->yellow->language->getTextHtml("editUpdateForce", $this->language)."</a><br />\n"; $rawData .= preg_replace("/@extension/i", htmlspecialchars(ucfirst($key)." $dataLatest[$key]"), $output); } } @@ -1297,7 +1319,7 @@ class YellowEditResponse { // Return raw data for generated page public function getRawDataGenerated($page) { $title = $page->get("title"); - $text = $this->yellow->text->getText("editDataGenerated", $page->get("language")); + $text = $this->yellow->language->getText("editDataGenerated", $page->get("language")); return "---\nTitle: $title\n---\n$text"; } @@ -1324,9 +1346,9 @@ class YellowEditResponse { $rawData = preg_replace("/@timestamp/i", time(), $rawData); $rawData = preg_replace("/@datetime/i", date("Y-m-d H:i:s"), $rawData); $rawData = preg_replace("/@date/i", date("Y-m-d"), $rawData); - $rawData = preg_replace("/@usershort/i", strtok($this->extension->users->getUser($this->userEmail, "name"), " "), $rawData); - $rawData = preg_replace("/@username/i", $this->extension->users->getUser($this->userEmail, "name"), $rawData); - $rawData = preg_replace("/@userlanguage/i", $this->extension->users->getUser($this->userEmail, "language"), $rawData); + $rawData = preg_replace("/@usershort/i", strtok($this->yellow->user->getUser("name", $this->userEmail), " "), $rawData); + $rawData = preg_replace("/@username/i", $this->yellow->user->getUser("name", $this->userEmail), $rawData); + $rawData = preg_replace("/@userlanguage/i", $this->yellow->user->getUser("language", $this->userEmail), $rawData); } else { $rawData = "---\nTitle: Page\n---\n"; } @@ -1481,7 +1503,7 @@ class YellowEditResponse { // Return next title public function getTitleNext($rawData) { $titleText = $titleNumber = ""; - if(preg_match("/^(.*?)(\d*)$/", $this->yellow->toolbox->getMetaData($rawData, "title"), $matches)) { + if (preg_match("/^(.*?)(\d*)$/", $this->yellow->toolbox->getMetaData($rawData, "title"), $matches)) { $titleText = $matches[1]; $titleNumber = strempty($matches[2]) ? " 2" : $matches[2]+1; } @@ -1495,7 +1517,7 @@ class YellowEditResponse { $userEmail = $this->yellow->system->get("email"); $userLanguage = $this->extension->getUserLanguage($userEmail); } else { - $userName = $this->extension->users->getUser($email, "name"); + $userName = $this->yellow->user->getUser("name", $email); $userEmail = $email; $userLanguage = $this->extension->getUserLanguage($email); } @@ -1503,22 +1525,22 @@ class YellowEditResponse { $url = "$scheme://$address$base/"; } else { $expire = time() + 60*60*24; - $actionToken = $this->extension->users->createActionToken($email, $action, $expire); + $actionToken = $this->createActionToken($email, $action, $expire); $url = "$scheme://$address$base"."/action:$action/email:$email/expire:$expire/language:$userLanguage/actiontoken:$actionToken/"; } $prefix = "edit".ucfirst($action); - $message = $this->yellow->text->getText("{$prefix}Message", $userLanguage); + $message = $this->yellow->language->getText("{$prefix}Message", $userLanguage); $message = str_replace("\\n", "\r\n", $message); $message = preg_replace("/@useraccount/i", $email, $message); $message = preg_replace("/@usershort/i", strtok($userName, " "), $message); $message = preg_replace("/@username/i", $userName, $message); $message = preg_replace("/@userlanguage/i", $userLanguage, $message); $sitename = $this->yellow->system->get("sitename"); - $footer = $this->yellow->text->getText("editMailFooter", $userLanguage); + $footer = $this->yellow->language->getText("editMailFooter", $userLanguage); $footer = str_replace("\\n", "\r\n", $footer); $footer = preg_replace("/@sitename/i", $sitename, $footer); $mailTo = mb_encode_mimeheader("$userName")." <$userEmail>"; - $mailSubject = mb_encode_mimeheader($this->yellow->text->getText("{$prefix}Subject", $userLanguage)); + $mailSubject = mb_encode_mimeheader($this->yellow->language->getText("{$prefix}Subject", $userLanguage)); $mailHeaders = mb_encode_mimeheader("From: $sitename")." <noreply>\r\n"; $mailHeaders .= mb_encode_mimeheader("X-Request-Url: $scheme://$address$base")."\r\n"; $mailHeaders .= "Mime-Version: 1.0\r\n"; @@ -1530,8 +1552,8 @@ class YellowEditResponse { // Create browser cookies public function createCookies($scheme, $address, $base, $email) { $expire = time() + $this->yellow->system->get("editLoginSessionTimeout"); - $authToken = $this->extension->users->createAuthToken($email, $expire); - $csrfToken = $this->extension->users->createCsrfToken(); + $authToken = $this->createAuthToken($email, $expire); + $csrfToken = $this->createCsrfToken(); setcookie("authtoken", $authToken, $expire, "$base/", "", $scheme=="https", true); setcookie("csrftoken", $csrfToken, $expire, "$base/", "", $scheme=="https", false); } @@ -1542,208 +1564,18 @@ class YellowEditResponse { setcookie("csrftoken", "", 1, "$base/", "", $scheme=="https", false); } - // Change content file - public function editContentFile($page, $action) { - if (!$page->isError()) { - foreach ($this->yellow->extensions->extensions as $key=>$value) { - if (method_exists($value["obj"], "onEditContentFile")) $value["obj"]->onEditContentFile($page, $action); - } - } - } - - // Change media file - public function editMediaFile($file, $action) { - if (!$file->isError()) { - foreach ($this->yellow->extensions->extensions as $key=>$value) { - if (method_exists($value["obj"], "onEditMediaFile")) $value["obj"]->onEditMediaFile($file, $action); - } - } - } - - // Change system file - public function editSystemFile($file, $action) { - if (!$file->isError()) { - foreach ($this->yellow->extensions->extensions as $key=>$value) { - if (method_exists($value["obj"], "onEditSystemFile")) $value["obj"]->onEditSystemFile($file, $action); - } - } - } - - // Check if meta data has been modified - public function isMetaModified($pageSource, $pageOther) { - return substrb($pageSource->rawData, 0, $pageSource->metaDataOffsetBytes) != - substrb($pageOther->rawData, 0, $pageOther->metaDataOffsetBytes); - } - - // Check if active - public function isActive() { - return $this->active; - } - - // Check if user is logged in - public function isUser() { - return !empty($this->userEmail); - } - - // Check if user with access - public function isUserAccess($action, $location = "") { - $userHome = $this->extension->users->getUser($this->userEmail, "home"); - $userAccess = preg_split("/\s*,\s*/", $this->extension->users->getUser($this->userEmail, "access")); - return in_array($action, $userAccess) && (empty($location) || substru($location, 0, strlenu($userHome))==$userHome); - } - - // Check if login with restriction - public function isLoginRestriction() { - return $this->yellow->system->get("editLoginRestriction"); - } -} - -class YellowEditUsers { - public $yellow; // access to API - public $users; // registered users - - public function __construct($yellow) { - $this->yellow = $yellow; - $this->users = array(); - } - - // Load users from file - public function load($fileName) { - if (defined("DEBUG") && DEBUG>=2) echo "YellowEditUsers::load file:$fileName<br/>\n"; - $fileData = $this->yellow->toolbox->readFile($fileName); - foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) { - if (preg_match("/^\#/", $line)) continue; - if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) { - if (lcfirst($matches[1])=="email" && !strempty($matches[2])) { - $email = $matches[2]; - if (defined("DEBUG") && DEBUG>=3) echo "YellowEditUsers::load email:$email<br/>\n"; - } - if (!empty($email) && !empty($matches[1]) && !strempty($matches[2])) { - $this->setUser($email, $matches[1], $matches[2]); - } - } - } - } - - // Save user to file - public function save($fileName, $email, $settings) { - $scan = false; - $fileData = $this->yellow->toolbox->readFile($fileName); - $fileDataStart = $fileDataMiddle = $fileDataEnd = ""; - foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) { - if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) { - if (lcfirst($matches[1])=="email" && !strempty($matches[2])) { - $scan = $matches[2]==$email; - } - } - if (!$scan && empty($fileDataMiddle)) { - $fileDataStart .= $line; - } elseif ($scan) { - $fileDataMiddle .= $line; - } else { - $fileDataEnd .= $line; - } - } - $settingsNew = new YellowDataCollection(); - $settingsNew["email"] = $email; - foreach ($settings as $key=>$value) { - if (!empty($key) && !strempty($value)) { - $this->setUser($email, $key, $value); - $settingsNew[$key] = $value; - } - } - $fileDataSettings = ""; - foreach ($this->yellow->toolbox->getTextLines($fileDataMiddle) as $line) { - if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) { - if (!empty($matches[1]) && isset($settingsNew[$matches[1]])) { - $fileDataSettings .= "$matches[1]: ".$settingsNew[$matches[1]]."\n"; - unset($settingsNew[$matches[1]]); - continue; - } - } - $fileDataSettings .= $line; - } - foreach ($settingsNew as $key=>$value) { - $fileDataSettings .= ucfirst($key).": $value\n"; - } - if (!empty($fileDataSettings)) { - $fileDataSettings = preg_replace("/\n+/", "\n", $fileDataSettings); - if (!empty($fileDataStart) && substr($fileDataStart, -2)!="\n\n") $fileDataSettings = "\n".$fileDataSettings; - if (!empty($fileDataEnd)) $fileDataSettings .= "\n"; - } - $fileDataNew = $fileDataStart.$fileDataSettings.$fileDataEnd; - return $this->yellow->toolbox->createFile($fileName, $fileDataNew); - } - - // Remove user from file - public function remove($fileName, $email) { - $scan = false; - $fileData = $this->yellow->toolbox->readFile($fileName); - $fileDataStart = $fileDataMiddle = $fileDataEnd = ""; - foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) { - if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) { - if (lcfirst($matches[1])=="email" && !strempty($matches[2])) { - $scan = $matches[2]==$email; - } - } - if (!$scan && empty($fileDataMiddle)) { - $fileDataStart .= $line; - } elseif ($scan) { - $fileDataMiddle .= $line; - } else { - $fileDataEnd .= $line; - } - } - if (isset($this->users[$email])) unset($this->users[$email]); - $fileDataNew = rtrim($fileDataStart.$fileDataEnd)."\n"; - return $this->yellow->toolbox->createFile($fileName, $fileDataNew); - } - - // Set user setting - public function setUser($email, $key, $value) { - if (!isset($this->users[$email])) $this->users[$email] = new YellowDataCollection(); - $this->users[$email][$key] = $value; - } - - // Return user setting - public function getUser($email, $key) { - return isset($this->users[$email]) && isset($this->users[$email][$key]) ? $this->users[$email][$key] : ""; - } - - // Check user authentication from email and password - public function checkAuthLogin($email, $password) { - $algorithm = $this->yellow->system->get("editUserHashAlgorithm"); - return $this->isExisting($email) && $this->users[$email]["status"]=="active" && - $this->yellow->toolbox->verifyHash($password, $algorithm, $this->users[$email]["hash"]); - } - - // Check user authentication from tokens - public function checkAuthToken($authToken, $csrfTokenExpected, $csrfTokenReceived, $csrfTokenIrrelevant) { - $signature = "$5y$".substrb($authToken, 0, 96); - $email = $this->getAuthEmail($authToken); - $expire = $this->getAuthExpire($authToken); - return $expire>time() && $this->isExisting($email) && $this->users[$email]["status"]=="active" && - $this->yellow->toolbox->verifyHash($this->users[$email]["hash"]."auth".$expire, "sha256", $signature) && - ($this->yellow->toolbox->verifyToken($csrfTokenExpected, $csrfTokenReceived) || $csrfTokenIrrelevant); - } - - // Check action token - public function checkActionToken($actionToken, $email, $action, $expire) { - $signature = "$5y$".$actionToken; - return $expire>time() && $this->isExisting($email) && - $this->yellow->toolbox->verifyHash($this->users[$email]["hash"].$action.$expire, "sha256", $signature); - } - // Create authentication token public function createAuthToken($email, $expire) { - $signature = $this->yellow->toolbox->createHash($this->users[$email]["hash"]."auth".$expire, "sha256"); + $hash = $this->yellow->user->getUser("hash", $email); + $signature = $this->yellow->toolbox->createHash($hash."auth".$expire, "sha256"); if (empty($signature)) $signature = "padd"."error-hash-algorithm-sha256"; - return substrb($signature, 4).$this->getUser($email, "stamp").dechex($expire); + return substrb($signature, 4).$this->yellow->user->getUser("stamp", $email).dechex($expire); } // Create action token public function createActionToken($email, $action, $expire) { - $signature = $this->yellow->toolbox->createHash($this->users[$email]["hash"].$action.$expire, "sha256"); + $hash = $this->yellow->user->getUser("hash", $email); + $signature = $this->yellow->toolbox->createHash($hash.$action.$expire, "sha256"); if (empty($signature)) $signature = "padd"."error-hash-algorithm-sha256"; return substrb($signature, 4); } @@ -1771,11 +1603,38 @@ class YellowEditUsers { return $stamp; } + // Check user authentication from email and password + public function checkAuthLogin($email, $password) { + $algorithm = $this->yellow->system->get("editUserHashAlgorithm"); + $hash = $this->yellow->user->getUser("hash", $email); + return $this->yellow->user->getUser("status", $email)=="active" && + $this->yellow->toolbox->verifyHash($password, $algorithm, $hash); + } + + // Check user authentication from tokens + public function checkAuthToken($authToken, $csrfTokenExpected, $csrfTokenReceived, $csrfTokenIrrelevant) { + $signature = "$5y$".substrb($authToken, 0, 96); + $email = $this->getAuthEmail($authToken); + $expire = $this->getAuthExpire($authToken); + $hash = $this->yellow->user->getUser("hash", $email); + return $expire>time() && $this->yellow->user->getUser("status", $email)=="active" && + $this->yellow->toolbox->verifyHash($hash."auth".$expire, "sha256", $signature) && + ($this->yellow->toolbox->verifyToken($csrfTokenExpected, $csrfTokenReceived) || $csrfTokenIrrelevant); + } + + // Check action token + public function checkActionToken($actionToken, $email, $action, $expire) { + $signature = "$5y$".$actionToken; + $hash = $this->yellow->user->getUser("hash", $email); + return $expire>time() && $this->yellow->user->isExisting($email) && + $this->yellow->toolbox->verifyHash($hash.$action.$expire, "sha256", $signature); + } + // Return user email from authentication, timing attack safe email lookup public function getAuthEmail($authToken, $stamp = "") { $email = ""; if (empty($stamp)) $stamp = substrb($authToken, 96, 20); - foreach ($this->users as $key=>$value) { + foreach ($this->yellow->user->settings as $key=>$value) { if ($this->yellow->toolbox->verifyToken($value["stamp"], $stamp)) $email = $key; } return $email; @@ -1786,37 +1645,59 @@ class YellowEditUsers { return hexdec(substrb($authToken, 96+20)); } - // Return number of users - public function getNumber() { - return count($this->users); + // Change content file + public function editContentFile($page, $action, $email) { + if (!$page->isError()) { + foreach ($this->yellow->extension->data as $key=>$value) { + if (method_exists($value["obj"], "onEditContentFile")) $value["obj"]->onEditContentFile($page, $action, $email); + } + } } - // Return user data - public function getData() { - $data = array(); - foreach ($this->users as $key=>$value) { - $name = $value["name"]; - if (preg_match("/\s/", $name)) $name = "\"$name\""; - $data[$key] = "$value[email] $name $value[status]"; + // Change media file + public function editMediaFile($file, $action, $email) { + if (!$file->isError()) { + foreach ($this->yellow->extension->data as $key=>$value) { + if (method_exists($value["obj"], "onEditMediaFile")) $value["obj"]->onEditMediaFile($file, $action, $email); + } } - uksort($data, "strnatcasecmp"); - return $data; } - // Check if user is taken - public function isTaken($email) { - $taken = false; - if ($this->isExisting($email)) { - $status = $this->users[$email]["status"]; - $reserved = strtotime($this->users[$email]["modified"]) + 60*60*24; - if ($status=="active" || $status=="inactive" || $reserved>time()) $taken = true; + // Change system file + public function editSystemFile($file, $action, $email) { + if (!$file->isError()) { + foreach ($this->yellow->extension->data as $key=>$value) { + if (method_exists($value["obj"], "onEditSystemFile")) $value["obj"]->onEditSystemFile($file, $action, $email); + } } - return $taken; } - // Check if user exists - public function isExisting($email) { - return isset($this->users[$email]); + // Check if meta data has been modified + public function isMetaModified($pageSource, $pageOther) { + return substrb($pageSource->rawData, 0, $pageSource->metaDataOffsetBytes) != + substrb($pageOther->rawData, 0, $pageOther->metaDataOffsetBytes); + } + + // Check if active + public function isActive() { + return $this->active; + } + + // Check if user is logged in + public function isUser() { + return !empty($this->userEmail); + } + + // Check if user with access + public function isUserAccess($action, $location = "") { + $userHome = $this->yellow->user->getUser("home", $this->userEmail); + $userAccess = preg_split("/\s*,\s*/", $this->yellow->user->getUser("access", $this->userEmail)); + return in_array($action, $userAccess) && (empty($location) || substru($location, 0, strlenu($userHome))==$userHome); + } + + // Check if login with restriction + public function isLoginRestriction() { + return $this->yellow->system->get("editLoginRestriction"); } } diff --git a/system/extensions/image.php b/system/extensions/image.php @@ -2,20 +2,19 @@ // Image extension, https://github.com/datenstrom/yellow-extensions/tree/master/source/image class YellowImage { - const VERSION = "0.8.8"; - const TYPE = "feature"; + const VERSION = "0.8.9"; public $yellow; // access to API // Handle initialisation public function onLoad($yellow) { $this->yellow = $yellow; - $this->yellow->system->setDefault("imageAlt", "Image"); $this->yellow->system->setDefault("imageUploadWidthMax", "1280"); $this->yellow->system->setDefault("imageUploadHeightMax", "1280"); $this->yellow->system->setDefault("imageUploadJpgQuality", "80"); $this->yellow->system->setDefault("imageThumbnailLocation", "/media/thumbnails/"); $this->yellow->system->setDefault("imageThumbnailDirectory", "media/thumbnails/"); $this->yellow->system->setDefault("imageThumbnailJpgQuality", "80"); + $this->yellow->language->setDefault("imageDefaultAlt"); } // Handle page content of shortcut @@ -24,12 +23,12 @@ class YellowImage { if ($name=="image" && $type=="inline") { list($name, $alt, $style, $width, $height) = $this->yellow->toolbox->getTextArguments($text); if (!preg_match("/^\w+:/", $name)) { - if (empty($alt)) $alt = $this->yellow->system->get("imageAlt"); + if (empty($alt)) $alt = $this->yellow->language->getText("imageDefaultAlt"); if (empty($width)) $width = "100%"; if (empty($height)) $height = $width; list($src, $width, $height) = $this->getImageInformation($this->yellow->system->get("coreImageDirectory").$name, $width, $height); } else { - if (empty($alt)) $alt = $this->yellow->system->get("imageAlt"); + if (empty($alt)) $alt = $this->yellow->language->getText("imageDefaultAlt"); $src = $this->yellow->lookup->normaliseUrl("", "", "", $name); $width = $height = 0; } @@ -43,7 +42,7 @@ class YellowImage { } // Handle media file changes - public function onEditMediaFile($file, $action) { + public function onEditMediaFile($file, $action, $email) { if ($action=="upload") { $fileName = $file->fileName; list($widthInput, $heightInput, $type) = $this->yellow->toolbox->detectImageInformation($fileName, $file->get("type")); @@ -83,7 +82,7 @@ class YellowImage { return $statusCode; } - // Return image info, create thumbnail on demand + // Return image information, create thumbnail on demand public function getImageInformation($fileName, $widthOutput, $heightOutput) { $fileNameShort = substru($fileName, strlenu($this->yellow->system->get("coreImageDirectory"))); list($widthInput, $heightInput, $type) = $this->yellow->toolbox->detectImageInformation($fileName); diff --git a/system/extensions/install-blog.zip b/system/extensions/install-blog.zip Binary files differ. diff --git a/system/extensions/install-languages.zip b/system/extensions/install-languages.zip Binary files differ. diff --git a/system/extensions/install-wiki.zip b/system/extensions/install-wiki.zip Binary files differ. diff --git a/system/extensions/install.php b/system/extensions/install.php @@ -2,8 +2,7 @@ // Install extension, https://github.com/datenstrom/yellow class YellowInstall { - const VERSION = "0.8.27"; - const TYPE = "feature"; + const VERSION = "0.8.28"; const PRIORITY = "1"; public $yellow; // access to API @@ -96,7 +95,7 @@ class YellowInstall { public function updateLanguage() { $statusCode = 200; $path = $this->yellow->system->get("coreExtensionDirectory")."install-languages.zip"; - if (is_file($path) && $this->yellow->extensions->isExisting("update")) { + if (is_file($path) && $this->yellow->extension->isExisting("update")) { $zip = new ZipArchive(); if ($zip->open($path)===true) { $languages = $this->detectBrowserLanguages("en, de, fr"); @@ -127,10 +126,10 @@ class YellowInstall { list($dummy1, $entry, $dummy2) = $this->yellow->toolbox->getTextList($matches[2], ",", 3); $fileData = $zip->getFromName($pathBase.basename($entry)); if (preg_match("/^(.*).php$/", basename($entry), $tokens) && in_array($tokens[1], $languagesFound) && !is_file($fileName)) { - $statusCode = $this->yellow->extensions->get("update")->updateExtensionFile($fileName, $fileData, $modified, 0, 0, "create", false, $extension); + $statusCode = $this->yellow->extension->get("update")->updateExtensionFile($fileName, $fileData, $modified, 0, 0, "create", false, $extension); } - if (preg_match("/^(.*)-language\.txt$/", basename($entry), $tokens) && in_array($tokens[1], $languagesFound) && !is_file($fileName)) { - $statusCode = $this->yellow->extensions->get("update")->updateExtensionFile($fileName, $fileData, $modified, 0, 0, "create", false, $extension); + if (preg_match("/^(.*)\.txt$/", basename($entry), $tokens) && in_array($tokens[1], $languagesFound) && !is_file($fileName)) { + $statusCode = $this->yellow->extension->get("update")->updateExtensionFile($fileName, $fileData, $modified, 0, 0, "create", false, $extension); $this->yellow->log($statusCode==200 ? "info" : "error", "Install extension '".ucfirst($tokens[1])." $version'"); } if ($statusCode!=200) break; @@ -138,7 +137,7 @@ class YellowInstall { } $zip->close(); if ($statusCode==200) { - $this->yellow->text->load($this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("coreLanguageFile"), ""); + $this->yellow->language->load($this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("coreLanguageFile")); } } else { $statusCode = 500; @@ -152,11 +151,11 @@ class YellowInstall { public function updateExtension($extension) { $statusCode = 200; $path = $this->yellow->system->get("coreExtensionDirectory"); - if (!empty($extension) && $this->yellow->extensions->isExisting("update")) { + if (!empty($extension) && $this->yellow->extension->isExisting("update")) { foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.zip$/", true, false) as $entry) { if (preg_match("/^install-(.*?)\./", basename($entry), $matches)) { if (strtoloweru($matches[1])==strtoloweru($extension)) { - $statusCode = $this->yellow->extensions->get("update")->updateExtensionArchive($entry, "install"); + $statusCode = $this->yellow->extension->get("update")->updateExtensionArchive($entry, "install"); break; } } @@ -168,21 +167,21 @@ class YellowInstall { // Update user public function updateUser($email, $password, $name, $language) { $statusCode = 200; - if (!empty($email) && !empty($password) && $this->yellow->extensions->isExisting("edit")) { + if (!empty($email) && !empty($password) && $this->yellow->extension->isExisting("edit")) { if (empty($name)) $name = $this->yellow->system->get("sitename"); - $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("editUserFile"); + $fileNameUser = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("coreUserFile"); $settings = array( "name" => $name, "language" => $language, "home" => "/", "access" => "create, edit, delete, upload, system, update", - "hash" => $this->yellow->extensions->get("edit")->users->createHash($password), - "stamp" => $this->yellow->extensions->get("edit")->users->createStamp(), + "hash" => $this->yellow->extension->get("edit")->response->createHash($password), + "stamp" => $this->yellow->extension->get("edit")->response->createStamp(), "pending" => "none", "failed" => "0", "modified" => time(), "status" => "active"); - if (!$this->yellow->extensions->get("edit")->users->save($fileNameUser, $email, $settings)) { + if (!$this->yellow->user->save($fileNameUser, $email, $settings)) { $statusCode = 500; $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); } @@ -197,10 +196,10 @@ class YellowInstall { $fileName = $this->yellow->lookup->findFileFromLocation($location); $fileData = str_replace("\r\n", "\n", $this->yellow->toolbox->readFile($fileName)); if (!empty($fileData) && $language!="en") { - $titleOld = "Title: ".$this->yellow->text->getText("{$name}Title", "en"); - $titleNew = "Title: ".$this->yellow->text->getText("{$name}Title", $language); - $textOld = str_replace("\\n", "\n", $this->yellow->text->getText("{$name}Text", "en")); - $textNew = str_replace("\\n", "\n", $this->yellow->text->getText("{$name}Text", $language)); + $titleOld = "Title: ".$this->yellow->language->getText("{$name}Title", "en"); + $titleNew = "Title: ".$this->yellow->language->getText("{$name}Title", $language); + $textOld = str_replace("\\n", "\n", $this->yellow->language->getText("{$name}Text", "en")); + $textNew = str_replace("\\n", "\n", $this->yellow->language->getText("{$name}Text", $language)); $fileData = str_replace($titleOld, $titleNew, $fileData); $fileData = str_replace($textOld, $textNew, $fileData); if (!$this->yellow->toolbox->createFile($fileName, $fileData)) { @@ -214,12 +213,11 @@ class YellowInstall { // Update text settings public function updateText($language) { $statusCode = 200; - $fileName = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("coreTextFile"); + $fileName = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("coreLanguageFile"); $fileData = $this->yellow->toolbox->readFile($fileName); if (count($this->yellow->toolbox->getTextLines($fileData))<4) { $fileData .= "Language: $language\n"; - $fileData .= "CoreDateFormatMedium: ".$this->yellow->text->getText("coreDateFormatMedium", $language)."\n"; - $fileData .= "picture.jpg: ".$this->yellow->text->getText("installExampleImage", $language)."\n"; + $fileData .= "media/images/photo.jpg: ".$this->yellow->language->getText("installExampleImage", $language)."\n"; if (!$this->yellow->toolbox->createFile($fileName, $fileData)) { $statusCode = 500; $this->yellow->page->error($statusCode, "Can't write file '$fileName'!"); @@ -257,7 +255,7 @@ class YellowInstall { $statusCode = 500; $this->yellow->page->error($statusCode, "Can't delete file '$path'!"); } - if ($statusCode==200) unset($this->yellow->extensions->extensions["install"]); + if ($statusCode==200) unset($this->yellow->extension->data["install"]); return $statusCode; } @@ -281,7 +279,7 @@ class YellowInstall { public function checkServerRewrite() { $curlHandle = curl_init(); list($scheme, $address, $base) = $this->yellow->getRequestInformation(); - $location = $this->yellow->system->get("coreResourceLocation").$this->yellow->lookup->normaliseName($this->yellow->system->get("theme")).".css"; + $location = $this->yellow->system->get("coreThemeLocation").$this->yellow->lookup->normaliseName($this->yellow->system->get("theme")).".css"; $url = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); curl_setopt($curlHandle, CURLOPT_URL, $url); curl_setopt($curlHandle, CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; YellowCore/".YellowCore::VERSION).")"; @@ -328,30 +326,31 @@ class YellowInstall { // Return raw data for install page public function getRawDataInstall() { - $language = $this->yellow->toolbox->detectBrowserLanguage($this->yellow->text->getLanguages(), $this->yellow->system->get("language")); - $this->yellow->text->setLanguage($language); - $rawData = "---\nTitle:".$this->yellow->text->get("installTitle")."\nLanguage:$language\nNavigation:navigation\nHeader:none\nFooter:none\nSidebar:none\n---\n"; + $languages = $this->yellow->system->getValues("language"); + $language = $this->yellow->toolbox->detectBrowserLanguage($languages, $this->yellow->system->get("language")); + $this->yellow->language->set($language); + $rawData = "---\nTitle:".$this->yellow->language->getText("installTitle")."\nLanguage:$language\nNavigation:navigation\nHeader:none\nFooter:none\nSidebar:none\n---\n"; $rawData .= "<form class=\"install-form\" action=\"".$this->yellow->page->getLocation(true)."\" method=\"post\">\n"; - $rawData .= "<p><label for=\"author\">".$this->yellow->text->get("editSignupName")."</label><br /><input class=\"form-control\" type=\"text\" maxlength=\"64\" name=\"author\" id=\"author\" value=\"\"></p>\n"; - $rawData .= "<p><label for=\"email\">".$this->yellow->text->get("editSignupEmail")."</label><br /><input class=\"form-control\" type=\"text\" maxlength=\"64\" name=\"email\" id=\"email\" value=\"\"></p>\n"; - $rawData .= "<p><label for=\"password\">".$this->yellow->text->get("editSignupPassword")."</label><br /><input class=\"form-control\" type=\"password\" maxlength=\"64\" name=\"password\" id=\"password\" value=\"\"></p>\n"; - if (count($this->yellow->text->getLanguages())>1) { + $rawData .= "<p><label for=\"author\">".$this->yellow->language->getText("editSignupName")."</label><br /><input class=\"form-control\" type=\"text\" maxlength=\"64\" name=\"author\" id=\"author\" value=\"\"></p>\n"; + $rawData .= "<p><label for=\"email\">".$this->yellow->language->getText("editSignupEmail")."</label><br /><input class=\"form-control\" type=\"text\" maxlength=\"64\" name=\"email\" id=\"email\" value=\"\"></p>\n"; + $rawData .= "<p><label for=\"password\">".$this->yellow->language->getText("editSignupPassword")."</label><br /><input class=\"form-control\" type=\"password\" maxlength=\"64\" name=\"password\" id=\"password\" value=\"\"></p>\n"; + if (count($languages)>1) { $rawData .= "<p>"; - foreach ($this->yellow->text->getLanguages() as $language) { - $checked = $language==$this->yellow->text->language ? " checked=\"checked\"" : ""; - $rawData .= "<label for=\"$language\"><input type=\"radio\" name=\"language\" id=\"$language\" value=\"$language\"$checked> ".$this->yellow->text->getTextHtml("languageDescription", $language)."</label><br />"; + foreach ($languages as $language) { + $checked = $language==$this->yellow->language->language ? " checked=\"checked\"" : ""; + $rawData .= "<label for=\"$language\"><input type=\"radio\" name=\"language\" id=\"$language\" value=\"$language\"$checked> ".$this->yellow->language->getTextHtml("languageDescription", $language)."</label><br />"; } $rawData .= "</p>\n"; } if (count($this->getExtensionsInstall())>1) { - $rawData .= "<p>".$this->yellow->text->get("installExtension")."<p>"; + $rawData .= "<p>".$this->yellow->language->getText("installExtension")."<p>"; foreach ($this->getExtensionsInstall() as $extension) { $checked = $extension=="website" ? " checked=\"checked\"" : ""; - $rawData .= "<label for=\"$extension\"><input type=\"radio\" name=\"extension\" id=\"$extension\" value=\"$extension\"$checked> ".$this->yellow->text->getHtml("installExtension".ucfirst($extension))."</label><br />"; + $rawData .= "<label for=\"$extension\"><input type=\"radio\" name=\"extension\" id=\"$extension\" value=\"$extension\"$checked> ".$this->yellow->language->getTextHtml("installExtension".ucfirst($extension))."</label><br />"; } $rawData .= "</p>\n"; } - $rawData .= "<input class=\"btn\" type=\"submit\" value=\"".$this->yellow->text->get("editOkButton")."\" />\n"; + $rawData .= "<input class=\"btn\" type=\"submit\" value=\"".$this->yellow->language->getText("editOkButton")."\" />\n"; $rawData .= "<input type=\"hidden\" name=\"status\" value=\"install\" />\n"; $rawData .= "</form>\n"; return $rawData; diff --git a/system/extensions/markdown.php b/system/extensions/markdown.php @@ -2,8 +2,7 @@ // Markdown extension, https://github.com/datenstrom/yellow-extensions/tree/master/source/markdown class YellowMarkdown { - const VERSION = "0.8.14"; - const TYPE = "feature"; + const VERSION = "0.8.15"; public $yellow; // access to API // Handle initialisation diff --git a/system/extensions/meta.php b/system/extensions/meta.php @@ -2,14 +2,13 @@ // Meta extension, https://github.com/datenstrom/yellow-extensions/tree/master/source/meta class YellowMeta { - const VERSION = "0.8.12"; - const TYPE = "feature"; + const VERSION = "0.8.14"; public $yellow; // access to API // Handle initialisation public function onLoad($yellow) { $this->yellow = $yellow; - $this->yellow->system->setDefault("metaDefaultImage", "icon"); + $this->yellow->system->setDefault("metaDefaultImage", "favicon"); } // Handle page extra data @@ -17,7 +16,7 @@ class YellowMeta { $output = null; if ($name=="header" && !$page->isError()) { list($imageUrl, $imageAlt) = $this->getImageInformation($page); - $locale = $this->yellow->text->getText("languageLocale", $page->get("language")); + $locale = $this->yellow->language->getText("languageLocale", $page->get("language")); $output .= "<meta property=\"og:url\" content=\"".htmlspecialchars($page->getUrl().$this->yellow->toolbox->getLocationArguments())."\" />\n"; $output .= "<meta property=\"og:locale\" content=\"".htmlspecialchars($locale)."\" />\n"; $output .= "<meta property=\"og:type\" content=\"website\" />\n"; @@ -52,8 +51,8 @@ class YellowMeta { $alt = $page->isExisting("imageAlt") ? $page->get("imageAlt") : $page->get("title"); } if (!preg_match("/^\w+:/", $name)) { - $location = $name!="icon" ? $this->yellow->system->get("coreImageLocation").$name : - $this->yellow->system->get("coreResourceLocation").$page->get("theme")."-icon.png"; + $location = $name!="favicon" ? $this->yellow->system->get("coreImageLocation").$name : + $this->yellow->system->get("coreThemeLocation").$this->yellow->lookup->normaliseName($page->get("theme")).".png"; $url = $this->yellow->lookup->normaliseUrl( $this->yellow->system->get("coreServerScheme"), $this->yellow->system->get("coreServerAddress"), diff --git a/system/extensions/stockholm.php b/system/extensions/stockholm.php @@ -2,8 +2,7 @@ // Stockholm extension, https://github.com/datenstrom/yellow-extensions/tree/master/source/stockholm class YellowStockholm { - const VERSION = "0.8.8"; - const TYPE = "theme"; + const VERSION = "0.8.9"; public $yellow; // access to API // Handle initialisation @@ -17,7 +16,7 @@ class YellowStockholm { if ($action=="install") { $this->yellow->system->save($fileName, array("theme" => "stockholm")); } elseif ($action=="uninstall" && $this->yellow->system->get("theme")=="stockholm") { - $theme = reset(array_diff($this->yellow->extensions->getExtensions("theme"), array("stockholm"))); + $theme = reset(array_diff($this->yellow->system->getValues("theme"), array("stockholm"))); $this->yellow->system->save($fileName, array("theme" => $theme)); } } diff --git a/system/extensions/update.php b/system/extensions/update.php @@ -2,8 +2,7 @@ // Update extension, https://github.com/datenstrom/yellow-extensions/tree/master/source/update class YellowUpdate { - const VERSION = "0.8.23"; - const TYPE = "feature"; + const VERSION = "0.8.24"; const PRIORITY = "2"; public $yellow; // access to API public $updates; // number of updates @@ -60,7 +59,9 @@ class YellowUpdate { if ($this->yellow->system->isExisting("multiLanguageMode")) { $coreMultiLanguageMode = $this->yellow->system->get("multiLanguageMode"); $fileName = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("coreSystemFile"); - $this->yellow->system->save($fileName, array("coreMultiLanguageMode" => $coreMultiLanguageMode)); + if ($this->yellow->system->save($fileName, array("coreMultiLanguageMode" => $coreMultiLanguageMode))) { + $this->yellow->log("error", "Can't write file '$fileName'!"); + } $path = $this->yellow->system->get("coreContentDirectory"); foreach ($this->yellow->toolbox->getDirectoryEntriesRecursive($path, "/^.*\.md$/", true, false) as $entry) { $fileData = $fileDataNew = $this->yellow->toolbox->readFile($entry); @@ -88,6 +89,39 @@ class YellowUpdate { } } } + if ($action=="update") { // TODO: remove later, converts old language settings + if ($this->yellow->system->isExisting("coreTextFile")) { + $fileNameSource = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("coreTextFile"); + $fileNameDestination = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("coreLanguageFile"); + if (is_file($fileNameSource) && !is_file($fileNameDestination)) { + if (!$this->yellow->toolbox->renameFile($fileNameSource, $fileNameDestination)) { + $this->yellow->log("error", "Can't write file '$fileNameDestination'!"); + } + } + $imageDirectoryLength = strlenu($this->yellow->system->get("coreImageDirectory")); + $fileData = $this->yellow->toolbox->readFile($fileNameDestination); + $fileDataNew = ""; + foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) { + if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) { + if (strposu($matches[1], ".") && + substru($matches[1], 0, $imageDirectoryLength)!=$this->yellow->system->get("coreImageDirectory")) { + $line = $this->yellow->system->get("coreImageDirectory").$line; + } + } + $fileDataNew .= $line; + } + if ($fileData!=$fileDataNew && !$this->yellow->toolbox->createFile($fileNameDestination, $fileDataNew)) { + $this->yellow->log("error", "Can't write file '$fileNameDestination'!"); + } + $path = $this->yellow->system->get("coreExtensionDirectory"); + foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/.*-language\.txt/", false, false) as $entry) { + $entryShort = str_replace("-language.txt", ".txt", $entry); + if (is_file($entryShort) && !$this->yellow->toolbox->deleteFile($entry, $this->yellow->system->get("coreTrashDirectory"))) { + $this->yellow->log("error", "Can't delete file '$entry'!"); + } + } + } + } if ($action=="update") { // TODO: remove later, converts old layout files if ($this->yellow->system->isExisting("coreLayoutDir")) { $path = $this->yellow->system->get("coreLayoutDir"); @@ -102,17 +136,34 @@ class YellowUpdate { $fileDataNew = str_replace("\$this->yellow->page->get(\"header\")", "\"header\"", $fileDataNew); $fileDataNew = str_replace("\$this->yellow->page->get(\"sidebar\")", "\"sidebar\"", $fileDataNew); $fileDataNew = str_replace("\$this->yellow->page->get(\"footer\")", "\"footer\"", $fileDataNew); + $fileDataNew = str_replace("<link rel=\"icon\" type=\"image/png\" href=\"<?php echo \$resourceLocation.\$this->yellow->page->getHtml(\"theme\").\"-icon.png\" ?>\" />", "<?php /* Add icons and files here */ ?>", $fileDataNew); if ($fileData!=$fileDataNew && !$this->yellow->toolbox->createFile($entry, $fileDataNew)) { $this->yellow->log("error", "Can't write file '$entry'!"); } } } } + if ($action=="update") { // TODO: remove later, converts old resource files + if ($this->yellow->system->isExisting("coreResourceDir")) { + $pathSource = "system/resources/"; + $pathDestination = $this->yellow->system->get("coreThemeDirectory"); + if (is_dir($pathSource) && !is_dir($pathDestination)) { + if (!$this->yellow->toolbox->renameDirectory($pathSource, $pathDestination)) { + $this->yellow->log("error", "Can't write directory '$pathDestination'!"); + } + } + foreach ($this->yellow->toolbox->getDirectoryEntriesRecursive($pathDestination, "/^.*\-icon\.png$/", true, false) as $entry) { + $entryShort = str_replace("-icon.png", ".png", $entry); + if (!is_file($entryShort) && !$this->yellow->toolbox->copyFile($entry, $entryShort)) { + $this->yellow->log("error", "Can't write file '$entryShort'!"); + } + } + } + } if ($action=="update") { // TODO: remove later, converts old commandline if ($this->yellow->system->isExisting("coreStaticDir")) { $fileName = "yellow.php"; $fileData = $fileDataNew = $this->yellow->toolbox->readFile($fileName); - $fileDataNew = str_replace("make websites", "make small websites", $fileDataNew); $fileDataNew = str_replace("command(\$argv[1], \$argv[2], \$argv[3], \$argv[4], \$argv[5], \$argv[6], \$argv[7])", "command()", $fileDataNew); if ($fileData!=$fileDataNew && !$this->yellow->toolbox->createFile($fileName, $fileDataNew)) { $this->yellow->log("error", "Can't write file '$fileName'!"); @@ -123,42 +174,93 @@ class YellowUpdate { if ($this->yellow->system->get("updateNotification")!="none") { foreach (explode(",", $this->yellow->system->get("updateNotification")) as $token) { list($extension, $action) = $this->yellow->toolbox->getTextList($token, "/", 2); - if ($this->yellow->extensions->isExisting($extension) && ($action!="startup" && $action!="uninstall")) { - $value = $this->yellow->extensions->extensions[$extension]; + if ($this->yellow->extension->isExisting($extension) && ($action!="startup" && $action!="uninstall")) { + $value = $this->yellow->extension->data[$extension]; if (method_exists($value["obj"], "onUpdate")) $value["obj"]->onUpdate($action); } } $fileName = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("coreSystemFile"); - $this->yellow->system->save($fileName, array("updateNotification" => "none")); - $fileData = $this->yellow->toolbox->readFile($fileName); - $fileDataHeader = $fileDataSettings = $fileDataFooter = ""; - $settings = new YellowDataCollection(); - $settings->exchangeArray($this->yellow->system->settingsDefaults->getArrayCopy()); - foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) { - preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); - if (empty($fileDataHeader) && preg_match("/^\#/", $line)) { - $fileDataHeader = $line; - } elseif (!empty($matches[1]) && isset($settings[$matches[1]])) { - $settings[$matches[1]] = $matches[2]; - } elseif (!empty($matches[1]) && substru($matches[1], 0, 1)!="#") { - $fileDataFooter .= "# $line"; - } elseif (!empty($matches[1])) { - $fileDataFooter .= $line; - } + if (!$this->yellow->system->save($fileName, array("updateNotification" => "none"))) { + $this->yellow->log("error", "Can't write file '$fileName'!"); } - unset($settings["coreSystemFile"]); - foreach ($settings as $key=>$value) { - if ($key=="coreStaticUrl") $fileDataSettings .= "\n"; - $fileDataSettings .= ucfirst($key).(strempty($value) ? ":\n" : ": $value\n"); + $this->updateSystemSettings(); + $this->updateLanguageSettings(); + } + } + } + + // Update system settings after update notification + public function updateSystemSettings() { + $fileName = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("coreSystemFile"); + $fileData = $this->yellow->toolbox->readFile($fileName); + $fileDataStart = $fileDataSettings = $fileDataComments = ""; + $settings = new YellowDataCollection(); + $settings->exchangeArray($this->yellow->system->settingsDefaults->getArrayCopy()); + foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) { + preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); + if (empty($fileDataStart) && preg_match("/^\#/", $line)) { + $fileDataStart = $line; + } elseif (!empty($matches[1]) && isset($settings[$matches[1]])) { + $settings[$matches[1]] = $matches[2]; + } elseif (!empty($matches[1]) && substru($matches[1], 0, 1)!="#") { + $fileDataComments .= "# $line"; + } elseif (!empty($matches[1])) { + $fileDataComments .= $line; + } + } + unset($settings["coreSystemFile"]); + foreach ($settings as $key=>$value) { + $fileDataSettings .= ucfirst($key).(strempty($value) ? ":\n" : ": $value\n"); + } + if (!empty($fileDataStart)) $fileDataStart .= "\n"; + if (!empty($fileDataComments)) $fileDataSettings .= "\n"; + $fileDataNew = $fileDataStart.$fileDataSettings.$fileDataComments; + if ($fileData!=$fileDataNew && !$this->yellow->toolbox->createFile($fileName, $fileDataNew)) { + $this->yellow->log("error", "Can't write file '$fileName'!"); + } + } + + // Update language settings after update notification + public function updateLanguageSettings() { + $fileName = $this->yellow->system->get("coreSettingDirectory").$this->yellow->system->get("coreLanguageFile"); + $fileData = $this->yellow->toolbox->readFile($fileName); + $fileDataStart = $fileDataSettings = $language = ""; + $settings = new YellowDataCollection(); + foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) { + preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); + if (empty($fileDataStart) && preg_match("/^\#/", $line)) { + $fileDataStart = $line; + } elseif (!empty($matches[1]) && !empty($matches[2])) { + if (lcfirst($matches[1])=="language" && !strempty($matches[2])) { + if (!empty($settings)) { + if (!empty($fileDataSettings)) $fileDataSettings .= "\n"; + foreach ($settings as $key=>$value) { + $fileDataSettings .= (strposu($key, "/") ? $key : ucfirst($key)).": $value\n"; + } + } + $language = $matches[2]; + $settings = new YellowDataCollection(); + $settings["language"] = $language; + foreach ($this->yellow->language->settingsDefaults as $key=>$value) { + if ($this->yellow->language->isText($key, $language)) { + $settings[$key] = $this->yellow->language->getText($key, $language); + } + } } - if (!empty($fileDataHeader)) $fileDataHeader .= "\n"; - if (!empty($fileDataFooter)) $fileDataSettings .= "\n"; - $fileDataNew = $fileDataHeader.$fileDataSettings.$fileDataFooter; - if ($fileData!=$fileDataNew && !$this->yellow->toolbox->createFile($fileName, $fileDataNew)) { - $this->yellow->log("error", "Can't write file '$fileName'!"); + if (!empty($language)) { + $settings[$matches[1]] = $matches[2]; } } } + if (!empty($fileDataStart)) $fileDataStart .= "\n"; + if (!empty($fileDataSettings)) $fileDataSettings .= "\n"; + foreach ($settings as $key=>$value) { + $fileDataSettings .= (strposu($key, "/") ? $key : ucfirst($key)).": $value\n"; + } + $fileDataNew = $fileDataStart.$fileDataSettings; + if ($fileData!=$fileDataNew && !$this->yellow->toolbox->createFile($fileName, $fileDataNew)) { + $this->yellow->log("error", "Can't write file '$fileName'!"); + } } // Process command to show website version and updates @@ -262,8 +364,8 @@ class YellowUpdate { // Process update notification public function processUpdateNotification($extension, $action) { $statusCode = 200; - if ($this->yellow->extensions->isExisting($extension) && $action=="uninstall") { - $value = $this->yellow->extensions->extensions[$extension]; + if ($this->yellow->extension->isExisting($extension) && $action=="uninstall") { + $value = $this->yellow->extension->data[$extension]; if (method_exists($value["obj"], "onUpdate")) $value["obj"]->onUpdate($action); } $updateNotification = $this->yellow->system->get("updateNotification"); @@ -507,7 +609,7 @@ class YellowUpdate { if (preg_match("/create/i", $flags) && !is_file($fileName) && !empty($fileData)) $create = true; if (preg_match("/update/i", $flags) && is_file($fileName) && !empty($fileData)) $update = true; if (preg_match("/delete/i", $flags) && is_file($fileName)) $delete = true; - if (preg_match("/optional/i", $flags) && $this->yellow->extensions->isExisting($extension)) $create = $update = $delete = false; + if (preg_match("/optional/i", $flags) && $this->yellow->extension->isExisting($extension)) $create = $update = $delete = false; if (preg_match("/careful/i", $flags) && is_file($fileName) && $lastModified!=$lastPublished && !$force) $update = false; if ($create) { if (!$this->yellow->toolbox->createFile($fileName, $fileData, true) || @@ -573,7 +675,7 @@ class YellowUpdate { $statusCode = max($statusCode, $this->removeExtensionsFile($fileName, $key)); } $statusCode = max($statusCode, $this->processUpdateNotification($key, "uninstall")); - $version = $this->yellow->extensions->isExisting($key) ? $this->yellow->extensions->extensions[$key]["version"] : ""; + $version = $this->yellow->extension->isExisting($key) ? $this->yellow->extension->data[$key]["version"] : ""; $this->yellow->log($statusCode==200 ? "info" : "error", "Uninstall extension '".ucfirst($key)." $version'"); ++$this->updates; } @@ -614,8 +716,11 @@ class YellowUpdate { } } else { $statusCode = 200; - $data = $this->yellow->extensions->getData(); + foreach ($this->yellow->extension->data as $key=>$value) { + $data[$key] = $value["version"]; + } } + uksort($data, "strnatcasecmp"); return array($statusCode, $data); } @@ -644,8 +749,8 @@ class YellowUpdate { // Return extensions modified files public function getExtensionsModified() { $data = array(); - $dataCurrent = $this->yellow->extensions->getData(); $url = $this->yellow->system->get("updateExtensionUrl")."/raw/master/".$this->yellow->system->get("updateWaffleFile"); + list($statusCode, $dataCurrent) = $this->getExtensionsVersion(); list($statusCode, $fileData) = $this->getExtensionFile($url); if ($statusCode==200) { $extension = ""; diff --git a/system/layouts/header.html b/system/layouts/header.html @@ -7,8 +7,6 @@ <meta name="author" content="<?php echo $this->yellow->page->getHtml("author") ?>" /> <meta name="generator" content="Datenstrom Yellow" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> -<?php $resourceLocation = $this->yellow->system->get("coreServerBase").$this->yellow->system->get("coreResourceLocation") ?> -<link rel="icon" type="image/png" href="<?php echo $resourceLocation.$this->yellow->page->getHtml("theme")."-icon.png" ?>" /> <?php echo $this->yellow->page->getExtra("header") ?> </head> <body> diff --git a/system/settings/language.ini b/system/settings/language.ini @@ -0,0 +1,2 @@ +# Datenstrom Yellow language settings + diff --git a/system/settings/system.ini b/system/settings/system.ini @@ -8,7 +8,6 @@ Layout: default Theme: stockholm Parser: markdown Status: public - CoreStaticUrl: CoreStaticDefaultFile: index.html CoreStaticErrorFile: 404.html @@ -22,14 +21,14 @@ CoreMediaLocation: /media/ CoreDownloadLocation: /media/downloads/ CoreImageLocation: /media/images/ CoreExtensionLocation: /media/extensions/ -CoreResourceLocation: /media/resources/ +CoreThemeLocation: /media/themes/ CoreMediaDirectory: media/ CoreDownloadDirectory: media/downloads/ CoreImageDirectory: media/images/ CoreSystemDirectory: system/ CoreExtensionDirectory: system/extensions/ CoreLayoutDirectory: system/layouts/ -CoreResourceDirectory: system/resources/ +CoreThemeDirectory: system/themes/ CoreSettingDirectory: system/settings/ CoreContentDirectory: content/ CoreContentRootDirectory: default/ @@ -39,7 +38,8 @@ CoreContentDefaultFile: page.md CoreContentErrorFile: page-error-(.*).md CoreContentExtension: .md CoreDownloadExtension: .download -CoreTextFile: text.ini +CoreUserFile: user.ini +CoreLanguageFile: language.ini CoreLogFile: yellow.log UpdateExtensionUrl: https://github.com/datenstrom/yellow-extensions UpdateExtensionDirectory: /Users/yourname/Documents/GitHub/ @@ -54,7 +54,6 @@ EditKeyboardShortcuts: ctrl+b bold, ctrl+i italic, ctrl+k strikethrough, ctrl+e EditToolbarButtons: auto EditEndOfLine: auto EditNewFile: page-new-(.*).md -EditUserFile: user.ini EditUserPasswordMinLength: 8 EditUserHashAlgorithm: bcrypt EditUserHashCost: 10 @@ -63,11 +62,10 @@ EditUserAccess: create, edit, delete, upload EditLoginRestriction: 0 EditLoginSessionTimeout: 2592000 EditBruteForceProtection: 25 -ImageAlt: Image ImageUploadWidthMax: 1280 ImageUploadHeightMax: 1280 ImageUploadJpgQuality: 80 ImageThumbnailLocation: /media/thumbnails/ ImageThumbnailDirectory: media/thumbnails/ ImageThumbnailJpgQuality: 80 -MetaDefaultImage: icon +MetaDefaultImage: favicon diff --git a/system/settings/text.ini b/system/settings/text.ini @@ -1,2 +0,0 @@ -# Datenstrom Yellow text settings - diff --git a/system/settings/user.ini b/system/settings/user.ini @@ -1,2 +1,2 @@ -# Datenstrom Yellow user accounts +# Datenstrom Yellow user settings diff --git a/system/resources/stockholm-opensans-bold.woff b/system/themes/stockholm-opensans-bold.woff Binary files differ. diff --git a/system/resources/stockholm-opensans-license.txt b/system/themes/stockholm-opensans-license.txt diff --git a/system/resources/stockholm-opensans-light.woff b/system/themes/stockholm-opensans-light.woff Binary files differ. diff --git a/system/resources/stockholm-opensans-regular.woff b/system/themes/stockholm-opensans-regular.woff Binary files differ. diff --git a/system/resources/stockholm.css b/system/themes/stockholm.css diff --git a/system/resources/stockholm-icon.png b/system/themes/stockholm.png Binary files differ.