mikuli.cz

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

commit 2f213b10bdad432a2e842959317acaed3f654131
parent b2d0812f5b7ee15eac2e8687d76927c61a2feee2
Author: markseu <mark2011@mayberg.se>
Date:   Sat, 11 Aug 2018 00:23:50 +0200

Updated coding standard, there is no spoon

Diffstat:
Msystem/plugins/command.php | 1205+++++++++++++++++++++++++++++++++++++------------------------------------------
Msystem/plugins/core.php | 6527+++++++++++++++++++++++++++++++++++++------------------------------------------
Msystem/plugins/edit.css | 640++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Msystem/plugins/edit.js | 2736+++++++++++++++++++++++++++++++++++++------------------------------------------
Msystem/plugins/edit.php | 3740+++++++++++++++++++++++++++++++++++++------------------------------------------
Msystem/plugins/image.php | 332++++++++++++++++++++++++++++++++++++-------------------------------------------
Msystem/plugins/language.php | 6++----
Msystem/plugins/markdown.php | 307+++++++++++++++++++++++++++++++++++++------------------------------------------
Msystem/plugins/update-blog.installation | 0
Msystem/plugins/update-wiki.installation | 0
Msystem/plugins/update.php | 1511++++++++++++++++++++++++++++++++++++-------------------------------------------
Msystem/themes/assets/flatsite.css | 648+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Msystem/themes/assets/flatsite.php | 6++----
13 files changed, 8570 insertions(+), 9088 deletions(-)

diff --git a/system/plugins/command.php b/system/plugins/command.php @@ -3,657 +3,568 @@ // Copyright (c) 2013-2018 Datenstrom, https://datenstrom.se // This file may be used and distributed under the terms of the public license. -class YellowCommand -{ - const VERSION = "0.7.8"; - var $yellow; //access to API - var $files; //number of files - var $links; //number of links - var $errors; //number of errors - var $locationsArgs; //locations with location arguments detected - var $locationsArgsPagination; //locations with pagination arguments detected - - // Handle initialisation - function onLoad($yellow) - { - $this->yellow = $yellow; - } - - // Handle command - function onCommand($args) - { - list($command) = $args; - switch($command) - { - case "": $statusCode = $this->helpCommand(); break; - case "build": $statusCode = $this->buildCommand($args); break; - case "check": $statusCode = $this->checkCommand($args); break; - case "clean": $statusCode = $this->cleanCommand($args); break; - case "version": $statusCode = $this->versionCommand($args); break; - default: $statusCode = 0; - } - return $statusCode; - } - - // Handle command help - function onCommandHelp() - { - $help .= "build [directory location]\n"; - $help .= "check [directory location]\n"; - $help .= "clean [directory location]\n"; - $help .= "version\n"; - return $help; - } - - // Show available commands - function helpCommand() - { - echo "Datenstrom Yellow ".YellowCore::VERSION."\n"; - $lineCounter = 0; - foreach($this->getCommandHelp() as $line) echo (++$lineCounter>1 ? " " : "Syntax: ")."yellow.php $line\n"; - return 200; - } - - // Build static website - function buildCommand($args) - { - $statusCode = 0; - list($command, $path, $location) = $args; - if(empty($location) || $location[0]=='/') - { - if($this->checkStaticConfig()) - { - $statusCode = $this->buildStaticFiles($path, $location); - } else { - $statusCode = 500; - $this->files = 0; $this->errors = 1; - $fileName = $this->yellow->config->get("configDir").$this->yellow->config->get("configFile"); - echo "ERROR building files: Please configure StaticUrl in file '$fileName'!\n"; - } - echo "Yellow $command: $this->files file".($this->files!=1 ? 's' : ''); - echo ", $this->errors error".($this->errors!=1 ? 's' : '')."\n"; - } else { - $statusCode = 400; - echo "Yellow $command: Invalid arguments\n"; - } - return $statusCode; - } - - // Build static files - function buildStaticFiles($path, $locationFilter) - { - $path = rtrim(empty($path) ? $this->yellow->config->get("staticDir") : $path, '/'); - $this->files = $this->errors = 0; - $this->locationsArgs = $this->locationsArgsPagination = array(); - $statusCode = empty($locationFilter) ? $this->cleanStaticFiles($path, $locationFilter) : 200; - $staticUrl = $this->yellow->config->get("staticUrl"); - list($scheme, $address, $base) = $this->yellow->lookup->getUrlInformation($staticUrl); - foreach($this->getContentLocations() as $location) - { - if(!preg_match("#^$base$locationFilter#", "$base$location")) continue; - $statusCode = max($statusCode, $this->buildStaticFile($path, $location, true)); - } - foreach($this->locationsArgs as $location) - { - if(!preg_match("#^$base$locationFilter#", "$base$location")) continue; - $statusCode = max($statusCode, $this->buildStaticFile($path, $location, true)); - } - foreach($this->locationsArgsPagination as $location) - { - if(!preg_match("#^$base$locationFilter#", "$base$location")) continue; - if(substru($location, -1)!=$this->yellow->toolbox->getLocationArgsSeparator()) - { - $statusCode = max($statusCode, $this->buildStaticFile($path, $location, false, true)); - } - for($pageNumber=2; $pageNumber<=999; ++$pageNumber) - { - $statusCodeLocation = $this->buildStaticFile($path, $location.$pageNumber, false, true); - $statusCode = max($statusCode, $statusCodeLocation); - if($statusCodeLocation==100) break; - } - } - if(empty($locationFilter)) - { - foreach($this->getMediaLocations() as $location) - { - $statusCode = max($statusCode, $this->buildStaticFile($path, $location)); - } - foreach($this->getSystemLocations() as $location) - { - $statusCode = max($statusCode, $this->buildStaticFile($path, $location)); - } - $statusCode = max($statusCode, $this->buildStaticFile($path, "/error/", false, false, true)); - } - return $statusCode; - } - - // Build static file - function buildStaticFile($path, $location, $analyse = false, $probe = false, $error = false) - { - $this->yellow->pages = new YellowPages($this->yellow); - $this->yellow->page = new YellowPage($this->yellow); - $this->yellow->page->fileName = substru($location, 1); - if(!is_readable($this->yellow->page->fileName)) - { - ob_start(); - $staticUrl = $this->yellow->config->get("staticUrl"); - list($scheme, $address, $base) = $this->yellow->lookup->getUrlInformation($staticUrl); - $statusCode = $this->requestStaticFile($scheme, $address, $base, $location); - if($statusCode<400 || $error) - { - $fileData = ob_get_contents(); - $modified = strtotime($this->yellow->page->getHeader("Last-Modified")); - if($modified==0) $modified = $this->yellow->toolbox->getFileModified($this->yellow->page->fileName); - if($statusCode>=301 && $statusCode<=303) - { - $fileData = $this->getStaticRedirect($this->yellow->page->getHeader("Location")); - $modified = time(); - } - $fileName = $this->getStaticFile($path, $location, $statusCode); - if(!$this->yellow->toolbox->createFile($fileName, $fileData, true) || - !$this->yellow->toolbox->modifyFile($fileName, $modified)) - { - $statusCode = 500; - $this->yellow->page->statusCode = $statusCode; - $this->yellow->page->set("pageError", "Can't write file '$fileName'!"); - } - } - ob_end_clean(); - } else { - $statusCode = 200; - $modified = $this->yellow->toolbox->getFileModified($this->yellow->page->fileName); - $fileName = $this->getStaticFile($path, $location, $statusCode); - if(!$this->yellow->toolbox->copyFile($this->yellow->page->fileName, $fileName, true) || - !$this->yellow->toolbox->modifyFile($fileName, $modified)) - { - $statusCode = 500; - $this->yellow->page->statusCode = $statusCode; - $this->yellow->page->set("pageError", "Can't write file '$fileName'!"); - } - } - if($statusCode==200 && $analyse) $this->analyseStaticFile($scheme, $address, $base, $fileData); - if($statusCode==404 && $probe) $statusCode = 100; - if($statusCode==404 && $error) $statusCode = 200; - if($statusCode>=200) ++$this->files; - if($statusCode>=400) - { - ++$this->errors; - echo "ERROR building location '$location', ".$this->yellow->page->getStatusCode(true)."\n"; - } - if(defined("DEBUG") && DEBUG>=1) echo "YellowCommand::buildStaticFile status:$statusCode location:$location<br/>\n"; - return $statusCode; - } - - // Request static file - function requestStaticFile($scheme, $address, $base, $location) - { - list($serverName, $serverPort) = explode(':', $address); - if(is_null($serverPort)) $serverPort = $scheme=="https" ? 443 : 80; - $_SERVER["HTTPS"] = $scheme=="https" ? "on" : "off"; - $_SERVER["SERVER_PROTOCOL"] = "HTTP/1.1"; - $_SERVER["SERVER_NAME"] = $serverName; - $_SERVER["SERVER_PORT"] = $serverPort; - $_SERVER["REQUEST_METHOD"] = "GET"; - $_SERVER["REQUEST_URI"] = $base.$location; - $_SERVER["SCRIPT_NAME"] = $base."/yellow.php"; - $_SERVER["REMOTE_ADDR"] = "127.0.0.1"; - $_REQUEST = array(); - return $this->yellow->request(); - } - - // Analyse static file, detect locations with arguments - function analyseStaticFile($scheme, $address, $base, $rawData) - { - $pagination = $this->yellow->config->get("contentPagination"); - preg_match_all("/<(.*?)href=\"([^\"]+)\"(.*?)>/i", $rawData, $matches); - foreach($matches[2] as $match) - { - $location = rawurldecode($match); - if(preg_match("/^(.*?)#(.*)$/", $location, $tokens)) $location = $tokens[1]; - if(preg_match("/^(\w+):\/\/([^\/]+)(.*)$/", $location, $tokens)) - { - if($tokens[1]!=$scheme) continue; - if($tokens[2]!=$address) continue; - $location = $tokens[3]; - } - if(substru($location, 0, strlenu($base))!=$base) continue; - $location = substru($location, strlenu($base)); - if(!$this->yellow->toolbox->isLocationArgs($location)) continue; - if(!$this->yellow->toolbox->isLocationArgsPagination($location, $pagination)) - { - $location = rtrim($location, '/').'/'; - if(is_null($this->locationsArgs[$location])) - { - $this->locationsArgs[$location] = $location; - if(defined("DEBUG") && DEBUG>=2) echo "YellowCommand::analyseStaticFile detected location:$location<br/>\n"; - } - } else { - $location = rtrim($location, "0..9"); - if(is_null($this->locationsArgsPagination[$location])) - { - $this->locationsArgsPagination[$location] = $location; - if(defined("DEBUG") && DEBUG>=2) echo "YellowCommand::analyseStaticFile detected location:$location<br/>\n"; - } - } - } - } +class YellowCommand { + const VERSION = "0.7.8"; + public $yellow; //access to API + public $files; //number of files + public $links; //number of links + public $errors; //number of errors + public $locationsArgs; //locations with location arguments detected + public $locationsArgsPagination; //locations with pagination arguments detected + + // Handle initialisation + public function onLoad($yellow) { + $this->yellow = $yellow; + } + + // Handle command + public function onCommand($args) { + list($command) = $args; + switch ($command) { + case "": $statusCode = $this->helpCommand(); break; + case "build": $statusCode = $this->buildCommand($args); break; + case "check": $statusCode = $this->checkCommand($args); break; + case "clean": $statusCode = $this->cleanCommand($args); break; + case "version": $statusCode = $this->versionCommand($args); break; + default: $statusCode = 0; + } + return $statusCode; + } + + // Handle command help + public function onCommandHelp() { + $help .= "build [directory location]\n"; + $help .= "check [directory location]\n"; + $help .= "clean [directory location]\n"; + $help .= "version\n"; + return $help; + } + + // Show available commands + public function helpCommand() { + echo "Datenstrom Yellow ".YellowCore::VERSION."\n"; + $lineCounter = 0; + foreach ($this->getCommandHelp() as $line) { + echo(++$lineCounter>1 ? " " : "Syntax: ")."yellow.php $line\n"; + } + return 200; + } + + // Build static website + public function buildCommand($args) { + $statusCode = 0; + list($command, $path, $location) = $args; + if (empty($location) || $location[0]=="/") { + if ($this->checkStaticConfig()) { + $statusCode = $this->buildStaticFiles($path, $location); + } else { + $statusCode = 500; + $this->files = 0; + $this->errors = 1; + $fileName = $this->yellow->config->get("configDir").$this->yellow->config->get("configFile"); + echo "ERROR building files: Please configure StaticUrl in file '$fileName'!\n"; + } + echo "Yellow $command: $this->files file".($this->files!=1 ? "s" : ""); + echo ", $this->errors error".($this->errors!=1 ? "s" : "")."\n"; + } else { + $statusCode = 400; + echo "Yellow $command: Invalid arguments\n"; + } + return $statusCode; + } + + // Build static files + public function buildStaticFiles($path, $locationFilter) { + $path = rtrim(empty($path) ? $this->yellow->config->get("staticDir") : $path, "/"); + $this->files = $this->errors = 0; + $this->locationsArgs = $this->locationsArgsPagination = array(); + $statusCode = empty($locationFilter) ? $this->cleanStaticFiles($path, $locationFilter) : 200; + $staticUrl = $this->yellow->config->get("staticUrl"); + list($scheme, $address, $base) = $this->yellow->lookup->getUrlInformation($staticUrl); + foreach ($this->getContentLocations() as $location) { + if (!preg_match("#^$base$locationFilter#", "$base$location")) continue; + $statusCode = max($statusCode, $this->buildStaticFile($path, $location, true)); + } + foreach ($this->locationsArgs as $location) { + if (!preg_match("#^$base$locationFilter#", "$base$location")) continue; + $statusCode = max($statusCode, $this->buildStaticFile($path, $location, true)); + } + foreach ($this->locationsArgsPagination as $location) { + if (!preg_match("#^$base$locationFilter#", "$base$location")) continue; + if (substru($location, -1)!=$this->yellow->toolbox->getLocationArgsSeparator()) { + $statusCode = max($statusCode, $this->buildStaticFile($path, $location, false, true)); + } + for ($pageNumber=2; $pageNumber<=999; ++$pageNumber) { + $statusCodeLocation = $this->buildStaticFile($path, $location.$pageNumber, false, true); + $statusCode = max($statusCode, $statusCodeLocation); + if ($statusCodeLocation==100) break; + } + } + if (empty($locationFilter)) { + foreach ($this->getMediaLocations() as $location) { + $statusCode = max($statusCode, $this->buildStaticFile($path, $location)); + } + foreach ($this->getSystemLocations() as $location) { + $statusCode = max($statusCode, $this->buildStaticFile($path, $location)); + } + $statusCode = max($statusCode, $this->buildStaticFile($path, "/error/", false, false, true)); + } + return $statusCode; + } + + // Build static file + public function buildStaticFile($path, $location, $analyse = false, $probe = false, $error = false) { + $this->yellow->pages = new YellowPages($this->yellow); + $this->yellow->page = new YellowPage($this->yellow); + $this->yellow->page->fileName = substru($location, 1); + if (!is_readable($this->yellow->page->fileName)) { + ob_start(); + $staticUrl = $this->yellow->config->get("staticUrl"); + list($scheme, $address, $base) = $this->yellow->lookup->getUrlInformation($staticUrl); + $statusCode = $this->requestStaticFile($scheme, $address, $base, $location); + if ($statusCode<400 || $error) { + $fileData = ob_get_contents(); + $modified = strtotime($this->yellow->page->getHeader("Last-Modified")); + if ($modified==0) $modified = $this->yellow->toolbox->getFileModified($this->yellow->page->fileName); + if ($statusCode>=301 && $statusCode<=303) { + $fileData = $this->getStaticRedirect($this->yellow->page->getHeader("Location")); + $modified = time(); + } + $fileName = $this->getStaticFile($path, $location, $statusCode); + if (!$this->yellow->toolbox->createFile($fileName, $fileData, true) || + !$this->yellow->toolbox->modifyFile($fileName, $modified)) { + $statusCode = 500; + $this->yellow->page->statusCode = $statusCode; + $this->yellow->page->set("pageError", "Can't write file '$fileName'!"); + } + } + ob_end_clean(); + } else { + $statusCode = 200; + $modified = $this->yellow->toolbox->getFileModified($this->yellow->page->fileName); + $fileName = $this->getStaticFile($path, $location, $statusCode); + if (!$this->yellow->toolbox->copyFile($this->yellow->page->fileName, $fileName, true) || + !$this->yellow->toolbox->modifyFile($fileName, $modified)) { + $statusCode = 500; + $this->yellow->page->statusCode = $statusCode; + $this->yellow->page->set("pageError", "Can't write file '$fileName'!"); + } + } + if ($statusCode==200 && $analyse) $this->analyseStaticFile($scheme, $address, $base, $fileData); + if ($statusCode==404 && $probe) $statusCode = 100; + if ($statusCode==404 && $error) $statusCode = 200; + if ($statusCode>=200) ++$this->files; + if ($statusCode>=400) { + ++$this->errors; + echo "ERROR building location '$location', ".$this->yellow->page->getStatusCode(true)."\n"; + } + if (defined("DEBUG") && DEBUG>=1) echo "YellowCommand::buildStaticFile status:$statusCode location:$location<br/>\n"; + return $statusCode; + } + + // Request static file + public function requestStaticFile($scheme, $address, $base, $location) { + list($serverName, $serverPort) = explode(":", $address); + if (is_null($serverPort)) $serverPort = $scheme=="https" ? 443 : 80; + $_SERVER["HTTPS"] = $scheme=="https" ? "on" : "off"; + $_SERVER["SERVER_PROTOCOL"] = "HTTP/1.1"; + $_SERVER["SERVER_NAME"] = $serverName; + $_SERVER["SERVER_PORT"] = $serverPort; + $_SERVER["REQUEST_METHOD"] = "GET"; + $_SERVER["REQUEST_URI"] = $base.$location; + $_SERVER["SCRIPT_NAME"] = $base."/yellow.php"; + $_SERVER["REMOTE_ADDR"] = "127.0.0.1"; + $_REQUEST = array(); + return $this->yellow->request(); + } + + // Analyse static file, detect locations with arguments + public function analyseStaticFile($scheme, $address, $base, $rawData) { + $pagination = $this->yellow->config->get("contentPagination"); + preg_match_all("/<(.*?)href=\"([^\"]+)\"(.*?)>/i", $rawData, $matches); + foreach ($matches[2] as $match) { + $location = rawurldecode($match); + if (preg_match("/^(.*?)#(.*)$/", $location, $tokens)) $location = $tokens[1]; + if (preg_match("/^(\w+):\/\/([^\/]+)(.*)$/", $location, $tokens)) { + if ($tokens[1]!=$scheme) continue; + if ($tokens[2]!=$address) continue; + $location = $tokens[3]; + } + if (substru($location, 0, strlenu($base))!=$base) continue; + $location = substru($location, strlenu($base)); + if (!$this->yellow->toolbox->isLocationArgs($location)) continue; + if (!$this->yellow->toolbox->isLocationArgsPagination($location, $pagination)) { + $location = rtrim($location, "/")."/"; + if (is_null($this->locationsArgs[$location])) { + $this->locationsArgs[$location] = $location; + if (defined("DEBUG") && DEBUG>=2) echo "YellowCommand::analyseStaticFile detected location:$location<br/>\n"; + } + } else { + $location = rtrim($location, "0..9"); + if (is_null($this->locationsArgsPagination[$location])) { + $this->locationsArgsPagination[$location] = $location; + if (defined("DEBUG") && DEBUG>=2) echo "YellowCommand::analyseStaticFile detected location:$location<br/>\n"; + } + } + } + } - // Check static files for broken links - function checkCommand($args) - { - $statusCode = 0; - list($command, $path, $location) = $args; - if(empty($location) || $location[0]=='/') - { - if($this->checkStaticConfig()) - { - $statusCode = $this->checkStaticFiles($path, $location); - } else { - $statusCode = 500; - $this->files = $this->links = 0; - $fileName = $this->yellow->config->get("configDir").$this->yellow->config->get("configFile"); - echo "ERROR checking files: Please configure StaticUrl in file '$fileName'!\n"; - } - echo "Yellow $command: $this->files file".($this->files!=1 ? 's' : ''); - echo ", $this->links link".($this->links!=1 ? 's' : '')."\n"; - } else { - $statusCode = 400; - echo "Yellow $command: Invalid arguments\n"; - } - return $statusCode; - } - - // Check static files - function checkStaticFiles($path, $locationFilter) - { - $path = rtrim(empty($path) ? $this->yellow->config->get("staticDir") : $path, '/'); - $this->files = $this->links = 0; - $regex = "/^[^.]+$|".$this->yellow->config->get("staticDefaultFile")."$/"; - $fileNames = $this->yellow->toolbox->getDirectoryEntriesRecursive($path, $regex, false, false); - list($statusCodeFiles, $links) = $this->analyseStaticFiles($path, $locationFilter, $fileNames); - list($statusCodeLinks, $broken, $redirect) = $this->analyseLinks($path, $links); - if($statusCodeLinks!=200) - { - $this->showLinks($broken, "Broken links"); - $this->showLinks($redirect, "Redirect links"); - } - return max($statusCodeFiles, $statusCodeLinks); - } - - // Analyse static files, detect links - function analyseStaticFiles($path, $locationFilter, $fileNames) - { - $statusCode = 200; - $links = array(); - if(!empty($fileNames)) - { - $staticUrl = $this->yellow->config->get("staticUrl"); - list($scheme, $address, $base) = $this->yellow->lookup->getUrlInformation($staticUrl); - foreach($fileNames as $fileName) - { - if(is_readable($fileName)) - { - $locationSource = $this->getStaticLocation($path, $fileName); - if(!preg_match("#^$base$locationFilter#", "$base$locationSource")) continue; - $fileData = $this->yellow->toolbox->readFile($fileName); - preg_match_all("/<(.*?)href=\"([^\"]+)\"(.*?)>/i", $fileData, $matches); - foreach($matches[2] as $match) - { - $location = rawurldecode($match); - if(preg_match("/^(.*?)#(.*)$/", $location, $tokens)) $location = $tokens[1]; - if(preg_match("/^(\w+):\/\/([^\/]+)(.*)$/", $location, $matches)) - { - $url = $location.(empty($matches[3]) ? "/" : ""); - if(!is_null($links[$url])) $links[$url] .= ","; - $links[$url] .= $locationSource; - if(defined("DEBUG") && DEBUG>=2) echo "YellowCommand::analyseStaticFiles detected url:$url<br/>\n"; - } else if($location[0]=='/') { - $url = "$scheme://$address$location"; - if(!is_null($links[$url])) $links[$url] .= ","; - $links[$url] .= $locationSource; - if(defined("DEBUG") && DEBUG>=2) echo "YellowCommand::analyseStaticFiles detected url:$url<br/>\n"; - } - } - ++$this->files; - } else { - $statusCode = 500; - echo "ERROR reading files: Can't read file '$fileName'!\n"; - } - } - $this->links = count($links); - } else { - $statusCode = 500; - echo "ERROR reading files: Can't find files in directory '$path'!\n"; - } - return array($statusCode, $links); - } - - // Analyse links, detect status - function analyseLinks($path, $links) - { - $statusCode = 200; - $broken = $redirect = $data = array(); - $staticUrl = $this->yellow->config->get("staticUrl"); - $staticUrlLength = strlenu(rtrim($staticUrl, '/')); - list($scheme, $address, $base) = $this->yellow->lookup->getUrlInformation($staticUrl); - $staticLocations = $this->getContentLocations(true); - uksort($links, "strnatcasecmp"); - foreach($links as $url=>$value) - { - if(defined("DEBUG") && DEBUG>=1) echo "YellowCommand::analyseLinks url:$url\n"; - if(preg_match("#^$staticUrl#", $url)) - { - $location = substru($url, $staticUrlLength); - $fileName = $path.substru($url, $staticUrlLength); - if(is_readable($fileName)) continue; - if(in_array($location, $staticLocations)) continue; - } - if(preg_match("/^(http|https):/", $url)) - { - $referer = "$scheme://$address$base".(($pos = strposu($value, ',')) ? substru($value, 0, $pos) : $value); - $statusCodeUrl = $this->getLinkStatus($url, $referer); - if($statusCodeUrl!=200) - { - $statusCode = max($statusCode, $statusCodeUrl); - $data[$url] = "$statusCodeUrl,$value"; - } - } - } - foreach($data as $url=>$value) - { - $locations = preg_split("/\s*,\s*/", $value); - $statusCodeUrl = array_shift($locations); - foreach($locations as $location) - { - if($statusCodeUrl==302) continue; - if($statusCodeUrl>=300 && $statusCodeUrl<=399) { - $redirect["$scheme://$address$base$location -> $url - ".$this->getStatusFormatted($statusCodeUrl)] = $statusCodeUrl; - } else { - $broken["$scheme://$address$base$location -> $url - ".$this->getStatusFormatted($statusCodeUrl)] = $statusCodeUrl; - } - } - } - return array($statusCode, $broken, $redirect); - } + // Check static files for broken links + public function checkCommand($args) { + $statusCode = 0; + list($command, $path, $location) = $args; + if (empty($location) || $location[0]=="/") { + if ($this->checkStaticConfig()) { + $statusCode = $this->checkStaticFiles($path, $location); + } else { + $statusCode = 500; + $this->files = $this->links = 0; + $fileName = $this->yellow->config->get("configDir").$this->yellow->config->get("configFile"); + echo "ERROR checking files: Please configure StaticUrl in file '$fileName'!\n"; + } + echo "Yellow $command: $this->files file".($this->files!=1 ? "s" : ""); + echo ", $this->links link".($this->links!=1 ? "s" : "")."\n"; + } else { + $statusCode = 400; + echo "Yellow $command: Invalid arguments\n"; + } + return $statusCode; + } + + // Check static files + public function checkStaticFiles($path, $locationFilter) { + $path = rtrim(empty($path) ? $this->yellow->config->get("staticDir") : $path, "/"); + $this->files = $this->links = 0; + $regex = "/^[^.]+$|".$this->yellow->config->get("staticDefaultFile")."$/"; + $fileNames = $this->yellow->toolbox->getDirectoryEntriesRecursive($path, $regex, false, false); + list($statusCodeFiles, $links) = $this->analyseStaticFiles($path, $locationFilter, $fileNames); + list($statusCodeLinks, $broken, $redirect) = $this->analyseLinks($path, $links); + if ($statusCodeLinks!=200) { + $this->showLinks($broken, "Broken links"); + $this->showLinks($redirect, "Redirect links"); + } + return max($statusCodeFiles, $statusCodeLinks); + } + + // Analyse static files, detect links + public function analyseStaticFiles($path, $locationFilter, $fileNames) { + $statusCode = 200; + $links = array(); + if (!empty($fileNames)) { + $staticUrl = $this->yellow->config->get("staticUrl"); + list($scheme, $address, $base) = $this->yellow->lookup->getUrlInformation($staticUrl); + foreach ($fileNames as $fileName) { + if (is_readable($fileName)) { + $locationSource = $this->getStaticLocation($path, $fileName); + if (!preg_match("#^$base$locationFilter#", "$base$locationSource")) continue; + $fileData = $this->yellow->toolbox->readFile($fileName); + preg_match_all("/<(.*?)href=\"([^\"]+)\"(.*?)>/i", $fileData, $matches); + foreach ($matches[2] as $match) { + $location = rawurldecode($match); + if (preg_match("/^(.*?)#(.*)$/", $location, $tokens)) $location = $tokens[1]; + if (preg_match("/^(\w+):\/\/([^\/]+)(.*)$/", $location, $matches)) { + $url = $location.(empty($matches[3]) ? "/" : ""); + if (!is_null($links[$url])) $links[$url] .= ","; + $links[$url] .= $locationSource; + if (defined("DEBUG") && DEBUG>=2) echo "YellowCommand::analyseStaticFiles detected url:$url<br/>\n"; + } elseif ($location[0]=="/") { + $url = "$scheme://$address$location"; + if (!is_null($links[$url])) $links[$url] .= ","; + $links[$url] .= $locationSource; + if (defined("DEBUG") && DEBUG>=2) echo "YellowCommand::analyseStaticFiles detected url:$url<br/>\n"; + } + } + ++$this->files; + } else { + $statusCode = 500; + echo "ERROR reading files: Can't read file '$fileName'!\n"; + } + } + $this->links = count($links); + } else { + $statusCode = 500; + echo "ERROR reading files: Can't find files in directory '$path'!\n"; + } + return array($statusCode, $links); + } + + // Analyse links, detect status + public function analyseLinks($path, $links) { + $statusCode = 200; + $broken = $redirect = $data = array(); + $staticUrl = $this->yellow->config->get("staticUrl"); + $staticUrlLength = strlenu(rtrim($staticUrl, "/")); + list($scheme, $address, $base) = $this->yellow->lookup->getUrlInformation($staticUrl); + $staticLocations = $this->getContentLocations(true); + uksort($links, "strnatcasecmp"); + foreach ($links as $url=>$value) { + if (defined("DEBUG") && DEBUG>=1) echo "YellowCommand::analyseLinks url:$url\n"; + if (preg_match("#^$staticUrl#", $url)) { + $location = substru($url, $staticUrlLength); + $fileName = $path.substru($url, $staticUrlLength); + if (is_readable($fileName)) continue; + if (in_array($location, $staticLocations)) continue; + } + if (preg_match("/^(http|https):/", $url)) { + $referer = "$scheme://$address$base".(($pos = strposu($value, ",")) ? substru($value, 0, $pos) : $value); + $statusCodeUrl = $this->getLinkStatus($url, $referer); + if ($statusCodeUrl!=200) { + $statusCode = max($statusCode, $statusCodeUrl); + $data[$url] = "$statusCodeUrl,$value"; + } + } + } + foreach ($data as $url=>$value) { + $locations = preg_split("/\s*,\s*/", $value); + $statusCodeUrl = array_shift($locations); + foreach ($locations as $location) { + if ($statusCodeUrl==302) continue; + if ($statusCodeUrl>=300 && $statusCodeUrl<=399) { + $redirect["$scheme://$address$base$location -> $url - ".$this->getStatusFormatted($statusCodeUrl)] = $statusCodeUrl; + } else { + $broken["$scheme://$address$base$location -> $url - ".$this->getStatusFormatted($statusCodeUrl)] = $statusCodeUrl; + } + } + } + return array($statusCode, $broken, $redirect); + } - // Show links - function showLinks($data, $text) - { - if(!empty($data)) - { - echo "$text\n\n"; - uksort($data, "strnatcasecmp"); - $data = array_slice($data, 0, 99); - foreach($data as $key=>$value) echo "- $key\n"; - echo "\n"; - } - } - - // Clean static files - function cleanCommand($args) - { - $statusCode = 0; - list($command, $path, $location) = $args; - if(empty($location) || $location[0]=='/') - { - $statusCode = $this->cleanStaticFiles($path, $location); - echo "Yellow $command: Static file".(empty($location) ? "s" : "")." ".($statusCode!=200 ? "not " : "")."cleaned\n"; - } else { - $statusCode = 400; - echo "Yellow $command: Invalid arguments\n"; - } - return $statusCode; - } - - // Clean static files and directories - function cleanStaticFiles($path, $location) - { - $statusCode = 200; - $path = rtrim(empty($path) ? $this->yellow->config->get("staticDir") : $path, '/'); - if(empty($location)) - { - $statusCode = max($statusCode, $this->commandBroadcast("clean", "all")); - $statusCode = max($statusCode, $this->cleanStaticDirectory($path)); - } else { - if($this->yellow->lookup->isFileLocation($location)) - { - $fileName = $this->getStaticFile($path, $location, $statusCode); - $statusCode = $this->cleanStaticFile($fileName); - } else { - $statusCode = $this->cleanStaticDirectory($path.$location); - } - } - return $statusCode; - } - - // Clean static directory - function cleanStaticDirectory($path) - { - $statusCode = 200; - if(is_dir($path) && $this->checkStaticDirectory($path)) - { - if(!$this->yellow->toolbox->deleteDirectory($path)) - { - $statusCode = 500; - echo "ERROR cleaning files: Can't delete directory '$path'!\n"; - } - } - return $statusCode; - } - - // Clean static file - function cleanStaticFile($fileName) - { - $statusCode = 200; - if(is_file($fileName)) - { - if(!$this->yellow->toolbox->deleteFile($fileName)) - { - $statusCode = 500; - echo "ERROR cleaning files: Can't delete file '$fileName'!\n"; - } - } - return $statusCode; - } - - // Broadcast command to other plugins - function commandBroadcast($args) - { - $statusCode = 0; - foreach($this->yellow->plugins->plugins as $key=>$value) - { - if($key=="command") continue; - if(method_exists($value["obj"], "onCommand")) - { - $statusCode = $value["obj"]->onCommand(func_get_args()); - if($statusCode!=0) break; - } - } - return $statusCode; - } - - // Show software version and updates - function versionCommand($args) - { - $serverVersion = $this->yellow->toolbox->getServerVersion(); - echo "Datenstrom Yellow ".YellowCore::VERSION.", PHP ".PHP_VERSION.", $serverVersion\n"; - list($statusCode, $dataCurrent) = $this->getSoftwareVersion(); - list($statusCode, $dataLatest) = $this->getSoftwareVersion(true); - foreach($dataCurrent as $key=>$value) - { - if(strnatcasecmp($dataCurrent[$key], $dataLatest[$key])>=0) - { - echo "$key $value\n"; - } else { - echo "$key $dataLatest[$key] - Update available\n"; - } - } - if($statusCode!=200) echo "ERROR checking updates: ".$this->yellow->page->get("pageError")."\n"; - return $statusCode; - } - - // Check static configuration - function checkStaticConfig() - { - $staticUrl = $this->yellow->config->get("staticUrl"); - return !empty($staticUrl); - } - - // Check static directory - function checkStaticDirectory($path) - { - $ok = false; - if(!empty($path)) - { - if($path==rtrim($this->yellow->config->get("staticDir"), '/')) $ok = true; - if($path==rtrim($this->yellow->config->get("trashDir"), '/')) $ok = true; - if(is_file("$path/".$this->yellow->config->get("staticDefaultFile"))) $ok = true; - if(is_file("$path/yellow.php")) $ok = false; - } - return $ok; - } - - // Return static file - function getStaticFile($path, $location, $statusCode) - { - if($statusCode<400) - { - $fileName = $path.$location; - if(!$this->yellow->lookup->isFileLocation($location)) $fileName .= $this->yellow->config->get("staticDefaultFile"); - } else if($statusCode==404) { - $fileName = $path."/".$this->yellow->config->get("staticErrorFile"); - } - return $fileName; - } - - // Return static location - function getStaticLocation($path, $fileName) - { - $location = substru($fileName, strlenu($path)); - if(basename($location)==$this->yellow->config->get("staticDefaultFile")) - { - $defaultFileLength = strlenu($this->yellow->config->get("staticDefaultFile")); - $location = substru($location, 0, -$defaultFileLength); - } - return $location; - } - - // Return static redirect - function getStaticRedirect($location) - { - $output = "<!DOCTYPE html><html>\n<head>\n"; - $output .= "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\" />\n"; - $output .= "<meta http-equiv=\"refresh\" content=\"0;url=".htmlspecialchars($location)."\" />\n"; - $output .= "</head>\n</html>"; - return $output; - } + // Show links + public function showLinks($data, $text) { + if (!empty($data)) { + echo "$text\n\n"; + uksort($data, "strnatcasecmp"); + $data = array_slice($data, 0, 99); + foreach ($data as $key=>$value) { + echo "- $key\n"; + } + echo "\n"; + } + } + + // Clean static files + public function cleanCommand($args) { + $statusCode = 0; + list($command, $path, $location) = $args; + if (empty($location) || $location[0]=="/") { + $statusCode = $this->cleanStaticFiles($path, $location); + echo "Yellow $command: Static file".(empty($location) ? "s" : "")." ".($statusCode!=200 ? "not " : "")."cleaned\n"; + } else { + $statusCode = 400; + echo "Yellow $command: Invalid arguments\n"; + } + return $statusCode; + } + + // Clean static files and directories + public function cleanStaticFiles($path, $location) { + $statusCode = 200; + $path = rtrim(empty($path) ? $this->yellow->config->get("staticDir") : $path, "/"); + if (empty($location)) { + $statusCode = max($statusCode, $this->commandBroadcast("clean", "all")); + $statusCode = max($statusCode, $this->cleanStaticDirectory($path)); + } else { + if ($this->yellow->lookup->isFileLocation($location)) { + $fileName = $this->getStaticFile($path, $location, $statusCode); + $statusCode = $this->cleanStaticFile($fileName); + } else { + $statusCode = $this->cleanStaticDirectory($path.$location); + } + } + return $statusCode; + } + + // Clean static directory + public function cleanStaticDirectory($path) { + $statusCode = 200; + if (is_dir($path) && $this->checkStaticDirectory($path)) { + if (!$this->yellow->toolbox->deleteDirectory($path)) { + $statusCode = 500; + echo "ERROR cleaning files: Can't delete directory '$path'!\n"; + } + } + return $statusCode; + } + + // Clean static file + public function cleanStaticFile($fileName) { + $statusCode = 200; + if (is_file($fileName)) { + if (!$this->yellow->toolbox->deleteFile($fileName)) { + $statusCode = 500; + echo "ERROR cleaning files: Can't delete file '$fileName'!\n"; + } + } + return $statusCode; + } + + // Broadcast command to other plugins + public function commandBroadcast($args) { + $statusCode = 0; + foreach ($this->yellow->plugins->plugins as $key=>$value) { + if ($key=="command") continue; + if (method_exists($value["obj"], "onCommand")) { + $statusCode = $value["obj"]->onCommand(func_get_args()); + if ($statusCode!=0) break; + } + } + return $statusCode; + } + + // Show software version and updates + public function versionCommand($args) { + $serverVersion = $this->yellow->toolbox->getServerVersion(); + echo "Datenstrom Yellow ".YellowCore::VERSION.", PHP ".PHP_VERSION.", $serverVersion\n"; + list($statusCode, $dataCurrent) = $this->getSoftwareVersion(); + list($statusCode, $dataLatest) = $this->getSoftwareVersion(true); + foreach ($dataCurrent as $key=>$value) { + if (strnatcasecmp($dataCurrent[$key], $dataLatest[$key])>=0) { + echo "$key $value\n"; + } else { + echo "$key $dataLatest[$key] - Update available\n"; + } + } + if ($statusCode!=200) echo "ERROR checking updates: ".$this->yellow->page->get("pageError")."\n"; + return $statusCode; + } + + // Check static configuration + public function checkStaticConfig() { + $staticUrl = $this->yellow->config->get("staticUrl"); + return !empty($staticUrl); + } + + // Check static directory + public function checkStaticDirectory($path) { + $ok = false; + if (!empty($path)) { + if ($path==rtrim($this->yellow->config->get("staticDir"), "/")) $ok = true; + if ($path==rtrim($this->yellow->config->get("trashDir"), "/")) $ok = true; + if (is_file("$path/".$this->yellow->config->get("staticDefaultFile"))) $ok = true; + if (is_file("$path/yellow.php")) $ok = false; + } + return $ok; + } + + // Return static file + public function getStaticFile($path, $location, $statusCode) { + if ($statusCode<400) { + $fileName = $path.$location; + if (!$this->yellow->lookup->isFileLocation($location)) $fileName .= $this->yellow->config->get("staticDefaultFile"); + } elseif ($statusCode==404) { + $fileName = $path."/".$this->yellow->config->get("staticErrorFile"); + } + return $fileName; + } + + // Return static location + public function getStaticLocation($path, $fileName) { + $location = substru($fileName, strlenu($path)); + if (basename($location)==$this->yellow->config->get("staticDefaultFile")) { + $defaultFileLength = strlenu($this->yellow->config->get("staticDefaultFile")); + $location = substru($location, 0, -$defaultFileLength); + } + return $location; + } + + // Return static redirect + public function getStaticRedirect($location) { + $output = "<!DOCTYPE html><html>\n<head>\n"; + $output .= "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\" />\n"; + $output .= "<meta http-equiv=\"refresh\" content=\"0;url=".htmlspecialchars($location)."\" />\n"; + $output .= "</head>\n</html>"; + return $output; + } - // Return human readable status - function getStatusFormatted($statusCode) - { - return $this->yellow->toolbox->getHttpStatusFormatted($statusCode, true); - } - - // Return content locations - function getContentLocations($includeAll = false) - { - $locations = array(); - $staticUrl = $this->yellow->config->get("staticUrl"); - list($scheme, $address, $base) = $this->yellow->lookup->getUrlInformation($staticUrl); - $this->yellow->page->setRequestInformation($scheme, $address, $base, "", ""); - foreach($this->yellow->pages->index(true, true) as $page) - { - if(($page->get("status")!="ignore" && $page->get("status")!="draft") || $includeAll) - { - array_push($locations, $page->location); - } - } - if(!$this->yellow->pages->find("/") && $this->yellow->config->get("multiLanguageMode")) array_unshift($locations, "/"); - return $locations; - } - - // Return media locations - function getMediaLocations() - { - $locations = array(); - $fileNames = $this->yellow->toolbox->getDirectoryEntriesRecursive($this->yellow->config->get("mediaDir"), "/.*/", false, false); - foreach($fileNames as $fileName) - { - array_push($locations, "/".$fileName); - } - return $locations; - } + // Return human readable status + public function getStatusFormatted($statusCode) { + return $this->yellow->toolbox->getHttpStatusFormatted($statusCode, true); + } + + // Return content locations + public function getContentLocations($includeAll = false) { + $locations = array(); + $staticUrl = $this->yellow->config->get("staticUrl"); + list($scheme, $address, $base) = $this->yellow->lookup->getUrlInformation($staticUrl); + $this->yellow->page->setRequestInformation($scheme, $address, $base, "", ""); + foreach ($this->yellow->pages->index(true, true) as $page) { + if (($page->get("status")!="ignore" && $page->get("status")!="draft") || $includeAll) { + array_push($locations, $page->location); + } + } + if (!$this->yellow->pages->find("/") && $this->yellow->config->get("multiLanguageMode")) array_unshift($locations, "/"); + return $locations; + } + + // Return media locations + public function getMediaLocations() { + $locations = array(); + $fileNames = $this->yellow->toolbox->getDirectoryEntriesRecursive($this->yellow->config->get("mediaDir"), "/.*/", false, false); + foreach ($fileNames as $fileName) { + array_push($locations, "/".$fileName); + } + return $locations; + } - // Return system locations - function getSystemLocations() - { - $locations = array(); - $regex = "/\.(css|gif|ico|js|jpg|png|svg|txt|woff|woff2)$/"; - $pluginDirLength = strlenu($this->yellow->config->get("pluginDir")); - $fileNames = $this->yellow->toolbox->getDirectoryEntriesRecursive($this->yellow->config->get("pluginDir"), $regex, false, false); - foreach($fileNames as $fileName) - { - array_push($locations, $this->yellow->config->get("pluginLocation").substru($fileName, $pluginDirLength)); - } - $themeDirLength = strlenu($this->yellow->config->get("themeDir")); - $fileNames = $this->yellow->toolbox->getDirectoryEntriesRecursive($this->yellow->config->get("themeDir"), $regex, false, false); - foreach($fileNames as $fileName) - { - array_push($locations, $this->yellow->config->get("themeLocation").substru($fileName, $themeDirLength)); - } - array_push($locations, "/".$this->yellow->config->get("robotsFile")); - return $locations; - } - - // Return command help - function getCommandHelp() - { - $data = array(); - foreach($this->yellow->plugins->plugins as $key=>$value) - { - if(method_exists($value["obj"], "onCommandHelp")) - { - foreach(preg_split("/[\r\n]+/", $value["obj"]->onCommandHelp()) as $line) - { - list($command) = explode(' ', $line); - if(!empty($command) && is_null($data[$command])) $data[$command] = $line; - } - } - } - uksort($data, "strnatcasecmp"); - return $data; - } + // Return system locations + public function getSystemLocations() { + $locations = array(); + $regex = "/\.(css|gif|ico|js|jpg|png|svg|txt|woff|woff2)$/"; + $pluginDirLength = strlenu($this->yellow->config->get("pluginDir")); + $fileNames = $this->yellow->toolbox->getDirectoryEntriesRecursive($this->yellow->config->get("pluginDir"), $regex, false, false); + foreach ($fileNames as $fileName) { + array_push($locations, $this->yellow->config->get("pluginLocation").substru($fileName, $pluginDirLength)); + } + $themeDirLength = strlenu($this->yellow->config->get("themeDir")); + $fileNames = $this->yellow->toolbox->getDirectoryEntriesRecursive($this->yellow->config->get("themeDir"), $regex, false, false); + foreach ($fileNames as $fileName) { + array_push($locations, $this->yellow->config->get("themeLocation").substru($fileName, $themeDirLength)); + } + array_push($locations, "/".$this->yellow->config->get("robotsFile")); + return $locations; + } + + // Return command help + public function getCommandHelp() { + $data = array(); + foreach ($this->yellow->plugins->plugins as $key=>$value) { + if (method_exists($value["obj"], "onCommandHelp")) { + foreach (preg_split("/[\r\n]+/", $value["obj"]->onCommandHelp()) as $line) { + list($command) = explode(" ", $line); + if (!empty($command) && is_null($data[$command])) $data[$command] = $line; + } + } + } + uksort($data, "strnatcasecmp"); + return $data; + } - // Return software version - function getSoftwareVersion($latest = false) - { - $data = array(); - if($this->yellow->plugins->isExisting("update")) - { - list($statusCode, $data) = $this->yellow->plugins->get("update")->getSoftwareVersion($latest); - } else { - $statusCode = 200; - $data = array_merge($this->yellow->plugins->getData(), $this->yellow->themes->getData()); - } - return array($statusCode, $data); - } - - // Return link status - function getLinkStatus($url, $referer) - { - $curlHandle = curl_init(); - curl_setopt($curlHandle, CURLOPT_URL, $url); - curl_setopt($curlHandle, CURLOPT_REFERER, $referer); - curl_setopt($curlHandle, CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; DatenstromYellow/".YellowCore::VERSION."; LinkChecker)"); - curl_setopt($curlHandle, CURLOPT_NOBODY, 1); - curl_setopt($curlHandle, CURLOPT_CONNECTTIMEOUT, 30); - curl_exec($curlHandle); - $statusCode = curl_getinfo($curlHandle, CURLINFO_HTTP_CODE); - curl_close($curlHandle); - if(defined("DEBUG") && DEBUG>=2) echo "YellowCommand::getLinkStatus status:$statusCode url:$url<br/>\n"; - return $statusCode; - } + // Return software version + public function getSoftwareVersion($latest = false) { + $data = array(); + if ($this->yellow->plugins->isExisting("update")) { + list($statusCode, $data) = $this->yellow->plugins->get("update")->getSoftwareVersion($latest); + } else { + $statusCode = 200; + $data = array_merge($this->yellow->plugins->getData(), $this->yellow->themes->getData()); + } + return array($statusCode, $data); + } + + // Return link status + public function getLinkStatus($url, $referer) { + $curlHandle = curl_init(); + curl_setopt($curlHandle, CURLOPT_URL, $url); + curl_setopt($curlHandle, CURLOPT_REFERER, $referer); + curl_setopt($curlHandle, CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; DatenstromYellow/".YellowCore::VERSION."; LinkChecker)"); + curl_setopt($curlHandle, CURLOPT_NOBODY, 1); + curl_setopt($curlHandle, CURLOPT_CONNECTTIMEOUT, 30); + curl_exec($curlHandle); + $statusCode = curl_getinfo($curlHandle, CURLINFO_HTTP_CODE); + curl_close($curlHandle); + if (defined("DEBUG") && DEBUG>=2) echo "YellowCommand::getLinkStatus status:$statusCode url:$url<br/>\n"; + return $statusCode; + } } - + $yellow->plugins->register("command", "YellowCommand", YellowCommand::VERSION); -?> diff --git a/system/plugins/core.php b/system/plugins/core.php @@ -3,3536 +3,3115 @@ // Copyright (c) 2013-2018 Datenstrom, https://datenstrom.se // This file may be used and distributed under the terms of the public license. -class YellowCore -{ - const VERSION = "0.7.5"; - var $page; //current page - var $pages; //pages from file system - var $files; //files from file system - var $plugins; //plugins - var $themes; //themes - var $config; //configuration - var $text; //text - var $lookup; //location and file lookup - var $toolbox; //toolbox with helpers +class YellowCore { + const VERSION = "0.7.5"; + public $page; //current page + public $pages; //pages from file system + public $files; //files from file system + public $plugins; //plugins + public $themes; //themes + public $config; //configuration + public $text; //text + public $lookup; //location and file lookup + public $toolbox; //toolbox with helpers - function __construct() - { - $this->page = new YellowPage($this); - $this->pages = new YellowPages($this); - $this->files = new YellowFiles($this); - $this->plugins = new YellowPlugins($this); - $this->themes = new YellowThemes($this); - $this->config = new YellowConfig($this); - $this->text = new YellowText($this); - $this->lookup = new YellowLookup($this); - $this->toolbox = new YellowToolbox(); - $this->config->setDefault("sitename", "Yellow"); - $this->config->setDefault("author", "Yellow"); - $this->config->setDefault("email", "webmaster"); - $this->config->setDefault("language", "en"); - $this->config->setDefault("timezone", "UTC"); - $this->config->setDefault("theme", "default"); - $this->config->setDefault("staticUrl", ""); - $this->config->setDefault("staticDefaultFile", "index.html"); - $this->config->setDefault("staticErrorFile", "404.html"); - $this->config->setDefault("staticDir", "cache/"); - $this->config->setDefault("mediaLocation", "/media/"); - $this->config->setDefault("downloadLocation", "/media/downloads/"); - $this->config->setDefault("imageLocation", "/media/images/"); - $this->config->setDefault("pluginLocation", "/media/plugins/"); - $this->config->setDefault("themeLocation", "/media/themes/"); - $this->config->setDefault("assetLocation", "/media/themes/assets/"); - $this->config->setDefault("mediaDir", "media/"); - $this->config->setDefault("downloadDir", "media/downloads/"); - $this->config->setDefault("imageDir", "media/images/"); - $this->config->setDefault("systemDir", "system/"); - $this->config->setDefault("configDir", "system/config/"); - $this->config->setDefault("pluginDir", "system/plugins/"); - $this->config->setDefault("themeDir", "system/themes/"); - $this->config->setDefault("assetDir", "system/themes/assets/"); - $this->config->setDefault("snippetDir", "system/themes/snippets/"); - $this->config->setDefault("templateDir", "system/themes/templates/"); - $this->config->setDefault("trashDir", "system/trash/"); - $this->config->setDefault("contentDir", "content/"); - $this->config->setDefault("contentRootDir", "default/"); - $this->config->setDefault("contentHomeDir", "home/"); - $this->config->setDefault("contentPagination", "page"); - $this->config->setDefault("contentDefaultFile", "page.txt"); - $this->config->setDefault("contentExtension", ".txt"); - $this->config->setDefault("configExtension", ".ini"); - $this->config->setDefault("downloadExtension", ".download"); - $this->config->setDefault("configFile", "config.ini"); - $this->config->setDefault("textFile", "text.ini"); - $this->config->setDefault("languageFile", "language-(.*).txt"); - $this->config->setDefault("errorFile", "page-error-(.*).txt"); - $this->config->setDefault("newFile", "page-new-(.*).txt"); - $this->config->setDefault("robotsFile", "robots.txt"); - $this->config->setDefault("faviconFile", "favicon.ico"); - $this->config->setDefault("serverUrl", ""); - $this->config->setDefault("template", "default"); - $this->config->setDefault("navigation", "navigation"); - $this->config->setDefault("sidebar", "sidebar"); - $this->config->setDefault("siteicon", "icon"); - $this->config->setDefault("tagline", ""); - $this->config->setDefault("parser", "markdown"); - $this->config->setDefault("parserSafeMode", "0"); - $this->config->setDefault("multiLanguageMode", "0"); - $this->config->setDefault("installationMode", "0"); - $this->config->setDefault("startupUpdate", "none"); - } - - function __destruct() - { - $this->shutdown(); - } - - // Handle initialisation - function load() - { - if(defined("DEBUG") && DEBUG>=2) - { - $serverVersion = $this->toolbox->getServerVersion(); - echo "Datenstrom Yellow ".YellowCore::VERSION.", PHP ".PHP_VERSION.", $serverVersion<br/>\n"; - } - $this->toolbox->timerStart($time); - $this->config->load($this->config->get("configDir").$this->config->get("configFile")); - $this->lookup->load(); - $this->themes->load(); - $this->plugins->load(); - $this->text->load($this->config->get("pluginDir").$this->config->get("languageFile"), ""); - $this->text->load($this->config->get("configDir").$this->config->get("textFile"), $this->config->get("language")); - $this->toolbox->timerStop($time); - $this->startup(); - if(defined("DEBUG") && DEBUG>=2) - { - $plugins = count($this->plugins->plugins); - $themes = count($this->themes->themes); - $languages = count($this->text->text); - echo "YellowCore::load plugins:$plugins themes:$themes languages:$languages time:$time ms<br/>\n"; - } - } - - // Handle request - function request() - { - ob_start(); - $statusCode = 0; - $this->toolbox->timerStart($time); - list($scheme, $address, $base, $location, $fileName) = $this->getRequestInformation(); - $this->page->setRequestInformation($scheme, $address, $base, $location, $fileName); - foreach($this->plugins->plugins as $key=>$value) - { - if(method_exists($value["obj"], "onRequest")) - { - $this->lookup->requestHandler = $key; - $statusCode = $value["obj"]->onRequest($scheme, $address, $base, $location, $fileName); - if($statusCode!=0) break; - } - } - if($statusCode==0) - { - $this->lookup->requestHandler = "core"; - $statusCode = $this->processRequest($scheme, $address, $base, $location, $fileName, true); - } - if($this->page->isExisting("pageError")) $statusCode = $this->processRequestError(); - $this->toolbox->timerStop($time); - ob_end_flush(); - if(defined("DEBUG") && DEBUG>=1 && $this->lookup->isContentFile($fileName)) - { - $handler = $this->getRequestHandler(); - echo "YellowCore::request status:$statusCode handler:$handler time:$time ms<br/>\n"; - } - return $statusCode; - } - - // Process request - function processRequest($scheme, $address, $base, $location, $fileName, $cacheable) - { - $statusCode = 0; - if(is_readable($fileName)) - { - if($this->toolbox->isRequestCleanUrl($location)) - { - $location = $location.$this->getRequestLocationArgsClean(); - $location = $this->lookup->normaliseUrl($scheme, $address, $base, $location); - $statusCode = $this->sendStatus(303, $location); - } - } else { - if($this->lookup->isRedirectLocation($location)) - { - $location = $this->lookup->isFileLocation($location) ? "$location/" : "/".$this->getRequestLanguage()."/"; - $location = $this->lookup->normaliseUrl($scheme, $address, $base, $location); - $statusCode = $this->sendStatus(301, $location); - } - } - if($statusCode==0) - { - $fileName = $this->lookup->findFileStatic($location, $fileName, $cacheable && !$this->isCommandLine()); - if($this->lookup->isContentFile($fileName) || !is_readable($fileName)) - { - $fileName = $this->readPage($scheme, $address, $base, $location, $fileName, $cacheable, - max(is_readable($fileName) ? 200 : 404, $this->page->statusCode), $this->page->get("pageError")); - $statusCode = $this->sendPage(); - } else { - $statusCode = $this->sendFile(200, $fileName, true); - } - } - if(defined("DEBUG") && DEBUG>=1 && $this->lookup->isContentFile($fileName)) - { - echo "YellowCore::processRequest file:$fileName<br/>\n"; - } - return $statusCode; - } - - // Process request with error - function processRequestError() - { - ob_clean(); - $fileName = $this->readPage($this->page->scheme, $this->page->address, $this->page->base, - $this->page->location, $this->page->fileName, $this->page->cacheable, $this->page->statusCode, - $this->page->get("pageError")); - $statusCode = $this->sendPage(); - if(defined("DEBUG") && DEBUG>=1) echo "YellowCore::processRequestError file:$fileName<br/>\n"; - return $statusCode; - } - - // Read page - function readPage($scheme, $address, $base, $location, $fileName, $cacheable, $statusCode, $pageError) - { - if($statusCode>=400) - { - $cacheable = false; - $fileName = $this->config->get("configDir").$this->config->get("errorFile"); - $fileName = strreplaceu("(.*)", $statusCode, $fileName); - $rawData = $this->toolbox->readFile($fileName); - if(empty($rawData)) $rawData = "---\nTitle:".$this->toolbox->getHttpStatusFormatted($statusCode, true)."\n---\n"; - } else { - $rawData = $this->toolbox->readFile($fileName); - } - $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->page->parseContent(); - return $fileName; - } - - // Send page response - function sendPage() - { - $this->page->parsePage(); - $statusCode = $this->page->statusCode; - $lastModifiedFormatted = $this->page->getHeader("Last-Modified"); - if($statusCode==200 && $this->page->isCacheable() && $this->toolbox->isRequestNotModified($lastModifiedFormatted)) - { - $statusCode = 304; - @header($this->toolbox->getHttpStatusFormatted($statusCode)); - } else { - @header($this->toolbox->getHttpStatusFormatted($statusCode)); - foreach($this->page->headerData as $key=>$value) @header("$key: $value"); - if(!is_null($this->page->outputData)) echo $this->page->outputData; - } - if(defined("DEBUG") && DEBUG>=1) - { - foreach($this->page->headerData as $key=>$value) echo "YellowCore::sendPage $key: $value<br/>\n"; - $theme = $this->page->get("theme"); - $template = $this->page->get("template"); - $parser = $this->page->get("parser"); - echo "YellowCore::sendPage theme:$theme template:$template parser:$parser<br/>\n"; - } - return $statusCode; - } - - // Send file response - function sendFile($statusCode, $fileName, $cacheable) - { - $lastModifiedFormatted = $this->toolbox->getHttpDateFormatted($this->toolbox->getFileModified($fileName)); - if($statusCode==200 && $cacheable && $this->toolbox->isRequestNotModified($lastModifiedFormatted)) - { - $statusCode = 304; - @header($this->toolbox->getHttpStatusFormatted($statusCode)); - } else { - @header($this->toolbox->getHttpStatusFormatted($statusCode)); - if(!$cacheable) @header("Cache-Control: no-cache, must-revalidate"); - @header("Content-Type: ".$this->toolbox->getMimeContentType($fileName)); - @header("Last-Modified: ".$lastModifiedFormatted); - echo $this->toolbox->readFile($fileName); - } - return $statusCode; - } - - // Send data response - function sendData($statusCode, $rawData, $fileName, $cacheable) - { - @header($this->toolbox->getHttpStatusFormatted($statusCode)); - if(!$cacheable) @header("Cache-Control: no-cache, must-revalidate"); - @header("Content-Type: ".$this->toolbox->getMimeContentType($fileName)); - @header("Last-Modified: ".$this->toolbox->getHttpDateFormatted(time())); - echo $rawData; - return $statusCode; - } + public function __construct() { + $this->page = new YellowPage($this); + $this->pages = new YellowPages($this); + $this->files = new YellowFiles($this); + $this->plugins = new YellowPlugins($this); + $this->themes = new YellowThemes($this); + $this->config = new YellowConfig($this); + $this->text = new YellowText($this); + $this->lookup = new YellowLookup($this); + $this->toolbox = new YellowToolbox(); + $this->config->setDefault("sitename", "Yellow"); + $this->config->setDefault("author", "Yellow"); + $this->config->setDefault("email", "webmaster"); + $this->config->setDefault("language", "en"); + $this->config->setDefault("timezone", "UTC"); + $this->config->setDefault("theme", "default"); + $this->config->setDefault("staticUrl", ""); + $this->config->setDefault("staticDefaultFile", "index.html"); + $this->config->setDefault("staticErrorFile", "404.html"); + $this->config->setDefault("staticDir", "cache/"); + $this->config->setDefault("mediaLocation", "/media/"); + $this->config->setDefault("downloadLocation", "/media/downloads/"); + $this->config->setDefault("imageLocation", "/media/images/"); + $this->config->setDefault("pluginLocation", "/media/plugins/"); + $this->config->setDefault("themeLocation", "/media/themes/"); + $this->config->setDefault("assetLocation", "/media/themes/assets/"); + $this->config->setDefault("mediaDir", "media/"); + $this->config->setDefault("downloadDir", "media/downloads/"); + $this->config->setDefault("imageDir", "media/images/"); + $this->config->setDefault("systemDir", "system/"); + $this->config->setDefault("configDir", "system/config/"); + $this->config->setDefault("pluginDir", "system/plugins/"); + $this->config->setDefault("themeDir", "system/themes/"); + $this->config->setDefault("assetDir", "system/themes/assets/"); + $this->config->setDefault("snippetDir", "system/themes/snippets/"); + $this->config->setDefault("templateDir", "system/themes/templates/"); + $this->config->setDefault("trashDir", "system/trash/"); + $this->config->setDefault("contentDir", "content/"); + $this->config->setDefault("contentRootDir", "default/"); + $this->config->setDefault("contentHomeDir", "home/"); + $this->config->setDefault("contentPagination", "page"); + $this->config->setDefault("contentDefaultFile", "page.txt"); + $this->config->setDefault("contentExtension", ".txt"); + $this->config->setDefault("configExtension", ".ini"); + $this->config->setDefault("downloadExtension", ".download"); + $this->config->setDefault("configFile", "config.ini"); + $this->config->setDefault("textFile", "text.ini"); + $this->config->setDefault("languageFile", "language-(.*).txt"); + $this->config->setDefault("errorFile", "page-error-(.*).txt"); + $this->config->setDefault("newFile", "page-new-(.*).txt"); + $this->config->setDefault("robotsFile", "robots.txt"); + $this->config->setDefault("faviconFile", "favicon.ico"); + $this->config->setDefault("serverUrl", ""); + $this->config->setDefault("template", "default"); + $this->config->setDefault("navigation", "navigation"); + $this->config->setDefault("sidebar", "sidebar"); + $this->config->setDefault("siteicon", "icon"); + $this->config->setDefault("tagline", ""); + $this->config->setDefault("parser", "markdown"); + $this->config->setDefault("parserSafeMode", "0"); + $this->config->setDefault("multiLanguageMode", "0"); + $this->config->setDefault("installationMode", "0"); + $this->config->setDefault("startupUpdate", "none"); + } + + public function __destruct() { + $this->shutdown(); + } + + // Handle initialisation + public function load() { + if (defined("DEBUG") && DEBUG>=2) { + $serverVersion = $this->toolbox->getServerVersion(); + echo "Datenstrom Yellow ".YellowCore::VERSION.", PHP ".PHP_VERSION.", $serverVersion<br/>\n"; + } + $this->toolbox->timerStart($time); + $this->config->load($this->config->get("configDir").$this->config->get("configFile")); + $this->lookup->load(); + $this->themes->load(); + $this->plugins->load(); + $this->text->load($this->config->get("pluginDir").$this->config->get("languageFile"), ""); + $this->text->load($this->config->get("configDir").$this->config->get("textFile"), $this->config->get("language")); + $this->toolbox->timerStop($time); + $this->startup(); + if (defined("DEBUG") && DEBUG>=2) { + $plugins = count($this->plugins->plugins); + $themes = count($this->themes->themes); + $languages = count($this->text->text); + echo "YellowCore::load plugins:$plugins themes:$themes languages:$languages time:$time ms<br/>\n"; + } + } + + // Handle request + public function request() { + ob_start(); + $statusCode = 0; + $this->toolbox->timerStart($time); + list($scheme, $address, $base, $location, $fileName) = $this->getRequestInformation(); + $this->page->setRequestInformation($scheme, $address, $base, $location, $fileName); + foreach ($this->plugins->plugins as $key=>$value) { + if (method_exists($value["obj"], "onRequest")) { + $this->lookup->requestHandler = $key; + $statusCode = $value["obj"]->onRequest($scheme, $address, $base, $location, $fileName); + if ($statusCode!=0) break; + } + } + if ($statusCode==0) { + $this->lookup->requestHandler = "core"; + $statusCode = $this->processRequest($scheme, $address, $base, $location, $fileName, true); + } + if ($this->page->isExisting("pageError")) $statusCode = $this->processRequestError(); + $this->toolbox->timerStop($time); + ob_end_flush(); + if (defined("DEBUG") && DEBUG>=1 && $this->lookup->isContentFile($fileName)) { + $handler = $this->getRequestHandler(); + echo "YellowCore::request status:$statusCode handler:$handler time:$time ms<br/>\n"; + } + return $statusCode; + } + + // Process request + public function processRequest($scheme, $address, $base, $location, $fileName, $cacheable) { + $statusCode = 0; + if (is_readable($fileName)) { + if ($this->toolbox->isRequestCleanUrl($location)) { + $location = $location.$this->getRequestLocationArgsClean(); + $location = $this->lookup->normaliseUrl($scheme, $address, $base, $location); + $statusCode = $this->sendStatus(303, $location); + } + } else { + if ($this->lookup->isRedirectLocation($location)) { + $location = $this->lookup->isFileLocation($location) ? "$location/" : "/".$this->getRequestLanguage()."/"; + $location = $this->lookup->normaliseUrl($scheme, $address, $base, $location); + $statusCode = $this->sendStatus(301, $location); + } + } + if ($statusCode==0) { + $fileName = $this->lookup->findFileStatic($location, $fileName, $cacheable && !$this->isCommandLine()); + if ($this->lookup->isContentFile($fileName) || !is_readable($fileName)) { + $fileName = $this->readPage($scheme, $address, $base, $location, $fileName, $cacheable, + max(is_readable($fileName) ? 200 : 404, $this->page->statusCode), $this->page->get("pageError")); + $statusCode = $this->sendPage(); + } else { + $statusCode = $this->sendFile(200, $fileName, true); + } + } + if (defined("DEBUG") && DEBUG>=1 && $this->lookup->isContentFile($fileName)) { + echo "YellowCore::processRequest file:$fileName<br/>\n"; + } + return $statusCode; + } + + // Process request with error + public function processRequestError() { + ob_clean(); + $fileName = $this->readPage($this->page->scheme, $this->page->address, $this->page->base, + $this->page->location, $this->page->fileName, $this->page->cacheable, $this->page->statusCode, + $this->page->get("pageError")); + $statusCode = $this->sendPage(); + if (defined("DEBUG") && DEBUG>=1) echo "YellowCore::processRequestError file:$fileName<br/>\n"; + return $statusCode; + } + + // Read page + public function readPage($scheme, $address, $base, $location, $fileName, $cacheable, $statusCode, $pageError) { + if ($statusCode>=400) { + $cacheable = false; + $fileName = $this->config->get("configDir").$this->config->get("errorFile"); + $fileName = strreplaceu("(.*)", $statusCode, $fileName); + $rawData = $this->toolbox->readFile($fileName); + if (empty($rawData)) $rawData = "---\nTitle:".$this->toolbox->getHttpStatusFormatted($statusCode, true)."\n---\n"; + } else { + $rawData = $this->toolbox->readFile($fileName); + } + $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->page->parseContent(); + return $fileName; + } + + // Send page response + public function sendPage() { + $this->page->parsePage(); + $statusCode = $this->page->statusCode; + $lastModifiedFormatted = $this->page->getHeader("Last-Modified"); + if ($statusCode==200 && $this->page->isCacheable() && $this->toolbox->isRequestNotModified($lastModifiedFormatted)) { + $statusCode = 304; + @header($this->toolbox->getHttpStatusFormatted($statusCode)); + } else { + @header($this->toolbox->getHttpStatusFormatted($statusCode)); + foreach ($this->page->headerData as $key=>$value) { + @header("$key: $value"); + } + if (!is_null($this->page->outputData)) echo $this->page->outputData; + } + if (defined("DEBUG") && DEBUG>=1) { + foreach ($this->page->headerData as $key=>$value) { + echo "YellowCore::sendPage $key: $value<br/>\n"; + } + $theme = $this->page->get("theme"); + $template = $this->page->get("template"); + $parser = $this->page->get("parser"); + echo "YellowCore::sendPage theme:$theme template:$template parser:$parser<br/>\n"; + } + return $statusCode; + } + + // Send file response + public function sendFile($statusCode, $fileName, $cacheable) { + $lastModifiedFormatted = $this->toolbox->getHttpDateFormatted($this->toolbox->getFileModified($fileName)); + if ($statusCode==200 && $cacheable && $this->toolbox->isRequestNotModified($lastModifiedFormatted)) { + $statusCode = 304; + @header($this->toolbox->getHttpStatusFormatted($statusCode)); + } else { + @header($this->toolbox->getHttpStatusFormatted($statusCode)); + if (!$cacheable) @header("Cache-Control: no-cache, must-revalidate"); + @header("Content-Type: ".$this->toolbox->getMimeContentType($fileName)); + @header("Last-Modified: ".$lastModifiedFormatted); + echo $this->toolbox->readFile($fileName); + } + return $statusCode; + } + + // Send data response + public function sendData($statusCode, $rawData, $fileName, $cacheable) { + @header($this->toolbox->getHttpStatusFormatted($statusCode)); + if (!$cacheable) @header("Cache-Control: no-cache, must-revalidate"); + @header("Content-Type: ".$this->toolbox->getMimeContentType($fileName)); + @header("Last-Modified: ".$this->toolbox->getHttpDateFormatted(time())); + echo $rawData; + return $statusCode; + } - // Send status response - function sendStatus($statusCode, $location = "") - { - if(!empty($location)) $this->page->clean($statusCode, $location); - @header($this->toolbox->getHttpStatusFormatted($statusCode)); - foreach($this->page->headerData as $key=>$value) @header("$key: $value"); - if(defined("DEBUG") && DEBUG>=1) - { - foreach($this->page->headerData as $key=>$value) echo "YellowCore::sendStatus $key: $value<br/>\n"; - } - return $statusCode; - } - - // Handle command - function command($args = null) - { - $statusCode = 0; - $this->toolbox->timerStart($time); - foreach($this->plugins->plugins as $key=>$value) - { - if(method_exists($value["obj"], "onCommand")) - { - $this->lookup->commandHandler = $key; - $statusCode = $value["obj"]->onCommand(func_get_args()); - if($statusCode!=0) break; - } - } - if($statusCode==0) - { - $this->lookup->commandHandler = "core"; - $statusCode = 400; - list($command) = func_get_args(); - echo "Yellow $command: Command not found\n"; - } - $this->toolbox->timerStop($time); - if(defined("DEBUG") && DEBUG>=1) - { - $handler = $this->getCommandHandler(); - echo "YellowCore::command status:$statusCode handler:$handler time:$time ms<br/>\n"; - } - return $statusCode; - } - - // Handle startup - function startup() - { - $tokens = explode(',', $this->config->get("startupUpdate")); - foreach($this->plugins->plugins as $key=>$value) - { - if(method_exists($value["obj"], "onStartup")) $value["obj"]->onStartup(in_array($value["plugin"], $tokens)); - } - foreach($this->themes->themes as $key=>$value) - { - if(method_exists($value["obj"], "onStartup")) $value["obj"]->onStartup(in_array($value["theme"], $tokens)); - } - if($this->config->get("startupUpdate")!="none") - { - $fileNameConfig = $this->config->get("configDir").$this->config->get("configFile"); - $this->config->save($fileNameConfig, array("startupUpdate" => "none")); - } - } - - // Handle shutdown - function shutdown() - { - foreach($this->plugins->plugins as $key=>$value) - { - if(method_exists($value["obj"], "onShutdown")) $value["obj"]->onShutdown(); - } - foreach($this->themes->themes as $key=>$value) - { - if(method_exists($value["obj"], "onShutdown")) $value["obj"]->onShutdown(); - } - } - - // Parse snippet - function snippet($name, $args = null) - { - $this->lookup->snippetArgs = func_get_args(); - $this->page->parseSnippet($name); - } - - // Return snippet arguments - function getSnippetArgs() - { - return $this->lookup->snippetArgs; - } - - // Return request information - function getRequestInformation($scheme = "", $address = "", $base = "") - { - if(empty($scheme) && empty($address) && empty($base)) - { - $url = $this->config->get("serverUrl"); - if(empty($url) || $this->isCommandLine()) $url = $this->toolbox->getServerUrl(); - list($scheme, $address, $base) = $this->lookup->getUrlInformation($url); - $this->config->set("serverScheme", $scheme); - $this->config->set("serverAddress", $address); - $this->config->set("serverBase", $base); - if(defined("DEBUG") && DEBUG>=3) echo "YellowCore::getRequestInformation $scheme://$address$base<br/>\n"; - } - $location = substru($this->toolbox->getLocation(), strlenu($base)); - if(empty($fileName)) $fileName = $this->lookup->findFileFromSystem($location); - if(empty($fileName)) $fileName = $this->lookup->findFileFromMedia($location); - if(empty($fileName)) $fileName = $this->lookup->findFileFromLocation($location); - return array($scheme, $address, $base, $location, $fileName); - } - - // Return request location - function getRequestLocationArgsClean() - { - return $this->toolbox->getLocationArgsClean($this->config->get("contentPagination")); - } - - // Return request language - function getRequestLanguage() - { - return $this->toolbox->detectBrowserLanguage($this->pages->getLanguages(), $this->config->get("language")); - } - - // Return request handler - function getRequestHandler() - { - return $this->lookup->requestHandler; - } + // Send status response + public function sendStatus($statusCode, $location = "") { + if (!empty($location)) $this->page->clean($statusCode, $location); + @header($this->toolbox->getHttpStatusFormatted($statusCode)); + foreach ($this->page->headerData as $key=>$value) { + @header("$key: $value"); + } + if (defined("DEBUG") && DEBUG>=1) { + foreach ($this->page->headerData as $key=>$value) { + echo "YellowCore::sendStatus $key: $value<br/>\n"; + } + } + return $statusCode; + } + + // Handle command + public function command($args = null) { + $statusCode = 0; + $this->toolbox->timerStart($time); + foreach ($this->plugins->plugins as $key=>$value) { + if (method_exists($value["obj"], "onCommand")) { + $this->lookup->commandHandler = $key; + $statusCode = $value["obj"]->onCommand(func_get_args()); + if ($statusCode!=0) break; + } + } + if ($statusCode==0) { + $this->lookup->commandHandler = "core"; + $statusCode = 400; + list($command) = func_get_args(); + echo "Yellow $command: Command not found\n"; + } + $this->toolbox->timerStop($time); + if (defined("DEBUG") && DEBUG>=1) { + $handler = $this->getCommandHandler(); + echo "YellowCore::command status:$statusCode handler:$handler time:$time ms<br/>\n"; + } + return $statusCode; + } + + // Handle startup + public function startup() { + $tokens = explode(",", $this->config->get("startupUpdate")); + foreach ($this->plugins->plugins as $key=>$value) { + if (method_exists($value["obj"], "onStartup")) $value["obj"]->onStartup(in_array($value["plugin"], $tokens)); + } + foreach ($this->themes->themes as $key=>$value) { + if (method_exists($value["obj"], "onStartup")) $value["obj"]->onStartup(in_array($value["theme"], $tokens)); + } + if ($this->config->get("startupUpdate")!="none") { + $fileNameConfig = $this->config->get("configDir").$this->config->get("configFile"); + $this->config->save($fileNameConfig, array("startupUpdate" => "none")); + } + } + + // Handle shutdown + public function shutdown() { + foreach ($this->plugins->plugins as $key=>$value) { + if (method_exists($value["obj"], "onShutdown")) $value["obj"]->onShutdown(); + } + foreach ($this->themes->themes as $key=>$value) { + if (method_exists($value["obj"], "onShutdown")) $value["obj"]->onShutdown(); + } + } + + // Parse snippet + public function snippet($name, $args = null) { + $this->lookup->snippetArgs = func_get_args(); + $this->page->parseSnippet($name); + } + + // Return snippet arguments + public function getSnippetArgs() { + return $this->lookup->snippetArgs; + } + + // Return request information + public function getRequestInformation($scheme = "", $address = "", $base = "") { + if (empty($scheme) && empty($address) && empty($base)) { + $url = $this->config->get("serverUrl"); + if (empty($url) || $this->isCommandLine()) $url = $this->toolbox->getServerUrl(); + list($scheme, $address, $base) = $this->lookup->getUrlInformation($url); + $this->config->set("serverScheme", $scheme); + $this->config->set("serverAddress", $address); + $this->config->set("serverBase", $base); + if (defined("DEBUG") && DEBUG>=3) echo "YellowCore::getRequestInformation $scheme://$address$base<br/>\n"; + } + $location = substru($this->toolbox->getLocation(), strlenu($base)); + if (empty($fileName)) $fileName = $this->lookup->findFileFromSystem($location); + if (empty($fileName)) $fileName = $this->lookup->findFileFromMedia($location); + if (empty($fileName)) $fileName = $this->lookup->findFileFromLocation($location); + return array($scheme, $address, $base, $location, $fileName); + } + + // Return request location + public function getRequestLocationArgsClean() { + return $this->toolbox->getLocationArgsClean($this->config->get("contentPagination")); + } + + // Return request language + public function getRequestLanguage() { + return $this->toolbox->detectBrowserLanguage($this->pages->getLanguages(), $this->config->get("language")); + } + + // Return request handler + public function getRequestHandler() { + return $this->lookup->requestHandler; + } - // Return command handler - function getCommandHandler() - { - return $this->lookup->commandHandler; - } - - // Check if running at command line - function isCommandLine() - { - return !empty($this->lookup->commandHandler); - } + // Return command handler + public function getCommandHandler() { + return $this->lookup->commandHandler; + } + + // Check if running at command line + public function isCommandLine() { + return !empty($this->lookup->commandHandler); + } } - -class YellowPage -{ - var $yellow; //access to API - var $scheme; //server scheme - var $address; //server address - var $base; //base location - var $location; //page location - var $fileName; //content file name - var $rawData; //raw data of page - var $metaDataOffsetBytes; //meta data offset - var $metaData; //meta data - var $pageCollection; //page collection - var $pageRelations; //page relations - var $headerData; //response header - var $outputData; //response output - var $parser; //content parser - var $parserData; //content data of page - var $parserSafeMode; //page is parsed in safe mode? (boolean) - var $available; //page is available? (boolean) - var $visible; //page is visible location? (boolean) - var $active; //page is active location? (boolean) - var $cacheable; //page is cacheable? (boolean) - var $lastModified; //last modification date - var $statusCode; //status code + +class YellowPage { + public $yellow; //access to API + public $scheme; //server scheme + public $address; //server address + public $base; //base location + public $location; //page location + public $fileName; //content file name + public $rawData; //raw data of page + public $metaDataOffsetBytes; //meta data offset + public $metaData; //meta data + public $pageCollection; //page collection + public $pageRelations; //page relations + public $headerData; //response header + public $outputData; //response output + public $parser; //content parser + public $parserData; //content data of page + public $parserSafeMode; //page is parsed in safe mode? (boolean) + public $available; //page is available? (boolean) + public $visible; //page is visible location? (boolean) + public $active; //page is active location? (boolean) + public $cacheable; //page is cacheable? (boolean) + public $lastModified; //last modification date + public $statusCode; //status code - function __construct($yellow) - { - $this->yellow = $yellow; - $this->metaData = new YellowDataCollection(); - $this->pageCollection = new YellowPageCollection($yellow); - $this->pageRelations = array(); - $this->headerData = array(); - } + public function __construct($yellow) { + $this->yellow = $yellow; + $this->metaData = new YellowDataCollection(); + $this->pageCollection = new YellowPageCollection($yellow); + $this->pageRelations = array(); + $this->headerData = array(); + } - // Set request information - function setRequestInformation($scheme, $address, $base, $location, $fileName) - { - $this->scheme = $scheme; - $this->address = $address; - $this->base = $base; - $this->location = $location; - $this->fileName = $fileName; - } - - // Parse page data - function parseData($rawData, $cacheable, $statusCode, $pageError = "") - { - $this->rawData = $rawData; - $this->parser = null; - $this->parserData = ""; - $this->parserSafeMode = intval($this->yellow->config->get("parserSafeMode")); - $this->available = true; - $this->visible = $this->yellow->lookup->isVisibleLocation($this->location, $this->fileName); - $this->active = $this->yellow->lookup->isActiveLocation($this->location, $this->yellow->page->location); - $this->cacheable = $cacheable; - $this->lastModified = 0; - $this->statusCode = $statusCode; - $this->parseMeta($pageError); - } - - // Parse page data update - function parseDataUpdate() - { - if($this->statusCode==0) - { - $this->rawData = $this->yellow->toolbox->readFile($this->fileName); - $this->statusCode = 200; - $this->parseMeta(); - } - } - - // Parse page meta data - function parseMeta($pageError = "") - { - $this->metaData = new YellowDataCollection(); - if(!is_null($this->rawData)) - { - $this->set("title", $this->yellow->toolbox->createTextTitle($this->location)); - $this->set("language", $this->yellow->lookup->findLanguageFromFile($this->fileName, $this->yellow->config->get("language"))); - $this->set("modified", date("Y-m-d H:i:s", $this->yellow->toolbox->getFileModified($this->fileName))); - $this->parseMetaRaw(array("theme", "template", "sitename", "siteicon", "tagline", "author", "navigation", "sidebar", "parser")); - $titleHeader = ($this->location==$this->yellow->pages->getHomeLocation($this->location)) ? - $this->get("sitename") : $this->get("title")." - ".$this->get("sitename"); - if(!$this->isExisting("titleContent")) $this->set("titleContent", $this->get("title")); - if(!$this->isExisting("titleNavigation")) $this->set("titleNavigation", $this->get("title")); - if(!$this->isExisting("titleHeader")) $this->set("titleHeader", $titleHeader); - if($this->get("status")=="hidden") $this->available = false; - $this->set("pageRead", $this->yellow->lookup->normaliseUrl( - $this->yellow->config->get("serverScheme"), - $this->yellow->config->get("serverAddress"), - $this->yellow->config->get("serverBase"), $this->location)); - $this->set("pageEdit", $this->yellow->lookup->normaliseUrl( - $this->yellow->config->get("serverScheme"), - $this->yellow->config->get("serverAddress"), - $this->yellow->config->get("serverBase"), - rtrim($this->yellow->config->get("editLocation"), '/').$this->location)); - } else { - $this->set("type", $this->yellow->toolbox->getFileType($this->fileName)); - $this->set("group", $this->yellow->toolbox->getFileGroup($this->fileName, $this->yellow->config->get("mediaDir"))); - $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->plugins->plugins as $key=>$value) - { - if(method_exists($value["obj"], "onParseMeta")) $value["obj"]->onParseMeta($this); - } - } - - // Parse page meta data from raw data - function parseMetaRaw($defaultKeys) - { - foreach($defaultKeys as $key) - { - $value = $this->yellow->config->get($key); - if(!empty($key) && !strempty($value)) $this->set($key, $value); - } - if(preg_match("/^(\xEF\xBB\xBF)?\-\-\-[\r\n]+(.+?)\-\-\-[\r\n]+/s", $this->rawData, $parts)) - { - $this->metaDataOffsetBytes = strlenb($parts[0]); - foreach(preg_split("/[\r\n]+/", $parts[2]) as $line) - { - preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); - if(!empty($matches[1]) && !strempty($matches[2])) $this->set($matches[1], $matches[2]); - } - } else if(preg_match("/^(\xEF\xBB\xBF)?([^\r\n]+)[\r\n]+=+[\r\n]+/", $this->rawData, $parts)) { - $this->metaDataOffsetBytes = strlenb($parts[0]); - $this->set("title", $parts[2]); - } - } - - // Parse page content on demand - function parseContent($sizeMax = 0) - { - if(!is_object($this->parser)) - { - if($this->yellow->plugins->isExisting($this->get("parser"))) - { - $plugin = $this->yellow->plugins->plugins[$this->get("parser")]; - if(method_exists($plugin["obj"], "onParseContentRaw")) - { - $this->parser = $plugin["obj"]; - $this->parserData = $this->getContent(true, $sizeMax); - $this->parserData = preg_replace("/@pageRead/i", $this->get("pageRead"), $this->parserData); - $this->parserData = preg_replace("/@pageEdit/i", $this->get("pageEdit"), $this->parserData); - $this->parserData = $this->parser->onParseContentRaw($this, $this->parserData); - foreach($this->yellow->plugins->plugins as $key=>$value) - { - if(method_exists($value["obj"], "onParseContentText")) - { - $output = $value["obj"]->onParseContentText($this, $this->parserData); - if(!is_null($output)) $this->parserData = $output; - } - } - } - } else { - $this->parserData = $this->getContent(true, $sizeMax); - $this->parserData = preg_replace("/\[yellow error\]/i", $this->get("pageError"), $this->parserData); - } - if(!$this->isExisting("description")) - { - $this->set("description", $this->yellow->toolbox->createTextDescription($this->parserData, 150)); - } - if(!$this->isExisting("keywords")) - { - $this->set("keywords", $this->yellow->toolbox->createTextKeywords($this->get("title"), 10)); - } - if(defined("DEBUG") && DEBUG>=3) echo "YellowPage::parseContent location:".$this->location."<br/>\n"; - } - } - - // Parse page content block - function parseContentBlock($name, $text, $shortcut) - { - $output = null; - foreach($this->yellow->plugins->plugins as $key=>$value) - { - if(method_exists($value["obj"], "onParseContentBlock")) - { - $output = $value["obj"]->onParseContentBlock($this, $name, $text, $shortcut); - if(!is_null($output)) break; - } - } - if(is_null($output)) - { - if($name=="yellow" && $shortcut) - { - $output = "Datenstrom Yellow ".YellowCore::VERSION; - if($text=="error") $output = $this->get("pageError"); - if($text=="version") - { - $output = "<span class=\"".htmlspecialchars($name)."\">\n"; - $serverVersion = $this->yellow->toolbox->getServerVersion(); - $output .= "Datenstrom Yellow ".YellowCore::VERSION.", PHP ".PHP_VERSION.", $serverVersion<br />\n"; - foreach(array_merge($this->yellow->plugins->getData(), $this->yellow->themes->getData()) as $key=>$value) - { - $output .= htmlspecialchars("$key $value")."<br />\n"; - } - $output .= "</span>\n"; - if($this->parserSafeMode) $this->error(500, "Yellow '$text' is not available in safe mode!"); - } - } - } - if(defined("DEBUG") && DEBUG>=3 && !empty($name)) echo "YellowPage::parseContentBlock name:$name shortcut:$shortcut<br/>\n"; - return $output; - } - - // Parse page - function parsePage() - { - $this->outputData = null; - if(!$this->isError()) - { - foreach($this->yellow->plugins->plugins as $key=>$value) - { - if(method_exists($value["obj"], "onParsePage")) $value["obj"]->onParsePage(); - } - } - if(is_null($this->outputData)) - { - ob_start(); - $this->parseTemplate($this->get("template")); - $this->outputData = ob_get_contents(); - ob_end_clean(); - } - if(!$this->isCacheable()) $this->setHeader("Cache-Control", "no-cache, must-revalidate"); - if(!$this->isHeader("Content-Type")) $this->setHeader("Content-Type", "text/html; charset=utf-8"); - if(!$this->isHeader("Page-Modified")) $this->setHeader("Page-Modified", $this->getModified(true)); - if(!$this->isHeader("Last-Modified")) $this->setHeader("Last-Modified", $this->getLastModified(true)); - if(!$this->yellow->text->isLanguage($this->get("language"))) - { - $this->error(500, "Language '".$this->get("language")."' does not exist!"); - } - if(!$this->yellow->themes->isExisting($this->get("theme"))) - { - $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->lookup->isNestedLocation($this->location, $this->fileName, true)) - { - $this->error(500, "Folder '".dirname($this->fileName)."' may not contain subfolders!"); - } - if($this->yellow->toolbox->isRequestSelf()) - { - $serverVersion = $this->yellow->toolbox->getServerVersion(true); - $this->error(500, "Rewrite module not working on $serverVersion web server!"); - } - if($this->yellow->getRequestHandler()=="core" && $this->isExisting("redirect") && $this->statusCode==200) - { - $location = $this->yellow->lookup->normaliseLocation($this->get("redirect"), $this->location); - $location = $this->yellow->lookup->normaliseUrl($this->scheme, $this->address, "", $location); - $this->clean(301, $location); - } - if($this->yellow->getRequestHandler()=="core" && !$this->isAvailable() && $this->statusCode==200) - { - $this->error(404); - } - if($this->isExisting("pageClean")) $this->outputData = null; - } - - // Parse template - function parseTemplate($name) - { - $fileNameTemplate = $this->yellow->config->get("templateDir").$this->yellow->lookup->normaliseName($name).".html"; - if(is_file($fileNameTemplate)) - { - $this->setLastModified(filemtime($fileNameTemplate)); - global $yellow; - require($fileNameTemplate); - } else { - $this->error(500, "Template '$name' does not exist!"); - echo "Template error<br/>\n"; - } - } - - // Parse snippet - function parseSnippet($name) - { - $fileNameSnippet = $this->yellow->config->get("snippetDir").$this->yellow->lookup->normaliseName($name).".php"; - if(is_file($fileNameSnippet)) - { - $this->setLastModified(filemtime($fileNameSnippet)); - global $yellow; - require($fileNameSnippet); - } else { - $this->error(500, "Snippet '$name' does not exist!"); - echo "Snippet error<br/>\n"; - } - } - - // Set page meta data - function set($key, $value) - { - $this->metaData[$key] = $value; - } - - // Return page meta data - function get($key) - { - return $this->isExisting($key) ? $this->metaData[$key] : ""; - } + // Set request information + public function setRequestInformation($scheme, $address, $base, $location, $fileName) { + $this->scheme = $scheme; + $this->address = $address; + $this->base = $base; + $this->location = $location; + $this->fileName = $fileName; + } + + // Parse page data + public function parseData($rawData, $cacheable, $statusCode, $pageError = "") { + $this->rawData = $rawData; + $this->parser = null; + $this->parserData = ""; + $this->parserSafeMode = intval($this->yellow->config->get("parserSafeMode")); + $this->available = true; + $this->visible = $this->yellow->lookup->isVisibleLocation($this->location, $this->fileName); + $this->active = $this->yellow->lookup->isActiveLocation($this->location, $this->yellow->page->location); + $this->cacheable = $cacheable; + $this->lastModified = 0; + $this->statusCode = $statusCode; + $this->parseMeta($pageError); + } + + // Parse page data update + public function parseDataUpdate() { + if ($this->statusCode==0) { + $this->rawData = $this->yellow->toolbox->readFile($this->fileName); + $this->statusCode = 200; + $this->parseMeta(); + } + } + + // Parse page meta data + public function parseMeta($pageError = "") { + $this->metaData = new YellowDataCollection(); + if (!is_null($this->rawData)) { + $this->set("title", $this->yellow->toolbox->createTextTitle($this->location)); + $this->set("language", $this->yellow->lookup->findLanguageFromFile($this->fileName, $this->yellow->config->get("language"))); + $this->set("modified", date("Y-m-d H:i:s", $this->yellow->toolbox->getFileModified($this->fileName))); + $this->parseMetaRaw(array("theme", "template", "sitename", "siteicon", "tagline", "author", "navigation", "sidebar", "parser")); + $titleHeader = ($this->location==$this->yellow->pages->getHomeLocation($this->location)) ? + $this->get("sitename") : $this->get("title")." - ".$this->get("sitename"); + if (!$this->isExisting("titleContent")) $this->set("titleContent", $this->get("title")); + if (!$this->isExisting("titleNavigation")) $this->set("titleNavigation", $this->get("title")); + if (!$this->isExisting("titleHeader")) $this->set("titleHeader", $titleHeader); + if ($this->get("status")=="hidden") $this->available = false; + $this->set("pageRead", $this->yellow->lookup->normaliseUrl( + $this->yellow->config->get("serverScheme"), + $this->yellow->config->get("serverAddress"), + $this->yellow->config->get("serverBase"), + $this->location)); + $this->set("pageEdit", $this->yellow->lookup->normaliseUrl( + $this->yellow->config->get("serverScheme"), + $this->yellow->config->get("serverAddress"), + $this->yellow->config->get("serverBase"), + rtrim($this->yellow->config->get("editLocation"), "/").$this->location)); + } else { + $this->set("type", $this->yellow->toolbox->getFileType($this->fileName)); + $this->set("group", $this->yellow->toolbox->getFileGroup($this->fileName, $this->yellow->config->get("mediaDir"))); + $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->plugins->plugins as $key=>$value) { + if (method_exists($value["obj"], "onParseMeta")) $value["obj"]->onParseMeta($this); + } + } + + // Parse page meta data from raw data + public function parseMetaRaw($defaultKeys) { + foreach ($defaultKeys as $key) { + $value = $this->yellow->config->get($key); + if (!empty($key) && !strempty($value)) $this->set($key, $value); + } + if (preg_match("/^(\xEF\xBB\xBF)?\-\-\-[\r\n]+(.+?)\-\-\-[\r\n]+/s", $this->rawData, $parts)) { + $this->metaDataOffsetBytes = strlenb($parts[0]); + foreach (preg_split("/[\r\n]+/", $parts[2]) as $line) { + preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); + if (!empty($matches[1]) && !strempty($matches[2])) $this->set($matches[1], $matches[2]); + } + } elseif (preg_match("/^(\xEF\xBB\xBF)?([^\r\n]+)[\r\n]+=+[\r\n]+/", $this->rawData, $parts)) { + $this->metaDataOffsetBytes = strlenb($parts[0]); + $this->set("title", $parts[2]); + } + } + + // Parse page content on demand + public function parseContent($sizeMax = 0) { + if (!is_object($this->parser)) { + if ($this->yellow->plugins->isExisting($this->get("parser"))) { + $plugin = $this->yellow->plugins->plugins[$this->get("parser")]; + if (method_exists($plugin["obj"], "onParseContentRaw")) { + $this->parser = $plugin["obj"]; + $this->parserData = $this->getContent(true, $sizeMax); + $this->parserData = preg_replace("/@pageRead/i", $this->get("pageRead"), $this->parserData); + $this->parserData = preg_replace("/@pageEdit/i", $this->get("pageEdit"), $this->parserData); + $this->parserData = $this->parser->onParseContentRaw($this, $this->parserData); + foreach ($this->yellow->plugins->plugins as $key=>$value) { + if (method_exists($value["obj"], "onParseContentText")) { + $output = $value["obj"]->onParseContentText($this, $this->parserData); + if (!is_null($output)) $this->parserData = $output; + } + } + } + } else { + $this->parserData = $this->getContent(true, $sizeMax); + $this->parserData = preg_replace("/\[yellow error\]/i", $this->get("pageError"), $this->parserData); + } + if (!$this->isExisting("description")) { + $this->set("description", $this->yellow->toolbox->createTextDescription($this->parserData, 150)); + } + if (!$this->isExisting("keywords")) { + $this->set("keywords", $this->yellow->toolbox->createTextKeywords($this->get("title"), 10)); + } + if (defined("DEBUG") && DEBUG>=3) echo "YellowPage::parseContent location:".$this->location."<br/>\n"; + } + } + + // Parse page content block + public function parseContentBlock($name, $text, $shortcut) { + $output = null; + foreach ($this->yellow->plugins->plugins as $key=>$value) { + if (method_exists($value["obj"], "onParseContentBlock")) { + $output = $value["obj"]->onParseContentBlock($this, $name, $text, $shortcut); + if (!is_null($output)) break; + } + } + if (is_null($output)) { + if ($name=="yellow" && $shortcut) { + $output = "Datenstrom Yellow ".YellowCore::VERSION; + if ($text=="error") $output = $this->get("pageError"); + if ($text=="version") { + $output = "<span class=\"".htmlspecialchars($name)."\">\n"; + $serverVersion = $this->yellow->toolbox->getServerVersion(); + $output .= "Datenstrom Yellow ".YellowCore::VERSION.", PHP ".PHP_VERSION.", $serverVersion<br />\n"; + foreach (array_merge($this->yellow->plugins->getData(), $this->yellow->themes->getData()) as $key=>$value) { + $output .= htmlspecialchars("$key $value")."<br />\n"; + } + $output .= "</span>\n"; + if ($this->parserSafeMode) $this->error(500, "Yellow '$text' is not available in safe mode!"); + } + } + } + if (defined("DEBUG") && DEBUG>=3 && !empty($name)) echo "YellowPage::parseContentBlock name:$name shortcut:$shortcut<br/>\n"; + return $output; + } + + // Parse page + public function parsePage() { + $this->outputData = null; + if (!$this->isError()) { + foreach ($this->yellow->plugins->plugins as $key=>$value) { + if (method_exists($value["obj"], "onParsePage")) $value["obj"]->onParsePage(); + } + } + if (is_null($this->outputData)) { + ob_start(); + $this->parseTemplate($this->get("template")); + $this->outputData = ob_get_contents(); + ob_end_clean(); + } + if (!$this->isCacheable()) $this->setHeader("Cache-Control", "no-cache, must-revalidate"); + if (!$this->isHeader("Content-Type")) $this->setHeader("Content-Type", "text/html; charset=utf-8"); + if (!$this->isHeader("Page-Modified")) $this->setHeader("Page-Modified", $this->getModified(true)); + if (!$this->isHeader("Last-Modified")) $this->setHeader("Last-Modified", $this->getLastModified(true)); + if (!$this->yellow->text->isLanguage($this->get("language"))) { + $this->error(500, "Language '".$this->get("language")."' does not exist!"); + } + if (!$this->yellow->themes->isExisting($this->get("theme"))) { + $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->lookup->isNestedLocation($this->location, $this->fileName, true)) { + $this->error(500, "Folder '".dirname($this->fileName)."' may not contain subfolders!"); + } + if ($this->yellow->toolbox->isRequestSelf()) { + $serverVersion = $this->yellow->toolbox->getServerVersion(true); + $this->error(500, "Rewrite module not working on $serverVersion web server!"); + } + if ($this->yellow->getRequestHandler()=="core" && $this->isExisting("redirect") && $this->statusCode==200) { + $location = $this->yellow->lookup->normaliseLocation($this->get("redirect"), $this->location); + $location = $this->yellow->lookup->normaliseUrl($this->scheme, $this->address, "", $location); + $this->clean(301, $location); + } + if ($this->yellow->getRequestHandler()=="core" && !$this->isAvailable() && $this->statusCode==200) { + $this->error(404); + } + if ($this->isExisting("pageClean")) $this->outputData = null; + } + + // Parse template + public function parseTemplate($name) { + $fileNameTemplate = $this->yellow->config->get("templateDir").$this->yellow->lookup->normaliseName($name).".html"; + if (is_file($fileNameTemplate)) { + $this->setLastModified(filemtime($fileNameTemplate)); + global $yellow; + require($fileNameTemplate); + } else { + $this->error(500, "Template '$name' does not exist!"); + echo "Template error<br/>\n"; + } + } + + // Parse snippet + public function parseSnippet($name) { + $fileNameSnippet = $this->yellow->config->get("snippetDir").$this->yellow->lookup->normaliseName($name).".php"; + if (is_file($fileNameSnippet)) { + $this->setLastModified(filemtime($fileNameSnippet)); + global $yellow; + require($fileNameSnippet); + } else { + $this->error(500, "Snippet '$name' does not exist!"); + echo "Snippet error<br/>\n"; + } + } + + // Set page meta data + public function set($key, $value) { + $this->metaData[$key] = $value; + } + + // Return page meta data + public function get($key) { + return $this->isExisting($key) ? $this->metaData[$key] : ""; + } - // Return page meta data, HTML encoded - function getHtml($key) - { - return htmlspecialchars($this->get($key)); - } - - // Return page meta data as language specific date - function getDate($key, $format = "") - { - if(!empty($format)) - { - $format = $this->yellow->text->get($format); - } else { - $format = $this->yellow->text->get("dateFormatMedium"); - } - return $this->yellow->text->getDateFormatted(strtotime($this->get($key)), $format); - } + // Return page meta data, HTML encoded + public function getHtml($key) { + return htmlspecialchars($this->get($key)); + } + + // Return page meta data as language specific date + public function getDate($key, $format = "") { + if (!empty($format)) { + $format = $this->yellow->text->get($format); + } else { + $format = $this->yellow->text->get("dateFormatMedium"); + } + return $this->yellow->text->getDateFormatted(strtotime($this->get($key)), $format); + } - // Return page meta data as language specific date, HTML encoded - function getDateHtml($key, $format = "") - { - return htmlspecialchars($this->getDate($key, $format)); - } + // Return page meta data as language specific date, HTML encoded + public function getDateHtml($key, $format = "") { + return htmlspecialchars($this->getDate($key, $format)); + } - // Return page meta data as language specific date and relative to today - function getDateRelative($key, $format = "", $daysLimit = 0) - { - if(!empty($format)) - { - $format = $this->yellow->text->get($format); - } else { - $format = $this->yellow->text->get("dateFormatMedium"); - } - return $this->yellow->text->getDateRelative(strtotime($this->get($key)), $format, $daysLimit); - } - - // Return page meta data as language specific date and relative to today, HTML encoded - function getDateRelativeHtml($key, $format = "", $daysLimit = 0) - { - return htmlspecialchars($this->getDateRelative($key, $format, $daysLimit)); - } + // Return page meta data as language specific date and relative to today + public function getDateRelative($key, $format = "", $daysLimit = 0) { + if (!empty($format)) { + $format = $this->yellow->text->get($format); + } else { + $format = $this->yellow->text->get("dateFormatMedium"); + } + return $this->yellow->text->getDateRelative(strtotime($this->get($key)), $format, $daysLimit); + } + + // Return page meta data as language specific date and relative to today, HTML encoded + public function getDateRelativeHtml($key, $format = "", $daysLimit = 0) { + return htmlspecialchars($this->getDateRelative($key, $format, $daysLimit)); + } - // Return page meta data as custom date - function getDateFormatted($key, $format) - { - return $this->yellow->text->getDateFormatted(strtotime($this->get($key)), $format); - } - - // Return page meta data as custom date, HTML encoded - function getDateFormattedHtml($key, $format) - { - return htmlspecialchars($this->getDateFormatted($key, $format)); - } + // Return page meta data as custom date + public function getDateFormatted($key, $format) { + return $this->yellow->text->getDateFormatted(strtotime($this->get($key)), $format); + } + + // Return page meta data as custom date, HTML encoded + public function getDateFormattedHtml($key, $format) { + return htmlspecialchars($this->getDateFormatted($key, $format)); + } - // Return page content, HTML encoded or raw format - function getContent($rawFormat = false, $sizeMax = 0) - { - if($rawFormat) - { - $this->parseDataUpdate(); - $text = substrb($this->rawData, $this->metaDataOffsetBytes); - } else { - $this->parseContent($sizeMax); - $text = $this->parserData; - } - return $sizeMax ? substrb($text, 0, $sizeMax) : $text; - } - - // Return parent page of current page, null if none - function getParent() - { - $parentLocation = $this->yellow->pages->getParentLocation($this->location); - return $this->yellow->pages->find($parentLocation); - } - - // Return top-level page for current page, null if none - function getParentTop($homeFailback = true) - { - $parentTopLocation = $this->yellow->pages->getParentTopLocation($this->location); - if(!$this->yellow->pages->find($parentTopLocation) && $homeFailback) - { - $parentTopLocation = $this->yellow->pages->getHomeLocation($this->location); - } - return $this->yellow->pages->find($parentTopLocation); - } - - // Return page collection with pages on the same level as current page - function getSiblings($showInvisible = false) - { - $parentLocation = $this->yellow->pages->getParentLocation($this->location); - return $this->yellow->pages->getChildren($parentLocation, $showInvisible); - } - - // Return page collection with child pages of current page - function getChildren($showInvisible = false) - { - return $this->yellow->pages->getChildren($this->location, $showInvisible); - } + // Return page content, HTML encoded or raw format + public function getContent($rawFormat = false, $sizeMax = 0) { + if ($rawFormat) { + $this->parseDataUpdate(); + $text = substrb($this->rawData, $this->metaDataOffsetBytes); + } else { + $this->parseContent($sizeMax); + $text = $this->parserData; + } + return $sizeMax ? substrb($text, 0, $sizeMax) : $text; + } + + // Return parent page of current page, null if none + public function getParent() { + $parentLocation = $this->yellow->pages->getParentLocation($this->location); + return $this->yellow->pages->find($parentLocation); + } + + // Return top-level page for current page, null if none + public function getParentTop($homeFailback = true) { + $parentTopLocation = $this->yellow->pages->getParentTopLocation($this->location); + if (!$this->yellow->pages->find($parentTopLocation) && $homeFailback) { + $parentTopLocation = $this->yellow->pages->getHomeLocation($this->location); + } + return $this->yellow->pages->find($parentTopLocation); + } + + // Return page collection with pages on the same level as current page + public function getSiblings($showInvisible = false) { + $parentLocation = $this->yellow->pages->getParentLocation($this->location); + return $this->yellow->pages->getChildren($parentLocation, $showInvisible); + } + + // Return page collection with child pages of current page + public function getChildren($showInvisible = false) { + return $this->yellow->pages->getChildren($this->location, $showInvisible); + } - // Return page collection with sub pages of current page - function getChildrenRecursive($showInvisible = false, $levelMax = 0) - { - return $this->yellow->pages->getChildrenRecursive($this->location, $showInvisible, $levelMax); - } - - // Set page collection with additional pages for current page - function setPages($pages) - { - $this->pageCollection = $pages; - } + // Return page collection with sub pages of current page + public function getChildrenRecursive($showInvisible = false, $levelMax = 0) { + return $this->yellow->pages->getChildrenRecursive($this->location, $showInvisible, $levelMax); + } + + // Set page collection with additional pages for current page + public function setPages($pages) { + $this->pageCollection = $pages; + } - // Return page collection with additional pages for current page - function getPages() - { - return $this->pageCollection; - } - - // Set related page - function setPage($key, $page) - { - $this->pageRelations[$key] = $page; - } - - // Return related page - function getPage($key) - { - return !is_null($this->pageRelations[$key]) ? $this->pageRelations[$key] : $this; - } - - // Return page base - function getBase($multiLanguage = false) - { - return $multiLanguage ? rtrim($this->base.$this->yellow->pages->getHomeLocation($this->location), '/') : $this->base; - } - - // Return page location - function getLocation($absoluteLocation = false) - { - return $absoluteLocation ? $this->base.$this->location : $this->location; - } - - // Return page URL - function getUrl() - { - return $this->yellow->lookup->normaliseUrl($this->scheme, $this->address, $this->base, $this->location); - } - - // Return page extra HTML data - function getExtra($name) - { - $output = ""; - foreach($this->yellow->plugins->plugins as $key=>$value) - { - if(method_exists($value["obj"], "onExtra")) - { - $outputPlugin = $value["obj"]->onExtra($name); - if(!is_null($outputPlugin)) $output .= $outputPlugin; - } - } - if($name=="header") - { - if(is_file($this->yellow->config->get("assetDir").$this->get("theme").".css")) - { - $location = $this->yellow->config->get("serverBase"). - $this->yellow->config->get("assetLocation").$this->get("theme").".css"; - $output .= "<link rel=\"stylesheet\" type=\"text/css\" media=\"all\" href=\"".htmlspecialchars($location)."\" />\n"; - } - if(is_file($this->yellow->config->get("assetDir").$this->get("theme").".js")) - { - $location = $this->yellow->config->get("serverBase"). - $this->yellow->config->get("assetLocation").$this->get("theme").".js"; - $output .= "<script type=\"text/javascript\" src=\"".htmlspecialchars($location)."\"></script>\n"; - } - if(is_file($this->yellow->config->get("assetDir").$this->get("siteicon").".png")) - { - $location = $this->yellow->config->get("serverBase"). - $this->yellow->config->get("assetLocation").$this->get("siteicon").".png"; - $contentType = $this->yellow->toolbox->getMimeContentType($location); - $output .= "<link rel=\"icon\" type=\"$contentType\" href=\"".htmlspecialchars($location)."\" />\n"; - $output .= "<link rel=\"apple-touch-icon\" type=\"$contentType\" href=\"".htmlspecialchars($location)."\" />\n"; - } - } - return $this->normaliseExtra($output); - } - - // Normalise page extra HTML data - function normaliseExtra($text) - { - $outputScript = $outputStylesheet = $outputOther = $locations = array(); - foreach($this->yellow->toolbox->getTextLines($text) as $line) - { - if(preg_match("/^<script (.*?)src=\"([^\"]+)\"(.*?)><\/script>$/i", $line, $matches)) - { - if(is_null($locations[$matches[2]])) - { - $locations[$matches[2]] = $matches[2]; - array_push($outputScript, $line); - } - } else if(preg_match("/^<link rel=\"stylesheet\"(.*?)href=\"([^\"]+)\"(.*?)>$/i", $line, $matches)) { - if(is_null($locations[$matches[2]])) - { - $locations[$matches[2]] = $matches[2]; - array_push($outputStylesheet, $line); - } - } else { - array_push($outputOther, $line); - } - } - return implode($outputScript).implode($outputStylesheet).implode($outputOther); - } - - // Set page response output - function setOutput($output) - { - $this->outputData = $output; - } - - // Set page response header - function setHeader($key, $value) - { - $this->headerData[$key] = $value; - } - - // Return page response header - function getHeader($key) - { - return $this->isHeader($key) ? $this->headerData[$key] : ""; - } - - // Return page modification date, Unix time or HTTP format - function getModified($httpFormat = false) - { - $modified = strtotime($this->get("modified")); - return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($modified) : $modified; - } - - // Set last modification date, Unix time - function setLastModified($modified) - { - $this->lastModified = max($this->lastModified, $modified); - } - - // Return last modification date, Unix time or HTTP format - function getLastModified($httpFormat = false) - { - $modified = max($this->lastModified, $this->getModified(), $this->yellow->config->getModified(), - $this->yellow->text->getModified(), $this->yellow->plugins->getModified()); - return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($modified) : $modified; - } - - // Return page status code, number or HTTP format - function getStatusCode($httpFormat = false) - { - $statusCode = $this->statusCode; - if($httpFormat) - { - $statusCode = $this->yellow->toolbox->getHttpStatusFormatted($statusCode); - if($this->isExisting("pageError")) $statusCode .= ": ".$this->get("pageError"); - } - return $statusCode; - } - - // Respond with error page - function error($statusCode, $pageError = "") - { - if(!$this->isExisting("pageError") && $statusCode>0) - { - $this->statusCode = $statusCode; - $this->set("pageError", empty($pageError) ? "Template/snippet error!" : $pageError); - } - } - - // Respond with status code, no page content - function clean($statusCode, $location = "") - { - if(!$this->isExisting("pageClean") && $statusCode>0) - { - $this->statusCode = $statusCode; - $this->lastModified = 0; - $this->headerData = array(); - if(!empty($location)) - { - $this->setHeader("Location", $location); - $this->setHeader("Cache-Control", "no-cache, must-revalidate"); - } - $this->set("pageClean", (string)$statusCode); - } - } - - // Check if page is available - function isAvailable() - { - return $this->available; - } - - // Check if page is visible - function isVisible() - { - return $this->visible; - } + // Return page collection with additional pages for current page + public function getPages() { + return $this->pageCollection; + } + + // Set related page + public function setPage($key, $page) { + $this->pageRelations[$key] = $page; + } + + // Return related page + public function getPage($key) { + return !is_null($this->pageRelations[$key]) ? $this->pageRelations[$key] : $this; + } + + // Return page base + public function getBase($multiLanguage = false) { + return $multiLanguage ? rtrim($this->base.$this->yellow->pages->getHomeLocation($this->location), "/") : $this->base; + } + + // Return page location + public function getLocation($absoluteLocation = false) { + return $absoluteLocation ? $this->base.$this->location : $this->location; + } + + // Return page URL + public function getUrl() { + return $this->yellow->lookup->normaliseUrl($this->scheme, $this->address, $this->base, $this->location); + } + + // Return page extra HTML data + public function getExtra($name) { + $output = ""; + foreach ($this->yellow->plugins->plugins as $key=>$value) { + if (method_exists($value["obj"], "onExtra")) { + $outputPlugin = $value["obj"]->onExtra($name); + if (!is_null($outputPlugin)) $output .= $outputPlugin; + } + } + if ($name=="header") { + if (is_file($this->yellow->config->get("assetDir").$this->get("theme").".css")) { + $location = $this->yellow->config->get("serverBase"). + $this->yellow->config->get("assetLocation").$this->get("theme").".css"; + $output .= "<link rel=\"stylesheet\" type=\"text/css\" media=\"all\" href=\"".htmlspecialchars($location)."\" />\n"; + } + if (is_file($this->yellow->config->get("assetDir").$this->get("theme").".js")) { + $location = $this->yellow->config->get("serverBase"). + $this->yellow->config->get("assetLocation").$this->get("theme").".js"; + $output .= "<script type=\"text/javascript\" src=\"".htmlspecialchars($location)."\"></script>\n"; + } + if (is_file($this->yellow->config->get("assetDir").$this->get("siteicon").".png")) { + $location = $this->yellow->config->get("serverBase"). + $this->yellow->config->get("assetLocation").$this->get("siteicon").".png"; + $contentType = $this->yellow->toolbox->getMimeContentType($location); + $output .= "<link rel=\"icon\" type=\"$contentType\" href=\"".htmlspecialchars($location)."\" />\n"; + $output .= "<link rel=\"apple-touch-icon\" type=\"$contentType\" href=\"".htmlspecialchars($location)."\" />\n"; + } + } + return $this->normaliseExtra($output); + } + + // Normalise page extra HTML data + public function normaliseExtra($text) { + $outputScript = $outputStylesheet = $outputOther = $locations = array(); + foreach ($this->yellow->toolbox->getTextLines($text) as $line) { + if (preg_match("/^<script (.*?)src=\"([^\"]+)\"(.*?)><\/script>$/i", $line, $matches)) { + if (is_null($locations[$matches[2]])) { + $locations[$matches[2]] = $matches[2]; + array_push($outputScript, $line); + } + } elseif (preg_match("/^<link rel=\"stylesheet\"(.*?)href=\"([^\"]+)\"(.*?)>$/i", $line, $matches)) { + if (is_null($locations[$matches[2]])) { + $locations[$matches[2]] = $matches[2]; + array_push($outputStylesheet, $line); + } + } else { + array_push($outputOther, $line); + } + } + return implode($outputScript).implode($outputStylesheet).implode($outputOther); + } + + // Set page response output + public function setOutput($output) { + $this->outputData = $output; + } + + // Set page response header + public function setHeader($key, $value) { + $this->headerData[$key] = $value; + } + + // Return page response header + public function getHeader($key) { + return $this->isHeader($key) ? $this->headerData[$key] : ""; + } + + // Return page modification date, Unix time or HTTP format + public function getModified($httpFormat = false) { + $modified = strtotime($this->get("modified")); + return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($modified) : $modified; + } + + // Set last modification date, Unix time + public function setLastModified($modified) { + $this->lastModified = max($this->lastModified, $modified); + } + + // Return last modification date, Unix time or HTTP format + public function getLastModified($httpFormat = false) { + $modified = max($this->lastModified, $this->getModified(), $this->yellow->config->getModified(), + $this->yellow->text->getModified(), $this->yellow->plugins->getModified()); + return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($modified) : $modified; + } + + // Return page status code, number or HTTP format + public function getStatusCode($httpFormat = false) { + $statusCode = $this->statusCode; + if ($httpFormat) { + $statusCode = $this->yellow->toolbox->getHttpStatusFormatted($statusCode); + if ($this->isExisting("pageError")) $statusCode .= ": ".$this->get("pageError"); + } + return $statusCode; + } + + // Respond with error page + public function error($statusCode, $pageError = "") { + if (!$this->isExisting("pageError") && $statusCode>0) { + $this->statusCode = $statusCode; + $this->set("pageError", empty($pageError) ? "Template/snippet error!" : $pageError); + } + } + + // Respond with status code, no page content + public function clean($statusCode, $location = "") { + if (!$this->isExisting("pageClean") && $statusCode>0) { + $this->statusCode = $statusCode; + $this->lastModified = 0; + $this->headerData = array(); + if (!empty($location)) { + $this->setHeader("Location", $location); + $this->setHeader("Cache-Control", "no-cache, must-revalidate"); + } + $this->set("pageClean", (string)$statusCode); + } + } + + // Check if page is available + public function isAvailable() { + return $this->available; + } + + // Check if page is visible + public function isVisible() { + return $this->visible; + } - // Check if page is within current request - function isActive() - { - return $this->active; - } - - // Check if page is cacheable - function isCacheable() - { - return $this->cacheable; - } + // Check if page is within current request + public function isActive() { + return $this->active; + } + + // Check if page is cacheable + public function isCacheable() { + return $this->cacheable; + } - // Check if page with error - function isError() - { - return $this->statusCode>=400; - } - - // Check if response header exists - function isHeader($key) - { - return !is_null($this->headerData[$key]); - } - - // Check if page meta data exists - function isExisting($key) - { - return !is_null($this->metaData[$key]); - } - - // Check if related page exists - function isPage($key) - { - return !is_null($this->pageRelations[$key]); - } + // Check if page with error + public function isError() { + return $this->statusCode>=400; + } + + // Check if response header exists + public function isHeader($key) { + return !is_null($this->headerData[$key]); + } + + // Check if page meta data exists + public function isExisting($key) { + return !is_null($this->metaData[$key]); + } + + // Check if related page exists + public function isPage($key) { + return !is_null($this->pageRelations[$key]); + } } -class YellowDataCollection extends ArrayObject -{ - function __construct() - { - parent::__construct(array()); - } - - // Return array element - function offsetGet($key) - { - if(is_string($key)) $key = lcfirst($key); - return parent::offsetGet($key); - } - - // Set array element - function offsetSet($key, $value) - { - if(is_string($key)) $key = lcfirst($key); - parent::offsetSet($key, $value); - } - - // Remove array element - function offsetUnset($key) - { - if(is_string($key)) $key = lcfirst($key); - parent::offsetUnset($key); - } - - // Check if array element exists - function offsetExists($key) - { - if(is_string($key)) $key = lcfirst($key); - return parent::offsetExists($key); - } +class YellowDataCollection extends ArrayObject { + public function __construct() { + parent::__construct(array()); + } + + // Return array element + public function offsetGet($key) { + if (is_string($key)) $key = lcfirst($key); + return parent::offsetGet($key); + } + + // Set array element + public function offsetSet($key, $value) { + if (is_string($key)) $key = lcfirst($key); + parent::offsetSet($key, $value); + } + + // Remove array element + public function offsetUnset($key) { + if (is_string($key)) $key = lcfirst($key); + parent::offsetUnset($key); + } + + // Check if array element exists + public function offsetExists($key) { + if (is_string($key)) $key = lcfirst($key); + return parent::offsetExists($key); + } } -class YellowPageCollection extends ArrayObject -{ - var $yellow; //access to API - var $filterValue; //current page filter value - var $paginationNumber; //current page number in pagination - var $paginationCount; //highest page number in pagination - - function __construct($yellow) - { - parent::__construct(array()); - $this->yellow = $yellow; - } - - // Filter page collection by meta data - function filter($key, $value, $exactMatch = true) - { - $array = array(); - $value = strreplaceu(' ', '-', strtoloweru($value)); - $valueLength = strlenu($value); - $this->filterValue = ""; - foreach($this->getArrayCopy() as $page) - { - if($page->isExisting($key)) - { - foreach(preg_split("/\s*,\s*/", $page->get($key)) as $pageValue) - { - $pageValueLength = $exactMatch ? strlenu($pageValue) : $valueLength; - if($value==substru(strreplaceu(' ', '-', strtoloweru($pageValue)), 0, $pageValueLength)) - { - if(empty($this->filterValue)) $this->filterValue = substru($pageValue, 0, $pageValueLength); - array_push($array, $page); - break; - } - } - } - } - $this->exchangeArray($array); - return $this; - } - - // Filter page collection by file name - function match($regex = "/.*/") - { - $array = array(); - foreach($this->getArrayCopy() as $page) - { - if(preg_match($regex, $page->fileName)) array_push($array, $page); - } - $this->exchangeArray($array); - return $this; - } - - // Sort page collection by meta data - function sort($key, $ascendingOrder = true) - { - $array = $this->getArrayCopy(); - foreach($array as $page) $page->set("sortindex", ++$i); - $callback = function($a, $b) use ($key, $ascendingOrder) - { - $result = $ascendingOrder ? - strnatcasecmp($a->get($key), $b->get($key)) : - strnatcasecmp($b->get($key), $a->get($key)); - return $result==0 ? $a->get("sortindex") - $b->get("sortindex") : $result; - }; - usort($array, $callback); - $this->exchangeArray($array); - return $this; - } - - // Sort page collection by meta data similarity - function similar($page, $ascendingOrder = false) - { - $location = $page->location; - $keywords = $this->yellow->toolbox->createTextKeywords($page->get("title")); - $keywords .= ",".$page->get("tag").",".$page->get("author"); - $tokens = array_unique(array_filter(preg_split("/\s*,\s*/", $keywords), "strlen")); - if(!empty($tokens)) - { - $array = array(); - foreach($this->getArrayCopy() as $page) - { - $searchScore = 0; - foreach($tokens as $token) - { - if(stristr($page->get("title"), $token)) $searchScore += 10; - if(stristr($page->get("tag"), $token)) $searchScore += 5; - if(stristr($page->get("author"), $token)) $searchScore += 2; - } - if($page->location!=$location) - { - $page->set("searchscore", $searchScore); - array_push($array, $page); - } - } - $this->exchangeArray($array); - $this->sort("modified", $ascendingOrder)->sort("searchscore", $ascendingOrder); - } - return $this; - } +class YellowPageCollection extends ArrayObject { + public $yellow; //access to API + public $filterValue; //current page filter value + public $paginationNumber; //current page number in pagination + public $paginationCount; //highest page number in pagination + + public function __construct($yellow) { + parent::__construct(array()); + $this->yellow = $yellow; + } + + // Filter page collection by meta data + public function filter($key, $value, $exactMatch = true) { + $array = array(); + $value = strreplaceu(" ", "-", strtoloweru($value)); + $valueLength = strlenu($value); + $this->filterValue = ""; + foreach ($this->getArrayCopy() as $page) { + if ($page->isExisting($key)) { + foreach (preg_split("/\s*,\s*/", $page->get($key)) as $pageValue) { + $pageValueLength = $exactMatch ? strlenu($pageValue) : $valueLength; + if ($value==substru(strreplaceu(" ", "-", strtoloweru($pageValue)), 0, $pageValueLength)) { + if (empty($this->filterValue)) $this->filterValue = substru($pageValue, 0, $pageValueLength); + array_push($array, $page); + break; + } + } + } + } + $this->exchangeArray($array); + return $this; + } + + // Filter page collection by file name + public function match($regex = "/.*/") { + $array = array(); + foreach ($this->getArrayCopy() as $page) { + if (preg_match($regex, $page->fileName)) array_push($array, $page); + } + $this->exchangeArray($array); + return $this; + } + + // Sort page collection by meta data + public function sort($key, $ascendingOrder = true) { + $array = $this->getArrayCopy(); + foreach ($array as $page) { + $page->set("sortindex", ++$i); + } + $callback = function ($a, $b) use ($key, $ascendingOrder) { + $result = $ascendingOrder ? + strnatcasecmp($a->get($key), $b->get($key)) : + strnatcasecmp($b->get($key), $a->get($key)); + return $result==0 ? $a->get("sortindex") - $b->get("sortindex") : $result; + }; + usort($array, $callback); + $this->exchangeArray($array); + return $this; + } + + // Sort page collection by meta data similarity + public function similar($page, $ascendingOrder = false) { + $location = $page->location; + $keywords = $this->yellow->toolbox->createTextKeywords($page->get("title")); + $keywords .= ",".$page->get("tag").",".$page->get("author"); + $tokens = array_unique(array_filter(preg_split("/\s*,\s*/", $keywords), "strlen")); + if (!empty($tokens)) { + $array = array(); + foreach ($this->getArrayCopy() as $page) { + $searchScore = 0; + foreach ($tokens as $token) { + if (stristr($page->get("title"), $token)) $searchScore += 10; + if (stristr($page->get("tag"), $token)) $searchScore += 5; + if (stristr($page->get("author"), $token)) $searchScore += 2; + } + if ($page->location!=$location) { + $page->set("searchscore", $searchScore); + array_push($array, $page); + } + } + $this->exchangeArray($array); + $this->sort("modified", $ascendingOrder)->sort("searchscore", $ascendingOrder); + } + return $this; + } - // Merge page collection - function merge($input) - { - $this->exchangeArray(array_merge($this->getArrayCopy(), (array)$input)); - return $this; - } - - // Append to end of page collection - function append($page) - { - parent::append($page); - return $this; - } - - // Prepend to start of page collection - function prepend($page) - { - $array = $this->getArrayCopy(); - array_unshift($array, $page); - $this->exchangeArray($array); - return $this; - } - - // Limit the number of pages in page collection - function limit($pagesMax) - { - $this->exchangeArray(array_slice($this->getArrayCopy(), 0, $pagesMax)); - return $this; - } - - // Reverse page collection - function reverse() - { - $this->exchangeArray(array_reverse($this->getArrayCopy())); - return $this; - } - - // Randomize page collection - function shuffle() - { - $array = $this->getArrayCopy(); - shuffle($array); - $this->exchangeArray($array); - return $this; - } + // Merge page collection + public function merge($input) { + $this->exchangeArray(array_merge($this->getArrayCopy(), (array)$input)); + return $this; + } + + // Append to end of page collection + public function append($page) { + parent::append($page); + return $this; + } + + // Prepend to start of page collection + public function prepend($page) { + $array = $this->getArrayCopy(); + array_unshift($array, $page); + $this->exchangeArray($array); + return $this; + } + + // Limit the number of pages in page collection + public function limit($pagesMax) { + $this->exchangeArray(array_slice($this->getArrayCopy(), 0, $pagesMax)); + return $this; + } + + // Reverse page collection + public function reverse() { + $this->exchangeArray(array_reverse($this->getArrayCopy())); + return $this; + } + + // Randomize page collection + public function shuffle() { + $array = $this->getArrayCopy(); + shuffle($array); + $this->exchangeArray($array); + return $this; + } - // Paginate page collection - function pagination($limit, $reverse = true) - { - $this->paginationNumber = 1; - $this->paginationCount = ceil($this->count() / $limit); - $pagination = $this->yellow->config->get("contentPagination"); - if(isset($_REQUEST[$pagination])) $this->paginationNumber = intval($_REQUEST[$pagination]); - if($this->paginationNumber>$this->paginationCount) $this->paginationNumber = 0; - if($this->paginationNumber>=1) - { - $array = $this->getArrayCopy(); - if($reverse) $array = array_reverse($array); - $this->exchangeArray(array_slice($array, ($this->paginationNumber - 1) * $limit, $limit)); - } - return $this; - } - - // Return current page number in pagination - function getPaginationNumber() - { - return $this->paginationNumber; - } - - // Return highest page number in pagination - function getPaginationCount() - { - return $this->paginationCount; - } - - // Return location for a page in pagination - function getPaginationLocation($absoluteLocation = true, $pageNumber = 1) - { - if($pageNumber>=1 && $pageNumber<=$this->paginationCount) - { - $pagination = $this->yellow->config->get("contentPagination"); - $location = $this->yellow->page->getLocation($absoluteLocation); - $locationArgs = $this->yellow->toolbox->getLocationArgsNew( - $pageNumber>1 ? "$pagination:$pageNumber" : "$pagination:", $pagination); - } - return $location.$locationArgs; - } - - // Return location for previous page in pagination - function getPaginationPrevious($absoluteLocation = true) - { - $pageNumber = $this->paginationNumber-1; - return $this->getPaginationLocation($absoluteLocation, $pageNumber); - } - - // Return location for next page in pagination - function getPaginationNext($absoluteLocation = true) - { - $pageNumber = $this->paginationNumber+1; - return $this->getPaginationLocation($absoluteLocation, $pageNumber); - } - - // Return current page number in collection - function getPageNumber($page) - { - $pageNumber = 0; - foreach($this->getIterator() as $key=>$value) - { - if($page->getLocation()==$value->getLocation()) { $pageNumber = $key+1; break; } - } - return $pageNumber; - } - - // Return page in collection, null if none - function getPage($pageNumber = 1) - { - return ($pageNumber>=1 && $pageNumber<=$this->count()) ? $this->offsetGet($pageNumber-1) : null; - } - - // Return previous page in collection, null if none - function getPagePrevious($page) - { - $pageNumber = $this->getPageNumber($page)-1; - return $this->getPage($pageNumber); - } - - // Return next page in collection, null if none - function getPageNext($page) - { - $pageNumber = $this->getPageNumber($page)+1; - return $this->getPage($pageNumber); - } - - // Return current page filter - function getFilter() - { - return $this->filterValue; - } - - // Return page collection modification date, Unix time or HTTP format - function getModified($httpFormat = false) - { - $modified = 0; - foreach($this->getIterator() as $page) $modified = max($modified, $page->getModified()); - return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($modified) : $modified; - } - - // Check if there is a pagination - function isPagination() - { - return $this->paginationCount>1; - } + // Paginate page collection + public function pagination($limit, $reverse = true) { + $this->paginationNumber = 1; + $this->paginationCount = ceil($this->count() / $limit); + $pagination = $this->yellow->config->get("contentPagination"); + if (isset($_REQUEST[$pagination])) $this->paginationNumber = intval($_REQUEST[$pagination]); + if ($this->paginationNumber>$this->paginationCount) $this->paginationNumber = 0; + if ($this->paginationNumber>=1) { + $array = $this->getArrayCopy(); + if ($reverse) $array = array_reverse($array); + $this->exchangeArray(array_slice($array, ($this->paginationNumber - 1) * $limit, $limit)); + } + return $this; + } + + // Return current page number in pagination + public function getPaginationNumber() { + return $this->paginationNumber; + } + + // Return highest page number in pagination + public function getPaginationCount() { + return $this->paginationCount; + } + + // Return location for a page in pagination + public function getPaginationLocation($absoluteLocation = true, $pageNumber = 1) { + if ($pageNumber>=1 && $pageNumber<=$this->paginationCount) { + $pagination = $this->yellow->config->get("contentPagination"); + $location = $this->yellow->page->getLocation($absoluteLocation); + $locationArgs = $this->yellow->toolbox->getLocationArgsNew( + $pageNumber>1 ? "$pagination:$pageNumber" : "$pagination:", $pagination); + } + return $location.$locationArgs; + } + + // Return location for previous page in pagination + public function getPaginationPrevious($absoluteLocation = true) { + $pageNumber = $this->paginationNumber-1; + return $this->getPaginationLocation($absoluteLocation, $pageNumber); + } + + // Return location for next page in pagination + public function getPaginationNext($absoluteLocation = true) { + $pageNumber = $this->paginationNumber+1; + return $this->getPaginationLocation($absoluteLocation, $pageNumber); + } + + // Return current page number in collection + public function getPageNumber($page) { + $pageNumber = 0; + foreach ($this->getIterator() as $key=>$value) { + if ($page->getLocation()==$value->getLocation()) { + $pageNumber = $key+1; + break; + } + } + return $pageNumber; + } + + // Return page in collection, null if none + public function getPage($pageNumber = 1) { + return ($pageNumber>=1 && $pageNumber<=$this->count()) ? $this->offsetGet($pageNumber-1) : null; + } + + // Return previous page in collection, null if none + public function getPagePrevious($page) { + $pageNumber = $this->getPageNumber($page)-1; + return $this->getPage($pageNumber); + } + + // Return next page in collection, null if none + public function getPageNext($page) { + $pageNumber = $this->getPageNumber($page)+1; + return $this->getPage($pageNumber); + } + + // Return current page filter + public function getFilter() { + return $this->filterValue; + } + + // Return page collection modification date, Unix time or HTTP format + public function getModified($httpFormat = false) { + $modified = 0; + foreach ($this->getIterator() as $page) { + $modified = max($modified, $page->getModified()); + } + return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($modified) : $modified; + } + + // Check if there is a pagination + public function isPagination() { + return $this->paginationCount>1; + } } -class YellowPages -{ - var $yellow; //access to API - var $pages; //scanned pages - - function __construct($yellow) - { - $this->yellow = $yellow; - $this->pages = array(); - } - - // Scan file system on demand - function scanLocation($location) - { - if(is_null($this->pages[$location])) - { - if(defined("DEBUG") && DEBUG>=2) echo "YellowPages::scanLocation location:$location<br/>\n"; - $this->pages[$location] = array(); - $scheme = $this->yellow->page->scheme; - $address = $this->yellow->page->address; - $base = $this->yellow->page->base; - if(empty($location)) - { - $rootLocations = $this->yellow->lookup->findRootLocations(); - foreach($rootLocations as $rootLocation) - { - list($rootLocation, $fileName) = explode(' ', $rootLocation, 2); - $page = new YellowPage($this->yellow); - $page->setRequestInformation($scheme, $address, $base, $rootLocation, $fileName); - $page->parseData("", false, 0); - array_push($this->pages[$location], $page); - } - } else { - $fileNames = $this->yellow->lookup->findChildrenFromLocation($location); - foreach($fileNames as $fileName) - { - $page = new YellowPage($this->yellow); - $page->setRequestInformation($scheme, $address, $base, - $this->yellow->lookup->findLocationFromFile($fileName), $fileName); - $page->parseData($this->yellow->toolbox->readFile($fileName, 4096), false, 0); - if(strlenb($page->rawData)<4096) $page->statusCode = 200; - array_push($this->pages[$location], $page); - } - } - } - return $this->pages[$location]; - } +class YellowPages { + public $yellow; //access to API + public $pages; //scanned pages + + public function __construct($yellow) { + $this->yellow = $yellow; + $this->pages = array(); + } + + // Scan file system on demand + public function scanLocation($location) { + if (is_null($this->pages[$location])) { + if (defined("DEBUG") && DEBUG>=2) echo "YellowPages::scanLocation location:$location<br/>\n"; + $this->pages[$location] = array(); + $scheme = $this->yellow->page->scheme; + $address = $this->yellow->page->address; + $base = $this->yellow->page->base; + if (empty($location)) { + $rootLocations = $this->yellow->lookup->findRootLocations(); + foreach ($rootLocations as $rootLocation) { + list($rootLocation, $fileName) = explode(" ", $rootLocation, 2); + $page = new YellowPage($this->yellow); + $page->setRequestInformation($scheme, $address, $base, $rootLocation, $fileName); + $page->parseData("", false, 0); + array_push($this->pages[$location], $page); + } + } else { + $fileNames = $this->yellow->lookup->findChildrenFromLocation($location); + foreach ($fileNames as $fileName) { + $page = new YellowPage($this->yellow); + $page->setRequestInformation($scheme, $address, $base, + $this->yellow->lookup->findLocationFromFile($fileName), $fileName); + $page->parseData($this->yellow->toolbox->readFile($fileName, 4096), false, 0); + if (strlenb($page->rawData)<4096) $page->statusCode = 200; + array_push($this->pages[$location], $page); + } + } + } + return $this->pages[$location]; + } - // Return page from file system, null if not found - function find($location, $absoluteLocation = false) - { - if($absoluteLocation) $location = substru($location, strlenu($this->yellow->page->base)); - foreach($this->scanLocation($this->getParentLocation($location)) as $page) - { - if($page->location==$location) - { - if(!$this->yellow->lookup->isRootLocation($page->location)) { $found = true; break; } - } - } - return $found ? $page : null; - } - - // Return page collection with all pages - function index($showInvisible = false, $multiLanguage = false, $levelMax = 0) - { - $rootLocation = $multiLanguage ? "" : $this->getRootLocation($this->yellow->page->location); - return $this->getChildrenRecursive($rootLocation, $showInvisible, $levelMax); - } - - // Return page collection with top-level navigation - function top($showInvisible = false) - { - $rootLocation = $this->getRootLocation($this->yellow->page->location); - return $this->getChildren($rootLocation, $showInvisible); - } - - // Return page collection with path ancestry - function path($location, $absoluteLocation = false) - { - $pages = new YellowPageCollection($this->yellow); - if($absoluteLocation) $location = substru($location, strlenu($this->yellow->page->base)); - if($page = $this->find($location)) - { - $pages->prepend($page); - for(; $parent = $page->getParent(); $page=$parent) $pages->prepend($parent); - $home = $this->find($this->getHomeLocation($page->location)); - if($home && $home->location!=$page->location) $pages->prepend($home); - } - return $pages; - } - - // Return page collection with multiple languages - function multi($location, $absoluteLocation = false, $showInvisible = false) - { - $pages = new YellowPageCollection($this->yellow); - if($absoluteLocation) $location = substru($location, strlenu($this->yellow->page->base)); - $locationEnd = substru($location, strlenu($this->getRootLocation($location)) - 4); - foreach($this->scanLocation("") as $page) - { - if($content = $this->find(substru($page->location, 4).$locationEnd)) - { - if($content->isAvailable() && ($content->isVisible() || $showInvisible)) - { - if(!$this->yellow->lookup->isRootLocation($content->location)) $pages->append($content); - } - } - } - return $pages; - } - - // Return page collection that's empty - function clean() - { - return new YellowPageCollection($this->yellow); - } - - // Return languages in multi language mode - function getLanguages($showInvisible = false) - { - $languages = array(); - foreach($this->scanLocation("") as $page) - { - if($page->isAvailable() && ($page->isVisible() || $showInvisible)) - { - array_push($languages, $page->get("language")); - } - } - return $languages; - } - - // Return child pages - function getChildren($location, $showInvisible = false) - { - $pages = new YellowPageCollection($this->yellow); - foreach($this->scanLocation($location) as $page) - { - if($page->isAvailable() && ($page->isVisible() || $showInvisible)) - { - if(!$this->yellow->lookup->isRootLocation($page->location) && is_readable($page->fileName)) $pages->append($page); - } - } - return $pages; - } - - // Return sub pages - function getChildrenRecursive($location, $showInvisible = false, $levelMax = 0) - { - --$levelMax; - $pages = new YellowPageCollection($this->yellow); - foreach($this->scanLocation($location) as $page) - { - if($page->isAvailable() && ($page->isVisible() || $showInvisible)) - { - if(!$this->yellow->lookup->isRootLocation($page->location) && is_readable($page->fileName)) $pages->append($page); - if(!$this->yellow->lookup->isFileLocation($page->location) && $levelMax!=0) - { - $pages->merge($this->getChildrenRecursive($page->location, $showInvisible, $levelMax)); - } - } - } - return $pages; - } - - // Return root location - function getRootLocation($location) - { - $rootLocation = "root/"; - if($this->yellow->config->get("multiLanguageMode")) - { - foreach($this->scanLocation("") as $page) - { - $token = substru($page->location, 4); - if($token!="/" && substru($location, 0, strlenu($token))==$token) { $rootLocation = "root$token"; break; } - } - } - return $rootLocation; - } + // Return page from file system, null if not found + public function find($location, $absoluteLocation = false) { + if ($absoluteLocation) $location = substru($location, strlenu($this->yellow->page->base)); + foreach ($this->scanLocation($this->getParentLocation($location)) as $page) { + if ($page->location==$location) { + if (!$this->yellow->lookup->isRootLocation($page->location)) { + $found = true; + break; + } + } + } + return $found ? $page : null; + } + + // Return page collection with all pages + public function index($showInvisible = false, $multiLanguage = false, $levelMax = 0) { + $rootLocation = $multiLanguage ? "" : $this->getRootLocation($this->yellow->page->location); + return $this->getChildrenRecursive($rootLocation, $showInvisible, $levelMax); + } + + // Return page collection with top-level navigation + public function top($showInvisible = false) { + $rootLocation = $this->getRootLocation($this->yellow->page->location); + return $this->getChildren($rootLocation, $showInvisible); + } + + // Return page collection with path ancestry + public function path($location, $absoluteLocation = false) { + $pages = new YellowPageCollection($this->yellow); + if ($absoluteLocation) $location = substru($location, strlenu($this->yellow->page->base)); + if ($page = $this->find($location)) { + $pages->prepend($page); + for (; $parent = $page->getParent(); $page=$parent) { + $pages->prepend($parent); + } + $home = $this->find($this->getHomeLocation($page->location)); + if ($home && $home->location!=$page->location) $pages->prepend($home); + } + return $pages; + } + + // Return page collection with multiple languages + public function multi($location, $absoluteLocation = false, $showInvisible = false) { + $pages = new YellowPageCollection($this->yellow); + if ($absoluteLocation) $location = substru($location, strlenu($this->yellow->page->base)); + $locationEnd = substru($location, strlenu($this->getRootLocation($location)) - 4); + foreach ($this->scanLocation("") as $page) { + if ($content = $this->find(substru($page->location, 4).$locationEnd)) { + if ($content->isAvailable() && ($content->isVisible() || $showInvisible)) { + if (!$this->yellow->lookup->isRootLocation($content->location)) $pages->append($content); + } + } + } + return $pages; + } + + // Return page collection that's empty + public function clean() { + return new YellowPageCollection($this->yellow); + } + + // Return languages in multi language mode + public function getLanguages($showInvisible = false) { + $languages = array(); + foreach ($this->scanLocation("") as $page) { + if ($page->isAvailable() && ($page->isVisible() || $showInvisible)) array_push($languages, $page->get("language")); + } + return $languages; + } + + // Return child pages + public function getChildren($location, $showInvisible = false) { + $pages = new YellowPageCollection($this->yellow); + foreach ($this->scanLocation($location) as $page) { + if ($page->isAvailable() && ($page->isVisible() || $showInvisible)) { + if (!$this->yellow->lookup->isRootLocation($page->location) && is_readable($page->fileName)) $pages->append($page); + } + } + return $pages; + } + + // Return sub pages + public function getChildrenRecursive($location, $showInvisible = false, $levelMax = 0) { + --$levelMax; + $pages = new YellowPageCollection($this->yellow); + foreach ($this->scanLocation($location) as $page) { + if ($page->isAvailable() && ($page->isVisible() || $showInvisible)) { + if (!$this->yellow->lookup->isRootLocation($page->location) && is_readable($page->fileName)) $pages->append($page); + if (!$this->yellow->lookup->isFileLocation($page->location) && $levelMax!=0) { + $pages->merge($this->getChildrenRecursive($page->location, $showInvisible, $levelMax)); + } + } + } + return $pages; + } + + // Return root location + public function getRootLocation($location) { + $rootLocation = "root/"; + if ($this->yellow->config->get("multiLanguageMode")) { + foreach ($this->scanLocation("") as $page) { + $token = substru($page->location, 4); + if ($token!="/" && substru($location, 0, strlenu($token))==$token) { + $rootLocation = "root$token"; + break; + } + } + } + return $rootLocation; + } - // Return home location - function getHomeLocation($location) - { - return substru($this->getRootLocation($location), 4); - } - - // Return parent location - function getParentLocation($location) - { - $token = rtrim(substru($this->getRootLocation($location), 4), '/'); - if(preg_match("#^($token.*\/).+?$#", $location, $matches)) - { - if($matches[1]!="$token/" || $this->yellow->lookup->isFileLocation($location)) $parentLocation = $matches[1]; - } - if(empty($parentLocation)) $parentLocation = "root$token/"; - return $parentLocation; - } - - // Return top-level location - function getParentTopLocation($location) - { - $token = rtrim(substru($this->getRootLocation($location), 4), '/'); - if(preg_match("#^($token.+?\/)#", $location, $matches)) $parentTopLocation = $matches[1]; - if(empty($parentTopLocation)) $parentTopLocation = "$token/"; - return $parentTopLocation; - } + // Return home location + public function getHomeLocation($location) { + return substru($this->getRootLocation($location), 4); + } + + // Return parent location + public function getParentLocation($location) { + $token = rtrim(substru($this->getRootLocation($location), 4), "/"); + if (preg_match("#^($token.*\/).+?$#", $location, $matches)) { + if ($matches[1]!="$token/" || $this->yellow->lookup->isFileLocation($location)) $parentLocation = $matches[1]; + } + if (empty($parentLocation)) $parentLocation = "root$token/"; + return $parentLocation; + } + + // Return top-level location + public function getParentTopLocation($location) { + $token = rtrim(substru($this->getRootLocation($location), 4), "/"); + if (preg_match("#^($token.+?\/)#", $location, $matches)) $parentTopLocation = $matches[1]; + if (empty($parentTopLocation)) $parentTopLocation = "$token/"; + return $parentTopLocation; + } } - -class YellowFiles -{ - var $yellow; //access to API - var $files; //scanned files - - function __construct($yellow) - { - $this->yellow = $yellow; - $this->files = array(); - } + +class YellowFiles { + public $yellow; //access to API + public $files; //scanned files + + public function __construct($yellow) { + $this->yellow = $yellow; + $this->files = array(); + } - // Scan file system on demand - function scanLocation($location) - { - if(is_null($this->files[$location])) - { - if(defined("DEBUG") && DEBUG>=2) echo "YellowFiles::scanLocation location:$location<br/>\n"; - $this->files[$location] = array(); - $scheme = $this->yellow->page->scheme; - $address = $this->yellow->page->address; - $base = $this->yellow->config->get("serverBase"); - if(empty($location)) - { - $fileNames = array($this->yellow->config->get("mediaDir")); - } else { - $fileNames = array(); - $path = substru($location, 1); - foreach($this->yellow->toolbox->getDirectoryEntries($path, "/.*/", true, true, true) as $entry) - { - array_push($fileNames, $entry."/"); - } - foreach($this->yellow->toolbox->getDirectoryEntries($path, "/.*/", true, false, true) as $entry) - { - array_push($fileNames, $entry); - } - } - foreach($fileNames as $fileName) - { - $file = new YellowPage($this->yellow); - $file->setRequestInformation($scheme, $address, $base, "/".$fileName, $fileName); - $file->parseData(null, false, 0); - array_push($this->files[$location], $file); - } - } - return $this->files[$location]; - } - - // Return page with media file information, null if not found - function find($location, $absoluteLocation = false) - { - if($absoluteLocation) $location = substru($location, strlenu($this->yellow->config->get("serverBase"))); - foreach($this->scanLocation($this->getParentLocation($location)) as $file) - { - if($file->location==$location) - { - if($this->yellow->lookup->isFileLocation($file->location)) { $found = true; break; } - } - } - return $found ? $file : null; - } - - // Return page collection with all media files - function index($showInvisible = false, $multiPass = false, $levelMax = 0) - { - return $this->getChildrenRecursive("", $showInvisible, $levelMax); - } - - // Return page collection that's empty - function clean() - { - return new YellowPageCollection($this->yellow); - } - - // Return child files - function getChildren($location, $showInvisible = false) - { - $files = new YellowPageCollection($this->yellow); - foreach($this->scanLocation($location) as $file) - { - if($file->isAvailable() && ($file->isVisible() || $showInvisible)) - { - if($this->yellow->lookup->isFileLocation($file->location)) $files->append($file); - } - } - return $files; - } - - // Return sub files - function getChildrenRecursive($location, $showInvisible = false, $levelMax = 0) - { - --$levelMax; - $files = new YellowPageCollection($this->yellow); - foreach($this->scanLocation($location) as $file) - { - if($file->isAvailable() && ($file->isVisible() || $showInvisible)) - { - if($this->yellow->lookup->isFileLocation($file->location)) $files->append($file); - if(!$this->yellow->lookup->isFileLocation($file->location) && $levelMax!=0) - { - $files->merge($this->getChildrenRecursive($file->location, $showInvisible, $levelMax)); - } - } - } - return $files; - } - - // Return home location - function getHomeLocation($location) - { - return $this->yellow->config->get("mediaLocation"); - } + // Scan file system on demand + public function scanLocation($location) { + if (is_null($this->files[$location])) { + if (defined("DEBUG") && DEBUG>=2) echo "YellowFiles::scanLocation location:$location<br/>\n"; + $this->files[$location] = array(); + $scheme = $this->yellow->page->scheme; + $address = $this->yellow->page->address; + $base = $this->yellow->config->get("serverBase"); + if (empty($location)) { + $fileNames = array($this->yellow->config->get("mediaDir")); + } else { + $fileNames = array(); + $path = substru($location, 1); + foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/.*/", true, true, true) as $entry) { + array_push($fileNames, $entry."/"); + } + foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/.*/", true, false, true) as $entry) { + array_push($fileNames, $entry); + } + } + foreach ($fileNames as $fileName) { + $file = new YellowPage($this->yellow); + $file->setRequestInformation($scheme, $address, $base, "/".$fileName, $fileName); + $file->parseData(null, false, 0); + array_push($this->files[$location], $file); + } + } + return $this->files[$location]; + } + + // Return page with media file information, null if not found + public function find($location, $absoluteLocation = false) { + if ($absoluteLocation) $location = substru($location, strlenu($this->yellow->config->get("serverBase"))); + foreach ($this->scanLocation($this->getParentLocation($location)) as $file) { + if ($file->location==$location) { + if ($this->yellow->lookup->isFileLocation($file->location)) { + $found = true; + break; + } + } + } + return $found ? $file : null; + } + + // Return page collection with all media files + public function index($showInvisible = false, $multiPass = false, $levelMax = 0) { + return $this->getChildrenRecursive("", $showInvisible, $levelMax); + } + + // Return page collection that's empty + public function clean() { + return new YellowPageCollection($this->yellow); + } + + // Return child files + public function getChildren($location, $showInvisible = false) { + $files = new YellowPageCollection($this->yellow); + foreach ($this->scanLocation($location) as $file) { + if ($file->isAvailable() && ($file->isVisible() || $showInvisible)) { + if ($this->yellow->lookup->isFileLocation($file->location)) $files->append($file); + } + } + return $files; + } + + // Return sub files + public function getChildrenRecursive($location, $showInvisible = false, $levelMax = 0) { + --$levelMax; + $files = new YellowPageCollection($this->yellow); + foreach ($this->scanLocation($location) as $file) { + if ($file->isAvailable() && ($file->isVisible() || $showInvisible)) { + if ($this->yellow->lookup->isFileLocation($file->location)) $files->append($file); + if (!$this->yellow->lookup->isFileLocation($file->location) && $levelMax!=0) { + $files->merge($this->getChildrenRecursive($file->location, $showInvisible, $levelMax)); + } + } + } + return $files; + } + + // Return home location + public function getHomeLocation($location) { + return $this->yellow->config->get("mediaLocation"); + } - // Return parent location - function getParentLocation($location) - { - $token = rtrim($this->yellow->config->get("mediaLocation"), '/'); - if(preg_match("#^($token.*\/).+?$#", $location, $matches)) - { - if($matches[1]!="$token/" || $this->yellow->lookup->isFileLocation($location)) $parentLocation = $matches[1]; - } - if(empty($parentLocation)) $parentLocation = ""; - return $parentLocation; - } - - // Return top-level location - function getParentTopLocation($location) - { - $token = rtrim($this->yellow->config->get("mediaLocation"), '/'); - if(preg_match("#^($token.+?\/)#", $location, $matches)) $parentTopLocation = $matches[1]; - if(empty($parentTopLocation)) $parentTopLocation = "$token/"; - return $parentTopLocation; - } + // Return parent location + public function getParentLocation($location) { + $token = rtrim($this->yellow->config->get("mediaLocation"), "/"); + if (preg_match("#^($token.*\/).+?$#", $location, $matches)) { + if ($matches[1]!="$token/" || $this->yellow->lookup->isFileLocation($location)) $parentLocation = $matches[1]; + } + if (empty($parentLocation)) $parentLocation = ""; + return $parentLocation; + } + + // Return top-level location + public function getParentTopLocation($location) { + $token = rtrim($this->yellow->config->get("mediaLocation"), "/"); + if (preg_match("#^($token.+?\/)#", $location, $matches)) $parentTopLocation = $matches[1]; + if (empty($parentTopLocation)) $parentTopLocation = "$token/"; + return $parentTopLocation; + } } -class YellowPlugins -{ - var $yellow; //access to API - var $modified; //plugin modification date - var $plugins; //registered plugins +class YellowPlugins { + public $yellow; //access to API + public $modified; //plugin modification date + public $plugins; //registered plugins - function __construct($yellow) - { - $this->yellow = $yellow; - $this->modified = 0; - $this->plugins = array(); - } - - // Load plugins - function load($path = "") - { - $path = empty($path) ? $this->yellow->config->get("pluginDir") : $path; - foreach($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.php$/", true, false) as $entry) - { - if(defined("DEBUG") && DEBUG>=3) echo "YellowPlugins::load file:$entry<br/>\n"; - $this->modified = max($this->modified, filemtime($entry)); - global $yellow; - require_once($entry); - } - $callback = function($a, $b) - { - return $a["priority"] - $b["priority"]; - }; - uasort($this->plugins, $callback); - foreach($this->plugins as $key=>$value) - { - $this->plugins[$key]["obj"] = new $value["plugin"]; - if(method_exists($this->plugins[$key]["obj"], "onLoad")) $this->plugins[$key]["obj"]->onLoad($yellow); - } - } - - // Register plugin - function register($name, $plugin, $version, $priority = 0) - { - if(!$this->isExisting($name)) - { - if($priority==0) $priority = count($this->plugins) + 10; - $this->plugins[$name] = array(); - $this->plugins[$name]["plugin"] = $plugin; - $this->plugins[$name]["version"] = $version; - $this->plugins[$name]["priority"] = $priority; - } - } - - // Return plugin - function get($name) - { - return $this->plugins[$name]["obj"]; - } - - // Return plugin version - function getData() - { - $data = array(); - $data["YellowCore"] = YellowCore::VERSION; - foreach($this->plugins as $key=>$value) - { - if(empty($value["plugin"]) || empty($value["version"])) continue; - $data[$value["plugin"]] = $value["version"]; - } - uksort($data, "strnatcasecmp"); - return $data; - } - - // Return plugin modification date, Unix time or HTTP format - function getModified($httpFormat = false) - { - return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($this->modified) : $this->modified; - } - - // Check if plugin exists - function isExisting($name) - { - return !is_null($this->plugins[$name]); - } + public function __construct($yellow) { + $this->yellow = $yellow; + $this->modified = 0; + $this->plugins = array(); + } + + // Load plugins + public function load($path = "") { + $path = empty($path) ? $this->yellow->config->get("pluginDir") : $path; + foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.php$/", true, false) as $entry) { + if (defined("DEBUG") && DEBUG>=3) echo "YellowPlugins::load file:$entry<br/>\n"; + $this->modified = max($this->modified, filemtime($entry)); + global $yellow; + require_once($entry); + } + $callback = function ($a, $b) { + return $a["priority"] - $b["priority"]; + }; + uasort($this->plugins, $callback); + foreach ($this->plugins as $key=>$value) { + $this->plugins[$key]["obj"] = new $value["plugin"]; + if (method_exists($this->plugins[$key]["obj"], "onLoad")) $this->plugins[$key]["obj"]->onLoad($yellow); + } + } + + // Register plugin + public function register($name, $plugin, $version, $priority = 0) { + if (!$this->isExisting($name)) { + if ($priority==0) $priority = count($this->plugins) + 10; + $this->plugins[$name] = array(); + $this->plugins[$name]["plugin"] = $plugin; + $this->plugins[$name]["version"] = $version; + $this->plugins[$name]["priority"] = $priority; + } + } + + // Return plugin + public function get($name) { + return $this->plugins[$name]["obj"]; + } + + // Return plugin version + public function getData() { + $data = array(); + $data["YellowCore"] = YellowCore::VERSION; + foreach ($this->plugins as $key=>$value) { + if (empty($value["plugin"]) || empty($value["version"])) continue; + $data[$value["plugin"]] = $value["version"]; + } + uksort($data, "strnatcasecmp"); + return $data; + } + + // Return plugin modification date, Unix time or HTTP format + public function getModified($httpFormat = false) { + return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($this->modified) : $this->modified; + } + + // Check if plugin exists + public function isExisting($name) { + return !is_null($this->plugins[$name]); + } } -class YellowThemes -{ - var $yellow; //access to API - var $modified; //theme modification date - var $themes; //themes +class YellowThemes { + public $yellow; //access to API + public $modified; //theme modification date + public $themes; //themes - function __construct($yellow) - { - $this->yellow = $yellow; - $this->modified = 0; - $this->themes = array(); - } - - // Load themes - function load($path = "") - { - $path = empty($path) ? $this->yellow->config->get("assetDir") : $path; - foreach($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.php$/", true, false) as $entry) - { - if(defined("DEBUG") && DEBUG>=3) echo "YellowThemes::load file:$entry<br/>\n"; - $this->modified = max($this->modified, filemtime($entry)); - global $yellow; - require_once($entry); - } - foreach($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.css$/", true, false) as $entry) - { - if(defined("DEBUG") && DEBUG>=3) echo "YellowThemes::load file:$entry<br/>\n"; - $this->modified = max($this->modified, filemtime($entry)); - $name = $this->yellow->lookup->normaliseName(basename($entry), true, true); - $this->register($name, "", ""); - } - $callback = function($a, $b) - { - return $a["priority"] - $b["priority"]; - }; - uasort($this->themes, $callback); - foreach($this->themes as $key=>$value) - { - $this->themes[$key]["obj"] = empty($value["theme"]) ? new stdClass : new $value["theme"]; - if(method_exists($this->themes[$key]["obj"], "onLoad")) $this->themes[$key]["obj"]->onLoad($yellow); - } - } - - // Register theme - function register($name, $theme, $version, $priority = 0) - { - if(!$this->isExisting($name)) - { - if($priority==0) $priority = count($this->themes) + 10; - $this->themes[$name] = array(); - $this->themes[$name]["theme"] = $theme; - $this->themes[$name]["version"] = $version; - $this->themes[$name]["priority"] = $priority; - } - } - - // Return theme - function get($name) - { - return $this->theme[$name]["obj"]; - } - - // Return theme version - function getData() - { - $data = array(); - foreach($this->themes as $key=>$value) - { - if(empty($value["theme"]) || empty($value["version"])) continue; - $data[$value["theme"]] = $value["version"]; - } - uksort($data, "strnatcasecmp"); - return $data; - } - - // Return theme modification date, Unix time or HTTP format - function getModified($httpFormat = false) - { - return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($this->modified) : $this->modified; - } + public function __construct($yellow) { + $this->yellow = $yellow; + $this->modified = 0; + $this->themes = array(); + } + + // Load themes + public function load($path = "") { + $path = empty($path) ? $this->yellow->config->get("assetDir") : $path; + foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.php$/", true, false) as $entry) { + if (defined("DEBUG") && DEBUG>=3) echo "YellowThemes::load file:$entry<br/>\n"; + $this->modified = max($this->modified, filemtime($entry)); + global $yellow; + require_once($entry); + } + foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.css$/", true, false) as $entry) { + if (defined("DEBUG") && DEBUG>=3) echo "YellowThemes::load file:$entry<br/>\n"; + $this->modified = max($this->modified, filemtime($entry)); + $name = $this->yellow->lookup->normaliseName(basename($entry), true, true); + $this->register($name, "", ""); + } + $callback = function ($a, $b) { + return $a["priority"] - $b["priority"]; + }; + uasort($this->themes, $callback); + foreach ($this->themes as $key=>$value) { + $this->themes[$key]["obj"] = empty($value["theme"]) ? new stdClass : new $value["theme"]; + if (method_exists($this->themes[$key]["obj"], "onLoad")) $this->themes[$key]["obj"]->onLoad($yellow); + } + } + + // Register theme + public function register($name, $theme, $version, $priority = 0) { + if (!$this->isExisting($name)) { + if ($priority==0) $priority = count($this->themes) + 10; + $this->themes[$name] = array(); + $this->themes[$name]["theme"] = $theme; + $this->themes[$name]["version"] = $version; + $this->themes[$name]["priority"] = $priority; + } + } + + // Return theme + public function get($name) { + return $this->theme[$name]["obj"]; + } + + // Return theme version + public function getData() { + $data = array(); + foreach ($this->themes as $key=>$value) { + if (empty($value["theme"]) || empty($value["version"])) continue; + $data[$value["theme"]] = $value["version"]; + } + uksort($data, "strnatcasecmp"); + return $data; + } + + // Return theme modification date, Unix time or HTTP format + public function getModified($httpFormat = false) { + return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($this->modified) : $this->modified; + } - // Check if theme exists - function isExisting($name) - { - return !is_null($this->themes[$name]); - } + // Check if theme exists + public function isExisting($name) { + return !is_null($this->themes[$name]); + } } - -class YellowConfig -{ - var $yellow; //access to API - var $modified; //configuration modification date - var $config; //configuration - var $configDefaults; //configuration defaults - - function __construct($yellow) - { - $this->yellow = $yellow; - $this->modified = 0; - $this->config = new YellowDataCollection(); - $this->configDefaults = new YellowDataCollection(); - } - - // Load configuration from file - function load($fileName) - { - if(defined("DEBUG") && DEBUG>=2) echo "YellowConfig::load file:$fileName<br/>\n"; - $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; - preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); - if(!empty($matches[1]) && !strempty($matches[2])) - { - $this->set($matches[1], $matches[2]); - if(defined("DEBUG") && DEBUG>=3) echo "YellowConfig::load $matches[1]:$matches[2]<br/>\n"; - } - } - } - - // Save configuration to file - function save($fileName, $config) - { - $configNew = new YellowDataCollection(); - foreach($config as $key=>$value) - { - if(!empty($key) && !strempty($value)) - { - $this->set($key, $value); - $configNew[$key] = $value; - } - } - $this->modified = time(); - $fileData = $this->yellow->toolbox->readFile($fileName); - foreach($this->yellow->toolbox->getTextLines($fileData) as $line) - { - preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); - if(!empty($matches[1]) && !is_null($configNew[$matches[1]])) - { - $fileDataNew .= "$matches[1]: ".$configNew[$matches[1]]."\n"; - unset($configNew[$matches[1]]); - } else { - $fileDataNew .= $line; - } - } - foreach($configNew as $key=>$value) - { - $fileDataNew .= ucfirst($key).": $value\n"; - } - return $this->yellow->toolbox->createFile($fileName, $fileDataNew); - } - - // Set default configuration - function setDefault($key, $value) - { - $this->configDefaults[$key] = $value; - } - - // Set configuration - function set($key, $value) - { - $this->config[$key] = $value; - } - - // Return configuration - function get($key) - { - if(!is_null($this->config[$key])) - { - $value = $this->config[$key]; - } else { - $value = !is_null($this->configDefaults[$key]) ? $this->configDefaults[$key] : ""; - } - return $value; - } - - // Return configuration, HTML encoded - function getHtml($key) - { - return htmlspecialchars($this->get($key)); - } - - // Return configuration strings - function getData($filterStart = "", $filterEnd = "") - { - $config = array(); - if(empty($filterStart) && empty($filterEnd)) - { - $config = array_merge($this->configDefaults->getArrayCopy(), $this->config->getArrayCopy()); - } else { - foreach(array_merge($this->configDefaults->getArrayCopy(), $this->config->getArrayCopy()) as $key=>$value) - { - if(!empty($filterStart) && substru($key, 0, strlenu($filterStart))==$filterStart) $config[$key] = $value; - if(!empty($filterEnd) && substru($key, -strlenu($filterEnd))==$filterEnd) $config[$key] = $value; - } - } - return $config; - } - - // Return configuration modification date, Unix time or HTTP format - function getModified($httpFormat = false) - { - return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($this->modified) : $this->modified; - } - - // Check if configuration exists - function isExisting($key) - { - return !is_null($this->config[$key]); - } + +class YellowConfig { + public $yellow; //access to API + public $modified; //configuration modification date + public $config; //configuration + public $configDefaults; //configuration defaults + + public function __construct($yellow) { + $this->yellow = $yellow; + $this->modified = 0; + $this->config = new YellowDataCollection(); + $this->configDefaults = new YellowDataCollection(); + } + + // Load configuration from file + public function load($fileName) { + if (defined("DEBUG") && DEBUG>=2) echo "YellowConfig::load file:$fileName<br/>\n"; + $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; + preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); + if (!empty($matches[1]) && !strempty($matches[2])) { + $this->set($matches[1], $matches[2]); + if (defined("DEBUG") && DEBUG>=3) echo "YellowConfig::load $matches[1]:$matches[2]<br/>\n"; + } + } + } + + // Save configuration to file + public function save($fileName, $config) { + $configNew = new YellowDataCollection(); + foreach ($config as $key=>$value) { + if (!empty($key) && !strempty($value)) { + $this->set($key, $value); + $configNew[$key] = $value; + } + } + $this->modified = time(); + $fileData = $this->yellow->toolbox->readFile($fileName); + foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) { + preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); + if (!empty($matches[1]) && !is_null($configNew[$matches[1]])) { + $fileDataNew .= "$matches[1]: ".$configNew[$matches[1]]."\n"; + unset($configNew[$matches[1]]); + } else { + $fileDataNew .= $line; + } + } + foreach ($configNew as $key=>$value) { + $fileDataNew .= ucfirst($key).": $value\n"; + } + return $this->yellow->toolbox->createFile($fileName, $fileDataNew); + } + + // Set default configuration + public function setDefault($key, $value) { + $this->configDefaults[$key] = $value; + } + + // Set configuration + public function set($key, $value) { + $this->config[$key] = $value; + } + + // Return configuration + public function get($key) { + if (!is_null($this->config[$key])) { + $value = $this->config[$key]; + } else { + $value = !is_null($this->configDefaults[$key]) ? $this->configDefaults[$key] : ""; + } + return $value; + } + + // Return configuration, HTML encoded + public function getHtml($key) { + return htmlspecialchars($this->get($key)); + } + + // Return configuration strings + public function getData($filterStart = "", $filterEnd = "") { + $config = array(); + if (empty($filterStart) && empty($filterEnd)) { + $config = array_merge($this->configDefaults->getArrayCopy(), $this->config->getArrayCopy()); + } else { + foreach (array_merge($this->configDefaults->getArrayCopy(), $this->config->getArrayCopy()) as $key=>$value) { + if (!empty($filterStart) && substru($key, 0, strlenu($filterStart))==$filterStart) $config[$key] = $value; + if (!empty($filterEnd) && substru($key, -strlenu($filterEnd))==$filterEnd) $config[$key] = $value; + } + } + return $config; + } + + // Return configuration modification date, Unix time or HTTP format + public function getModified($httpFormat = false) { + return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($this->modified) : $this->modified; + } + + // Check if configuration exists + public function isExisting($key) { + return !is_null($this->config[$key]); + } } -class YellowText -{ - var $yellow; //access to API - var $modified; //text modification date - var $text; //text - var $language; //current language - - function __construct($yellow) - { - $this->yellow = $yellow; - $this->modified = 0; - $this->text = new YellowDataCollection(); - } - - // Load text strings from file - function load($fileName, $languageDefault) - { - $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; - $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; - preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); - if(lcfirst($matches[1])=="language" && !strempty($matches[2])) $language = $matches[2]; - 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"; - } - } - } - } - - // Set current language - function setLanguage($language) - { - $this->language = $language; - } - - // Set text string for specific language - function setText($key, $value, $language) - { - if(is_null($this->text[$language])) $this->text[$language] = new YellowDataCollection(); - $this->text[$language][$key] = $value; - } - - // Return text string - function get($key) - { - return $this->getText($key, $this->language); - } - - // Return text string, HTML encoded - function getHtml($key) - { - return htmlspecialchars($this->getText($key, $this->language)); - } - - // Return text string for specific language - function getText($key, $language) - { - return $this->isExisting($key, $language) ? $this->text[$language][$key] : "[$key]"; - } - - // Return text string for specific language, HTML encoded - function getTextHtml($key, $language) - { - return htmlspecialchars($this->getText($key, $language)); - } - - // Return text strings - 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 - function getDateFormatted($timestamp, $format) - { - $dateMonths = preg_split("/\s*,\s*/", $this->get("dateMonths")); - $dateWeekdays = preg_split("/\s*,\s*/", $this->get("dateWeekdays")); - $month = $dateMonths[date('n', $timestamp) - 1]; - $weekday = $dateWeekdays[date('N', $timestamp) - 1]; - $timeZone = $this->yellow->config->get("timezone"); - $timeZoneHelper = new DateTime(null, new DateTimeZone($timeZone)); - $timeZoneOffset = $timeZoneHelper->getOffset(); - $timeZoneAbbreviation = "GMT".($timeZoneOffset<0 ? "-" : "+").abs(intval($timeZoneOffset/3600)); - $format = preg_replace("/(?<!\\\)F/", addcslashes($month, 'A..Za..z'), $format); - $format = preg_replace("/(?<!\\\)M/", addcslashes(substru($month, 0, 3), 'A..Za..z'), $format); - $format = preg_replace("/(?<!\\\)D/", addcslashes(substru($weekday, 0, 3), 'A..Za..z'), $format); - $format = preg_replace("/(?<!\\\)l/", addcslashes($weekday, 'A..Za..z'), $format); - $format = preg_replace("/(?<!\\\)T/", addcslashes($timeZoneAbbreviation, 'A..Za..z'), $format); - return date($format, $timestamp); - } - - // Return human readable date, relative to today - function getDateRelative($timestamp, $format, $daysLimit) - { - $timeDifference = time() - $timestamp; - $days = abs(intval($timeDifference / 86400)); - if($days<=$daysLimit || $daysLimit==0) - { - $tokens = preg_split("/\s*,\s*/", $this->get($timeDifference>=0 ? "datePast" : "dateFuture")); - if($days==0) - { - $output = $tokens[0]; - } else if($days==1) { - $output = $tokens[1]; - } else if($days>=2 && $days<=29) { - $output = preg_replace("/@x/i", $days, $tokens[2]); - } else if($days>=30 && $days<=59) { - $output = $tokens[3]; - } else if($days>=60 && $days<=364) { - $output = preg_replace("/@x/i", intval($days/30), $tokens[4]); - } else if($days>=365 && $days<=729) { - $output = $tokens[5]; - } else { - $output = preg_replace("/@x/i", intval($days/365.25), $tokens[6]); - } - } else { - $output = $this->getDateFormatted($timestamp, $format); - } - return $output; - } - - // Return languages - function getLanguages() - { - $languages = array(); - foreach($this->text as $key=>$value) array_push($languages, $key); - return $languages; - } - - // Return text modification date, Unix time or HTTP format - function getModified($httpFormat = false) - { - return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($this->modified) : $this->modified; - } +class YellowText { + public $yellow; //access to API + public $modified; //text modification date + public $text; //text + public $language; //current language + + public function __construct($yellow) { + $this->yellow = $yellow; + $this->modified = 0; + $this->text = new YellowDataCollection(); + } + + // Load text strings from file + public function load($fileName, $languageDefault) { + $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; + $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; + preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); + if (lcfirst($matches[1])=="language" && !strempty($matches[2])) $language = $matches[2]; + 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"; + } + } + } + } + + // Set current language + public function setLanguage($language) { + $this->language = $language; + } + + // Set text string for specific language + public function setText($key, $value, $language) { + if (is_null($this->text[$language])) $this->text[$language] = new YellowDataCollection(); + $this->text[$language][$key] = $value; + } + + // Return text string + public function get($key) { + return $this->getText($key, $this->language); + } + + // Return text string, HTML encoded + public function getHtml($key) { + return htmlspecialchars($this->getText($key, $this->language)); + } + + // Return text string for specific language + public function getText($key, $language) { + return $this->isExisting($key, $language) ? $this->text[$language][$key] : "[$key]"; + } + + // Return text string for specific language, HTML encoded + public function getTextHtml($key, $language) { + return htmlspecialchars($this->getText($key, $language)); + } + + // Return text strings + 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 + public function getDateFormatted($timestamp, $format) { + $dateMonths = preg_split("/\s*,\s*/", $this->get("dateMonths")); + $dateWeekdays = preg_split("/\s*,\s*/", $this->get("dateWeekdays")); + $month = $dateMonths[date("n", $timestamp) - 1]; + $weekday = $dateWeekdays[date("N", $timestamp) - 1]; + $timeZone = $this->yellow->config->get("timezone"); + $timeZoneHelper = new DateTime(null, new DateTimeZone($timeZone)); + $timeZoneOffset = $timeZoneHelper->getOffset(); + $timeZoneAbbreviation = "GMT".($timeZoneOffset<0 ? "-" : "+").abs(intval($timeZoneOffset/3600)); + $format = preg_replace("/(?<!\\\)F/", addcslashes($month, "A..Za..z"), $format); + $format = preg_replace("/(?<!\\\)M/", addcslashes(substru($month, 0, 3), "A..Za..z"), $format); + $format = preg_replace("/(?<!\\\)D/", addcslashes(substru($weekday, 0, 3), "A..Za..z"), $format); + $format = preg_replace("/(?<!\\\)l/", addcslashes($weekday, "A..Za..z"), $format); + $format = preg_replace("/(?<!\\\)T/", addcslashes($timeZoneAbbreviation, "A..Za..z"), $format); + return date($format, $timestamp); + } + + // Return human readable date, relative to today + public function getDateRelative($timestamp, $format, $daysLimit) { + $timeDifference = time() - $timestamp; + $days = abs(intval($timeDifference / 86400)); + if ($days<=$daysLimit || $daysLimit==0) { + $tokens = preg_split("/\s*,\s*/", $this->get($timeDifference>=0 ? "datePast" : "dateFuture")); + if ($days==0) { + $output = $tokens[0]; + } elseif ($days==1) { + $output = $tokens[1]; + } elseif ($days>=2 && $days<=29) { + $output = preg_replace("/@x/i", $days, $tokens[2]); + } elseif ($days>=30 && $days<=59) { + $output = $tokens[3]; + } elseif ($days>=60 && $days<=364) { + $output = preg_replace("/@x/i", intval($days/30), $tokens[4]); + } elseif ($days>=365 && $days<=729) { + $output = $tokens[5]; + } else { + $output = preg_replace("/@x/i", intval($days/365.25), $tokens[6]); + } + } else { + $output = $this->getDateFormatted($timestamp, $format); + } + return $output; + } + + // Return languages + public function getLanguages() { + $languages = array(); + foreach ($this->text as $key=>$value) { + array_push($languages, $key); + } + return $languages; + } + + // Return text modification date, Unix time or HTTP format + public function getModified($httpFormat = false) { + return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($this->modified) : $this->modified; + } - // Normalise date into known format - function normaliseDate($text) - { - if(preg_match("/^\d+\-\d+$/", $text)) - { - $output = $this->getDateFormatted(strtotime($text), $this->get("dateFormatShort")); - } else if(preg_match("/^\d+\-\d+\-\d+$/", $text)) { - $output = $this->getDateFormatted(strtotime($text), $this->get("dateFormatMedium")); - } else if(preg_match("/^\d+\-\d+\-\d+ \d+\:\d+$/", $text)) { - $output = $this->getDateFormatted(strtotime($text), $this->get("dateFormatLong")); - } else { - $output = $text; - } - return $output; - } - - // Check if language exists - function isLanguage($language) - { - return !is_null($this->text[$language]); - } - - // Check if text string exists - function isExisting($key, $language = "") - { - if(empty($language)) $language = $this->language; - return !is_null($this->text[$language]) && !is_null($this->text[$language][$key]); - } + // Normalise date into known format + public function normaliseDate($text) { + if (preg_match("/^\d+\-\d+$/", $text)) { + $output = $this->getDateFormatted(strtotime($text), $this->get("dateFormatShort")); + } elseif (preg_match("/^\d+\-\d+\-\d+$/", $text)) { + $output = $this->getDateFormatted(strtotime($text), $this->get("dateFormatMedium")); + } elseif (preg_match("/^\d+\-\d+\-\d+ \d+\:\d+$/", $text)) { + $output = $this->getDateFormatted(strtotime($text), $this->get("dateFormatLong")); + } else { + $output = $text; + } + return $output; + } + + // Check if language exists + public function isLanguage($language) { + return !is_null($this->text[$language]); + } + + // Check if text string exists + public function isExisting($key, $language = "") { + if (empty($language)) $language = $this->language; + return !is_null($this->text[$language]) && !is_null($this->text[$language][$key]); + } } -class YellowLookup -{ - var $yellow; //access to API - var $requestHandler; //request handler name - var $commandHandler; //command handler name - var $snippetArgs; //snippet arguments - - function __construct($yellow) - { - $this->yellow = $yellow; - } +class YellowLookup { + public $yellow; //access to API + public $requestHandler; //request handler name + public $commandHandler; //command handler name + public $snippetArgs; //snippet arguments + + public function __construct($yellow) { + $this->yellow = $yellow; + } - // Load file system information - function load() - { - list($pathRoot, $pathHome) = $this->detectFileSystem(); - $this->yellow->config->set("contentRootDir", $pathRoot); - $this->yellow->config->set("contentHomeDir", $pathHome); - date_default_timezone_set($this->yellow->config->get("timezone")); - } - - // Detect file system - function detectFileSystem() - { - $path = $this->yellow->config->get("contentDir"); - $pathRoot = $this->yellow->config->get("contentRootDir"); - $pathHome = $this->yellow->config->get("contentHomeDir"); - if(!$this->yellow->config->get("multiLanguageMode")) $pathRoot = ""; - if(!empty($pathRoot)) - { - $token = $root = rtrim($pathRoot, '/'); - foreach($this->yellow->toolbox->getDirectoryEntries($path, "/.*/", true, true, false) as $entry) - { - if(empty($firstRoot)) { $firstRoot = $token = $entry; } - if($this->normaliseToken($entry)==$root) { $token = $entry; break; } - } - $pathRoot = $this->normaliseToken($token)."/"; - $path .= "$firstRoot/"; - } - if(!empty($pathHome)) - { - $token = $home = rtrim($pathHome, '/'); - foreach($this->yellow->toolbox->getDirectoryEntries($path, "/.*/", true, true, false) as $entry) - { - if(empty($firstHome)) { $firstHome = $token = $entry; } - if($this->normaliseToken($entry)==$home) { $token = $entry; break; } - } - $pathHome = $this->normaliseToken($token)."/"; - } - return array($pathRoot, $pathHome); - } - - // Return root locations - function findRootLocations($includePath = true) - { - $locations = array(); - $pathBase = $this->yellow->config->get("contentDir"); - $pathRoot = $this->yellow->config->get("contentRootDir"); - if(!empty($pathRoot)) - { - foreach($this->yellow->toolbox->getDirectoryEntries($pathBase, "/.*/", true, true, false) as $entry) - { - $token = $this->normaliseToken($entry)."/"; - if($token==$pathRoot) $token = ""; - array_push($locations, $includePath ? "root/$token $pathBase$entry/" : "root/$token"); - if(defined("DEBUG") && DEBUG>=2) echo "YellowLookup::findRootLocations root/$token<br/>\n"; - } - } else { - array_push($locations, $includePath ? "root/ $pathBase" : "root/"); - } - return $locations; - } - - // Return location from file path - function findLocationFromFile($fileName) - { - $location = "/"; - $pathBase = $this->yellow->config->get("contentDir"); - $pathRoot = $this->yellow->config->get("contentRootDir"); - $pathHome = $this->yellow->config->get("contentHomeDir"); - $fileDefault = $this->yellow->config->get("contentDefaultFile"); - $fileExtension = $this->yellow->config->get("contentExtension"); - if(substru($fileName, 0, strlenu($pathBase))==$pathBase) - { - $fileName = substru($fileName, strlenu($pathBase)); - $tokens = explode('/', $fileName); - if(!empty($pathRoot)) - { - $token = $this->normaliseToken($tokens[0]).'/'; - if($token!=$pathRoot) $location .= $token; - array_shift($tokens); - } - for($i=0; $i<count($tokens)-1; ++$i) - { - $token = $this->normaliseToken($tokens[$i]).'/'; - if($i || $token!=$pathHome) $location .= $token; - } - $token = $this->normaliseToken($tokens[$i], $fileExtension); - $fileFolder = $this->normaliseToken($tokens[$i-1], $fileExtension); - if($token!=$fileDefault && $token!=$fileFolder) $location .= $this->normaliseToken($tokens[$i], $fileExtension, true); - $extension = ($pos = strrposu($fileName, '.')) ? substru($fileName, $pos) : ""; - if($extension!=$fileExtension) $invalid = true; - } else { - $invalid = true; - } - if(defined("DEBUG") && DEBUG>=2) - { - $debug = ($invalid ? "INVALID" : $location)." <- $pathBase$fileName"; - echo "YellowLookup::findLocationFromFile $debug<br/>\n"; - } - return $invalid ? "" : $location; - } - - // Return file path from location - function findFileFromLocation($location, $directory = false) - { - $path = $this->yellow->config->get("contentDir"); - $pathRoot = $this->yellow->config->get("contentRootDir"); - $pathHome = $this->yellow->config->get("contentHomeDir"); - $fileDefault = $this->yellow->config->get("contentDefaultFile"); - $fileExtension = $this->yellow->config->get("contentExtension"); - $tokens = explode('/', $location); - if($this->isRootLocation($location)) - { - if(!empty($pathRoot)) - { - $token = (count($tokens)>2) ? $tokens[1] : rtrim($pathRoot, '/'); - $path .= $this->findFileDirectory($path, $token, "", true, true, $found, $invalid); - } - } else { - if(!empty($pathRoot)) - { - if(count($tokens)>2) - { - if($this->normaliseToken($tokens[1])==$this->normaliseToken(rtrim($pathRoot, '/'))) $invalid = true; - $path .= $this->findFileDirectory($path, $tokens[1], "", true, false, $found, $invalid); - if($found) array_shift($tokens); - } - if(!$found) $path .= $this->findFileDirectory($path, rtrim($pathRoot, '/'), "", true, true, $found, $invalid); - - } - if(count($tokens)>2) - { - if($this->normaliseToken($tokens[1])==$this->normaliseToken(rtrim($pathHome, '/'))) $invalid = true; - for($i=1; $i<count($tokens)-1; ++$i) - { - $path .= $this->findFileDirectory($path, $tokens[$i], "", true, true, $found, $invalid); - } - } else { - $i = 1; - $tokens[0] = rtrim($pathHome, '/'); - $path .= $this->findFileDirectory($path, $tokens[0], "", true, true, $found, $invalid); - } - if(!$directory) - { - if(!strempty($tokens[$i])) - { - $token = $tokens[$i].$fileExtension; - $fileFolder = $tokens[$i-1].$fileExtension; - if($token==$fileDefault || $token==$fileFolder) $invalid = true; - $path .= $this->findFileDirectory($path, $token, $fileExtension, false, true, $found, $invalid); - } else { - $path .= $this->findFileDefault($path, $fileDefault, $fileExtension, false); - } - if(defined("DEBUG") && DEBUG>=2) - { - $debug = "$location -> ".($invalid ? "INVALID" : $path); - echo "YellowLookup::findFileFromLocation $debug<br/>\n"; - } - } - } - return $invalid ? "" : $path; - } - - // Return file or directory that matches token - function findFileDirectory($path, $token, $fileExtension, $directory, $default, &$found, &$invalid) - { - if($this->normaliseToken($token, $fileExtension)!=$token) $invalid = true; - if(!$invalid) - { - $regex = "/^[\d\-\_\.]*".strreplaceu('-', '.', $token)."$/"; - foreach($this->yellow->toolbox->getDirectoryEntries($path, $regex, false, $directory, false) as $entry) - { - if($this->normaliseToken($entry, $fileExtension)==$token) { $token = $entry; $found = true; break; } - } - } - if($directory) $token .= '/'; - return ($default || $found) ? $token : ""; - } - - // Return default file in directory - function findFileDefault($path, $fileDefault, $fileExtension, $includePath = true) - { - $token = $fileDefault; - if(!is_file($path."/".$fileDefault)) - { - $fileFolder = $this->normaliseToken(basename($path), $fileExtension); - $regex = "/^[\d\-\_\.]*($fileDefault|$fileFolder)$/"; - foreach($this->yellow->toolbox->getDirectoryEntries($path, $regex, true, false, false) as $entry) - { - if($this->normaliseToken($entry, $fileExtension)==$fileDefault) { $token = $entry; break; } - if($this->normaliseToken($entry, $fileExtension)==$fileFolder) { $token = $entry; break; } - } - } - return $includePath ? "$path/$token" : $token; - } - - // Return new file - function findFileNew($location, $filePrefix = "") - { - $fileName = $this->findFileFromLocation($location); - if(!empty($filePrefix) && !empty($fileName)) - { - preg_match("/^([\d\-\_\.]*)(.*)$/", $filePrefix, $matches); - $filePrefix = empty($matches[1]) ? "" : $matches[1].'-'; - $fileText = $this->normaliseName(basename($fileName), true, true); - if(preg_match("/^[\d\-\_\.]*$/", $fileText) && !empty($filePrefix)) $filePrefix = ""; - $fileName = dirname($fileName)."/".$filePrefix.$fileText.$this->yellow->config->get("contentExtension"); - } - if(!is_dir(dirname($fileName))) - { - $tokens = explode('/', $fileName); - for($i=0; $i<count($tokens)-1; ++$i) - { - if(!is_dir($path.$tokens[$i])) - { - if(!preg_match("/^[\d\-\_\.]+(.*)$/", $tokens[$i])) - { - $number = 1; - foreach($this->yellow->toolbox->getDirectoryEntries($path, "/^[\d\-\_\.]+(.*)$/", true, true, false) as $entry) - { - if($number!=1 && $number!=intval($entry)) break; - $number = intval($entry)+1; - } - $tokens[$i] = "$number-".$tokens[$i]; - } - $tokens[$i] = $this->normaliseName($tokens[$i], false, false, true); - } - $path .= $tokens[$i]."/"; - } - $fileName = $path.$tokens[$i]; - } - return $fileName; - } - - // Return static file if possible - function findFileStatic($location, $fileName, $cacheable) - { - if($cacheable) - { - $location .= $this->yellow->toolbox->getLocationArgs(); - $fileNameStatic = rtrim($this->yellow->config->get("staticDir"), '/').$location; - if(!$this->isFileLocation($location)) $fileNameStatic .= $this->yellow->config->get("staticDefaultFile"); - if(is_readable($fileNameStatic)) $fileName = $fileNameStatic; - } - return $fileName; - } - - // Return children from location - function findChildrenFromLocation($location) - { - $fileNames = array(); - $fileDefault = $this->yellow->config->get("contentDefaultFile"); - $fileExtension = $this->yellow->config->get("contentExtension"); - if(!$this->isFileLocation($location)) - { - $path = $this->findFileFromLocation($location, true); - foreach($this->yellow->toolbox->getDirectoryEntries($path, "/.*/", true, true, false) as $entry) - { - $token = $this->findFileDefault($path.$entry, $fileDefault, $fileExtension, false); - array_push($fileNames, $path.$entry."/".$token); - } - if(!$this->isRootLocation($location)) - { - $fileFolder = $this->normaliseToken(basename($path), $fileExtension); - $regex = "/^.*\\".$fileExtension."$/"; - foreach($this->yellow->toolbox->getDirectoryEntries($path, $regex, true, false, false) as $entry) - { - if($this->normaliseToken($entry, $fileExtension)==$fileDefault) continue; - if($this->normaliseToken($entry, $fileExtension)==$fileFolder) continue; - array_push($fileNames, $path.$entry); - } - } - } - return $fileNames; - } + // Load file system information + public function load() { + list($pathRoot, $pathHome) = $this->detectFileSystem(); + $this->yellow->config->set("contentRootDir", $pathRoot); + $this->yellow->config->set("contentHomeDir", $pathHome); + date_default_timezone_set($this->yellow->config->get("timezone")); + } + + // Detect file system + public function detectFileSystem() { + $path = $this->yellow->config->get("contentDir"); + $pathRoot = $this->yellow->config->get("contentRootDir"); + $pathHome = $this->yellow->config->get("contentHomeDir"); + if (!$this->yellow->config->get("multiLanguageMode")) $pathRoot = ""; + if (!empty($pathRoot)) { + $token = $root = rtrim($pathRoot, "/"); + foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/.*/", true, true, false) as $entry) { + if (empty($firstRoot)) $firstRoot = $token = $entry; + if ($this->normaliseToken($entry)==$root) { + $token = $entry; + break; + } + } + $pathRoot = $this->normaliseToken($token)."/"; + $path .= "$firstRoot/"; + } + if (!empty($pathHome)) { + $token = $home = rtrim($pathHome, "/"); + foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/.*/", true, true, false) as $entry) { + if (empty($firstHome)) $firstHome = $token = $entry; + if ($this->normaliseToken($entry)==$home) { + $token = $entry; + break; + } + } + $pathHome = $this->normaliseToken($token)."/"; + } + return array($pathRoot, $pathHome); + } + + // Return root locations + public function findRootLocations($includePath = true) { + $locations = array(); + $pathBase = $this->yellow->config->get("contentDir"); + $pathRoot = $this->yellow->config->get("contentRootDir"); + if (!empty($pathRoot)) { + foreach ($this->yellow->toolbox->getDirectoryEntries($pathBase, "/.*/", true, true, false) as $entry) { + $token = $this->normaliseToken($entry)."/"; + if ($token==$pathRoot) $token = ""; + array_push($locations, $includePath ? "root/$token $pathBase$entry/" : "root/$token"); + if (defined("DEBUG") && DEBUG>=2) echo "YellowLookup::findRootLocations root/$token<br/>\n"; + } + } else { + array_push($locations, $includePath ? "root/ $pathBase" : "root/"); + } + return $locations; + } + + // Return location from file path + public function findLocationFromFile($fileName) { + $location = "/"; + $pathBase = $this->yellow->config->get("contentDir"); + $pathRoot = $this->yellow->config->get("contentRootDir"); + $pathHome = $this->yellow->config->get("contentHomeDir"); + $fileDefault = $this->yellow->config->get("contentDefaultFile"); + $fileExtension = $this->yellow->config->get("contentExtension"); + if (substru($fileName, 0, strlenu($pathBase))==$pathBase) { + $fileName = substru($fileName, strlenu($pathBase)); + $tokens = explode("/", $fileName); + if (!empty($pathRoot)) { + $token = $this->normaliseToken($tokens[0])."/"; + if ($token!=$pathRoot) $location .= $token; + array_shift($tokens); + } + for ($i=0; $i<count($tokens)-1; ++$i) { + $token = $this->normaliseToken($tokens[$i])."/"; + if ($i || $token!=$pathHome) $location .= $token; + } + $token = $this->normaliseToken($tokens[$i], $fileExtension); + $fileFolder = $this->normaliseToken($tokens[$i-1], $fileExtension); + if ($token!=$fileDefault && $token!=$fileFolder) { + $location .= $this->normaliseToken($tokens[$i], $fileExtension, true); + } + $extension = ($pos = strrposu($fileName, ".")) ? substru($fileName, $pos) : ""; + if ($extension!=$fileExtension) $invalid = true; + } else { + $invalid = true; + } + if (defined("DEBUG") && DEBUG>=2) { + $debug = ($invalid ? "INVALID" : $location)." <- $pathBase$fileName"; + echo "YellowLookup::findLocationFromFile $debug<br/>\n"; + } + return $invalid ? "" : $location; + } + + // Return file path from location + public function findFileFromLocation($location, $directory = false) { + $path = $this->yellow->config->get("contentDir"); + $pathRoot = $this->yellow->config->get("contentRootDir"); + $pathHome = $this->yellow->config->get("contentHomeDir"); + $fileDefault = $this->yellow->config->get("contentDefaultFile"); + $fileExtension = $this->yellow->config->get("contentExtension"); + $tokens = explode("/", $location); + if ($this->isRootLocation($location)) { + if (!empty($pathRoot)) { + $token = (count($tokens)>2) ? $tokens[1] : rtrim($pathRoot, "/"); + $path .= $this->findFileDirectory($path, $token, "", true, true, $found, $invalid); + } + } else { + if (!empty($pathRoot)) { + if (count($tokens)>2) { + if ($this->normaliseToken($tokens[1])==$this->normaliseToken(rtrim($pathRoot, "/"))) $invalid = true; + $path .= $this->findFileDirectory($path, $tokens[1], "", true, false, $found, $invalid); + if ($found) array_shift($tokens); + } + if (!$found) { + $path .= $this->findFileDirectory($path, rtrim($pathRoot, "/"), "", true, true, $found, $invalid); + } + } + if (count($tokens)>2) { + if ($this->normaliseToken($tokens[1])==$this->normaliseToken(rtrim($pathHome, "/"))) $invalid = true; + for ($i=1; $i<count($tokens)-1; ++$i) { + $path .= $this->findFileDirectory($path, $tokens[$i], "", true, true, $found, $invalid); + } + } else { + $i = 1; + $tokens[0] = rtrim($pathHome, "/"); + $path .= $this->findFileDirectory($path, $tokens[0], "", true, true, $found, $invalid); + } + if (!$directory) { + if (!strempty($tokens[$i])) { + $token = $tokens[$i].$fileExtension; + $fileFolder = $tokens[$i-1].$fileExtension; + if ($token==$fileDefault || $token==$fileFolder) $invalid = true; + $path .= $this->findFileDirectory($path, $token, $fileExtension, false, true, $found, $invalid); + } else { + $path .= $this->findFileDefault($path, $fileDefault, $fileExtension, false); + } + if (defined("DEBUG") && DEBUG>=2) { + $debug = "$location -> ".($invalid ? "INVALID" : $path); + echo "YellowLookup::findFileFromLocation $debug<br/>\n"; + } + } + } + return $invalid ? "" : $path; + } + + // Return file or directory that matches token + public function findFileDirectory($path, $token, $fileExtension, $directory, $default, &$found, &$invalid) { + if ($this->normaliseToken($token, $fileExtension)!=$token) $invalid = true; + if (!$invalid) { + $regex = "/^[\d\-\_\.]*".strreplaceu("-", ".", $token)."$/"; + foreach ($this->yellow->toolbox->getDirectoryEntries($path, $regex, false, $directory, false) as $entry) { + if ($this->normaliseToken($entry, $fileExtension)==$token) { + $token = $entry; + $found = true; + break; + } + } + } + if ($directory) $token .= "/"; + return ($default || $found) ? $token : ""; + } + + // Return default file in directory + public function findFileDefault($path, $fileDefault, $fileExtension, $includePath = true) { + $token = $fileDefault; + if (!is_file($path."/".$fileDefault)) { + $fileFolder = $this->normaliseToken(basename($path), $fileExtension); + $regex = "/^[\d\-\_\.]*($fileDefault|$fileFolder)$/"; + foreach ($this->yellow->toolbox->getDirectoryEntries($path, $regex, true, false, false) as $entry) { + if ($this->normaliseToken($entry, $fileExtension)==$fileDefault) { + $token = $entry; + break; + } + if ($this->normaliseToken($entry, $fileExtension)==$fileFolder) { + $token = $entry; + break; + } + } + } + return $includePath ? "$path/$token" : $token; + } + + // Return new file + public function findFileNew($location, $filePrefix = "") { + $fileName = $this->findFileFromLocation($location); + if (!empty($filePrefix) && !empty($fileName)) { + preg_match("/^([\d\-\_\.]*)(.*)$/", $filePrefix, $matches); + $filePrefix = empty($matches[1]) ? "" : $matches[1]."-"; + $fileText = $this->normaliseName(basename($fileName), true, true); + if (preg_match("/^[\d\-\_\.]*$/", $fileText) && !empty($filePrefix)) $filePrefix = ""; + $fileName = dirname($fileName)."/".$filePrefix.$fileText.$this->yellow->config->get("contentExtension"); + } + if (!is_dir(dirname($fileName))) { + $tokens = explode("/", $fileName); + for ($i=0; $i<count($tokens)-1; ++$i) { + if (!is_dir($path.$tokens[$i])) { + if (!preg_match("/^[\d\-\_\.]+(.*)$/", $tokens[$i])) { + $number = 1; + foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/^[\d\-\_\.]+(.*)$/", true, true, false) as $entry) { + if ($number!=1 && $number!=intval($entry)) break; + $number = intval($entry)+1; + } + $tokens[$i] = "$number-".$tokens[$i]; + } + $tokens[$i] = $this->normaliseName($tokens[$i], false, false, true); + } + $path .= $tokens[$i]."/"; + } + $fileName = $path.$tokens[$i]; + } + return $fileName; + } + + // Return static file if possible + public function findFileStatic($location, $fileName, $cacheable) { + if ($cacheable) { + $location .= $this->yellow->toolbox->getLocationArgs(); + $fileNameStatic = rtrim($this->yellow->config->get("staticDir"), "/").$location; + if (!$this->isFileLocation($location)) $fileNameStatic .= $this->yellow->config->get("staticDefaultFile"); + if (is_readable($fileNameStatic)) $fileName = $fileNameStatic; + } + return $fileName; + } + + // Return children from location + public function findChildrenFromLocation($location) { + $fileNames = array(); + $fileDefault = $this->yellow->config->get("contentDefaultFile"); + $fileExtension = $this->yellow->config->get("contentExtension"); + if (!$this->isFileLocation($location)) { + $path = $this->findFileFromLocation($location, true); + foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/.*/", true, true, false) as $entry) { + $token = $this->findFileDefault($path.$entry, $fileDefault, $fileExtension, false); + array_push($fileNames, $path.$entry."/".$token); + } + if (!$this->isRootLocation($location)) { + $fileFolder = $this->normaliseToken(basename($path), $fileExtension); + $regex = "/^.*\\".$fileExtension."$/"; + foreach ($this->yellow->toolbox->getDirectoryEntries($path, $regex, true, false, false) as $entry) { + if ($this->normaliseToken($entry, $fileExtension)==$fileDefault) continue; + if ($this->normaliseToken($entry, $fileExtension)==$fileFolder) continue; + array_push($fileNames, $path.$entry); + } + } + } + return $fileNames; + } - // Return language from file path - function findLanguageFromFile($fileName, $languageDefault) - { - $language = $languageDefault; - $pathBase = $this->yellow->config->get("contentDir"); - $pathRoot = $this->yellow->config->get("contentRootDir"); - if(!empty($pathRoot)) - { - $fileName = substru($fileName, strlenu($pathBase)); - if(preg_match("/^(.+?)\//", $fileName, $matches)) $name = $this->normaliseToken($matches[1]); - if(strlenu($name)==2) $language = $name; - } - return $language; - } + // Return language from file path + public function findLanguageFromFile($fileName, $languageDefault) { + $language = $languageDefault; + $pathBase = $this->yellow->config->get("contentDir"); + $pathRoot = $this->yellow->config->get("contentRootDir"); + if (!empty($pathRoot)) { + $fileName = substru($fileName, strlenu($pathBase)); + if (preg_match("/^(.+?)\//", $fileName, $matches)) $name = $this->normaliseToken($matches[1]); + if (strlenu($name)==2) $language = $name; + } + return $language; + } - // Return file path from media location - function findFileFromMedia($location) - { - if($this->isFileLocation($location)) - { - $mediaLocationLength = strlenu($this->yellow->config->get("mediaLocation")); - if(substru($location, 0, $mediaLocationLength)==$this->yellow->config->get("mediaLocation")) - { - $fileName = $this->yellow->config->get("mediaDir").substru($location, 7); - } - } - return $fileName; - } - - // Return file path from system location - function findFileFromSystem($location) - { - if(preg_match("/\.(css|gif|ico|js|jpg|png|svg|txt|woff|woff2)$/", $location)) - { - $pluginLocationLength = strlenu($this->yellow->config->get("pluginLocation")); - $themeLocationLength = strlenu($this->yellow->config->get("themeLocation")); - if(substru($location, 0, $pluginLocationLength)==$this->yellow->config->get("pluginLocation")) { - $fileName = $this->yellow->config->get("pluginDir").substru($location, $pluginLocationLength); - } else if(substru($location, 0, $themeLocationLength)==$this->yellow->config->get("themeLocation")) { - $fileName = $this->yellow->config->get("themeDir").substru($location, $themeLocationLength); - } else if($location=="/".$this->yellow->config->get("robotsFile")) { - $fileName = $this->yellow->config->get("configDir").$this->yellow->config->get("robotsFile"); - } else if($location=="/".$this->yellow->config->get("faviconFile")) { - $fileName = $this->yellow->config->get("assetDir").$this->yellow->config->get("siteicon").".png"; - } - } - return $fileName; - } - - // Normalise file/directory token - function normaliseToken($text, $fileExtension = "", $removeExtension = false) - { - if(!empty($fileExtension)) $text = ($pos = strrposu($text, '.')) ? substru($text, 0, $pos) : $text; - if(preg_match("/^[\d\-\_\.]+(.*)$/", $text, $matches) && !empty($matches[1])) $text = $matches[1]; - return preg_replace("/[^\pL\d\-\_]/u", "-", $text).($removeExtension ? "" : $fileExtension); - } - - // Normalise name - function normaliseName($text, $removePrefix = false, $removeExtension = false, $filterStrict = false) - { - if($removeExtension) $text = ($pos = strrposu($text, '.')) ? substru($text, 0, $pos) : $text; - if($removePrefix && preg_match("/^[\d\-\_\.]+(.*)$/", $text, $matches) && !empty($matches[1])) $text = $matches[1]; - if($filterStrict) $text = strtoloweru($text); - return preg_replace("/[^\pL\d\-\_]/u", "-", $text); - } - - // Normalise array, make keys with same upper/lower case - function normaliseUpperLower($input) - { - $array = array(); - foreach($input as $key=>$value) - { - if(empty($key) || strempty($value)) continue; - $keySearch = strtoloweru($key); - foreach($array as $keyNew=>$valueNew) if(strtoloweru($keyNew)==$keySearch) { $key = $keyNew; break; } - $array[$key] += $value; - } - return $array; - } - - // Normalise location, make absolute location - function normaliseLocation($location, $pageLocation, $filterStrict = true) - { - if(!preg_match("/^\w+:/", trim(html_entity_decode($location, ENT_QUOTES, "UTF-8")))) - { - $pageBase = $this->yellow->page->base; - $mediaBase = $this->yellow->config->get("serverBase").$this->yellow->config->get("mediaLocation"); - if(preg_match("/^\#/", $location)) - { - $location = $pageBase.$pageLocation.$location; - } else if(!preg_match("/^\//", $location)) { - $location = $this->getDirectoryLocation($pageBase.$pageLocation).$location; - } else if(!preg_match("#^($pageBase|$mediaBase)#", $location)) { - $location = $pageBase.$location; - } - $location = strreplaceu("/./", "/", $location); - $location = strreplaceu(':', $this->yellow->toolbox->getLocationArgsSeparator(), $location); - } else { - if($filterStrict && !preg_match("/^(http|https|ftp|mailto):/", $location)) $location = "error-xss-filter"; - } - return $location; - } - - // Normalise URL, make absolute URL - function normaliseUrl($scheme, $address, $base, $location, $filterStrict = true) - { - if(!preg_match("/^\w+:/", $location)) - { - $url = "$scheme://$address$base$location"; - } else { - if($filterStrict && !preg_match("/^(http|https|ftp|mailto):/", $location)) $location = "error-xss-filter"; - $url = $location; - } - return $url; - } - - // Return URL information - function getUrlInformation($url) - { - if(preg_match("#^(\w+)://([^/]+)(.*)$#", rtrim($url, '/'), $matches)) - { - $scheme = $matches[1]; - $address = $matches[2]; - $base = $matches[3]; - } - return array($scheme, $address, $base); - } - - // Return directory location - function getDirectoryLocation($location) - { - return ($pos = strrposu($location, '/')) ? substru($location, 0, $pos+1) : "/"; - } - - // Check if location is specifying root - function isRootLocation($location) - { - return $location[0]!="/"; - } - - // Check if location is specifying file or directory - function isFileLocation($location) - { - return substru($location, -1, 1)!="/"; - } - - // Check if location can be redirected into directory - function isRedirectLocation($location) - { - $redirect = false; - if($this->isFileLocation($location)) - { - $redirect = is_dir($this->findFileFromLocation("$location/", true)); - } else if($location=="/") { - $redirect = $this->yellow->config->get("multiLanguageMode"); - } - return $redirect; - } - - // Check if location contains nested directories - function isNestedLocation($location, $fileName, $checkHomeLocation = false) - { - $nested = false; - if(!$checkHomeLocation || $location==$this->yellow->pages->getHomeLocation($location)) - { - $path = dirname($fileName); - if(count($this->yellow->toolbox->getDirectoryEntries($path, "/.*/", true, true, false))) $nested = true; - } - return $nested; - } - - // Check if location is visible - function isVisibleLocation($location, $fileName) - { - $visible = true; - $pathBase = $this->yellow->config->get("contentDir"); - if(substru($fileName, 0, strlenu($pathBase))==$pathBase) - { - $fileName = substru($fileName, strlenu($pathBase)); - $tokens = explode('/', $fileName); - for($i=0; $i<count($tokens)-1; ++$i) - { - if(!preg_match("/^[\d\-\_\.]+(.*)$/", $tokens[$i])) { $visible = false; break; } - } - } else { - $visible = false; - } - return $visible; - } - - // Check if location is within current request - function isActiveLocation($location, $currentLocation) - { - if($this->isFileLocation($location)) - { - $active = $currentLocation==$location; - } else { - if($location==$this->yellow->pages->getHomeLocation($location)) - { - $active = $this->getDirectoryLocation($currentLocation)==$location; - } else { - $active = substru($currentLocation, 0, strlenu($location))==$location; - } - } - return $active; - } - - // Check if file is valid - function isValidFile($fileName) - { - $contentDirLength = strlenu($this->yellow->config->get("contentDir")); - $mediaDirLength = strlenu($this->yellow->config->get("mediaDir")); - $systemDirLength = strlenu($this->yellow->config->get("systemDir")); - return substru($fileName, 0, $contentDirLength)==$this->yellow->config->get("contentDir") || - substru($fileName, 0, $mediaDirLength)==$this->yellow->config->get("mediaDir") || - substru($fileName, 0, $systemDirLength)==$this->yellow->config->get("systemDir"); - } - - // Check if content file - function isContentFile($fileName) - { - $contentDirLength = strlenu($this->yellow->config->get("contentDir")); - return substru($fileName, 0, $contentDirLength)==$this->yellow->config->get("contentDir"); - } - - // Check if media file - function isMediaFile($fileName) - { - $mediaDirLength = strlenu($this->yellow->config->get("mediaDir")); - return substru($fileName, 0, $mediaDirLength)==$this->yellow->config->get("mediaDir"); - } - - // Check if system file - function isSystemFile($fileName) - { - $systemDirLength = strlenu($this->yellow->config->get("systemDir")); - return substru($fileName, 0, $systemDirLength)==$this->yellow->config->get("systemDir"); - } + // Return file path from media location + public function findFileFromMedia($location) { + if ($this->isFileLocation($location)) { + $mediaLocationLength = strlenu($this->yellow->config->get("mediaLocation")); + if (substru($location, 0, $mediaLocationLength)==$this->yellow->config->get("mediaLocation")) { + $fileName = $this->yellow->config->get("mediaDir").substru($location, 7); + } + } + return $fileName; + } + + // Return file path from system location + public function findFileFromSystem($location) { + if (preg_match("/\.(css|gif|ico|js|jpg|png|svg|txt|woff|woff2)$/", $location)) { + $pluginLocationLength = strlenu($this->yellow->config->get("pluginLocation")); + $themeLocationLength = strlenu($this->yellow->config->get("themeLocation")); + if (substru($location, 0, $pluginLocationLength)==$this->yellow->config->get("pluginLocation")) { + $fileName = $this->yellow->config->get("pluginDir").substru($location, $pluginLocationLength); + } elseif (substru($location, 0, $themeLocationLength)==$this->yellow->config->get("themeLocation")) { + $fileName = $this->yellow->config->get("themeDir").substru($location, $themeLocationLength); + } elseif ($location=="/".$this->yellow->config->get("robotsFile")) { + $fileName = $this->yellow->config->get("configDir").$this->yellow->config->get("robotsFile"); + } elseif ($location=="/".$this->yellow->config->get("faviconFile")) { + $fileName = $this->yellow->config->get("assetDir").$this->yellow->config->get("siteicon").".png"; + } + } + return $fileName; + } + + // Normalise file/directory token + public function normaliseToken($text, $fileExtension = "", $removeExtension = false) { + if (!empty($fileExtension)) $text = ($pos = strrposu($text, ".")) ? substru($text, 0, $pos) : $text; + if (preg_match("/^[\d\-\_\.]+(.*)$/", $text, $matches) && !empty($matches[1])) $text = $matches[1]; + return preg_replace("/[^\pL\d\-\_]/u", "-", $text).($removeExtension ? "" : $fileExtension); + } + + // Normalise name + public function normaliseName($text, $removePrefix = false, $removeExtension = false, $filterStrict = false) { + if ($removeExtension) $text = ($pos = strrposu($text, ".")) ? substru($text, 0, $pos) : $text; + if ($removePrefix && preg_match("/^[\d\-\_\.]+(.*)$/", $text, $matches) && !empty($matches[1])) $text = $matches[1]; + if ($filterStrict) $text = strtoloweru($text); + return preg_replace("/[^\pL\d\-\_]/u", "-", $text); + } + + // Normalise array, make keys with same upper/lower case + public function normaliseUpperLower($input) { + $array = array(); + foreach ($input as $key=>$value) { + if (empty($key) || strempty($value)) continue; + $keySearch = strtoloweru($key); + foreach ($array as $keyNew=>$valueNew) { + if (strtoloweru($keyNew)==$keySearch) { + $key = $keyNew; + break; + } + } + $array[$key] += $value; + } + return $array; + } + + // Normalise location, make absolute location + public function normaliseLocation($location, $pageLocation, $filterStrict = true) { + if (!preg_match("/^\w+:/", trim(html_entity_decode($location, ENT_QUOTES, "UTF-8")))) { + $pageBase = $this->yellow->page->base; + $mediaBase = $this->yellow->config->get("serverBase").$this->yellow->config->get("mediaLocation"); + if (preg_match("/^\#/", $location)) { + $location = $pageBase.$pageLocation.$location; + } elseif (!preg_match("/^\//", $location)) { + $location = $this->getDirectoryLocation($pageBase.$pageLocation).$location; + } elseif (!preg_match("#^($pageBase|$mediaBase)#", $location)) { + $location = $pageBase.$location; + } + $location = strreplaceu("/./", "/", $location); + $location = strreplaceu(":", $this->yellow->toolbox->getLocationArgsSeparator(), $location); + } else { + if ($filterStrict && !preg_match("/^(http|https|ftp|mailto):/", $location)) $location = "error-xss-filter"; + } + return $location; + } + + // Normalise URL, make absolute URL + public function normaliseUrl($scheme, $address, $base, $location, $filterStrict = true) { + if (!preg_match("/^\w+:/", $location)) { + $url = "$scheme://$address$base$location"; + } else { + if ($filterStrict && !preg_match("/^(http|https|ftp|mailto):/", $location)) $location = "error-xss-filter"; + $url = $location; + } + return $url; + } + + // Return URL information + public function getUrlInformation($url) { + if (preg_match("#^(\w+)://([^/]+)(.*)$#", rtrim($url, "/"), $matches)) { + $scheme = $matches[1]; + $address = $matches[2]; + $base = $matches[3]; + } + return array($scheme, $address, $base); + } + + // Return directory location + public function getDirectoryLocation($location) { + return ($pos = strrposu($location, "/")) ? substru($location, 0, $pos+1) : "/"; + } + + // Check if location is specifying root + public function isRootLocation($location) { + return $location[0]!="/"; + } + + // Check if location is specifying file or directory + public function isFileLocation($location) { + return substru($location, -1, 1)!="/"; + } + + // Check if location can be redirected into directory + public function isRedirectLocation($location) { + $redirect = false; + if ($this->isFileLocation($location)) { + $redirect = is_dir($this->findFileFromLocation("$location/", true)); + } elseif ($location=="/") { + $redirect = $this->yellow->config->get("multiLanguageMode"); + } + return $redirect; + } + + // Check if location contains nested directories + public function isNestedLocation($location, $fileName, $checkHomeLocation = false) { + $nested = false; + if (!$checkHomeLocation || $location==$this->yellow->pages->getHomeLocation($location)) { + $path = dirname($fileName); + if (count($this->yellow->toolbox->getDirectoryEntries($path, "/.*/", true, true, false))) $nested = true; + } + return $nested; + } + + // Check if location is visible + public function isVisibleLocation($location, $fileName) { + $visible = true; + $pathBase = $this->yellow->config->get("contentDir"); + if (substru($fileName, 0, strlenu($pathBase))==$pathBase) { + $fileName = substru($fileName, strlenu($pathBase)); + $tokens = explode("/", $fileName); + for ($i=0; $i<count($tokens)-1; ++$i) { + if (!preg_match("/^[\d\-\_\.]+(.*)$/", $tokens[$i])) { + $visible = false; + break; + } + } + } else { + $visible = false; + } + return $visible; + } + + // Check if location is within current request + public function isActiveLocation($location, $currentLocation) { + if ($this->isFileLocation($location)) { + $active = $currentLocation==$location; + } else { + if ($location==$this->yellow->pages->getHomeLocation($location)) { + $active = $this->getDirectoryLocation($currentLocation)==$location; + } else { + $active = substru($currentLocation, 0, strlenu($location))==$location; + } + } + return $active; + } + + // Check if file is valid + public function isValidFile($fileName) { + $contentDirLength = strlenu($this->yellow->config->get("contentDir")); + $mediaDirLength = strlenu($this->yellow->config->get("mediaDir")); + $systemDirLength = strlenu($this->yellow->config->get("systemDir")); + return substru($fileName, 0, $contentDirLength)==$this->yellow->config->get("contentDir") || + substru($fileName, 0, $mediaDirLength)==$this->yellow->config->get("mediaDir") || + substru($fileName, 0, $systemDirLength)==$this->yellow->config->get("systemDir"); + } + + // Check if content file + public function isContentFile($fileName) { + $contentDirLength = strlenu($this->yellow->config->get("contentDir")); + return substru($fileName, 0, $contentDirLength)==$this->yellow->config->get("contentDir"); + } + + // Check if media file + public function isMediaFile($fileName) { + $mediaDirLength = strlenu($this->yellow->config->get("mediaDir")); + return substru($fileName, 0, $mediaDirLength)==$this->yellow->config->get("mediaDir"); + } + + // Check if system file + public function isSystemFile($fileName) { + $systemDirLength = strlenu($this->yellow->config->get("systemDir")); + return substru($fileName, 0, $systemDirLength)==$this->yellow->config->get("systemDir"); + } } -class YellowToolbox -{ - // Return server version from current HTTP request - function getServerVersion($shortFormat = false) - { - $serverVersion = strtoupperu(PHP_SAPI)." ".PHP_OS; - if(preg_match("/^(\S+)/", $_SERVER["SERVER_SOFTWARE"], $matches)) $serverVersion = $matches[1]." ".PHP_OS; - if($shortFormat && preg_match("/^(\pL+)/u", $serverVersion, $matches)) $serverVersion = $matches[1]; - return $serverVersion; - } - - // Return server URL from current HTTP request - function getServerUrl() - { - $scheme = $this->getScheme(); - $address = $this->getAddress(); - $base = $this->getBase(); - return "$scheme://$address$base/"; - } - - // Return scheme from current HTTP request - function getScheme() - { - $scheme = ""; - if(preg_match("/^HTTP\//", $_SERVER["SERVER_PROTOCOL"])) - { - $secure = isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"]!="off"; - $scheme = $secure ? "https" : "http"; - } - return $scheme; - } - - // Return address from current HTTP request - function getAddress() - { - $address = $_SERVER["SERVER_NAME"]; - $port = $_SERVER["SERVER_PORT"]; - if($port!=80 && $port!=443) $address .= ":$port"; - return $address; - } - - // Return base from current HTTP request - function getBase() - { - $base = ""; - if(preg_match("/^(.*)\/.*\.php$/", $_SERVER["SCRIPT_NAME"], $matches)) $base = $matches[1]; - return $base; - } - - // Return location from current HTTP request - function getLocation($filterStrict = true) - { - $location = $_SERVER["REQUEST_URI"]; - $location = rawurldecode(($pos = strposu($location, '?')) ? substru($location, 0, $pos) : $location); - if($filterStrict) - { - $location = $this->normaliseTokens($location, true); - $separator = $this->getLocationArgsSeparator(); - if(preg_match("/^(.*?\/)([^\/]+$separator.*)$/", $location, $matches)) - { - $_SERVER["LOCATION"] = $location = $matches[1]; - $_SERVER["LOCATION_ARGS"] = $matches[2]; - foreach(explode('/', $matches[2]) as $token) - { - preg_match("/^(.*?)$separator(.*)$/", $token, $matches); - if(!empty($matches[1]) && !strempty($matches[2])) - { - $matches[1] = strreplaceu(array("\x1c", "\x1d", "\x1e"), array('/', ':', '='), $matches[1]); - $matches[2] = strreplaceu(array("\x1c", "\x1d", "\x1e"), array('/', ':', '='), $matches[2]); - $_REQUEST[$matches[1]] = $matches[2]; - } - } - } else { - $_SERVER["LOCATION"] = $location; - $_SERVER["LOCATION_ARGS"] = ""; - } - } - return $location; - } - - // Return location arguments from current HTTP request - function getLocationArgs() - { - return $_SERVER["LOCATION_ARGS"]; - } - - // Return location arguments from current HTTP request, modify existing arguments - function getLocationArgsNew($arg, $pagination) - { - $separator = $this->getLocationArgsSeparator(); - preg_match("/^(.*?):(.*)$/", $arg, $args); - foreach(explode('/', $_SERVER["LOCATION_ARGS"]) as $token) - { - preg_match("/^(.*?)$separator(.*)$/", $token, $matches); - if($matches[1]==$args[1]) { $matches[2] = $args[2]; $found = true; } - if(!empty($matches[1]) && !strempty($matches[2])) - { - if(!empty($locationArgs)) $locationArgs .= '/'; - $locationArgs .= "$matches[1]:$matches[2]"; - } - } - if(!$found && !empty($args[1]) && !strempty($args[2])) - { - if(!empty($locationArgs)) $locationArgs .= '/'; - $locationArgs .= "$args[1]:$args[2]"; - } - if(!empty($locationArgs)) - { - $locationArgs = $this->normaliseArgs($locationArgs, false, false); - if(!$this->isLocationArgsPagination($locationArgs, $pagination)) $locationArgs .= '/'; - } - return $locationArgs; - } - - // Return location arguments from current HTTP request, convert form parameters - function getLocationArgsClean($pagination) - { - foreach(array_merge($_GET, $_POST) as $key=>$value) - { - if(!empty($key) && !strempty($value)) - { - if(!empty($locationArgs)) $locationArgs .= '/'; - $key = strreplaceu(array('/', ':', '='), array("\x1c", "\x1d", "\x1e"), $key); - $value = strreplaceu(array('/', ':', '='), array("\x1c", "\x1d", "\x1e"), $value); - $locationArgs .= "$key:$value"; - } - } - if(!empty($locationArgs)) - { - $locationArgs = $this->normaliseArgs($locationArgs, false, false); - if(!$this->isLocationArgsPagination($locationArgs, $pagination)) $locationArgs .= '/'; - } - return $locationArgs; - } +class YellowToolbox { + + // Return server version from current HTTP request + public function getServerVersion($shortFormat = false) { + $serverVersion = strtoupperu(PHP_SAPI)." ".PHP_OS; + if (preg_match("/^(\S+)/", $_SERVER["SERVER_SOFTWARE"], $matches)) $serverVersion = $matches[1]." ".PHP_OS; + if ($shortFormat && preg_match("/^(\pL+)/u", $serverVersion, $matches)) $serverVersion = $matches[1]; + return $serverVersion; + } + + // Return server URL from current HTTP request + public function getServerUrl() { + $scheme = $this->getScheme(); + $address = $this->getAddress(); + $base = $this->getBase(); + return "$scheme://$address$base/"; + } + + // Return scheme from current HTTP request + public function getScheme() { + $scheme = ""; + if (preg_match("/^HTTP\//", $_SERVER["SERVER_PROTOCOL"])) { + $secure = isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"]!="off"; + $scheme = $secure ? "https" : "http"; + } + return $scheme; + } + + // Return address from current HTTP request + public function getAddress() { + $address = $_SERVER["SERVER_NAME"]; + $port = $_SERVER["SERVER_PORT"]; + if ($port!=80 && $port!=443) $address .= ":$port"; + return $address; + } + + // Return base from current HTTP request + public function getBase() { + $base = ""; + if (preg_match("/^(.*)\/.*\.php$/", $_SERVER["SCRIPT_NAME"], $matches)) $base = $matches[1]; + return $base; + } + + // Return location from current HTTP request + public function getLocation($filterStrict = true) { + $location = $_SERVER["REQUEST_URI"]; + $location = rawurldecode(($pos = strposu($location, "?")) ? substru($location, 0, $pos) : $location); + if ($filterStrict) { + $location = $this->normaliseTokens($location, true); + $separator = $this->getLocationArgsSeparator(); + if (preg_match("/^(.*?\/)([^\/]+$separator.*)$/", $location, $matches)) { + $_SERVER["LOCATION"] = $location = $matches[1]; + $_SERVER["LOCATION_ARGS"] = $matches[2]; + foreach (explode("/", $matches[2]) as $token) { + preg_match("/^(.*?)$separator(.*)$/", $token, $matches); + if (!empty($matches[1]) && !strempty($matches[2])) { + $matches[1] = strreplaceu(array("\x1c", "\x1d", "\x1e"), array("/", ":", "="), $matches[1]); + $matches[2] = strreplaceu(array("\x1c", "\x1d", "\x1e"), array("/", ":", "="), $matches[2]); + $_REQUEST[$matches[1]] = $matches[2]; + } + } + } else { + $_SERVER["LOCATION"] = $location; + $_SERVER["LOCATION_ARGS"] = ""; + } + } + return $location; + } + + // Return location arguments from current HTTP request + public function getLocationArgs() { + return $_SERVER["LOCATION_ARGS"]; + } + + // Return location arguments from current HTTP request, modify existing arguments + public function getLocationArgsNew($arg, $pagination) { + $separator = $this->getLocationArgsSeparator(); + preg_match("/^(.*?):(.*)$/", $arg, $args); + foreach (explode("/", $_SERVER["LOCATION_ARGS"]) as $token) { + preg_match("/^(.*?)$separator(.*)$/", $token, $matches); + if ($matches[1]==$args[1]) { + $matches[2] = $args[2]; + $found = true; + } + if (!empty($matches[1]) && !strempty($matches[2])) { + if (!empty($locationArgs)) $locationArgs .= "/"; + $locationArgs .= "$matches[1]:$matches[2]"; + } + } + if (!$found && !empty($args[1]) && !strempty($args[2])) { + if (!empty($locationArgs)) $locationArgs .= "/"; + $locationArgs .= "$args[1]:$args[2]"; + } + if (!empty($locationArgs)) { + $locationArgs = $this->normaliseArgs($locationArgs, false, false); + if (!$this->isLocationArgsPagination($locationArgs, $pagination)) $locationArgs .= "/"; + } + return $locationArgs; + } + + // Return location arguments from current HTTP request, convert form parameters + public function getLocationArgsClean($pagination) { + foreach (array_merge($_GET, $_POST) as $key=>$value) { + if (!empty($key) && !strempty($value)) { + if (!empty($locationArgs)) $locationArgs .= "/"; + $key = strreplaceu(array("/", ":", "="), array("\x1c", "\x1d", "\x1e"), $key); + $value = strreplaceu(array("/", ":", "="), array("\x1c", "\x1d", "\x1e"), $value); + $locationArgs .= "$key:$value"; + } + } + if (!empty($locationArgs)) { + $locationArgs = $this->normaliseArgs($locationArgs, false, false); + if (!$this->isLocationArgsPagination($locationArgs, $pagination)) $locationArgs .= "/"; + } + return $locationArgs; + } - // Return location arguments separator - function getLocationArgsSeparator() - { - return (strtoupperu(substru(PHP_OS, 0, 3))!="WIN") ? ':' : '='; - } - - // Check if there are location arguments in current HTTP request - function isLocationArgs($location = "") - { - $location = empty($location) ? $_SERVER["LOCATION"].$_SERVER["LOCATION_ARGS"] : $location; - $separator = $this->getLocationArgsSeparator(); - return preg_match("/[^\/]+$separator.*$/", $location); - } - - // Check if there are pagination arguments in current HTTP request - function isLocationArgsPagination($location, $pagination) - { - $separator = $this->getLocationArgsSeparator(); - return preg_match("/^(.*\/)?$pagination$separator.*$/", $location); - } + // Return location arguments separator + public function getLocationArgsSeparator() { + return (strtoupperu(substru(PHP_OS, 0, 3))!="WIN") ? ":" : "="; + } + + // Check if there are location arguments in current HTTP request + public function isLocationArgs($location = "") { + $location = empty($location) ? $_SERVER["LOCATION"].$_SERVER["LOCATION_ARGS"] : $location; + $separator = $this->getLocationArgsSeparator(); + return preg_match("/[^\/]+$separator.*$/", $location); + } + + // Check if there are pagination arguments in current HTTP request + public function isLocationArgsPagination($location, $pagination) { + $separator = $this->getLocationArgsSeparator(); + return preg_match("/^(.*\/)?$pagination$separator.*$/", $location); + } - // Check if script location is requested - function isRequestSelf() - { - return substru($_SERVER["REQUEST_URI"], -10, 10)=="yellow.php"; - } + // Check if script location is requested + public function isRequestSelf() { + return substru($_SERVER["REQUEST_URI"], -10, 10)=="yellow.php"; + } - // Check if clean URL is requested - function isRequestCleanUrl($location) - { - return (isset($_GET["clean-url"]) || isset($_POST["clean-url"])) && substru($location, -1, 1)=="/"; - } + // Check if clean URL is requested + public function isRequestCleanUrl($location) { + return (isset($_GET["clean-url"]) || isset($_POST["clean-url"])) && substru($location, -1, 1)=="/"; + } - // Check if unmodified since last HTTP request - function isRequestNotModified($lastModifiedFormatted) - { - return isset($_SERVER["HTTP_IF_MODIFIED_SINCE"]) && $_SERVER["HTTP_IF_MODIFIED_SINCE"]==$lastModifiedFormatted; - } - - // Normalise path or location, take care of relative path tokens - function normaliseTokens($text, $prependSlash = false) - { - $textFiltered = ""; - if($prependSlash && $text[0]!='/') $textFiltered .= '/'; - for($pos=0; $pos<strlenb($text); ++$pos) - { - if($text[$pos]=='/' || $pos==0) - { - 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 location arguments - function normaliseArgs($text, $appendSlash = true, $filterStrict = true) - { - if($appendSlash) $text .= '/'; - if($filterStrict) $text = strreplaceu(' ', '-', strtoloweru($text)); - $text = strreplaceu(':', $this->getLocationArgsSeparator(), $text); - return strreplaceu(array('%2F','%3A','%3D'), array('/',':','='), rawurlencode($text)); - } - - // Normalise text into UTF-8 NFC - 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 timezone - function getTimezone() - { - $timezone = @date_default_timezone_get(); - if(PHP_OS=="Darwin" && $timezone=="UTC") - { - if(preg_match("#zoneinfo/(.*)#", @readlink("/etc/localtime"), $matches)) $timezone = $matches[1]; - } - return $timezone; - } - - // Return human readable HTTP server status - 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 = $_SERVER["SERVER_PROTOCOL"]; - if(!preg_match("/^HTTP\//", $serverProtocol)) $serverProtocol = "HTTP/1.1"; - return $shortFormat ? $text : "$serverProtocol $statusCode $text"; - } - - // Return human readable HTTP date - function getHttpDateFormatted($timestamp) - { - return gmdate("D, d M Y H:i:s", $timestamp)." GMT"; - } - - // Return MIME content type - function getMimeContentType($fileName) - { - $contentType = ""; - $contentTypes = array( - "css" => "text/css", - "gif" => "image/gif", - "html" => "text/html; charset=utf-8", - "ico" => "image/x-icon", - "js" => "application/javascript", - "json" => "application/json", - "jpg" => "image/jpeg", - "png" => "image/png", - "svg" => "image/svg+xml", - "txt" => "text/plain", - "woff" => "application/font-woff", - "woff2" => "application/font-woff2", - "xml" => "text/xml; charset=utf-8"); - $fileType = $this->getFileType($fileName); - if(empty($fileType)) - { - $contentType = $contentTypes["html"]; - } else if(array_key_exists($fileType, $contentTypes)) { - $contentType = $contentTypes[$fileType]; - } - return $contentType; - } - - // Return file type - function getFileType($fileName) - { - return strtoloweru(($pos = strrposu($fileName, '.')) ? substru($fileName, $pos+1) : ""); - } - - // Return file group - function getFileGroup($fileName, $path) - { - preg_match("#^$path(.+?)\/#", $fileName, $matches); - return strtoloweru($matches[1]); - } - - // Return number of bytes - function getNumberBytes($string) - { - $bytes = intval($string); - switch(strtoupperu(substru($string, -1))) - { - case 'G': $bytes *= 1024*1024*1024; break; - case 'M': $bytes *= 1024*1024; break; - case 'K': $bytes *= 1024; break; - } - return $bytes; - } - - // Return files and directories - function getDirectoryEntries($path, $regex = "/.*/", $sort = true, $directories = true, $includePath = true) - { - $entries = array(); - $dirHandle = @opendir($path); - if($dirHandle) - { - $path = rtrim($path, '/'); - while(($entry = readdir($dirHandle))!==false) - { - if(substru($entry, 0, 1)==".") continue; - $entry = $this->normaliseUnicode($entry); - if(preg_match($regex, $entry)) - { - if($directories) - { - if(is_dir("$path/$entry")) array_push($entries, $includePath ? "$path/$entry" : $entry); - } else { - if(is_file("$path/$entry")) array_push($entries, $includePath ? "$path/$entry" : $entry); - } - } - } - if($sort) natcasesort($entries); - closedir($dirHandle); - } - return $entries; - } - - // Return files and directories recursively - function getDirectoryEntriesRecursive($path, $regex = "/.*/", $sort = true, $directories = true, $levelMax = 0) - { - --$levelMax; - $entries = $this->getDirectoryEntries($path, $regex, $sort, $directories); - if($levelMax!=0) - { - foreach($this->getDirectoryEntries($path, "/.*/", $sort, true) as $entry) - { - $entries = array_merge($entries, $this->getDirectoryEntriesRecursive($entry, $regex, $sort, $directories, $levelMax)); - } - } - return $entries; - } - - // Read file, empty string if not found - function readFile($fileName, $sizeMax = 0) - { - $fileData = ""; - $fileHandle = @fopen($fileName, "rb"); - if($fileHandle) - { - clearstatcache(true, $fileName); - $fileSize = $sizeMax ? $sizeMax : filesize($fileName); - if($fileSize) $fileData = fread($fileHandle, $fileSize); - fclose($fileHandle); - } - return $fileData; - } - - // Create file - function createFile($fileName, $fileData, $mkdir = false) - { - $ok = false; - if($mkdir) - { - $path = dirname($fileName); - if(!empty($path) && !is_dir($path)) @mkdir($path, 0777, true); - } - $fileHandle = @fopen($fileName, "wb"); - if($fileHandle) - { - clearstatcache(true, $fileName); - if(flock($fileHandle, LOCK_EX)) - { - ftruncate($fileHandle, 0); - fwrite($fileHandle, $fileData); - flock($fileHandle, LOCK_UN); - } - fclose($fileHandle); - $ok = true; - } - return $ok; - } - - // Copy file - function copyFile($fileNameSource, $fileNameDestination, $mkdir = false) - { - clearstatcache(); - if($mkdir) - { - $path = dirname($fileNameDestination); - if(!empty($path) && !is_dir($path)) @mkdir($path, 0777, true); - } - return @copy($fileNameSource, $fileNameDestination); - } - - // Rename file - function renameFile($fileNameSource, $fileNameDestination, $mkdir = false) - { - clearstatcache(); - if($mkdir) - { - $path = dirname($fileNameDestination); - if(!empty($path) && !is_dir($path)) @mkdir($path, 0777, true); - } - return @rename($fileNameSource, $fileNameDestination); - } - - // Delete file - function deleteFile($fileName, $pathTrash = "") - { - clearstatcache(); - if(empty($pathTrash)) - { - $ok = @unlink($fileName); - } else { - if(!is_dir($pathTrash)) @mkdir($pathTrash, 0777, true); - $fileNameDestination = $pathTrash; - $fileNameDestination .= pathinfo($fileName, PATHINFO_FILENAME); - $fileNameDestination .= "-".str_replace(array(" ", ":"), "-", date("Y-m-d H:i:s", filemtime($fileName))); - $fileNameDestination .= ".".pathinfo($fileName, PATHINFO_EXTENSION); - $ok = @rename($fileName, $fileNameDestination); - } - return $ok; - } - - // Delete directory - function deleteDirectory($path, $pathTrash = "") - { - clearstatcache(); - if(empty($pathTrash)) - { - $iterator = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS); - $files = new RecursiveIteratorIterator($iterator, RecursiveIteratorIterator::CHILD_FIRST); - foreach($files as $file) - { - if($file->isDir()) - { - @rmdir($file->getRealPath()); - } else { - @unlink($file->getRealPath()); - } - } - $ok = @rmdir($path); - } else { - if(!is_dir($pathTrash)) @mkdir($pathTrash, 0777, true); - $pathDestination = $pathTrash; - $pathDestination .= basename($path); - $pathDestination .= "-".str_replace(array(" ", ":"), "-", date("Y-m-d H:i:s", filemtime($path))); - $ok = @rename($path, $pathDestination); - } - return $ok; - } - - // Set file modification date, Unix time - function modifyFile($fileName, $modified) - { - clearstatcache(true, $fileName); - return @touch($fileName, $modified); - } - - // Return file modification date, Unix time - function getFileModified($fileName) - { - return is_file($fileName) ? filemtime($fileName) : 0; - } - - // Return lines from text string, including newline - function getTextLines($text) - { - $lines = preg_split("/\n/", $text); - foreach($lines as &$line) $line = $line."\n"; - if(strempty($text) || substru($text, -1, 1)=="\n") array_pop($lines); - return $lines; - } - - // Return arguments from text string, space separated - function getTextArgs($text, $optional = "-") - { - $text = preg_replace("/\s+/s", " ", trim($text)); - $tokens = str_getcsv($text, ' ', '"'); - foreach($tokens as $key=>$value) if($value==$optional) $tokens[$key] = ""; - return $tokens; - } + // Check if unmodified since last HTTP request + public function isRequestNotModified($lastModifiedFormatted) { + return isset($_SERVER["HTTP_IF_MODIFIED_SINCE"]) && $_SERVER["HTTP_IF_MODIFIED_SINCE"]==$lastModifiedFormatted; + } + + // Normalise path or location, take care of relative path tokens + public function normaliseTokens($text, $prependSlash = false) { + $textFiltered = ""; + if ($prependSlash && $text[0]!="/") $textFiltered .= "/"; + for ($pos=0; $pos<strlenb($text); ++$pos) { + if ($text[$pos]=="/" || $pos==0) { + 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 location arguments + public function normaliseArgs($text, $appendSlash = true, $filterStrict = true) { + if ($appendSlash) $text .= "/"; + if ($filterStrict) $text = strreplaceu(" ", "-", strtoloweru($text)); + $text = strreplaceu(":", $this->getLocationArgsSeparator(), $text); + return strreplaceu(array("%2F","%3A","%3D"), array("/",":","="), rawurlencode($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 timezone + public function getTimezone() { + $timezone = @date_default_timezone_get(); + if (PHP_OS=="Darwin" && $timezone=="UTC") { + if (preg_match("#zoneinfo/(.*)#", @readlink("/etc/localtime"), $matches)) $timezone = $matches[1]; + } + return $timezone; + } + + // 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 = $_SERVER["SERVER_PROTOCOL"]; + if (!preg_match("/^HTTP\//", $serverProtocol)) $serverProtocol = "HTTP/1.1"; + return $shortFormat ? $text : "$serverProtocol $statusCode $text"; + } + + // Return human readable HTTP date + public function getHttpDateFormatted($timestamp) { + return gmdate("D, d M Y H:i:s", $timestamp)." GMT"; + } + + // Return MIME content type + public function getMimeContentType($fileName) { + $contentType = ""; + $contentTypes = array( + "css" => "text/css", + "gif" => "image/gif", + "html" => "text/html; charset=utf-8", + "ico" => "image/x-icon", + "js" => "application/javascript", + "json" => "application/json", + "jpg" => "image/jpeg", + "png" => "image/png", + "svg" => "image/svg+xml", + "txt" => "text/plain", + "woff" => "application/font-woff", + "woff2" => "application/font-woff2", + "xml" => "text/xml; charset=utf-8"); + $fileType = $this->getFileType($fileName); + if (empty($fileType)) { + $contentType = $contentTypes["html"]; + } elseif (array_key_exists($fileType, $contentTypes)) { + $contentType = $contentTypes[$fileType]; + } + 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) { + preg_match("#^$path(.+?)\/#", $fileName, $matches); + return strtoloweru($matches[1]); + } + + // Return number of bytes + public function getNumberBytes($string) { + $bytes = intval($string); + switch (strtoupperu(substru($string, -1))) { + case "G": $bytes *= 1024*1024*1024; break; + case "M": $bytes *= 1024*1024; break; + case "K": $bytes *= 1024; break; + } + return $bytes; + } + + // Return files and directories + public function getDirectoryEntries($path, $regex = "/.*/", $sort = true, $directories = true, $includePath = true) { + $entries = array(); + $dirHandle = @opendir($path); + if ($dirHandle) { + $path = rtrim($path, "/"); + while (($entry = readdir($dirHandle))!==false) { + if (substru($entry, 0, 1)==".") continue; + $entry = $this->normaliseUnicode($entry); + if (preg_match($regex, $entry)) { + if ($directories) { + if (is_dir("$path/$entry")) array_push($entries, $includePath ? "$path/$entry" : $entry); + } else { + if (is_file("$path/$entry")) array_push($entries, $includePath ? "$path/$entry" : $entry); + } + } + } + if ($sort) natcasesort($entries); + closedir($dirHandle); + } + return $entries; + } + + // Return files and directories recursively + public function getDirectoryEntriesRecursive($path, $regex = "/.*/", $sort = true, $directories = true, $levelMax = 0) { + --$levelMax; + $entries = $this->getDirectoryEntries($path, $regex, $sort, $directories); + if ($levelMax!=0) { + foreach ($this->getDirectoryEntries($path, "/.*/", $sort, true) as $entry) { + $entries = array_merge($entries, $this->getDirectoryEntriesRecursive($entry, $regex, $sort, $directories, $levelMax)); + } + } + return $entries; + } + + // Read file, empty string if not found + public function readFile($fileName, $sizeMax = 0) { + $fileData = ""; + $fileHandle = @fopen($fileName, "rb"); + if ($fileHandle) { + clearstatcache(true, $fileName); + $fileSize = $sizeMax ? $sizeMax : filesize($fileName); + if ($fileSize) $fileData = fread($fileHandle, $fileSize); + fclose($fileHandle); + } + return $fileData; + } + + // Create file + public function createFile($fileName, $fileData, $mkdir = false) { + $ok = false; + if ($mkdir) { + $path = dirname($fileName); + if (!empty($path) && !is_dir($path)) @mkdir($path, 0777, true); + } + $fileHandle = @fopen($fileName, "wb"); + if ($fileHandle) { + clearstatcache(true, $fileName); + if (flock($fileHandle, LOCK_EX)) { + ftruncate($fileHandle, 0); + fwrite($fileHandle, $fileData); + flock($fileHandle, LOCK_UN); + } + fclose($fileHandle); + $ok = true; + } + return $ok; + } + + // Copy file + public function copyFile($fileNameSource, $fileNameDestination, $mkdir = false) { + clearstatcache(); + if ($mkdir) { + $path = dirname($fileNameDestination); + if (!empty($path) && !is_dir($path)) @mkdir($path, 0777, true); + } + return @copy($fileNameSource, $fileNameDestination); + } + + // Rename file + public function renameFile($fileNameSource, $fileNameDestination, $mkdir = false) { + clearstatcache(); + if ($mkdir) { + $path = dirname($fileNameDestination); + if (!empty($path) && !is_dir($path)) @mkdir($path, 0777, true); + } + return @rename($fileNameSource, $fileNameDestination); + } + + // Delete file + public function deleteFile($fileName, $pathTrash = "") { + clearstatcache(); + if (empty($pathTrash)) { + $ok = @unlink($fileName); + } else { + if (!is_dir($pathTrash)) @mkdir($pathTrash, 0777, true); + $fileNameDestination = $pathTrash; + $fileNameDestination .= pathinfo($fileName, PATHINFO_FILENAME); + $fileNameDestination .= "-".str_replace(array(" ", ":"), "-", date("Y-m-d H:i:s", filemtime($fileName))); + $fileNameDestination .= ".".pathinfo($fileName, PATHINFO_EXTENSION); + $ok = @rename($fileName, $fileNameDestination); + } + return $ok; + } + + // Delete directory + public function deleteDirectory($path, $pathTrash = "") { + clearstatcache(); + if (empty($pathTrash)) { + $iterator = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS); + $files = new RecursiveIteratorIterator($iterator, RecursiveIteratorIterator::CHILD_FIRST); + foreach ($files as $file) { + if ($file->isDir()) { + @rmdir($file->getRealPath()); + } else { + @unlink($file->getRealPath()); + } + } + $ok = @rmdir($path); + } else { + if (!is_dir($pathTrash)) @mkdir($pathTrash, 0777, true); + $pathDestination = $pathTrash; + $pathDestination .= basename($path); + $pathDestination .= "-".str_replace(array(" ", ":"), "-", date("Y-m-d H:i:s", filemtime($path))); + $ok = @rename($path, $pathDestination); + } + return $ok; + } + + // Set file modification date, Unix time + public function modifyFile($fileName, $modified) { + clearstatcache(true, $fileName); + return @touch($fileName, $modified); + } + + // Return file modification date, Unix time + public function getFileModified($fileName) { + return is_file($fileName) ? filemtime($fileName) : 0; + } + + // Return lines from text string, including newline + public function getTextLines($text) { + $lines = preg_split("/\n/", $text); + foreach ($lines as &$line) { + $line = $line."\n"; + } + if (strempty($text) || substru($text, -1, 1)=="\n") array_pop($lines); + return $lines; + } + + // Return arguments from text string, space separated + public function getTextArgs($text, $optional = "-") { + $text = preg_replace("/\s+/s", " ", trim($text)); + $tokens = str_getcsv($text, " ", "\""); + foreach ($tokens as $key=>$value) { + if ($value==$optional) $tokens[$key] = ""; + } + return $tokens; + } - // Return number of words in text string - 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); - } - - // Create description from text string - function createTextDescription($text, $lengthMax = 0, $removeHtml = true, $endMarker = "", $endMarkerText = "") - { - if(preg_match("/^<h1>.*?<\/h1>(.*)$/si", $text, $matches)) $text = $matches[1]; - if($lengthMax==0) $lengthMax = strlenu($text); - if($removeHtml) - { - while(true) - { - $elementFound = preg_match("/<\s*?([\/!]?\w*)(.*?)\s*?\>/s", $text, $matches, PREG_OFFSET_CAPTURE, $offsetBytes); - $element = $matches[0][0]; - $elementName = $matches[1][0]; - $elementText = $matches[2][0]; - $elementOffsetBytes = $elementFound ? $matches[0][1] : strlenb($text); - $string = html_entity_decode(substrb($text, $offsetBytes, $elementOffsetBytes - $offsetBytes), ENT_QUOTES, "UTF-8"); - if(preg_match("/^(blockquote|br|div|h\d|hr|li|ol|p|pre|ul)/i", $elementName)) $string .= ' '; - if(preg_match("/^\/(code|pre)/i", $elementName)) $string = preg_replace("/^(\d+\n){2,}$/", "", $string); - $string = preg_replace("/\s+/s", " ", $string); - if(substru($string, 0, 1)==" " && (empty($output) || substru($output, -1)==' ')) $string = substru($string, 1); - $length = strlenu($string); - $output .= substru($string, 0, $length<$lengthMax ? $length : $lengthMax-1); - $lengthMax -= $length; - if(!empty($element) && $element==$endMarker) { $lengthMax = 0; $endMarkerFound = true; } - if($lengthMax<=0 || !$elementFound) break; - $offsetBytes = $elementOffsetBytes + strlenb($element); - } - $output = rtrim($output); - if($lengthMax<=0) $output .= $endMarkerFound ? $endMarkerText : "…"; - } else { - $elementsOpen = array(); - while(true) - { - $elementFound = preg_match("/&.*?\;|<\s*?([\/!]?\w*)(.*?)\s*?\>/s", $text, $matches, PREG_OFFSET_CAPTURE, $offsetBytes); - $element = $matches[0][0]; - $elementName = $matches[1][0]; - $elementText = $matches[2][0]; - $elementOffsetBytes = $elementFound ? $matches[0][1] : strlenb($text); - $string = substrb($text, $offsetBytes, $elementOffsetBytes - $offsetBytes); - $length = strlenu($string); - $output .= substru($string, 0, $length<$lengthMax ? $length : $lengthMax-1); - $lengthMax -= $length + ($element[0]=='&' ? 1 : 0); - if(!empty($element) && $element==$endMarker) { $lengthMax = 0; $endMarkerFound = true; } - if($lengthMax<=0 || !$elementFound) break; - if(!empty($elementName) && substru($elementText, -1)!='/' && - !preg_match("/^(area|br|col|hr|img|input|col|param|!)/i", $elementName)) - { - if($elementName[0]!='/') - { - array_push($elementsOpen, $elementName); - } else { - array_pop($elementsOpen); - } - } - $output .= $element; - $offsetBytes = $elementOffsetBytes + strlenb($element); - } - $output = rtrim($output); - for($i=count($elementsOpen)-1; $i>=0; --$i) - { - if(!preg_match("/^(dl|ol|ul|table|tbody|thead|tfoot|tr)/i", $elementsOpen[$i])) break; - $output .= "</".$elementsOpen[$i].">"; - } - if($lengthMax<=0) $output .= $endMarkerFound ? $endMarkerText : "…"; - for(; $i>=0; --$i) $output .= "</".$elementsOpen[$i].">"; - } - return $output; - } - - // Create keywords from text string - function createTextKeywords($text, $keywordsMax = 0) - { - $tokens = array_unique(preg_split("/[,\s\(\)\+\-]/", strtoloweru($text))); - foreach($tokens as $key=>$value) if(strlenu($value)<3) unset($tokens[$key]); - if($keywordsMax) $tokens = array_slice($tokens, 0, $keywordsMax); - return implode(", ", $tokens); - } - - // Create title from text string - function createTextTitle($text) - { - if(preg_match("/^.*\/([\w\-]+)/", $text, $matches)) $text = strreplaceu('-', ' ', ucfirst($matches[1])); - return $text; - } + // Return number of words in text string + 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); + } + + // Create description from text string + public function createTextDescription($text, $lengthMax = 0, $removeHtml = true, $endMarker = "", $endMarkerText = "") { + if (preg_match("/^<h1>.*?<\/h1>(.*)$/si", $text, $matches)) $text = $matches[1]; + if ($lengthMax==0) $lengthMax = strlenu($text); + if ($removeHtml) { + while (true) { + $elementFound = preg_match("/<\s*?([\/!]?\w*)(.*?)\s*?\>/s", $text, $matches, PREG_OFFSET_CAPTURE, $offsetBytes); + $element = $matches[0][0]; + $elementName = $matches[1][0]; + $elementText = $matches[2][0]; + $elementOffsetBytes = $elementFound ? $matches[0][1] : strlenb($text); + $string = html_entity_decode(substrb($text, $offsetBytes, $elementOffsetBytes - $offsetBytes), ENT_QUOTES, "UTF-8"); + if (preg_match("/^(blockquote|br|div|h\d|hr|li|ol|p|pre|ul)/i", $elementName)) $string .= " "; + if (preg_match("/^\/(code|pre)/i", $elementName)) $string = preg_replace("/^(\d+\n){2,}$/", "", $string); + $string = preg_replace("/\s+/s", " ", $string); + if (substru($string, 0, 1)==" " && (empty($output) || substru($output, -1)==" ")) $string = substru($string, 1); + $length = strlenu($string); + $output .= substru($string, 0, $length<$lengthMax ? $length : $lengthMax-1); + $lengthMax -= $length; + if (!empty($element) && $element==$endMarker) { + $lengthMax = 0; + $endMarkerFound = true; + } + if ($lengthMax<=0 || !$elementFound) break; + $offsetBytes = $elementOffsetBytes + strlenb($element); + } + $output = rtrim($output); + if ($lengthMax<=0) $output .= $endMarkerFound ? $endMarkerText : "…"; + } else { + $elementsOpen = array(); + while (true) { + $elementFound = preg_match("/&.*?\;|<\s*?([\/!]?\w*)(.*?)\s*?\>/s", $text, $matches, PREG_OFFSET_CAPTURE, $offsetBytes); + $element = $matches[0][0]; + $elementName = $matches[1][0]; + $elementText = $matches[2][0]; + $elementOffsetBytes = $elementFound ? $matches[0][1] : strlenb($text); + $string = substrb($text, $offsetBytes, $elementOffsetBytes - $offsetBytes); + $length = strlenu($string); + $output .= substru($string, 0, $length<$lengthMax ? $length : $lengthMax-1); + $lengthMax -= $length + ($element[0]=="&" ? 1 : 0); + if (!empty($element) && $element==$endMarker) { + $lengthMax = 0; + $endMarkerFound = true; + } + if ($lengthMax<=0 || !$elementFound) break; + if (!empty($elementName) && substru($elementText, -1)!="/" && + !preg_match("/^(area|br|col|hr|img|input|col|param|!)/i", $elementName)) { + if ($elementName[0]!="/") { + array_push($elementsOpen, $elementName); + } else { + array_pop($elementsOpen); + } + } + $output .= $element; + $offsetBytes = $elementOffsetBytes + strlenb($element); + } + $output = rtrim($output); + for ($i=count($elementsOpen)-1; $i>=0; --$i) { + if (!preg_match("/^(dl|ol|ul|table|tbody|thead|tfoot|tr)/i", $elementsOpen[$i])) break; + $output .= "</".$elementsOpen[$i].">"; + } + if ($lengthMax<=0) $output .= $endMarkerFound ? $endMarkerText : "…"; + for (; $i>=0; --$i) { + $output .= "</".$elementsOpen[$i].">"; + } + } + return $output; + } + + // Create keywords from text string + public function createTextKeywords($text, $keywordsMax = 0) { + $tokens = array_unique(preg_split("/[,\s\(\)\+\-]/", strtoloweru($text))); + foreach ($tokens as $key=>$value) { + if (strlenu($value)<3) unset($tokens[$key]); + } + if ($keywordsMax) $tokens = array_slice($tokens, 0, $keywordsMax); + return implode(", ", $tokens); + } + + // Create title from text string + public function createTextTitle($text) { + if (preg_match("/^.*\/([\w\-]+)/", $text, $matches)) $text = strreplaceu("-", " ", ucfirst($matches[1])); + return $text; + } - // Create random text for cryptography - function createSalt($length, $bcryptFormat = false) - { - $dataBuffer = $salt = ""; - $dataBufferSize = $bcryptFormat ? intval(ceil($length/4) * 3) : intval(ceil($length/2)); - if(empty($dataBuffer) && function_exists("random_bytes")) - { - $dataBuffer = @random_bytes($dataBufferSize); - } - if(empty($dataBuffer) && function_exists("mcrypt_create_iv")) - { - $dataBuffer = @mcrypt_create_iv($dataBufferSize, MCRYPT_DEV_URANDOM); - } - if(empty($dataBuffer) && function_exists("openssl_random_pseudo_bytes")) - { - $dataBuffer = @openssl_random_pseudo_bytes($dataBufferSize); - } - if(strlenb($dataBuffer)==$dataBufferSize) - { - if($bcryptFormat) - { - $salt = substrb(base64_encode($dataBuffer), 0, $length); - $base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - $bcrypt64Chars = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - $salt = strtr($salt, $base64Chars, $bcrypt64Chars); - } else { - $salt = substrb(bin2hex($dataBuffer), 0, $length); - } - } - return $salt; - } - - // Create hash with random salt, bcrypt or sha256 - function createHash($text, $algorithm, $cost = 0) - { - $hash = ""; - switch($algorithm) - { - case "bcrypt": $prefix = sprintf("$2y$%02d$", $cost); - $salt = $this->createSalt(22, true); - $hash = crypt($text, $prefix.$salt); - if(empty($salt) || strlenb($hash)!=60) $hash = ""; - break; - case "sha256": $prefix = "$5y$"; - $salt = $this->createSalt(32); - $hash = "$prefix$salt".hash("sha256", $salt.$text); - if(empty($salt) || strlenb($hash)!=100) $hash = ""; - break; - } - return $hash; - } - - // Verify that text matches hash - function verifyHash($text, $algorithm, $hash) - { - $hashCalculated = ""; - switch($algorithm) - { - case "bcrypt": if(substrb($hash, 0, 4)=="$2y$" || substrb($hash, 0, 4)=="$2a$") - { - $hashCalculated = crypt($text, $hash); - } - break; - case "sha256": if(substrb($hash, 0, 4)=="$5y$") - { - $prefix = "$5y$"; - $salt = substrb($hash, 4, 32); - $hashCalculated = "$prefix$salt".hash("sha256", $salt.$text); - } - break; - } - return $this->verifyToken($hashCalculated, $hash); - } - - // Verify that token is not empty and identical, timing attack safe text string comparison - function verifyToken($tokenExpected, $tokenReceived) - { - $ok = false; - $lengthExpected = strlenb($tokenExpected); - $lengthReceived = strlenb($tokenReceived); - if($lengthExpected!=0 && $lengthReceived!=0) - { - $ok = $lengthExpected==$lengthReceived; - for($i=0; $i<$lengthReceived; ++$i) $ok &= $tokenExpected[$i<$lengthExpected ? $i : 0]==$tokenReceived[$i]; - } - return $ok; - } - - // Return meta data from raw data - function getMetaData($rawData, $key) - { - $value = ""; - if(preg_match("/^(\xEF\xBB\xBF)?\-\-\-[\r\n]+(.+?)\-\-\-[\r\n]+(.*)$/s", $rawData, $parts)) - { - $key = lcfirst($key); - foreach($this->getTextLines($parts[2]) as $line) - { - preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); - if(lcfirst($matches[1])==$key && !strempty($matches[2])) { $value = $matches[2]; break; } - } - } - return $value; - } - - // Set meta data in raw data - function setMetaData($rawData, $key, $value) - { - if(preg_match("/^(\xEF\xBB\xBF)?\-\-\-[\r\n]+(.+?)\-\-\-[\r\n]+(.*)$/s", $rawData, $parts)) - { - $key = lcfirst($key); - foreach($this->getTextLines($parts[2]) as $line) - { - preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); - if(lcfirst($matches[1])==$key) - { - $rawDataNew .= "$matches[1]: $value\n"; - $found = true; - } else { - $rawDataNew .= $line; - } - } - if(!$found) $rawDataNew .= ucfirst($key).": $value\n"; - $rawDataNew = $parts[1]."---\n".$rawDataNew."---\n".$parts[3]; - } else { - $rawDataNew = $rawData; - } - return $rawDataNew; - } - - // Detect web browser language - function detectBrowserLanguage($languages, $languageDefault) - { - $language = $languageDefault; - if(isset($_SERVER["HTTP_ACCEPT_LANGUAGE"])) - { - foreach(preg_split("/\s*,\s*/", $_SERVER["HTTP_ACCEPT_LANGUAGE"]) as $string) - { - $tokens = explode(';', $string); - if(in_array($tokens[0], $languages)) { $language = $tokens[0]; break; } - } - } - return $language; - } - - // Detect image dimensions and type for gif/jpg/png/svg - function detectImageInfo($fileName) - { - $width = $height = 0; - $type = ""; - $fileHandle = @fopen($fileName, "rb"); - if($fileHandle) - { - if(substru(strtoloweru($fileName), -3)=="gif") - { - $dataSignature = fread($fileHandle, 6); - $dataHeader = fread($fileHandle, 7); - if(!feof($fileHandle) && ($dataSignature=="GIF87a" || $dataSignature=="GIF89a")) - { - $width = (ord($dataHeader[1])<<8) + ord($dataHeader[0]); - $height = (ord($dataHeader[3])<<8) + ord($dataHeader[2]); - $type = "gif"; - } - } else if(substru(strtoloweru($fileName), -3)=="jpg") { - $dataBufferSizeMax = filesize($fileName); - $dataBufferSize = min($dataBufferSizeMax, 4096); - if($dataBufferSize) $dataBuffer = fread($fileHandle, $dataBufferSize); - $dataSignature = substrb($dataBuffer, 0, 4); - if(!feof($fileHandle) && ($dataSignature=="\xff\xd8\xff\xe0" || $dataSignature=="\xff\xd8\xff\xe1")) - { - for($pos=2; $pos+8<$dataBufferSize; $pos+=$length) - { - if($dataBuffer[$pos]!="\xff") break; - if($dataBuffer[$pos+1]=="\xc0" || $dataBuffer[$pos+1]=="\xc2") - { - $width = (ord($dataBuffer[$pos+7])<<8) + ord($dataBuffer[$pos+8]); - $height = (ord($dataBuffer[$pos+5])<<8) + ord($dataBuffer[$pos+6]); - $type = "jpg"; - break; - } - $length = (ord($dataBuffer[$pos+2])<<8) + ord($dataBuffer[$pos+3]) + 2; - while($pos+$length+8>=$dataBufferSize) - { - if($dataBufferSize==$dataBufferSizeMax) break; - $dataBufferDiff = min($dataBufferSizeMax, $dataBufferSize*2) - $dataBufferSize; - $dataBufferSize += $dataBufferDiff; - $dataBufferChunk = fread($fileHandle, $dataBufferDiff); - if(feof($fileHandle) || $dataBufferChunk===false) { $dataBufferSize = 0; break; } - $dataBuffer .= $dataBufferChunk; - } - } - } - } else if(substru(strtoloweru($fileName), -3)=="png") { - $dataSignature = fread($fileHandle, 8); - $dataHeader = fread($fileHandle, 16); - if(!feof($fileHandle) && $dataSignature=="\x89PNG\r\n\x1a\n") - { - $width = (ord($dataHeader[10])<<8) + ord($dataHeader[11]); - $height = (ord($dataHeader[14])<<8) + ord($dataHeader[15]); - $type = "png"; - } - } else if(substru(strtoloweru($fileName), -3)=="svg") { - $dataBufferSizeMax = filesize($fileName); - $dataBufferSize = min($dataBufferSizeMax, 4096); - if($dataBufferSize) $dataBuffer = fread($fileHandle, $dataBufferSize); - $dataSignature = substrb($dataBuffer, 0, 5); - if(!feof($fileHandle) && $dataSignature=="\x3csvg\x20") - { - $dataBuffer = ($pos = strposu($dataBuffer, '>')) ? substru($dataBuffer, 0, $pos) : $dataBuffer; - if(preg_match("/ width=\"(\d+)\"/", $dataBuffer, $matches)) $width = $matches[1]; - if(preg_match("/ height=\"(\d+)\"/", $dataBuffer, $matches)) $height = $matches[1]; - $type = "svg"; - } - } - fclose($fileHandle); - } - return array($width, $height, $type); - } - - // Start timer - function timerStart(&$time) - { - $time = microtime(true); - } - - // Stop timer and calculate elapsed time in milliseconds - function timerStop(&$time) - { - $time = intval((microtime(true)-$time) * 1000); - } + // Create random text for cryptography + public function createSalt($length, $bcryptFormat = false) { + $dataBuffer = $salt = ""; + $dataBufferSize = $bcryptFormat ? intval(ceil($length/4) * 3) : intval(ceil($length/2)); + if (empty($dataBuffer) && function_exists("random_bytes")) { + $dataBuffer = @random_bytes($dataBufferSize); + } + if (empty($dataBuffer) && function_exists("mcrypt_create_iv")) { + $dataBuffer = @mcrypt_create_iv($dataBufferSize, MCRYPT_DEV_URANDOM); + } + if (empty($dataBuffer) && function_exists("openssl_random_pseudo_bytes")) { + $dataBuffer = @openssl_random_pseudo_bytes($dataBufferSize); + } + if (strlenb($dataBuffer)==$dataBufferSize) { + if ($bcryptFormat) { + $salt = substrb(base64_encode($dataBuffer), 0, $length); + $base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + $bcrypt64Chars = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + $salt = strtr($salt, $base64Chars, $bcrypt64Chars); + } else { + $salt = substrb(bin2hex($dataBuffer), 0, $length); + } + } + return $salt; + } + + // Create hash with random salt, bcrypt or sha256 + public function createHash($text, $algorithm, $cost = 0) { + $hash = ""; + switch ($algorithm) { + case "bcrypt": $prefix = sprintf("$2y$%02d$", $cost); + $salt = $this->createSalt(22, true); + $hash = crypt($text, $prefix.$salt); + if (empty($salt) || strlenb($hash)!=60) $hash = ""; + break; + case "sha256": $prefix = "$5y$"; + $salt = $this->createSalt(32); + $hash = "$prefix$salt".hash("sha256", $salt.$text); + if (empty($salt) || strlenb($hash)!=100) $hash = ""; + break; + } + return $hash; + } + + // Verify that text matches hash + public function verifyHash($text, $algorithm, $hash) { + $hashCalculated = ""; + switch ($algorithm) { + case "bcrypt": if (substrb($hash, 0, 4)=="$2y$" || substrb($hash, 0, 4)=="$2a$") { + $hashCalculated = crypt($text, $hash); + } + break; + case "sha256": if (substrb($hash, 0, 4)=="$5y$") { + $prefix = "$5y$"; + $salt = substrb($hash, 4, 32); + $hashCalculated = "$prefix$salt".hash("sha256", $salt.$text); + } + break; + } + return $this->verifyToken($hashCalculated, $hash); + } + + // Verify that token is not empty and identical, timing attack safe text string comparison + public function verifyToken($tokenExpected, $tokenReceived) { + $ok = false; + $lengthExpected = strlenb($tokenExpected); + $lengthReceived = strlenb($tokenReceived); + if ($lengthExpected!=0 && $lengthReceived!=0) { + $ok = $lengthExpected==$lengthReceived; + for ($i=0; $i<$lengthReceived; ++$i) { + $ok &= $tokenExpected[$i<$lengthExpected ? $i : 0]==$tokenReceived[$i]; + } + } + return $ok; + } + + // Return meta data from raw data + public function getMetaData($rawData, $key) { + $value = ""; + if (preg_match("/^(\xEF\xBB\xBF)?\-\-\-[\r\n]+(.+?)\-\-\-[\r\n]+(.*)$/s", $rawData, $parts)) { + $key = lcfirst($key); + foreach ($this->getTextLines($parts[2]) as $line) { + preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); + if (lcfirst($matches[1])==$key && !strempty($matches[2])) { + $value = $matches[2]; + break; + } + } + } + return $value; + } + + // Set meta data in raw data + public function setMetaData($rawData, $key, $value) { + if (preg_match("/^(\xEF\xBB\xBF)?\-\-\-[\r\n]+(.+?)\-\-\-[\r\n]+(.*)$/s", $rawData, $parts)) { + $key = lcfirst($key); + foreach ($this->getTextLines($parts[2]) as $line) { + preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); + if (lcfirst($matches[1])==$key) { + $rawDataNew .= "$matches[1]: $value\n"; + $found = true; + } else { + $rawDataNew .= $line; + } + } + if (!$found) $rawDataNew .= ucfirst($key).": $value\n"; + $rawDataNew = $parts[1]."---\n".$rawDataNew."---\n".$parts[3]; + } else { + $rawDataNew = $rawData; + } + return $rawDataNew; + } + + // Detect web browser language + public function detectBrowserLanguage($languages, $languageDefault) { + $language = $languageDefault; + if (isset($_SERVER["HTTP_ACCEPT_LANGUAGE"])) { + foreach (preg_split("/\s*,\s*/", $_SERVER["HTTP_ACCEPT_LANGUAGE"]) as $string) { + $tokens = explode(";", $string); + if (in_array($tokens[0], $languages)) { + $language = $tokens[0]; + break; + } + } + } + return $language; + } + + // Detect image dimensions and type for gif/jpg/png/svg + public function detectImageInfo($fileName) { + $width = $height = 0; + $type = ""; + $fileHandle = @fopen($fileName, "rb"); + if ($fileHandle) { + if (substru(strtoloweru($fileName), -3)=="gif") { + $dataSignature = fread($fileHandle, 6); + $dataHeader = fread($fileHandle, 7); + if (!feof($fileHandle) && ($dataSignature=="GIF87a" || $dataSignature=="GIF89a")) { + $width = (ord($dataHeader[1])<<8) + ord($dataHeader[0]); + $height = (ord($dataHeader[3])<<8) + ord($dataHeader[2]); + $type = "gif"; + } + } elseif (substru(strtoloweru($fileName), -3)=="jpg") { + $dataBufferSizeMax = filesize($fileName); + $dataBufferSize = min($dataBufferSizeMax, 4096); + if ($dataBufferSize) $dataBuffer = fread($fileHandle, $dataBufferSize); + $dataSignature = substrb($dataBuffer, 0, 4); + if (!feof($fileHandle) && ($dataSignature=="\xff\xd8\xff\xe0" || $dataSignature=="\xff\xd8\xff\xe1")) { + for ($pos=2; $pos+8<$dataBufferSize; $pos+=$length) { + if ($dataBuffer[$pos]!="\xff") break; + if ($dataBuffer[$pos+1]=="\xc0" || $dataBuffer[$pos+1]=="\xc2") { + $width = (ord($dataBuffer[$pos+7])<<8) + ord($dataBuffer[$pos+8]); + $height = (ord($dataBuffer[$pos+5])<<8) + ord($dataBuffer[$pos+6]); + $type = "jpg"; + break; + } + $length = (ord($dataBuffer[$pos+2])<<8) + ord($dataBuffer[$pos+3]) + 2; + while ($pos+$length+8>=$dataBufferSize) { + if ($dataBufferSize==$dataBufferSizeMax) break; + $dataBufferDiff = min($dataBufferSizeMax, $dataBufferSize*2) - $dataBufferSize; + $dataBufferSize += $dataBufferDiff; + $dataBufferChunk = fread($fileHandle, $dataBufferDiff); + if (feof($fileHandle) || $dataBufferChunk===false) { + $dataBufferSize = 0; + break; + } + $dataBuffer .= $dataBufferChunk; + } + } + } + } elseif (substru(strtoloweru($fileName), -3)=="png") { + $dataSignature = fread($fileHandle, 8); + $dataHeader = fread($fileHandle, 16); + if (!feof($fileHandle) && $dataSignature=="\x89PNG\r\n\x1a\n") { + $width = (ord($dataHeader[10])<<8) + ord($dataHeader[11]); + $height = (ord($dataHeader[14])<<8) + ord($dataHeader[15]); + $type = "png"; + } + } elseif (substru(strtoloweru($fileName), -3)=="svg") { + $dataBufferSizeMax = filesize($fileName); + $dataBufferSize = min($dataBufferSizeMax, 4096); + if ($dataBufferSize) $dataBuffer = fread($fileHandle, $dataBufferSize); + $dataSignature = substrb($dataBuffer, 0, 5); + if (!feof($fileHandle) && $dataSignature=="\x3csvg\x20") { + $dataBuffer = ($pos = strposu($dataBuffer, ">")) ? substru($dataBuffer, 0, $pos) : $dataBuffer; + if (preg_match("/ width=\"(\d+)\"/", $dataBuffer, $matches)) $width = $matches[1]; + if (preg_match("/ height=\"(\d+)\"/", $dataBuffer, $matches)) $height = $matches[1]; + $type = "svg"; + } + } + fclose($fileHandle); + } + return array($width, $height, $type); + } + + // Start timer + public function timerStart(&$time) { + $time = microtime(true); + } + + // Stop timer and calculate elapsed time in milliseconds + public function timerStop(&$time) { + $time = intval((microtime(true)-$time) * 1000); + } } // Unicode support for PHP mb_internal_encoding("UTF-8"); -function strempty($string) { return is_null($string) || $string===""; } -function strencode($string) { return addcslashes($string, "\'\"\\\/"); } -function strreplaceu() { return call_user_func_array("str_replace", func_get_args()); } -function strtoloweru() { return call_user_func_array("mb_strtolower", func_get_args()); } -function strtoupperu() { return call_user_func_array("mb_strtoupper", func_get_args()); } -function strlenu() { return call_user_func_array("mb_strlen", func_get_args()); } -function strlenb() { return call_user_func_array("strlen", func_get_args()); } -function strposu() { return call_user_func_array("mb_strpos", func_get_args()); } -function strposb() { return call_user_func_array("strpos", func_get_args()); } -function strrposu() { return call_user_func_array("mb_strrpos", func_get_args()); } -function strrposb() { return call_user_func_array("strrpos", func_get_args()); } -function substru() { return call_user_func_array("mb_substr", func_get_args()); } -function substrb() { return call_user_func_array("substr", func_get_args()); } +function strempty($string) { + return is_null($string) || $string===""; +} +function strencode($string) { + return addcslashes($string, "\'\"\\\/"); +} +function strreplaceu() { + return call_user_func_array("str_replace", func_get_args()); +} +function strtoloweru() { + return call_user_func_array("mb_strtolower", func_get_args()); +} +function strtoupperu() { + return call_user_func_array("mb_strtoupper", func_get_args()); +} +function strlenu() { + return call_user_func_array("mb_strlen", func_get_args()); +} +function strlenb() { + return call_user_func_array("strlen", func_get_args()); +} +function strposu() { + return call_user_func_array("mb_strpos", func_get_args()); +} +function strposb() { + return call_user_func_array("strpos", func_get_args()); +} +function strrposu() { + return call_user_func_array("mb_strrpos", func_get_args()); +} +function strrposb() { + return call_user_func_array("strrpos", func_get_args()); +} +function substru() { + return call_user_func_array("mb_substr", func_get_args()); +} +function substrb() { + return call_user_func_array("substr", func_get_args()); +} // Error reporting for PHP error_reporting(E_ALL ^ E_NOTICE); -?> diff --git a/system/plugins/edit.css b/system/plugins/edit.css @@ -2,312 +2,552 @@ /* Copyright (c) 2013-2018 Datenstrom, https://datenstrom.se */ /* This file may be used and distributed under the terms of the public license. */ -.yellow-bar { position:relative; line-height:2em; margin-bottom:10px; } -.yellow-bar-left { display:block; float:left; } -.yellow-bar-right { display:block; float:right; } -.yellow-bar-right a { margin-left:1em; } -.yellow-bar-right #yellow-pane-create-link { padding:0 0.5em; } -.yellow-bar-right #yellow-pane-delete-link { padding:0 0.5em; } -.yellow-bar-banner { clear:both; } -.yellow-body-modal-open { overflow:hidden; } +.yellow-bar { + position: relative; + line-height: 2em; + margin-bottom: 10px; +} +.yellow-bar-left { + display: block; + float: left; +} +.yellow-bar-right { + display: block; + float: right; +} +.yellow-bar-right a { + margin-left: 1em; +} +.yellow-bar-right #yellow-pane-create-link { + padding: 0 0.5em; +} +.yellow-bar-right #yellow-pane-delete-link { + padding: 0 0.5em; +} +.yellow-bar-banner { + clear: both; +} +.yellow-body-modal-open { + overflow: hidden; +} .yellow-pane { - position:absolute; display:none; z-index:100; padding:10px; - background-color:#fff; color:#000; - border:1px solid #bbb; - border-radius:4px; box-shadow:2px 4px 10px rgba(0, 0, 0, 0.2); -} -.yellow-pane h1 { color:#000; font-size:2em; margin:0 1em; } -.yellow-pane p { margin:0.5em; } -.yellow-pane .yellow-status { margin-bottom:1em; } -.yellow-pane .yellow-fields { width:15em; text-align:left; margin:0 auto; } -.yellow-pane .yellow-form-control { width:15em; box-sizing:border-box; } -.yellow-pane .yellow-fields .yellow-btn { width:15em; margin:1em 0 0.5em 0; } -.yellow-pane .yellow-buttons .yellow-btn { width:15em; margin:0.5em 0; } + position: absolute; + display: none; + z-index: 100; + padding: 10px; + background-color: #fff; + color: #000; + border: 1px solid #bbb; + border-radius: 4px; + box-shadow: 2px 4px 10px rgba(0, 0, 0, 0.2); +} +.yellow-pane h1 { + color: #000; + font-size: 2em; + margin: 0 1em; +} +.yellow-pane p { + margin: 0.5em; +} +.yellow-pane .yellow-status { + margin-bottom: 1em; +} +.yellow-pane .yellow-fields { + width: 15em; + text-align: left; + margin: 0 auto; +} +.yellow-pane .yellow-form-control { + width: 15em; + box-sizing: border-box; +} +.yellow-pane .yellow-fields .yellow-btn { + width: 15em; + margin: 1em 0 0.5em 0; +} +.yellow-pane .yellow-buttons .yellow-btn { + width: 15em; + margin: 0.5em 0; +} .yellow-close { - position:absolute; - top:0.8em; right:1em; cursor:pointer; - font-size:0.9em; color:#bbb; text-decoration:none; -} -.yellow-close:hover { color:#000; text-decoration:none; } -.yellow-arrow { position:absolute; top:0; left:0; } -.yellow-arrow:after, .yellow-arrow:before { - position:absolute; - pointer-events:none; - bottom:100%; - height:0; width:0; - border:solid transparent; - content:""; + position: absolute; + top: 0.8em; + right: 1em; + cursor: pointer; + font-size: 0.9em; + color: #bbb; + text-decoration: none; +} +.yellow-close:hover { + color: #000; + text-decoration: none; +} +.yellow-arrow { + position: absolute; + top: 0; + left: 0; +} +.yellow-arrow:after, +.yellow-arrow:before { + position: absolute; + pointer-events: none; + bottom: 100%; + height: 0; + width: 0; + border: solid transparent; + content: ""; } .yellow-arrow:after { - border-color:rgba(255, 255, 255, 0); - border-bottom-color:#fff; - border-width:10px; - margin-left:-10px; + border-color: rgba(255, 255, 255, 0); + border-bottom-color: #fff; + border-width: 10px; + margin-left: -10px; } .yellow-arrow:before { - border-color:rgba(187, 187, 187, 0); - border-bottom-color:#bbb; - border-width:11px; - margin-left:-11px; + border-color: rgba(187, 187, 187, 0); + border-bottom-color: #bbb; + border-width: 11px; + margin-left: -11px; } .yellow-popup { - position:absolute; display:none; z-index:200; padding:10px 0; - background-color:#fff; color:#000; - border:1px solid #bbb; - border-radius:4px; box-shadow:2px 4px 10px rgba(0, 0, 0, 0.2); -} -.yellow-dropdown { list-style:none; margin:0; padding:0; } -.yellow-dropdown span { display:block; margin:0; padding:0.25em 1em; } -.yellow-dropdown a { display:block; padding:0.2em 1em; text-decoration:none; } -.yellow-dropdown a:hover { color:#fff; background-color:#18e; text-decoration:none; } -.yellow-dropdown-menu a { color:#000; } -.yellow-toolbar { list-style:none; margin:0; padding:0; } -.yellow-toolbar-left { display:inline-block; float:left; } -.yellow-toolbar-right { display:inline-block; float:right; } -.yellow-toolbar-banner { clear:both; } -.yellow-toolbar li { display:inline-block; vertical-align:top; } + position: absolute; + display: none; + z-index: 200; + padding: 10px 0; + background-color: #fff; + color: #000; + border: 1px solid #bbb; + border-radius: 4px; + box-shadow: 2px 4px 10px rgba(0, 0, 0, 0.2); +} +.yellow-dropdown { + list-style: none; + margin: 0; + padding: 0; +} +.yellow-dropdown span { + display: block; + margin: 0; + padding: 0.25em 1em; +} +.yellow-dropdown a { + display: block; + padding: 0.2em 1em; + text-decoration: none; +} +.yellow-dropdown a:hover { + color: #fff; + background-color: #18e; + text-decoration: none; +} +.yellow-dropdown-menu a { + color: #000; +} +.yellow-toolbar { + list-style: none; + margin: 0; + padding: 0; +} +.yellow-toolbar-left { + display: inline-block; + float: left; +} +.yellow-toolbar-right { + display: inline-block; + float: right; +} +.yellow-toolbar-banner { + clear: both; +} +.yellow-toolbar li { + display: inline-block; + vertical-align: top; +} .yellow-toolbar a { - display:inline-block; padding:6px 16px; text-decoration:none; - background-color:#fff; color:#000; - font-size:0.9em; font-weight:normal; - border:1px solid #bbb; - border-radius:4px; + display: inline-block; + padding: 6px 16px; + text-decoration: none; + background-color: #fff; + color: #000; + font-size: 0.9em; + font-weight: normal; + border: 1px solid #bbb; + border-radius: 4px; } .yellow-toolbar a:hover { - background-color:#18e; background-image:none; border-color:#18e; color:#fff; - text-decoration:none; + background-color: #18e; + background-image: none; + border-color: #18e; + color: #fff; + text-decoration: none; +} +.yellow-toolbar-left a { + margin-right: 4px; + margin-bottom: 10px; } -.yellow-toolbar-left a { margin-right:4px; margin-bottom:10px; } -.yellow-toolbar-right a { margin-left:4px; margin-bottom:10px; } -.yellow-toolbar .yellow-icon -{ - font-size:0.9em; min-width:1em; text-align:center; +.yellow-toolbar-right a { + margin-left: 4px; + margin-bottom: 10px; +} +.yellow-toolbar .yellow-icon { + font-size: 0.9em; + min-width: 1em; + text-align: center; } .yellow-toolbar .yellow-toolbar-btn { - padding:6px 10px; min-width:4em; text-align:center; + padding: 6px 10px; + min-width: 4em; + text-align: center; } .yellow-toolbar .yellow-toolbar-btn-edit { - background-color:#29f; border-color:#29f; color:#fff; + background-color: #29f; + border-color: #29f; + color: #fff; } .yellow-toolbar .yellow-toolbar-btn-create { - background-color:#29f; border-color:#29f; color:#fff + background-color: #29f; + border-color: #29f; + color: #fff; } .yellow-toolbar .yellow-toolbar-btn-delete { - background-color:#e55; border-color:#e55; color:#fff + background-color: #e55; + border-color: #e55; + color: #fff; } -.yellow-toolbar .yellow-toolbar-btn-delete:hover { background-color:#d44; border-color:#d44; } -.yellow-toolbar .yellow-toolbar-btn-separator { visibility:hidden; padding:6px; } -.yellow-toolbar .yellow-toolbar-checked { background-color:#666; border-color:#666; color:#fff; } -.yellow-toolbar-tooltip { position:relative; } -.yellow-toolbar-tooltip::after, .yellow-toolbar-tooltip::before { - position:absolute; z-index:300; display:none; - pointer-events:none; +.yellow-toolbar .yellow-toolbar-btn-delete:hover { + background-color: #d44; + border-color: #d44; } -.yellow-toolbar-tooltip::after { - padding:2px 9px; - font-weight:normal; - font-size:0.9em; - text-align:center; - white-space:nowrap; - content:attr(aria-label); - background-color:#111; color:#ddd; - border-radius:3px; - top:100%; - right:50%; - margin-top:6px; - transform:translateX(50%); +.yellow-toolbar .yellow-toolbar-btn-separator { + visibility: hidden; + padding: 6px; } +.yellow-toolbar .yellow-toolbar-checked { + background-color: #666; + border-color: #666; + color: #fff; +} +.yellow-toolbar-tooltip { + position: relative; +} +.yellow-toolbar-tooltip::after, .yellow-toolbar-tooltip::before { - width:0; height:0; - content:""; - border:4px solid transparent; - top:auto; - right:50%; - bottom:-6px; - margin-right:-4px; - border-bottom-color:#111; + position: absolute; + z-index: 300; + display: none; + pointer-events: none; } -.yellow-toolbar-tooltip:hover::before, .yellow-toolbar-tooltip:hover::after { - display:inline-block; +.yellow-toolbar-tooltip::after { + padding: 2px 9px; + font-weight: normal; + font-size: 0.9em; + text-align: center; + white-space: nowrap; + content: attr(aria-label); + background-color: #111; + color: #ddd; + border-radius: 3px; + top: 100%; + right: 50%; + margin-top: 6px; + transform: translateX(50%); } -.yellow-toolbar-selected.yellow-toolbar-tooltip::before, .yellow-toolbar-selected.yellow-toolbar-tooltip::after { - display:none; +.yellow-toolbar-tooltip::before { + width: 0; + height: 0; + content: ""; + border: 4px solid transparent; + top: auto; + right: 50%; + bottom: -6px; + margin-right: -4px; + border-bottom-color: #111; +} +.yellow-toolbar-tooltip:hover::before, +.yellow-toolbar-tooltip:hover::after { + display: inline-block; +} +.yellow-toolbar-selected.yellow-toolbar-tooltip::before, +.yellow-toolbar-selected.yellow-toolbar-tooltip::after { + display: none; } .yellow-form-control { - margin:0; padding:2px 4px; - display:inline-block; - background-color:#fff; color:#000; - background-image:linear-gradient(to bottom, #fff, #fff); - border:1px solid #bbb; - border-radius:4px; - font-size:0.9em; font-family:inherit; font-weight:normal; line-height:normal; + margin: 0; + padding: 2px 4px; + display: inline-block; + background-color: #fff; + color: #000; + background-image: linear-gradient(to bottom, #fff, #fff); + border: 1px solid #bbb; + border-radius: 4px; + font-size: 0.9em; + font-family: inherit; + font-weight: normal; + line-height: normal; } .yellow-btn { - margin:0; padding:4px 22px; - display:inline-block; min-width:8em; - background-color:#eaeaea; color:#333333; - background-image:linear-gradient(to bottom, #f8f8f8, #e1e1e1); - border:1px solid #bbb; - border-color:#c1c1c1 #c1c1c1 #aaaaaa; - border-radius:4px; - outline-offset:-2px; - font-size:0.9em; font-family:inherit; font-weight:normal; line-height:1; - text-align:center; text-decoration:none; - box-sizing:border-box; -} -.yellow-btn:hover, .yellow-btn:focus, .yellow-btn:active { - color:#333333; - background-image:none; - text-decoration:none; -} -.yellow-btn:active { box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.1); } + margin: 0; + padding: 4px 22px; + display: inline-block; + min-width: 8em; + background-color: #eaeaea; + color: #333333; + background-image: linear-gradient(to bottom, #f8f8f8, #e1e1e1); + border: 1px solid #bbb; + border-color: #c1c1c1 #c1c1c1 #aaaaaa; + border-radius: 4px; + outline-offset: -2px; + font-size: 0.9em; + font-family: inherit; + font-weight: normal; + line-height: 1; + text-align: center; + text-decoration: none; + box-sizing: border-box; +} +.yellow-btn:hover, +.yellow-btn:focus, +.yellow-btn:active { + color: #333333; + background-image: none; + text-decoration: none; +} +.yellow-btn:active { + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1); +} + +/* Specific panes */ -#yellow-pane-login, #yellow-pane-signup, #yellow-pane-forgot, #yellow-pane-recover, #yellow-pane-settings, #yellow-pane-version, #yellow-pane-quit { - text-align:center; -} -#yellow-pane-edit-toolbar-title { margin:-5px 0 0 0; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; } -#yellow-pane-edit-text { padding:0 2px; outline:none; resize:none; border:none; } -#yellow-pane-edit-preview { padding:0; overflow:auto; } -#yellow-pane-edit-preview h1 { margin:0.67em 0; } -#yellow-pane-edit-preview p { margin:1em 0; } -#yellow-pane-edit-preview .content { margin:0; padding:0; } -#yellow-pane-user { padding:10px 0; } +#yellow-pane-login, +#yellow-pane-signup, +#yellow-pane-forgot, +#yellow-pane-recover, +#yellow-pane-settings, +#yellow-pane-version, +#yellow-pane-quit { + text-align: center; +} +#yellow-pane-edit-toolbar-title { + margin: -5px 0 0 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +#yellow-pane-edit-text { + padding: 0 2px; + outline: none; + resize: none; + border: none; +} +#yellow-pane-edit-preview { + padding: 0; + overflow: auto; +} +#yellow-pane-edit-preview h1 { + margin: 0.67em 0; +} +#yellow-pane-edit-preview p { + margin: 1em 0; +} +#yellow-pane-edit-preview .content { + margin: 0; + padding: 0; +} +#yellow-pane-user { + padding: 10px 0; +} -#yellow-popup-format, #yellow-popup-heading, #yellow-popup-list { width:16em; } -#yellow-popup-format a, #yellow-popup-heading a { padding:0.25em 16px; } -#yellow-popup-format #yellow-popup-format-h1, #yellow-popup-heading #yellow-popup-heading-h1 { font-size:2em; font-weight:bold; } -#yellow-popup-format #yellow-popup-format-h2, #yellow-popup-heading #yellow-popup-heading-h2 { font-size:1.6em; font-weight:bold; } -#yellow-popup-format #yellow-popup-format-h3, #yellow-popup-heading #yellow-popup-heading-h3 { font-size:1.3em; font-weight:bold; } -#yellow-popup-format #yellow-popup-format-quote { font-style:italic; } -#yellow-popup-format #yellow-popup-format-pre { font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace; font-size:0.9em; line-height:1.8; } -#yellow-popup-emojiawesome { padding:10px; width:14em; } -#yellow-popup-emojiawesome a { padding:0.2em; } -#yellow-popup-emojiawesome .yellow-dropdown li { display:inline-block; } +/* Specific popups */ + +#yellow-popup-format, +#yellow-popup-heading, +#yellow-popup-list { + width: 16em; +} +#yellow-popup-format a, +#yellow-popup-heading a { + padding: 0.25em 16px; +} +#yellow-popup-format #yellow-popup-format-h1, +#yellow-popup-heading #yellow-popup-heading-h1 { + font-size: 2em; + font-weight: bold; +} +#yellow-popup-format #yellow-popup-format-h2, +#yellow-popup-heading #yellow-popup-heading-h2 { + font-size: 1.6em; + font-weight: bold; +} +#yellow-popup-format #yellow-popup-format-h3, +#yellow-popup-heading #yellow-popup-heading-h3 { + font-size: 1.3em; + font-weight: bold; +} +#yellow-popup-format #yellow-popup-format-quote { + font-style: italic; +} +#yellow-popup-format #yellow-popup-format-pre { + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; + font-size: 0.9em; + line-height: 1.8; +} +#yellow-popup-emojiawesome { + padding: 10px; + width: 14em; +} +#yellow-popup-emojiawesome a { + padding: 0.2em; +} +#yellow-popup-emojiawesome .yellow-dropdown li { + display: inline-block; +} +#yellow-popup-fontawesome { + padding: 10px; + width: 13em; +} +#yellow-popup-fontawesome a { + padding: 0.18em 0.3em; + min-width: 1em; + text-align: center; +} +#yellow-popup-fontawesome .yellow-dropdown li { + display: inline-block; +} -#yellow-popup-fontawesome { padding:10px; width:13em; } -#yellow-popup-fontawesome a { padding:0.18em 0.3em; min-width:1em; text-align:center; } -#yellow-popup-fontawesome .yellow-dropdown li { display:inline-block; } +/* Icons */ @font-face { - font-family:'Edit'; - font-weight:normal; - font-style:normal; - src:url('edit.woff') format('woff'); + font-family: "Edit"; + font-weight: normal; + font-style: normal; + src: url("edit.woff") format("woff"); } .yellow-icon { - display:inline-block; - font-family:Edit; - font-style:normal; - font-weight:normal; - -webkit-font-smoothing:antialiased; - -moz-osx-font-smoothing:grayscale; + display: inline-block; + font-family: Edit; + font-style: normal; + font-weight: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } .yellow-spin { - -webkit-animation:yellow-spin 1s infinite steps(16); - animation:yellow-spin 1s infinite steps(16); + -webkit-animation: yellow-spin 1s infinite steps(16); + animation: yellow-spin 1s infinite steps(16); } @-webkit-keyframes yellow-spin { - 0% { -webkit-transform:rotate(0deg); transform:rotate(0deg); } - 100% { -webkit-transform:rotate(359deg); transform:rotate(359deg); } + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } } @keyframes yellow-spin { - 0% { -webkit-transform:rotate(0deg); transform:rotate(0deg); } - 100% { -webkit-transform:rotate(359deg); transform:rotate(359deg); } + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } } .yellow-icon-preview:before { - content: "\f100"; + content: "\f100"; } .yellow-icon-format:before { - content: "\f101"; + content: "\f101"; } .yellow-icon-paragraph:before { - content: "\f101"; + content: "\f101"; } .yellow-icon-heading:before { - content: "\f102"; + content: "\f102"; } .yellow-icon-h1:before { - content: "\f103"; + content: "\f103"; } .yellow-icon-h2:before { - content: "\f104"; + content: "\f104"; } .yellow-icon-h3:before { - content: "\f105"; + content: "\f105"; } .yellow-icon-bold:before { - content: "\f106"; + content: "\f106"; } .yellow-icon-italic:before { - content: "\f0f7"; + content: "\f0f7"; } .yellow-icon-strikethrough:before { - content: "\f108"; + content: "\f108"; } .yellow-icon-quote:before { - content: "\f109"; + content: "\f109"; } .yellow-icon-code:before { - content: "\f10a"; + content: "\f10a"; } .yellow-icon-pre:before { - content: "\f10a"; + content: "\f10a"; } .yellow-icon-link:before { - content: "\f10b"; + content: "\f10b"; } .yellow-icon-file:before { - content: "\f10c"; + content: "\f10c"; } .yellow-icon-list:before { - content: "\f10d"; + content: "\f10d"; } .yellow-icon-ul:before { - content: "\f10d"; + content: "\f10d"; } .yellow-icon-ol:before { - content: "\f10e"; + content: "\f10e"; } .yellow-icon-tl:before { - content: "\f10f"; + content: "\f10f"; } .yellow-icon-hr:before { - content: "\f110"; + content: "\f110"; } .yellow-icon-table:before { - content: "\f111"; + content: "\f111"; } .yellow-icon-emojiawesome:before { - content: "\f112"; + content: "\f112"; } .yellow-icon-fontawesome:before { - content: "\f113"; + content: "\f113"; } .yellow-icon-draft:before { - content: "\f114"; + content: "\f114"; } .yellow-icon-undo:before { - content: "\f115"; + content: "\f115"; } .yellow-icon-redo:before { - content: "\f116"; + content: "\f116"; } .yellow-icon-spinner:before { - content: "\f200"; + content: "\f200"; } .yellow-icon-search:before { - content: "\f201"; + content: "\f201"; } .yellow-icon-close:before { - content: "\f202"; + content: "\f202"; } .yellow-icon-help:before { - content: "\f203"; + content: "\f203"; } .yellow-icon-markdown:before { - content: "\f203"; + content: "\f203"; } .yellow-icon-logo:before { - content: "\f8ff"; + content: "\f8ff"; } diff --git a/system/plugins/edit.js b/system/plugins/edit.js @@ -2,1478 +2,1316 @@ // Copyright (c) 2013-2018 Datenstrom, https://datenstrom.se // This file may be used and distributed under the terms of the public license. -var yellow = -{ - action: function(action, status, args) { yellow.edit.action(action, status, args); }, - onLoad: function() { yellow.edit.load(); }, - onClickAction: function(e) { yellow.edit.clickAction(e); }, - onClick: function(e) { yellow.edit.click(e); }, - onKeydown: function(e) { yellow.edit.keydown(e); }, - onDrag: function(e) { yellow.edit.drag(e); }, - onDrop: function(e) { yellow.edit.drop(e); }, - onUpdate: function() { yellow.edit.updatePane(yellow.edit.paneId, yellow.edit.paneAction, yellow.edit.paneStatus); }, - onResize: function() { yellow.edit.resizePane(yellow.edit.paneId, yellow.edit.paneAction, yellow.edit.paneStatus); } +var yellow = { + + // Main event handlers + action: function(action, status, args) { yellow.edit.action(action, status, args); }, + onLoad: function() { yellow.edit.load(); }, + onClickAction: function(e) { yellow.edit.clickAction(e); }, + onClick: function(e) { yellow.edit.click(e); }, + onKeydown: function(e) { yellow.edit.keydown(e); }, + onDrag: function(e) { yellow.edit.drag(e); }, + onDrop: function(e) { yellow.edit.drop(e); }, + onUpdate: function() { yellow.edit.updatePane(yellow.edit.paneId, yellow.edit.paneAction, yellow.edit.paneStatus); }, + onResize: function() { yellow.edit.resizePane(yellow.edit.paneId, yellow.edit.paneAction, yellow.edit.paneStatus); } }; -yellow.edit = -{ - paneId: 0, //visible pane ID - paneActionOld: 0, //previous pane action - paneAction: 0, //current pane action - paneStatus: 0, //current pane status - popupId: 0, //visible popup ID - intervalId: 0, //timer interval ID +yellow.edit = { + paneId: 0, //visible pane ID + paneActionOld: 0, //previous pane action + paneAction: 0, //current pane action + paneStatus: 0, //current pane status + popupId: 0, //visible popup ID + intervalId: 0, //timer interval ID - // Handle initialisation - load: function() - { - var body = document.getElementsByTagName("body")[0]; - if(body && body.firstChild && !document.getElementById("yellow-bar")) - { - this.createBar("yellow-bar"); - this.createPane("yellow-pane-edit", "none", "none"); - this.action(yellow.page.action, yellow.page.status); - clearInterval(this.intervalId); - } - }, - - // Handle action - action: function(action, status, args) - { - status = status ? status : "none"; - args = args ? args : "none"; - switch(action) - { - case "login": this.showPane("yellow-pane-login", action, status); break; - case "logout": this.sendPane("yellow-pane-logout", action); break; - case "signup": this.showPane("yellow-pane-signup", action, status); break; - case "confirm": this.showPane("yellow-pane-signup", action, status); break; - case "approve": this.showPane("yellow-pane-signup", action, status); break; - case "forgot": this.showPane("yellow-pane-forgot", action, status); break; - case "recover": this.showPane("yellow-pane-recover", action, status); break; - case "reactivate": this.showPane("yellow-pane-settings", action, status); break; - case "settings": this.showPane("yellow-pane-settings", action, status); break; - case "verify": this.showPane("yellow-pane-settings", action, status); break; - case "change": this.showPane("yellow-pane-settings", action, status); break; - case "version": this.showPane("yellow-pane-version", action, status); break; - case "update": this.sendPane("yellow-pane-update", action, status, args); break; - case "quit": this.showPane("yellow-pane-quit", action, status); break; - case "remove": this.showPane("yellow-pane-quit", action, status); break; - case "create": this.showPane("yellow-pane-edit", action, status, true); break; - case "edit": this.showPane("yellow-pane-edit", action, status, true); break; - case "delete": this.showPane("yellow-pane-edit", action, status, true); break; - case "user": this.showPane("yellow-pane-user", action, status); break; - case "send": this.sendPane(this.paneId, this.paneAction); break; - case "close": this.hidePane(this.paneId); break; - case "toolbar": this.processToolbar(status, args); break; - case "help": this.processHelp(); break; - } - }, - - // Handle action clicked - clickAction: function(e) - { - e.stopPropagation(); - e.preventDefault(); - var element = e.target; - for(; element; element=element.parentNode) - { - if(element.tagName=="A") break; - } - this.action(element.getAttribute("data-action"), element.getAttribute("data-status"), element.getAttribute("data-args")); - }, - - // Handle mouse clicked - click: function(e) - { - if(this.popupId && !document.getElementById(this.popupId).contains(e.target)) this.hidePopup(this.popupId, true); - if(this.paneId && !document.getElementById(this.paneId).contains(e.target)) this.hidePane(this.paneId, true); - }, - - // Handle keyboard - keydown: function(e) - { - if(this.paneId=="yellow-pane-edit") this.processShortcut(e); - if(this.paneId && e.keyCode==27) this.hidePane(this.paneId); - }, - - // Handle drag - drag: function(e) - { - e.stopPropagation(); - e.preventDefault(); - }, - - // Handle drop - drop: function(e) - { - e.stopPropagation(); - e.preventDefault(); - var elementText = document.getElementById("yellow-pane-edit-text"); - var files = e.dataTransfer ? e.dataTransfer.files : e.target.files; - for(var i=0; i<files.length; i++) this.uploadFile(elementText, files[i]); - }, - - // Create bar - createBar: function(barId) - { - if(yellow.config.debug) console.log("yellow.edit.createBar id:"+barId); - var elementBar = document.createElement("div"); - elementBar.className = "yellow-bar"; - elementBar.setAttribute("id", barId); - if(barId=="yellow-bar") - { - yellow.toolbox.addEvent(document, "click", yellow.onClick); - yellow.toolbox.addEvent(document, "keydown", yellow.onKeydown); - yellow.toolbox.addEvent(window, "resize", yellow.onResize); - } - var elementDiv = document.createElement("div"); - elementDiv.setAttribute("id", barId+"-content"); - if(yellow.config.userName) - { - elementDiv.innerHTML = - "<div class=\"yellow-bar-left\">"+ - "<a href=\"#\" id=\"yellow-pane-edit-link\" data-action=\"edit\">"+this.getText("Edit")+"</a>"+ - "</div>"+ - "<div class=\"yellow-bar-right\">"+ - "<a href=\"#\" id=\"yellow-pane-create-link\" data-action=\"create\">"+this.getText("Create")+"</a>"+ - "<a href=\"#\" id=\"yellow-pane-delete-link\" data-action=\"delete\">"+this.getText("Delete")+"</a>"+ - "<a href=\"#\" id=\"yellow-pane-user-link\" data-action=\"user\">"+yellow.toolbox.encodeHtml(yellow.config.userName)+"</a>"+ - "</div>"+ - "<div class=\"yellow-bar-banner\"></div>"; - } - elementBar.appendChild(elementDiv); - yellow.toolbox.insertBefore(elementBar, document.getElementsByTagName("body")[0].firstChild); - this.bindActions(elementBar); - }, - - // Create pane - createPane: function(paneId, paneAction, paneStatus) - { - if(yellow.config.debug) console.log("yellow.edit.createPane id:"+paneId); - var elementPane = document.createElement("div"); - elementPane.className = "yellow-pane"; - elementPane.setAttribute("id", paneId); - elementPane.style.display = "none"; - if(paneId=="yellow-pane-edit") - { - yellow.toolbox.addEvent(elementPane, "input", yellow.onUpdate); - yellow.toolbox.addEvent(elementPane, "dragenter", yellow.onDrag); - yellow.toolbox.addEvent(elementPane, "dragover", yellow.onDrag); - yellow.toolbox.addEvent(elementPane, "drop", yellow.onDrop); - } - if(paneId=="yellow-pane-edit" || paneId=="yellow-pane-user") - { - var elementArrow = document.createElement("span"); - elementArrow.className = "yellow-arrow"; - elementArrow.setAttribute("id", paneId+"-arrow"); - elementPane.appendChild(elementArrow); - } - var elementDiv = document.createElement("div"); - elementDiv.className = "yellow-content"; - elementDiv.setAttribute("id", paneId+"-content"); - switch(paneId) - { - case "yellow-pane-login": - elementDiv.innerHTML = - "<form method=\"post\">"+ - "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\"></i></a>"+ - "<div class=\"yellow-title\"><h1>"+this.getText("LoginTitle")+"</h1></div>"+ - "<div class=\"yellow-fields\" id=\"yellow-pane-login-fields\">"+ - "<input type=\"hidden\" name=\"action\" value=\"login\" />"+ - "<p><label for=\"yellow-pane-login-email\">"+this.getText("LoginEmail")+"</label><br /><input class=\"yellow-form-control\" name=\"email\" id=\"yellow-pane-login-email\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(yellow.config.editLoginEmail)+"\" /></p>"+ - "<p><label for=\"yellow-pane-login-password\">"+this.getText("LoginPassword")+"</label><br /><input class=\"yellow-form-control\" type=\"password\" name=\"password\" id=\"yellow-pane-login-password\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(yellow.config.editLoginPassword)+"\" /></p>"+ - "<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("LoginButton")+"\" /></p>"+ - "</div>"+ - "<div class=\"yellow-actions\" id=\"yellow-pane-login-actions\">"+ - "<p><a href=\"#\" id=\"yellow-pane-login-forgot\" data-action=\"forgot\">"+this.getText("LoginForgot")+"</a><br /><a href=\"#\" id=\"yellow-pane-login-signup\" data-action=\"signup\">"+this.getText("LoginSignup")+"</a></p>"+ - "</div>"+ - "</form>"; - break; - case "yellow-pane-signup": - elementDiv.innerHTML = - "<form method=\"post\">"+ - "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\"></i></a>"+ - "<div class=\"yellow-title\"><h1>"+this.getText("SignupTitle")+"</h1></div>"+ - "<div class=\"yellow-status\"><p id=\"yellow-pane-signup-status\" class=\""+paneStatus+"\">"+this.getText(paneAction+"Status", "", paneStatus)+"</p></div>"+ - "<div class=\"yellow-fields\" id=\"yellow-pane-signup-fields\">"+ - "<input type=\"hidden\" name=\"action\" value=\"signup\" />"+ - "<p><label for=\"yellow-pane-signup-name\">"+this.getText("SignupName")+"</label><br /><input class=\"yellow-form-control\" name=\"name\" id=\"yellow-pane-signup-name\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(this.getRequest("name"))+"\" /></p>"+ - "<p><label for=\"yellow-pane-signup-email\">"+this.getText("SignupEmail")+"</label><br /><input class=\"yellow-form-control\" name=\"email\" id=\"yellow-pane-signup-email\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(this.getRequest("email"))+"\" /></p>"+ - "<p><label for=\"yellow-pane-signup-password\">"+this.getText("SignupPassword")+"</label><br /><input class=\"yellow-form-control\" type=\"password\" name=\"password\" id=\"yellow-pane-signup-password\" maxlength=\"64\" value=\"\" /></p>"+ - "<p><input type=\"checkbox\" name=\"consent\" value=\"consent\" id=\"consent\""+(this.getRequest("consent") ? " checked=\"checked\"" : "")+"> <label for=\"consent\">"+this.getText("SignupConsent")+"</label></p>"+ - "<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("SignupButton")+"\" /></p>"+ - "</div>"+ - "<div class=\"yellow-buttons\" id=\"yellow-pane-signup-buttons\">"+ - "<p><a href=\"#\" class=\"yellow-btn\" data-action=\"close\">"+this.getText("OkButton")+"</a></p>"+ - "</div>"+ - "</form>"; - break; - case "yellow-pane-forgot": - elementDiv.innerHTML = - "<form method=\"post\">"+ - "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\"></i></a>"+ - "<div class=\"yellow-title\"><h1>"+this.getText("ForgotTitle")+"</h1></div>"+ - "<div class=\"yellow-status\"><p id=\"yellow-pane-forgot-status\" class=\""+paneStatus+"\">"+this.getText(paneAction+"Status", "", paneStatus)+"</p></div>"+ - "<div class=\"yellow-fields\" id=\"yellow-pane-forgot-fields\">"+ - "<input type=\"hidden\" name=\"action\" value=\"forgot\" />"+ - "<p><label for=\"yellow-pane-forgot-email\">"+this.getText("ForgotEmail")+"</label><br /><input class=\"yellow-form-control\" name=\"email\" id=\"yellow-pane-forgot-email\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(this.getRequest("email"))+"\" /></p>"+ - "<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("OkButton")+"\" /></p>"+ - "</div>"+ - "<div class=\"yellow-buttons\" id=\"yellow-pane-forgot-buttons\">"+ - "<p><a href=\"#\" class=\"yellow-btn\" data-action=\"close\">"+this.getText("OkButton")+"</a></p>"+ - "</div>"+ - "</form>"; - break; - case "yellow-pane-recover": - elementDiv.innerHTML = - "<form method=\"post\">"+ - "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\"></i></a>"+ - "<div class=\"yellow-title\"><h1>"+this.getText("RecoverTitle")+"</h1></div>"+ - "<div class=\"yellow-status\"><p id=\"yellow-pane-recover-status\" class=\""+paneStatus+"\">"+this.getText(paneAction+"Status", "", paneStatus)+"</p></div>"+ - "<div class=\"yellow-fields\" id=\"yellow-pane-recover-fields\">"+ - "<p><label for=\"yellow-pane-recover-password\">"+this.getText("RecoverPassword")+"</label><br /><input class=\"yellow-form-control\" type=\"password\" name=\"password\" id=\"yellow-pane-recover-password\" maxlength=\"64\" value=\"\" /></p>"+ - "<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("OkButton")+"\" /></p>"+ - "</div>"+ - "<div class=\"yellow-buttons\" id=\"yellow-pane-recover-buttons\">"+ - "<p><a href=\"#\" class=\"yellow-btn\" data-action=\"close\">"+this.getText("OkButton")+"</a></p>"+ - "</div>"+ - "</form>"; - break; - case "yellow-pane-settings": - var rawDataLanguages = ""; - if(yellow.config.serverLanguages && Object.keys(yellow.config.serverLanguages).length>1) - { - rawDataLanguages += "<p>"; - for(var language in yellow.config.serverLanguages) - { - var checked = language==this.getRequest("language") ? " checked=\"checked\"" : ""; - rawDataLanguages += "<label for=\"yellow-pane-settings-"+language+"\"><input type=\"radio\" name=\"language\" id=\"yellow-pane-settings-"+language+"\" value=\""+language+"\""+checked+"> "+yellow.toolbox.encodeHtml(yellow.config.serverLanguages[language])+"</label><br />"; - } - rawDataLanguages += "</p>"; - } - elementDiv.innerHTML = - "<form method=\"post\">"+ - "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\"></i></a>"+ - "<div class=\"yellow-title\"><h1 id=\"yellow-pane-settings-title\">"+this.getText("SettingsTitle")+"</h1></div>"+ - "<div class=\"yellow-status\"><p id=\"yellow-pane-settings-status\" class=\""+paneStatus+"\">"+this.getText(paneAction+"Status", "", paneStatus)+"</p></div>"+ - "<div class=\"yellow-fields\" id=\"yellow-pane-settings-fields\">"+ - "<input type=\"hidden\" name=\"action\" value=\"settings\" />"+ - "<input type=\"hidden\" name=\"csrftoken\" value=\""+yellow.toolbox.encodeHtml(this.getCookie("csrftoken"))+"\" />"+ - "<p><label for=\"yellow-pane-settings-name\">"+this.getText("SignupName")+"</label><br /><input class=\"yellow-form-control\" name=\"name\" id=\"yellow-pane-settings-name\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(this.getRequest("name"))+"\" /></p>"+ - "<p><label for=\"yellow-pane-settings-email\">"+this.getText("SignupEmail")+"</label><br /><input class=\"yellow-form-control\" name=\"email\" id=\"yellow-pane-settings-email\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(this.getRequest("email"))+"\" /></p>"+ - "<p><label for=\"yellow-pane-settings-password\">"+this.getText("SignupPassword")+"</label><br /><input class=\"yellow-form-control\" type=\"password\" name=\"password\" id=\"yellow-pane-settings-password\" maxlength=\"64\" value=\"\" /></p>"+rawDataLanguages+ - "<p>"+this.getText("SettingsQuit")+" <a href=\"#\" data-action=\"quit\">"+this.getText("SettingsMore")+"</a></p>"+ - "<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("OkButton")+"\" /></p>"+ - "</div>"+ - "<div class=\"yellow-buttons\" id=\"yellow-pane-settings-buttons\">"+ - "<p><a href=\"#\" class=\"yellow-btn\" data-action=\"close\">"+this.getText("OkButton")+"</a></p>"+ - "</div>"+ - "</form>"; - break; - case "yellow-pane-version": - elementDiv.innerHTML = - "<form method=\"post\">"+ - "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\"></i></a>"+ - "<div class=\"yellow-title\"><h1 id=\"yellow-pane-version-title\">"+yellow.toolbox.encodeHtml(yellow.config.serverVersion)+"</h1></div>"+ - "<div class=\"yellow-status\"><p id=\"yellow-pane-version-status\" class=\""+paneStatus+"\">"+this.getText("VersionStatus", "", paneStatus)+"</p></div>"+ - "<div class=\"yellow-output\" id=\"yellow-pane-version-output\">"+yellow.page.rawDataOutput+"</div>"+ - "<div class=\"yellow-buttons\" id=\"yellow-pane-version-buttons\">"+ - "<p><a href=\"#\" class=\"yellow-btn\" data-action=\"close\">"+this.getText("OkButton")+"</a></p>"+ - "</div>"+ - "</form>"; - break; - case "yellow-pane-quit": - elementDiv.innerHTML = - "<form method=\"post\">"+ - "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\"></i></a>"+ - "<div class=\"yellow-title\"><h1>"+this.getText("QuitTitle")+"</h1></div>"+ - "<div class=\"yellow-status\"><p id=\"yellow-pane-quit-status\" class=\""+paneStatus+"\">"+this.getText(paneAction+"Status", "", paneStatus)+"</p></div>"+ - "<div class=\"yellow-fields\" id=\"yellow-pane-quit-fields\">"+ - "<input type=\"hidden\" name=\"action\" value=\"quit\" />"+ - "<input type=\"hidden\" name=\"csrftoken\" value=\""+yellow.toolbox.encodeHtml(this.getCookie("csrftoken"))+"\" />"+ - "<p><label for=\"yellow-pane-quit-name\">"+this.getText("SignupName")+"</label><br /><input class=\"yellow-form-control\" name=\"name\" id=\"yellow-pane-quit-name\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(this.getRequest("name"))+"\" /></p>"+ - "<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("DeleteButton")+"\" /></p>"+ - "</div>"+ - "<div class=\"yellow-buttons\" id=\"yellow-pane-quit-buttons\">"+ - "<p><a href=\"#\" class=\"yellow-btn\" data-action=\"close\">"+this.getText("OkButton")+"</a></p>"+ - "</div>"+ - "</form>"; - break; - case "yellow-pane-edit": - var rawDataButtons = ""; - if(yellow.config.editToolbarButtons && yellow.config.editToolbarButtons!="none") - { - var tokens = yellow.config.editToolbarButtons.split(","); - for(var i=0; i<tokens.length; i++) - { - var token = tokens[i].trim(); - if(token!="separator") - { - rawDataButtons += "<li><a href=\"#\" id=\"yellow-toolbar-"+yellow.toolbox.encodeHtml(token)+"\" class=\"yellow-toolbar-btn-icon yellow-toolbar-tooltip\" data-action=\"toolbar\" data-status=\""+yellow.toolbox.encodeHtml(token)+"\" aria-label=\""+this.getText("Toolbar", "", token)+"\"><i class=\"yellow-icon yellow-icon-"+yellow.toolbox.encodeHtml(token)+"\"></i></a></li>"; - } else { - rawDataButtons += "<li><a href=\"#\" class=\"yellow-toolbar-btn-separator\"></a></li>"; - } - } - if(yellow.config.debug) console.log("yellow.edit.createPane buttons:"+yellow.config.editToolbarButtons); - } - elementDiv.innerHTML = - "<form method=\"post\">"+ - "<div id=\"yellow-pane-edit-toolbar\">"+ - "<h1 id=\"yellow-pane-edit-toolbar-title\" class=\"yellow-toolbar yellow-toolbar-left\">"+this.getText("Edit")+"</h1>"+ - "<ul id=\"yellow-pane-edit-toolbar-buttons\" class=\"yellow-toolbar yellow-toolbar-left\">"+rawDataButtons+"</ul>"+ - "<ul id=\"yellow-pane-edit-toolbar-main\" class=\"yellow-toolbar yellow-toolbar-right\">"+ - "<li><a href=\"#\" id=\"yellow-pane-edit-cancel\" class=\"yellow-toolbar-btn\" data-action=\"close\">"+this.getText("CancelButton")+"</a></li>"+ - "<li><a href=\"#\" id=\"yellow-pane-edit-send\" class=\"yellow-toolbar-btn\" data-action=\"send\">"+this.getText("EditButton")+"</a></li>"+ - "</ul>"+ - "<ul class=\"yellow-toolbar yellow-toolbar-banner\"></ul>"+ - "</div>"+ - "<textarea id=\"yellow-pane-edit-text\" class=\"yellow-form-control\"></textarea>"+ - "<div id=\"yellow-pane-edit-preview\"></div>"+ - "</form>"; - break; - case "yellow-pane-user": - elementDiv.innerHTML = - "<ul class=\"yellow-dropdown\">"+ - "<li><span>"+yellow.toolbox.encodeHtml(yellow.config.userEmail)+"</span></li>"+ - "<li><a href=\"#\" data-action=\"settings\">"+this.getText("SettingsTitle")+"</a></li>" + - "<li><a href=\"#\" data-action=\"help\">"+this.getText("UserHelp")+"</a></li>" + - "<li><a href=\"#\" data-action=\"logout\">"+this.getText("UserLogout")+"</a></li>"+ - "</ul>"; - break; - } - elementPane.appendChild(elementDiv); - yellow.toolbox.insertAfter(elementPane, document.getElementsByTagName("body")[0].firstChild); - this.bindActions(elementPane); - }, + // Handle initialisation + load: function() { + var body = document.getElementsByTagName("body")[0]; + if (body && body.firstChild && !document.getElementById("yellow-bar")) { + this.createBar("yellow-bar"); + this.createPane("yellow-pane-edit", "none", "none"); + this.action(yellow.page.action, yellow.page.status); + clearInterval(this.intervalId); + } + }, + + // Handle action + action: function(action, status, args) { + status = status ? status : "none"; + args = args ? args : "none"; + switch (action) { + case "login": this.showPane("yellow-pane-login", action, status); break; + case "logout": this.sendPane("yellow-pane-logout", action); break; + case "signup": this.showPane("yellow-pane-signup", action, status); break; + case "confirm": this.showPane("yellow-pane-signup", action, status); break; + case "approve": this.showPane("yellow-pane-signup", action, status); break; + case "forgot": this.showPane("yellow-pane-forgot", action, status); break; + case "recover": this.showPane("yellow-pane-recover", action, status); break; + case "reactivate": this.showPane("yellow-pane-settings", action, status); break; + case "settings": this.showPane("yellow-pane-settings", action, status); break; + case "verify": this.showPane("yellow-pane-settings", action, status); break; + case "change": this.showPane("yellow-pane-settings", action, status); break; + case "version": this.showPane("yellow-pane-version", action, status); break; + case "update": this.sendPane("yellow-pane-update", action, status, args); break; + case "quit": this.showPane("yellow-pane-quit", action, status); break; + case "remove": this.showPane("yellow-pane-quit", action, status); break; + case "create": this.showPane("yellow-pane-edit", action, status, true); break; + case "edit": this.showPane("yellow-pane-edit", action, status, true); break; + case "delete": this.showPane("yellow-pane-edit", action, status, true); break; + case "user": this.showPane("yellow-pane-user", action, status); break; + case "send": this.sendPane(this.paneId, this.paneAction); break; + case "close": this.hidePane(this.paneId); break; + case "toolbar": this.processToolbar(status, args); break; + case "help": this.processHelp(); break; + } + }, + + // Handle action clicked + clickAction: function(e) { + e.stopPropagation(); + e.preventDefault(); + var element = e.target; + for (; element; element=element.parentNode) { + if (element.tagName=="A") break; + } + this.action(element.getAttribute("data-action"), element.getAttribute("data-status"), element.getAttribute("data-args")); + }, + + // Handle mouse clicked + click: function(e) { + if (this.popupId && !document.getElementById(this.popupId).contains(e.target)) this.hidePopup(this.popupId, true); + if (this.paneId && !document.getElementById(this.paneId).contains(e.target)) this.hidePane(this.paneId, true); + }, + + // Handle keyboard + keydown: function(e) { + if (this.paneId=="yellow-pane-edit") this.processShortcut(e); + if (this.paneId && e.keyCode==27) this.hidePane(this.paneId); + }, + + // Handle drag + drag: function(e) { + e.stopPropagation(); + e.preventDefault(); + }, + + // Handle drop + drop: function(e) { + e.stopPropagation(); + e.preventDefault(); + var elementText = document.getElementById("yellow-pane-edit-text"); + var files = e.dataTransfer ? e.dataTransfer.files : e.target.files; + for (var i=0; i<files.length; i++) this.uploadFile(elementText, files[i]); + }, + + // Create bar + createBar: function(barId) { + if (yellow.config.debug) console.log("yellow.edit.createBar id:"+barId); + var elementBar = document.createElement("div"); + elementBar.className = "yellow-bar"; + elementBar.setAttribute("id", barId); + if (barId=="yellow-bar") { + yellow.toolbox.addEvent(document, "click", yellow.onClick); + yellow.toolbox.addEvent(document, "keydown", yellow.onKeydown); + yellow.toolbox.addEvent(window, "resize", yellow.onResize); + } + var elementDiv = document.createElement("div"); + elementDiv.setAttribute("id", barId+"-content"); + if (yellow.config.userName) { + elementDiv.innerHTML = + "<div class=\"yellow-bar-left\">"+ + "<a href=\"#\" id=\"yellow-pane-edit-link\" data-action=\"edit\">"+this.getText("Edit")+"</a>"+ + "</div>"+ + "<div class=\"yellow-bar-right\">"+ + "<a href=\"#\" id=\"yellow-pane-create-link\" data-action=\"create\">"+this.getText("Create")+"</a>"+ + "<a href=\"#\" id=\"yellow-pane-delete-link\" data-action=\"delete\">"+this.getText("Delete")+"</a>"+ + "<a href=\"#\" id=\"yellow-pane-user-link\" data-action=\"user\">"+yellow.toolbox.encodeHtml(yellow.config.userName)+"</a>"+ + "</div>"+ + "<div class=\"yellow-bar-banner\"></div>"; + } + elementBar.appendChild(elementDiv); + yellow.toolbox.insertBefore(elementBar, document.getElementsByTagName("body")[0].firstChild); + this.bindActions(elementBar); + }, + + // Create pane + createPane: function(paneId, paneAction, paneStatus) { + if (yellow.config.debug) console.log("yellow.edit.createPane id:"+paneId); + var elementPane = document.createElement("div"); + elementPane.className = "yellow-pane"; + elementPane.setAttribute("id", paneId); + elementPane.style.display = "none"; + if (paneId=="yellow-pane-edit") { + yellow.toolbox.addEvent(elementPane, "input", yellow.onUpdate); + yellow.toolbox.addEvent(elementPane, "dragenter", yellow.onDrag); + yellow.toolbox.addEvent(elementPane, "dragover", yellow.onDrag); + yellow.toolbox.addEvent(elementPane, "drop", yellow.onDrop); + } + if (paneId=="yellow-pane-edit" || paneId=="yellow-pane-user") { + var elementArrow = document.createElement("span"); + elementArrow.className = "yellow-arrow"; + elementArrow.setAttribute("id", paneId+"-arrow"); + elementPane.appendChild(elementArrow); + } + var elementDiv = document.createElement("div"); + elementDiv.className = "yellow-content"; + elementDiv.setAttribute("id", paneId+"-content"); + switch (paneId) { + case "yellow-pane-login": + elementDiv.innerHTML = + "<form method=\"post\">"+ + "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\"></i></a>"+ + "<div class=\"yellow-title\"><h1>"+this.getText("LoginTitle")+"</h1></div>"+ + "<div class=\"yellow-fields\" id=\"yellow-pane-login-fields\">"+ + "<input type=\"hidden\" name=\"action\" value=\"login\" />"+ + "<p><label for=\"yellow-pane-login-email\">"+this.getText("LoginEmail")+"</label><br /><input class=\"yellow-form-control\" name=\"email\" id=\"yellow-pane-login-email\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(yellow.config.editLoginEmail)+"\" /></p>"+ + "<p><label for=\"yellow-pane-login-password\">"+this.getText("LoginPassword")+"</label><br /><input class=\"yellow-form-control\" type=\"password\" name=\"password\" id=\"yellow-pane-login-password\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(yellow.config.editLoginPassword)+"\" /></p>"+ + "<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("LoginButton")+"\" /></p>"+ + "</div>"+ + "<div class=\"yellow-actions\" id=\"yellow-pane-login-actions\">"+ + "<p><a href=\"#\" id=\"yellow-pane-login-forgot\" data-action=\"forgot\">"+this.getText("LoginForgot")+"</a><br /><a href=\"#\" id=\"yellow-pane-login-signup\" data-action=\"signup\">"+this.getText("LoginSignup")+"</a></p>"+ + "</div>"+ + "</form>"; + break; + case "yellow-pane-signup": + elementDiv.innerHTML = + "<form method=\"post\">"+ + "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\"></i></a>"+ + "<div class=\"yellow-title\"><h1>"+this.getText("SignupTitle")+"</h1></div>"+ + "<div class=\"yellow-status\"><p id=\"yellow-pane-signup-status\" class=\""+paneStatus+"\">"+this.getText(paneAction+"Status", "", paneStatus)+"</p></div>"+ + "<div class=\"yellow-fields\" id=\"yellow-pane-signup-fields\">"+ + "<input type=\"hidden\" name=\"action\" value=\"signup\" />"+ + "<p><label for=\"yellow-pane-signup-name\">"+this.getText("SignupName")+"</label><br /><input class=\"yellow-form-control\" name=\"name\" id=\"yellow-pane-signup-name\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(this.getRequest("name"))+"\" /></p>"+ + "<p><label for=\"yellow-pane-signup-email\">"+this.getText("SignupEmail")+"</label><br /><input class=\"yellow-form-control\" name=\"email\" id=\"yellow-pane-signup-email\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(this.getRequest("email"))+"\" /></p>"+ + "<p><label for=\"yellow-pane-signup-password\">"+this.getText("SignupPassword")+"</label><br /><input class=\"yellow-form-control\" type=\"password\" name=\"password\" id=\"yellow-pane-signup-password\" maxlength=\"64\" value=\"\" /></p>"+ + "<p><input type=\"checkbox\" name=\"consent\" value=\"consent\" id=\"consent\""+(this.getRequest("consent") ? " checked=\"checked\"" : "")+"> <label for=\"consent\">"+this.getText("SignupConsent")+"</label></p>"+ + "<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("SignupButton")+"\" /></p>"+ + "</div>"+ + "<div class=\"yellow-buttons\" id=\"yellow-pane-signup-buttons\">"+ + "<p><a href=\"#\" class=\"yellow-btn\" data-action=\"close\">"+this.getText("OkButton")+"</a></p>"+ + "</div>"+ + "</form>"; + break; + case "yellow-pane-forgot": + elementDiv.innerHTML = + "<form method=\"post\">"+ + "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\"></i></a>"+ + "<div class=\"yellow-title\"><h1>"+this.getText("ForgotTitle")+"</h1></div>"+ + "<div class=\"yellow-status\"><p id=\"yellow-pane-forgot-status\" class=\""+paneStatus+"\">"+this.getText(paneAction+"Status", "", paneStatus)+"</p></div>"+ + "<div class=\"yellow-fields\" id=\"yellow-pane-forgot-fields\">"+ + "<input type=\"hidden\" name=\"action\" value=\"forgot\" />"+ + "<p><label for=\"yellow-pane-forgot-email\">"+this.getText("ForgotEmail")+"</label><br /><input class=\"yellow-form-control\" name=\"email\" id=\"yellow-pane-forgot-email\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(this.getRequest("email"))+"\" /></p>"+ + "<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("OkButton")+"\" /></p>"+ + "</div>"+ + "<div class=\"yellow-buttons\" id=\"yellow-pane-forgot-buttons\">"+ + "<p><a href=\"#\" class=\"yellow-btn\" data-action=\"close\">"+this.getText("OkButton")+"</a></p>"+ + "</div>"+ + "</form>"; + break; + case "yellow-pane-recover": + elementDiv.innerHTML = + "<form method=\"post\">"+ + "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\"></i></a>"+ + "<div class=\"yellow-title\"><h1>"+this.getText("RecoverTitle")+"</h1></div>"+ + "<div class=\"yellow-status\"><p id=\"yellow-pane-recover-status\" class=\""+paneStatus+"\">"+this.getText(paneAction+"Status", "", paneStatus)+"</p></div>"+ + "<div class=\"yellow-fields\" id=\"yellow-pane-recover-fields\">"+ + "<p><label for=\"yellow-pane-recover-password\">"+this.getText("RecoverPassword")+"</label><br /><input class=\"yellow-form-control\" type=\"password\" name=\"password\" id=\"yellow-pane-recover-password\" maxlength=\"64\" value=\"\" /></p>"+ + "<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("OkButton")+"\" /></p>"+ + "</div>"+ + "<div class=\"yellow-buttons\" id=\"yellow-pane-recover-buttons\">"+ + "<p><a href=\"#\" class=\"yellow-btn\" data-action=\"close\">"+this.getText("OkButton")+"</a></p>"+ + "</div>"+ + "</form>"; + break; + case "yellow-pane-settings": + var rawDataLanguages = ""; + if (yellow.config.serverLanguages && Object.keys(yellow.config.serverLanguages).length>1) { + rawDataLanguages += "<p>"; + for (var language in yellow.config.serverLanguages) { + var checked = language==this.getRequest("language") ? " checked=\"checked\"" : ""; + rawDataLanguages += "<label for=\"yellow-pane-settings-"+language+"\"><input type=\"radio\" name=\"language\" id=\"yellow-pane-settings-"+language+"\" value=\""+language+"\""+checked+"> "+yellow.toolbox.encodeHtml(yellow.config.serverLanguages[language])+"</label><br />"; + } + rawDataLanguages += "</p>"; + } + elementDiv.innerHTML = + "<form method=\"post\">"+ + "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\"></i></a>"+ + "<div class=\"yellow-title\"><h1 id=\"yellow-pane-settings-title\">"+this.getText("SettingsTitle")+"</h1></div>"+ + "<div class=\"yellow-status\"><p id=\"yellow-pane-settings-status\" class=\""+paneStatus+"\">"+this.getText(paneAction+"Status", "", paneStatus)+"</p></div>"+ + "<div class=\"yellow-fields\" id=\"yellow-pane-settings-fields\">"+ + "<input type=\"hidden\" name=\"action\" value=\"settings\" />"+ + "<input type=\"hidden\" name=\"csrftoken\" value=\""+yellow.toolbox.encodeHtml(this.getCookie("csrftoken"))+"\" />"+ + "<p><label for=\"yellow-pane-settings-name\">"+this.getText("SignupName")+"</label><br /><input class=\"yellow-form-control\" name=\"name\" id=\"yellow-pane-settings-name\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(this.getRequest("name"))+"\" /></p>"+ + "<p><label for=\"yellow-pane-settings-email\">"+this.getText("SignupEmail")+"</label><br /><input class=\"yellow-form-control\" name=\"email\" id=\"yellow-pane-settings-email\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(this.getRequest("email"))+"\" /></p>"+ + "<p><label for=\"yellow-pane-settings-password\">"+this.getText("SignupPassword")+"</label><br /><input class=\"yellow-form-control\" type=\"password\" name=\"password\" id=\"yellow-pane-settings-password\" maxlength=\"64\" value=\"\" /></p>"+rawDataLanguages+ + "<p>"+this.getText("SettingsQuit")+" <a href=\"#\" data-action=\"quit\">"+this.getText("SettingsMore")+"</a></p>"+ + "<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("OkButton")+"\" /></p>"+ + "</div>"+ + "<div class=\"yellow-buttons\" id=\"yellow-pane-settings-buttons\">"+ + "<p><a href=\"#\" class=\"yellow-btn\" data-action=\"close\">"+this.getText("OkButton")+"</a></p>"+ + "</div>"+ + "</form>"; + break; + case "yellow-pane-version": + elementDiv.innerHTML = + "<form method=\"post\">"+ + "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\"></i></a>"+ + "<div class=\"yellow-title\"><h1 id=\"yellow-pane-version-title\">"+yellow.toolbox.encodeHtml(yellow.config.serverVersion)+"</h1></div>"+ + "<div class=\"yellow-status\"><p id=\"yellow-pane-version-status\" class=\""+paneStatus+"\">"+this.getText("VersionStatus", "", paneStatus)+"</p></div>"+ + "<div class=\"yellow-output\" id=\"yellow-pane-version-output\">"+yellow.page.rawDataOutput+"</div>"+ + "<div class=\"yellow-buttons\" id=\"yellow-pane-version-buttons\">"+ + "<p><a href=\"#\" class=\"yellow-btn\" data-action=\"close\">"+this.getText("OkButton")+"</a></p>"+ + "</div>"+ + "</form>"; + break; + case "yellow-pane-quit": + elementDiv.innerHTML = + "<form method=\"post\">"+ + "<a href=\"#\" class=\"yellow-close\" data-action=\"close\"><i class=\"yellow-icon yellow-icon-close\"></i></a>"+ + "<div class=\"yellow-title\"><h1>"+this.getText("QuitTitle")+"</h1></div>"+ + "<div class=\"yellow-status\"><p id=\"yellow-pane-quit-status\" class=\""+paneStatus+"\">"+this.getText(paneAction+"Status", "", paneStatus)+"</p></div>"+ + "<div class=\"yellow-fields\" id=\"yellow-pane-quit-fields\">"+ + "<input type=\"hidden\" name=\"action\" value=\"quit\" />"+ + "<input type=\"hidden\" name=\"csrftoken\" value=\""+yellow.toolbox.encodeHtml(this.getCookie("csrftoken"))+"\" />"+ + "<p><label for=\"yellow-pane-quit-name\">"+this.getText("SignupName")+"</label><br /><input class=\"yellow-form-control\" name=\"name\" id=\"yellow-pane-quit-name\" maxlength=\"64\" value=\""+yellow.toolbox.encodeHtml(this.getRequest("name"))+"\" /></p>"+ + "<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("DeleteButton")+"\" /></p>"+ + "</div>"+ + "<div class=\"yellow-buttons\" id=\"yellow-pane-quit-buttons\">"+ + "<p><a href=\"#\" class=\"yellow-btn\" data-action=\"close\">"+this.getText("OkButton")+"</a></p>"+ + "</div>"+ + "</form>"; + break; + case "yellow-pane-edit": + var rawDataButtons = ""; + if (yellow.config.editToolbarButtons && yellow.config.editToolbarButtons!="none") { + var tokens = yellow.config.editToolbarButtons.split(","); + for (var i=0; i<tokens.length; i++) { + var token = tokens[i].trim(); + if (token!="separator") { + rawDataButtons += "<li><a href=\"#\" id=\"yellow-toolbar-"+yellow.toolbox.encodeHtml(token)+"\" class=\"yellow-toolbar-btn-icon yellow-toolbar-tooltip\" data-action=\"toolbar\" data-status=\""+yellow.toolbox.encodeHtml(token)+"\" aria-label=\""+this.getText("Toolbar", "", token)+"\"><i class=\"yellow-icon yellow-icon-"+yellow.toolbox.encodeHtml(token)+"\"></i></a></li>"; + } else { + rawDataButtons += "<li><a href=\"#\" class=\"yellow-toolbar-btn-separator\"></a></li>"; + } + } + if (yellow.config.debug) console.log("yellow.edit.createPane buttons:"+yellow.config.editToolbarButtons); + } + elementDiv.innerHTML = + "<form method=\"post\">"+ + "<div id=\"yellow-pane-edit-toolbar\">"+ + "<h1 id=\"yellow-pane-edit-toolbar-title\" class=\"yellow-toolbar yellow-toolbar-left\">"+this.getText("Edit")+"</h1>"+ + "<ul id=\"yellow-pane-edit-toolbar-buttons\" class=\"yellow-toolbar yellow-toolbar-left\">"+rawDataButtons+"</ul>"+ + "<ul id=\"yellow-pane-edit-toolbar-main\" class=\"yellow-toolbar yellow-toolbar-right\">"+ + "<li><a href=\"#\" id=\"yellow-pane-edit-cancel\" class=\"yellow-toolbar-btn\" data-action=\"close\">"+this.getText("CancelButton")+"</a></li>"+ + "<li><a href=\"#\" id=\"yellow-pane-edit-send\" class=\"yellow-toolbar-btn\" data-action=\"send\">"+this.getText("EditButton")+"</a></li>"+ + "</ul>"+ + "<ul class=\"yellow-toolbar yellow-toolbar-banner\"></ul>"+ + "</div>"+ + "<textarea id=\"yellow-pane-edit-text\" class=\"yellow-form-control\"></textarea>"+ + "<div id=\"yellow-pane-edit-preview\"></div>"+ + "</form>"; + break; + case "yellow-pane-user": + elementDiv.innerHTML = + "<ul class=\"yellow-dropdown\">"+ + "<li><span>"+yellow.toolbox.encodeHtml(yellow.config.userEmail)+"</span></li>"+ + "<li><a href=\"#\" data-action=\"settings\">"+this.getText("SettingsTitle")+"</a></li>" + + "<li><a href=\"#\" data-action=\"help\">"+this.getText("UserHelp")+"</a></li>" + + "<li><a href=\"#\" data-action=\"logout\">"+this.getText("UserLogout")+"</a></li>"+ + "</ul>"; + break; + } + elementPane.appendChild(elementDiv); + yellow.toolbox.insertAfter(elementPane, document.getElementsByTagName("body")[0].firstChild); + this.bindActions(elementPane); + }, - // Update pane - updatePane: function(paneId, paneAction, paneStatus, init) - { - if(yellow.config.debug) console.log("yellow.edit.updatePane id:"+paneId); - var showFields = paneStatus!="next" && paneStatus!="done"; - switch(paneId) - { - case "yellow-pane-login": - if(yellow.config.editLoginRestrictions) - { - yellow.toolbox.setVisible(document.getElementById("yellow-pane-login-signup"), false); - } - break; - case "yellow-pane-signup": - yellow.toolbox.setVisible(document.getElementById("yellow-pane-signup-fields"), showFields); - yellow.toolbox.setVisible(document.getElementById("yellow-pane-signup-buttons"), !showFields); - break; - case "yellow-pane-forgot": - yellow.toolbox.setVisible(document.getElementById("yellow-pane-forgot-fields"), showFields); - yellow.toolbox.setVisible(document.getElementById("yellow-pane-forgot-buttons"), !showFields); - break; - case "yellow-pane-recover": - yellow.toolbox.setVisible(document.getElementById("yellow-pane-recover-fields"), showFields); - yellow.toolbox.setVisible(document.getElementById("yellow-pane-recover-buttons"), !showFields); - break; - case "yellow-pane-settings": - yellow.toolbox.setVisible(document.getElementById("yellow-pane-settings-fields"), showFields); - yellow.toolbox.setVisible(document.getElementById("yellow-pane-settings-buttons"), !showFields); - if(paneStatus=="none") - { - document.getElementById("yellow-pane-settings-status").innerHTML = "<a href=\"#\" data-action=\"version\">"+this.getText("VersionTitle")+"</a>"; - document.getElementById("yellow-pane-settings-name").value = yellow.config.userName; - document.getElementById("yellow-pane-settings-email").value = yellow.config.userEmail; - document.getElementById("yellow-pane-settings-"+yellow.config.userLanguage).checked = true; - } - break; - case "yellow-pane-version": - if(paneStatus=="none" && this.isPlugin("update")) - { - document.getElementById("yellow-pane-version-status").innerHTML = this.getText("VersionStatusCheck"); - document.getElementById("yellow-pane-version-output").innerHTML = ""; - setTimeout("yellow.action('send');", 500); - } - if(paneStatus=="updates" && this.isPlugin("update")) - { - document.getElementById("yellow-pane-version-status").innerHTML = "<a href=\"#\" data-action=\"update\">"+this.getText("VersionStatusUpdates")+"</a>"; - } - break; - case "yellow-pane-quit": - yellow.toolbox.setVisible(document.getElementById("yellow-pane-quit-fields"), showFields); - yellow.toolbox.setVisible(document.getElementById("yellow-pane-quit-buttons"), !showFields); - if(paneStatus=="none") - { - document.getElementById("yellow-pane-quit-status").innerHTML = this.getText("QuitStatusNone"); - document.getElementById("yellow-pane-quit-name").value = ""; - } - break; - case "yellow-pane-edit": - document.getElementById("yellow-pane-edit-text").focus(); - if(init) - { - yellow.toolbox.setVisible(document.getElementById("yellow-pane-edit-text"), true); - yellow.toolbox.setVisible(document.getElementById("yellow-pane-edit-preview"), false); - document.getElementById("yellow-pane-edit-toolbar-title").innerHTML = yellow.toolbox.encodeHtml(yellow.page.title); - document.getElementById("yellow-pane-edit-text").value = paneAction=="create" ? yellow.page.rawDataNew : yellow.page.rawDataEdit; - var matches = document.getElementById("yellow-pane-edit-text").value.match(/^(\xEF\xBB\xBF)?\-\-\-[\r\n]+/); - var position = document.getElementById("yellow-pane-edit-text").value.indexOf("\n", matches ? matches[0].length : 0); - document.getElementById("yellow-pane-edit-text").setSelectionRange(position, position); - if(yellow.config.editToolbarButtons!="none") - { - yellow.toolbox.setVisible(document.getElementById("yellow-pane-edit-toolbar-title"), false); - this.updateToolbar(0, "yellow-toolbar-checked"); - } - if(yellow.config.userRestrictions) - { - yellow.toolbox.setVisible(document.getElementById("yellow-pane-edit-send"), false); - document.getElementById("yellow-pane-edit-text").readOnly = true; - } - } - if(!yellow.config.userRestrictions) - { - var key, className; - switch(this.getAction(paneId, paneAction)) - { - case "create": key = "CreateButton"; className = "yellow-toolbar-btn yellow-toolbar-btn-create"; break; - case "edit": key = "EditButton"; className = "yellow-toolbar-btn yellow-toolbar-btn-edit"; break; - case "delete": key = "DeleteButton"; className = "yellow-toolbar-btn yellow-toolbar-btn-delete"; break; - } - if(document.getElementById("yellow-pane-edit-send").className != className) - { - document.getElementById("yellow-pane-edit-send").innerHTML = this.getText(key); - document.getElementById("yellow-pane-edit-send").className = className; - this.resizePane(paneId, paneAction, paneStatus); - } - } - break; - } - this.bindActions(document.getElementById(paneId)); - }, + // Update pane + updatePane: function(paneId, paneAction, paneStatus, init) { + if (yellow.config.debug) console.log("yellow.edit.updatePane id:"+paneId); + var showFields = paneStatus!="next" && paneStatus!="done"; + switch (paneId) { + case "yellow-pane-login": + if (yellow.config.editLoginRestrictions) { + yellow.toolbox.setVisible(document.getElementById("yellow-pane-login-signup"), false); + } + break; + case "yellow-pane-signup": + yellow.toolbox.setVisible(document.getElementById("yellow-pane-signup-fields"), showFields); + yellow.toolbox.setVisible(document.getElementById("yellow-pane-signup-buttons"), !showFields); + break; + case "yellow-pane-forgot": + yellow.toolbox.setVisible(document.getElementById("yellow-pane-forgot-fields"), showFields); + yellow.toolbox.setVisible(document.getElementById("yellow-pane-forgot-buttons"), !showFields); + break; + case "yellow-pane-recover": + yellow.toolbox.setVisible(document.getElementById("yellow-pane-recover-fields"), showFields); + yellow.toolbox.setVisible(document.getElementById("yellow-pane-recover-buttons"), !showFields); + break; + case "yellow-pane-settings": + yellow.toolbox.setVisible(document.getElementById("yellow-pane-settings-fields"), showFields); + yellow.toolbox.setVisible(document.getElementById("yellow-pane-settings-buttons"), !showFields); + if (paneStatus=="none") { + document.getElementById("yellow-pane-settings-status").innerHTML = "<a href=\"#\" data-action=\"version\">"+this.getText("VersionTitle")+"</a>"; + document.getElementById("yellow-pane-settings-name").value = yellow.config.userName; + document.getElementById("yellow-pane-settings-email").value = yellow.config.userEmail; + document.getElementById("yellow-pane-settings-"+yellow.config.userLanguage).checked = true; + } + break; + case "yellow-pane-version": + if (paneStatus=="none" && this.isPlugin("update")) { + document.getElementById("yellow-pane-version-status").innerHTML = this.getText("VersionStatusCheck"); + document.getElementById("yellow-pane-version-output").innerHTML = ""; + setTimeout("yellow.action('send');", 500); + } + if (paneStatus=="updates" && this.isPlugin("update")) { + document.getElementById("yellow-pane-version-status").innerHTML = "<a href=\"#\" data-action=\"update\">"+this.getText("VersionStatusUpdates")+"</a>"; + } + break; + case "yellow-pane-quit": + yellow.toolbox.setVisible(document.getElementById("yellow-pane-quit-fields"), showFields); + yellow.toolbox.setVisible(document.getElementById("yellow-pane-quit-buttons"), !showFields); + if (paneStatus=="none") { + document.getElementById("yellow-pane-quit-status").innerHTML = this.getText("QuitStatusNone"); + document.getElementById("yellow-pane-quit-name").value = ""; + } + break; + case "yellow-pane-edit": + document.getElementById("yellow-pane-edit-text").focus(); + if (init) { + yellow.toolbox.setVisible(document.getElementById("yellow-pane-edit-text"), true); + yellow.toolbox.setVisible(document.getElementById("yellow-pane-edit-preview"), false); + document.getElementById("yellow-pane-edit-toolbar-title").innerHTML = yellow.toolbox.encodeHtml(yellow.page.title); + document.getElementById("yellow-pane-edit-text").value = paneAction=="create" ? yellow.page.rawDataNew : yellow.page.rawDataEdit; + var matches = document.getElementById("yellow-pane-edit-text").value.match(/^(\xEF\xBB\xBF)?\-\-\-[\r\n]+/); + var position = document.getElementById("yellow-pane-edit-text").value.indexOf("\n", matches ? matches[0].length : 0); + document.getElementById("yellow-pane-edit-text").setSelectionRange(position, position); + if (yellow.config.editToolbarButtons!="none") { + yellow.toolbox.setVisible(document.getElementById("yellow-pane-edit-toolbar-title"), false); + this.updateToolbar(0, "yellow-toolbar-checked"); + } + if (yellow.config.userRestrictions) { + yellow.toolbox.setVisible(document.getElementById("yellow-pane-edit-send"), false); + document.getElementById("yellow-pane-edit-text").readOnly = true; + } + } + if (!yellow.config.userRestrictions) { + var key, className; + switch (this.getAction(paneId, paneAction)) { + case "create": key = "CreateButton"; className = "yellow-toolbar-btn yellow-toolbar-btn-create"; break; + case "edit": key = "EditButton"; className = "yellow-toolbar-btn yellow-toolbar-btn-edit"; break; + case "delete": key = "DeleteButton"; className = "yellow-toolbar-btn yellow-toolbar-btn-delete"; break; + } + if (document.getElementById("yellow-pane-edit-send").className != className) { + document.getElementById("yellow-pane-edit-send").innerHTML = this.getText(key); + document.getElementById("yellow-pane-edit-send").className = className; + this.resizePane(paneId, paneAction, paneStatus); + } + } + break; + } + this.bindActions(document.getElementById(paneId)); + }, - // Resize pane - resizePane: function(paneId, paneAction, paneStatus) - { - var elementBar = document.getElementById("yellow-bar-content"); - var paneLeft = yellow.toolbox.getOuterLeft(elementBar); - var paneTop = yellow.toolbox.getOuterTop(elementBar) + yellow.toolbox.getOuterHeight(elementBar) + 10; - var paneWidth = yellow.toolbox.getOuterWidth(elementBar); - var paneHeight = yellow.toolbox.getWindowHeight() - paneTop - Math.min(yellow.toolbox.getOuterHeight(elementBar) + 10, (yellow.toolbox.getWindowWidth()-yellow.toolbox.getOuterWidth(elementBar))/2); - switch(paneId) - { - case "yellow-pane-login": - case "yellow-pane-signup": - case "yellow-pane-forgot": - case "yellow-pane-recover": - case "yellow-pane-settings": - case "yellow-pane-version": - case "yellow-pane-quit": - yellow.toolbox.setOuterLeft(document.getElementById(paneId), paneLeft); - yellow.toolbox.setOuterTop(document.getElementById(paneId), paneTop); - yellow.toolbox.setOuterWidth(document.getElementById(paneId), paneWidth); - break; - case "yellow-pane-edit": - yellow.toolbox.setOuterLeft(document.getElementById("yellow-pane-edit"), paneLeft); - yellow.toolbox.setOuterTop(document.getElementById("yellow-pane-edit"), paneTop); - yellow.toolbox.setOuterHeight(document.getElementById("yellow-pane-edit"), paneHeight); - yellow.toolbox.setOuterWidth(document.getElementById("yellow-pane-edit"), paneWidth); - var elementWidth = yellow.toolbox.getWidth(document.getElementById("yellow-pane-edit")); - yellow.toolbox.setOuterWidth(document.getElementById("yellow-pane-edit-text"), elementWidth); - yellow.toolbox.setOuterWidth(document.getElementById("yellow-pane-edit-preview"), elementWidth); - var buttonsWidth = 0; - var buttonsWidthMax = yellow.toolbox.getOuterWidth(document.getElementById("yellow-pane-edit-toolbar")) - - yellow.toolbox.getOuterWidth(document.getElementById("yellow-pane-edit-toolbar-main")) - 1; - var element = document.getElementById("yellow-pane-edit-toolbar-buttons").firstChild; - for(; element; element=element.nextSibling) - { - element.removeAttribute("style"); - buttonsWidth += yellow.toolbox.getOuterWidth(element); - if(buttonsWidth>buttonsWidthMax) yellow.toolbox.setVisible(element, false); - } - yellow.toolbox.setOuterWidth(document.getElementById("yellow-pane-edit-toolbar-title"), buttonsWidthMax); - var height1 = yellow.toolbox.getHeight(document.getElementById("yellow-pane-edit")); - var height2 = yellow.toolbox.getOuterHeight(document.getElementById("yellow-pane-edit-toolbar")); - yellow.toolbox.setOuterHeight(document.getElementById("yellow-pane-edit-text"), height1 - height2); - yellow.toolbox.setOuterHeight(document.getElementById("yellow-pane-edit-preview"), height1 - height2); - var elementLink = document.getElementById("yellow-pane-"+paneAction+"-link"); - var position = yellow.toolbox.getOuterLeft(elementLink) + yellow.toolbox.getOuterWidth(elementLink)/2; - position -= yellow.toolbox.getOuterLeft(document.getElementById("yellow-pane-edit")) + 1; - yellow.toolbox.setOuterLeft(document.getElementById("yellow-pane-edit-arrow"), position); - break; - case "yellow-pane-user": - yellow.toolbox.setOuterLeft(document.getElementById("yellow-pane-user"), paneLeft + paneWidth - yellow.toolbox.getOuterWidth(document.getElementById("yellow-pane-user"))); - yellow.toolbox.setOuterTop(document.getElementById("yellow-pane-user"), paneTop); - var elementLink = document.getElementById("yellow-pane-user-link"); - var position = yellow.toolbox.getOuterLeft(elementLink) + yellow.toolbox.getOuterWidth(elementLink)/2; - position -= yellow.toolbox.getOuterLeft(document.getElementById("yellow-pane-user")); - yellow.toolbox.setOuterLeft(document.getElementById("yellow-pane-user-arrow"), position); - break; - } - }, - - // Show or hide pane - showPane: function(paneId, paneAction, paneStatus, modal) - { - if(this.paneId!=paneId || this.paneAction!=paneAction) - { - this.hidePane(this.paneId); - if(!document.getElementById(paneId)) this.createPane(paneId, paneAction, paneStatus); - var element = document.getElementById(paneId); - if(!yellow.toolbox.isVisible(element)) - { - if(yellow.config.debug) console.log("yellow.edit.showPane id:"+paneId); - yellow.toolbox.setVisible(element, true); - if(modal) - { - yellow.toolbox.addClass(document.body, "yellow-body-modal-open"); - yellow.toolbox.addValue("meta[name=viewport]", "content", ", maximum-scale=1, user-scalable=0"); - } - this.paneId = paneId; - this.paneAction = paneAction; - this.paneStatus = paneStatus; - this.updatePane(paneId, paneAction, paneStatus, this.paneActionOld!=this.paneAction); - this.resizePane(paneId, paneAction, paneStatus); - } - } else { - this.hidePane(this.paneId, true); - } - }, + // Resize pane + resizePane: function(paneId, paneAction, paneStatus) { + var elementBar = document.getElementById("yellow-bar-content"); + var paneLeft = yellow.toolbox.getOuterLeft(elementBar); + var paneTop = yellow.toolbox.getOuterTop(elementBar) + yellow.toolbox.getOuterHeight(elementBar) + 10; + var paneWidth = yellow.toolbox.getOuterWidth(elementBar); + var paneHeight = yellow.toolbox.getWindowHeight() - paneTop - Math.min(yellow.toolbox.getOuterHeight(elementBar) + 10, (yellow.toolbox.getWindowWidth()-yellow.toolbox.getOuterWidth(elementBar))/2); + switch (paneId) { + case "yellow-pane-login": + case "yellow-pane-signup": + case "yellow-pane-forgot": + case "yellow-pane-recover": + case "yellow-pane-settings": + case "yellow-pane-version": + case "yellow-pane-quit": + yellow.toolbox.setOuterLeft(document.getElementById(paneId), paneLeft); + yellow.toolbox.setOuterTop(document.getElementById(paneId), paneTop); + yellow.toolbox.setOuterWidth(document.getElementById(paneId), paneWidth); + break; + case "yellow-pane-edit": + yellow.toolbox.setOuterLeft(document.getElementById("yellow-pane-edit"), paneLeft); + yellow.toolbox.setOuterTop(document.getElementById("yellow-pane-edit"), paneTop); + yellow.toolbox.setOuterHeight(document.getElementById("yellow-pane-edit"), paneHeight); + yellow.toolbox.setOuterWidth(document.getElementById("yellow-pane-edit"), paneWidth); + var elementWidth = yellow.toolbox.getWidth(document.getElementById("yellow-pane-edit")); + yellow.toolbox.setOuterWidth(document.getElementById("yellow-pane-edit-text"), elementWidth); + yellow.toolbox.setOuterWidth(document.getElementById("yellow-pane-edit-preview"), elementWidth); + var buttonsWidth = 0; + var buttonsWidthMax = yellow.toolbox.getOuterWidth(document.getElementById("yellow-pane-edit-toolbar")) - + yellow.toolbox.getOuterWidth(document.getElementById("yellow-pane-edit-toolbar-main")) - 1; + var element = document.getElementById("yellow-pane-edit-toolbar-buttons").firstChild; + for (; element; element=element.nextSibling) { + element.removeAttribute("style"); + buttonsWidth += yellow.toolbox.getOuterWidth(element); + if (buttonsWidth>buttonsWidthMax) yellow.toolbox.setVisible(element, false); + } + yellow.toolbox.setOuterWidth(document.getElementById("yellow-pane-edit-toolbar-title"), buttonsWidthMax); + var height1 = yellow.toolbox.getHeight(document.getElementById("yellow-pane-edit")); + var height2 = yellow.toolbox.getOuterHeight(document.getElementById("yellow-pane-edit-toolbar")); + yellow.toolbox.setOuterHeight(document.getElementById("yellow-pane-edit-text"), height1 - height2); + yellow.toolbox.setOuterHeight(document.getElementById("yellow-pane-edit-preview"), height1 - height2); + var elementLink = document.getElementById("yellow-pane-"+paneAction+"-link"); + var position = yellow.toolbox.getOuterLeft(elementLink) + yellow.toolbox.getOuterWidth(elementLink)/2; + position -= yellow.toolbox.getOuterLeft(document.getElementById("yellow-pane-edit")) + 1; + yellow.toolbox.setOuterLeft(document.getElementById("yellow-pane-edit-arrow"), position); + break; + case "yellow-pane-user": + yellow.toolbox.setOuterLeft(document.getElementById("yellow-pane-user"), paneLeft + paneWidth - yellow.toolbox.getOuterWidth(document.getElementById("yellow-pane-user"))); + yellow.toolbox.setOuterTop(document.getElementById("yellow-pane-user"), paneTop); + var elementLink = document.getElementById("yellow-pane-user-link"); + var position = yellow.toolbox.getOuterLeft(elementLink) + yellow.toolbox.getOuterWidth(elementLink)/2; + position -= yellow.toolbox.getOuterLeft(document.getElementById("yellow-pane-user")); + yellow.toolbox.setOuterLeft(document.getElementById("yellow-pane-user-arrow"), position); + break; + } + }, + + // Show or hide pane + showPane: function(paneId, paneAction, paneStatus, modal) { + if (this.paneId!=paneId || this.paneAction!=paneAction) { + this.hidePane(this.paneId); + if (!document.getElementById(paneId)) this.createPane(paneId, paneAction, paneStatus); + var element = document.getElementById(paneId); + if (!yellow.toolbox.isVisible(element)) { + if (yellow.config.debug) console.log("yellow.edit.showPane id:"+paneId); + yellow.toolbox.setVisible(element, true); + if (modal) { + yellow.toolbox.addClass(document.body, "yellow-body-modal-open"); + yellow.toolbox.addValue("meta[name=viewport]", "content", ", maximum-scale=1, user-scalable=0"); + } + this.paneId = paneId; + this.paneAction = paneAction; + this.paneStatus = paneStatus; + this.updatePane(paneId, paneAction, paneStatus, this.paneActionOld!=this.paneAction); + this.resizePane(paneId, paneAction, paneStatus); + } + } else { + this.hidePane(this.paneId, true); + } + }, - // Hide pane - hidePane: function(paneId, fadeout) - { - var element = document.getElementById(paneId); - if(yellow.toolbox.isVisible(element)) - { - yellow.toolbox.removeClass(document.body, "yellow-body-modal-open"); - yellow.toolbox.removeValue("meta[name=viewport]", "content", ", maximum-scale=1, user-scalable=0"); - yellow.toolbox.setVisible(element, false, fadeout); - this.paneId = 0; - this.paneActionOld = this.paneAction; - this.paneAction = 0; - this.paneStatus = 0; - } - this.hidePopup(this.popupId); - }, + // Hide pane + hidePane: function(paneId, fadeout) { + var element = document.getElementById(paneId); + if (yellow.toolbox.isVisible(element)) { + yellow.toolbox.removeClass(document.body, "yellow-body-modal-open"); + yellow.toolbox.removeValue("meta[name=viewport]", "content", ", maximum-scale=1, user-scalable=0"); + yellow.toolbox.setVisible(element, false, fadeout); + this.paneId = 0; + this.paneActionOld = this.paneAction; + this.paneAction = 0; + this.paneStatus = 0; + } + this.hidePopup(this.popupId); + }, - // Send pane - sendPane: function(paneId, paneAction, paneStatus, paneArgs) - { - if(yellow.config.debug) console.log("yellow.edit.sendPane id:"+paneId); - var args = { "action":paneAction, "csrftoken":this.getCookie("csrftoken") }; - if(paneId=="yellow-pane-edit") - { - args.action = this.getAction(paneId, paneAction); - args.rawdatasource = yellow.page.rawDataSource; - args.rawdataedit = document.getElementById("yellow-pane-edit-text").value; - args.rawdataendofline = yellow.page.rawDataEndOfLine; - } - if(paneArgs) - { - var tokens = paneArgs.split("/"); - for(var i=0; i<tokens.length; i++) - { - var pair = tokens[i].split(/[:=]/); - if(!pair[0] || !pair[1]) continue; - args[pair[0]] = pair[1]; - } - } - yellow.toolbox.submitForm(args); - }, - - // Process help - processHelp: function() - { - this.hidePane(this.paneId); - window.open(this.getText("HelpUrl", "yellow"), "_self"); - }, - - // Process shortcut - processShortcut: function(e) - { - var shortcut = yellow.toolbox.getEventShortcut(e) - if(shortcut) - { - var tokens = yellow.config.editKeyboardShortcuts.split(","); - for(var i=0; i<tokens.length; i++) - { - var pair = tokens[i].trim().split(" "); - if(shortcut==pair[0] || shortcut.replace("meta+", "ctrl+")==pair[0]) - { - e.stopPropagation(); - e.preventDefault(); - this.processToolbar(pair[1]); - } - } - } - }, - - // Process toolbar - processToolbar: function(status, args) - { - if(yellow.config.debug) console.log("yellow.edit.processToolbar status:"+status); - var elementText = document.getElementById("yellow-pane-edit-text"); - var elementPreview = document.getElementById("yellow-pane-edit-preview"); - if(!yellow.config.userRestrictions && this.paneAction!="delete" && !yellow.toolbox.isVisible(elementPreview)) - { - switch(status) - { - case "h1": yellow.editor.setMarkdown(elementText, "# ", "insert-multiline-block", true); break; - case "h2": yellow.editor.setMarkdown(elementText, "## ", "insert-multiline-block", true); break; - case "h3": yellow.editor.setMarkdown(elementText, "### ", "insert-multiline-block", true); break; - case "paragraph": yellow.editor.setMarkdown(elementText, "", "remove-multiline-block"); - yellow.editor.setMarkdown(elementText, "", "remove-fenced-block"); break; - case "quote": yellow.editor.setMarkdown(elementText, "> ", "insert-multiline-block", true); break; - case "pre": yellow.editor.setMarkdown(elementText, "```\n", "insert-fenced-block", true); break; - case "bold": yellow.editor.setMarkdown(elementText, "**", "insert-inline", true); break; - case "italic": yellow.editor.setMarkdown(elementText, "*", "insert-inline", true); break; - case "strikethrough": yellow.editor.setMarkdown(elementText, "~~", "insert-inline", true); break; - case "code": yellow.editor.setMarkdown(elementText, "`", "insert-autodetect", true); break; - case "ul": yellow.editor.setMarkdown(elementText, "* ", "insert-multiline-block", true); break; - case "ol": yellow.editor.setMarkdown(elementText, "1. ", "insert-multiline-block", true); break; - case "tl": yellow.editor.setMarkdown(elementText, "- [ ] ", "insert-multiline-block", true); break; - case "link": yellow.editor.setMarkdown(elementText, "[link](url)", "insert", false, yellow.editor.getMarkdownLink); break; - case "text": yellow.editor.setMarkdown(elementText, args, "insert"); break; - case "draft": yellow.editor.setMetaData(elementText, "status", "draft", true); break; - case "file": this.showFileDialog(); break; - case "undo": yellow.editor.undo(); break; - case "redo": yellow.editor.redo(); break; - } - } - if(status=="preview") this.showPreview(elementText, elementPreview); - if(status=="save" && !yellow.config.userRestrictions && this.paneAction!="delete") this.action("send"); - if(status=="help") window.open(this.getText("HelpUrl", "yellow"), "_blank"); - if(status=="markdown") window.open(this.getText("MarkdownUrl", "yellow"), "_blank"); - if(status=="format" || status=="heading" || status=="list" || status=="emojiawesome" || status=="fontawesome") - { - this.showPopup("yellow-popup-"+status, status); - } else { - this.hidePopup(this.popupId); - } - }, - - // Update toolbar - updateToolbar: function(status, name) - { - if(status) - { - var element = document.getElementById("yellow-toolbar-"+status); - if(element) yellow.toolbox.addClass(element, name); - } else { - var elements = document.getElementsByClassName(name); - for(var i=0, l=elements.length; i<l; i++) - { - yellow.toolbox.removeClass(elements[i], name); - } - } - }, - - // Create popup - createPopup: function(popupId) - { - if(yellow.config.debug) console.log("yellow.edit.createPopup id:"+popupId); - var elementPopup = document.createElement("div"); - elementPopup.className = "yellow-popup"; - elementPopup.setAttribute("id", popupId); - elementPopup.style.display = "none"; - var elementDiv = document.createElement("div"); - elementDiv.setAttribute("id", popupId+"-content"); - switch(popupId) - { - case "yellow-popup-format": - elementDiv.innerHTML = - "<ul class=\"yellow-dropdown yellow-dropdown-menu\">"+ - "<li><a href=\"#\" id=\"yellow-popup-format-h1\" data-action=\"toolbar\" data-status=\"h1\">"+this.getText("ToolbarH1")+"</a></li>"+ - "<li><a href=\"#\" id=\"yellow-popup-format-h2\" data-action=\"toolbar\" data-status=\"h2\">"+this.getText("ToolbarH2")+"</a></li>"+ - "<li><a href=\"#\" id=\"yellow-popup-format-h3\" data-action=\"toolbar\" data-status=\"h3\">"+this.getText("ToolbarH3")+"</a></li>"+ - "<li><a href=\"#\" id=\"yellow-popup-format-paragraph\" data-action=\"toolbar\" data-status=\"paragraph\">"+this.getText("ToolbarParagraph")+"</a></li>"+ - "<li><a href=\"#\" id=\"yellow-popup-format-pre\" data-action=\"toolbar\" data-status=\"pre\">"+this.getText("ToolbarPre")+"</a></li>"+ - "<li><a href=\"#\" id=\"yellow-popup-format-quote\" data-action=\"toolbar\" data-status=\"quote\">"+this.getText("ToolbarQuote")+"</a></li>"+ - "</ul>"; - break; - case "yellow-popup-heading": - elementDiv.innerHTML = - "<ul class=\"yellow-dropdown yellow-dropdown-menu\">"+ - "<li><a href=\"#\" id=\"yellow-popup-heading-h1\" data-action=\"toolbar\" data-status=\"h1\">"+this.getText("ToolbarH1")+"</a></li>"+ - "<li><a href=\"#\" id=\"yellow-popup-heading-h2\" data-action=\"toolbar\" data-status=\"h2\">"+this.getText("ToolbarH2")+"</a></li>"+ - "<li><a href=\"#\" id=\"yellow-popup-heading-h3\" data-action=\"toolbar\" data-status=\"h3\">"+this.getText("ToolbarH3")+"</a></li>"+ - "</ul>"; - break; - case "yellow-popup-list": - elementDiv.innerHTML = - "<ul class=\"yellow-dropdown yellow-dropdown-menu\">"+ - "<li><a href=\"#\" id=\"yellow-popup-list-ul\" data-action=\"toolbar\" data-status=\"ul\">"+this.getText("ToolbarUl")+"</a></li>"+ - "<li><a href=\"#\" id=\"yellow-popup-list-ol\" data-action=\"toolbar\" data-status=\"ol\">"+this.getText("ToolbarOl")+"</a></li>"+ - "</ul>"; - break; - case "yellow-popup-emojiawesome": - var rawDataEmojis = ""; - if(yellow.config.emojiawesomeToolbarButtons && yellow.config.emojiawesomeToolbarButtons!="none") - { - var tokens = yellow.config.emojiawesomeToolbarButtons.split(" "); - for(var i=0; i<tokens.length; i++) - { - var token = tokens[i].replace(/[\:]/g,""); - var className = token.replace("+1", "plus1").replace("-1", "minus1").replace(/_/g, "-"); - rawDataEmojis += "<li><a href=\"#\" id=\"yellow-popup-list-"+yellow.toolbox.encodeHtml(token)+"\" data-action=\"toolbar\" data-status=\"text\" data-args=\":"+yellow.toolbox.encodeHtml(token)+":\"><i class=\"ea ea-"+yellow.toolbox.encodeHtml(className)+"\"></i></a></li>"; - } - } - elementDiv.innerHTML = "<ul class=\"yellow-dropdown yellow-dropdown-menu\">"+rawDataEmojis+"</ul>"; - break; - case "yellow-popup-fontawesome": - var rawDataIcons = ""; - if(yellow.config.fontawesomeToolbarButtons && yellow.config.fontawesomeToolbarButtons!="none") - { - var tokens = yellow.config.fontawesomeToolbarButtons.split(" "); - for(var i=0; i<tokens.length; i++) - { - var token = tokens[i].replace(/[\:]/g,""); - rawDataIcons += "<li><a href=\"#\" id=\"yellow-popup-list-"+yellow.toolbox.encodeHtml(token)+"\" data-action=\"toolbar\" data-status=\"text\" data-args=\":"+yellow.toolbox.encodeHtml(token)+":\"><i class=\"fa "+yellow.toolbox.encodeHtml(token)+"\"></i></a></li>"; - } - } - elementDiv.innerHTML = "<ul class=\"yellow-dropdown yellow-dropdown-menu\">"+rawDataIcons+"</ul>"; - break; - } - elementPopup.appendChild(elementDiv); - yellow.toolbox.insertAfter(elementPopup, document.getElementsByTagName("body")[0].firstChild); - this.bindActions(elementPopup); - }, - - // Show or hide popup - showPopup: function(popupId, status) - { - if(this.popupId!=popupId) - { - this.hidePopup(this.popupId); - if(!document.getElementById(popupId)) this.createPopup(popupId); - var element = document.getElementById(popupId); - if(yellow.config.debug) console.log("yellow.edit.showPopup id:"+popupId); - yellow.toolbox.setVisible(element, true); - this.popupId = popupId; - this.updateToolbar(status, "yellow-toolbar-selected"); - var elementParent = document.getElementById("yellow-toolbar-"+status); - var popupLeft = yellow.toolbox.getOuterLeft(elementParent); - var popupTop = yellow.toolbox.getOuterTop(elementParent) + yellow.toolbox.getOuterHeight(elementParent) - 1; - yellow.toolbox.setOuterLeft(document.getElementById(popupId), popupLeft); - yellow.toolbox.setOuterTop(document.getElementById(popupId), popupTop); - } else { - this.hidePopup(this.popupId, true); - } - }, - - // Hide popup - hidePopup: function(popupId, fadeout) - { - var element = document.getElementById(popupId); - if(yellow.toolbox.isVisible(element)) - { - yellow.toolbox.setVisible(element, false, fadeout); - this.popupId = 0; - this.updateToolbar(0, "yellow-toolbar-selected"); - } - }, - - // Show or hide preview - showPreview: function(elementText, elementPreview) - { - if(!yellow.toolbox.isVisible(elementPreview)) - { - var thisObject = this; - var formData = new FormData(); - formData.append("action", "preview"); - formData.append("csrftoken", this.getCookie("csrftoken")); - formData.append("rawdataedit", elementText.value); - formData.append("rawdataendofline", yellow.page.rawDataEndOfLine); - var request = new XMLHttpRequest(); - request.open("POST", window.location.pathname, true); - request.onload = function() { if(this.status==200) thisObject.showPreviewDone.call(thisObject, elementText, elementPreview, this.responseText); }; - request.send(formData); - } else { - this.showPreviewDone(elementText, elementPreview, ""); - } - }, - - // Preview done - showPreviewDone: function(elementText, elementPreview, responseText) - { - var showPreview = responseText.length!=0; - yellow.toolbox.setVisible(elementText, !showPreview); - yellow.toolbox.setVisible(elementPreview, showPreview); - if(showPreview) - { - this.updateToolbar("preview", "yellow-toolbar-checked"); - elementPreview.innerHTML = responseText; - dispatchEvent(new Event("load")); - } else { - this.updateToolbar(0, "yellow-toolbar-checked"); - elementText.focus(); - } - }, - - // Show file dialog and trigger upload - showFileDialog: function() - { - var element = document.createElement("input"); - element.setAttribute("id", "yellow-file-dialog"); - element.setAttribute("type", "file"); - element.setAttribute("accept", yellow.config.editUploadExtensions); - element.setAttribute("multiple", "multiple"); - yellow.toolbox.addEvent(element, "change", yellow.onDrop); - element.click(); - }, - - // Upload file - uploadFile: function(elementText, file) - { - var extension = (file.name.lastIndexOf(".")!=-1 ? file.name.substring(file.name.lastIndexOf("."), file.name.length) : "").toLowerCase(); - var extensions = yellow.config.editUploadExtensions.split(/\s*,\s*/); - if(file.size<=yellow.config.serverFileSizeMax && extensions.indexOf(extension)!=-1) - { - var text = this.getText("UploadProgress")+"\u200b"; - yellow.editor.setMarkdown(elementText, text, "insert"); - var thisObject = this; - var formData = new FormData(); - formData.append("action", "upload"); - formData.append("csrftoken", this.getCookie("csrftoken")); - formData.append("file", file); - var request = new XMLHttpRequest(); - request.open("POST", window.location.pathname, true); - request.onload = function() { if(this.status==200) { thisObject.uploadFileDone.call(thisObject, elementText, this.responseText); } else { thisObject.uploadFileError.call(thisObject, elementText, this.responseText); } }; - request.send(formData); - } - }, - - // Upload done - uploadFileDone: function(elementText, responseText) - { - var result = JSON.parse(responseText); - if(result) - { - var textOld = this.getText("UploadProgress")+"\u200b"; - var textNew; - if(result.location.substring(0, yellow.config.imageLocation.length)==yellow.config.imageLocation) - { - textNew = "[image "+result.location.substring(yellow.config.imageLocation.length)+"]"; - } else { - textNew = "[link]("+result.location+")"; - } - yellow.editor.replace(elementText, textOld, textNew); - } - }, - - // Upload error - uploadFileError: function(elementText, responseText) - { - var result = JSON.parse(responseText); - if(result) - { - var textOld = this.getText("UploadProgress")+"\u200b"; - var textNew = "["+result.error+"]"; - yellow.editor.replace(elementText, textOld, textNew); - } - }, + // Send pane + sendPane: function(paneId, paneAction, paneStatus, paneArgs) { + if (yellow.config.debug) console.log("yellow.edit.sendPane id:"+paneId); + var args = { "action":paneAction, "csrftoken":this.getCookie("csrftoken") }; + if (paneId=="yellow-pane-edit") { + args.action = this.getAction(paneId, paneAction); + args.rawdatasource = yellow.page.rawDataSource; + args.rawdataedit = document.getElementById("yellow-pane-edit-text").value; + args.rawdataendofline = yellow.page.rawDataEndOfLine; + } + if (paneArgs) { + var tokens = paneArgs.split("/"); + for (var i=0; i<tokens.length; i++) { + var pair = tokens[i].split(/[:=]/); + if (!pair[0] || !pair[1]) continue; + args[pair[0]] = pair[1]; + } + } + yellow.toolbox.submitForm(args); + }, + + // Process help + processHelp: function() { + this.hidePane(this.paneId); + window.open(this.getText("HelpUrl", "yellow"), "_self"); + }, + + // Process shortcut + processShortcut: function(e) { + var shortcut = yellow.toolbox.getEventShortcut(e); + if (shortcut) { + var tokens = yellow.config.editKeyboardShortcuts.split(","); + for (var i=0; i<tokens.length; i++) { + var pair = tokens[i].trim().split(" "); + if (shortcut==pair[0] || shortcut.replace("meta+", "ctrl+")==pair[0]) { + e.stopPropagation(); + e.preventDefault(); + this.processToolbar(pair[1]); + } + } + } + }, + + // Process toolbar + processToolbar: function(status, args) { + if (yellow.config.debug) console.log("yellow.edit.processToolbar status:"+status); + var elementText = document.getElementById("yellow-pane-edit-text"); + var elementPreview = document.getElementById("yellow-pane-edit-preview"); + if (!yellow.config.userRestrictions && this.paneAction!="delete" && !yellow.toolbox.isVisible(elementPreview)) { + switch (status) { + case "h1": yellow.editor.setMarkdown(elementText, "# ", "insert-multiline-block", true); break; + case "h2": yellow.editor.setMarkdown(elementText, "## ", "insert-multiline-block", true); break; + case "h3": yellow.editor.setMarkdown(elementText, "### ", "insert-multiline-block", true); break; + case "paragraph": yellow.editor.setMarkdown(elementText, "", "remove-multiline-block"); + yellow.editor.setMarkdown(elementText, "", "remove-fenced-block"); break; + case "quote": yellow.editor.setMarkdown(elementText, "> ", "insert-multiline-block", true); break; + case "pre": yellow.editor.setMarkdown(elementText, "```\n", "insert-fenced-block", true); break; + case "bold": yellow.editor.setMarkdown(elementText, "**", "insert-inline", true); break; + case "italic": yellow.editor.setMarkdown(elementText, "*", "insert-inline", true); break; + case "strikethrough": yellow.editor.setMarkdown(elementText, "~~", "insert-inline", true); break; + case "code": yellow.editor.setMarkdown(elementText, "`", "insert-autodetect", true); break; + case "ul": yellow.editor.setMarkdown(elementText, "* ", "insert-multiline-block", true); break; + case "ol": yellow.editor.setMarkdown(elementText, "1. ", "insert-multiline-block", true); break; + case "tl": yellow.editor.setMarkdown(elementText, "- [ ] ", "insert-multiline-block", true); break; + case "link": yellow.editor.setMarkdown(elementText, "[link](url)", "insert", false, yellow.editor.getMarkdownLink); break; + case "text": yellow.editor.setMarkdown(elementText, args, "insert"); break; + case "draft": yellow.editor.setMetaData(elementText, "status", "draft", true); break; + case "file": this.showFileDialog(); break; + case "undo": yellow.editor.undo(); break; + case "redo": yellow.editor.redo(); break; + } + } + if (status=="preview") this.showPreview(elementText, elementPreview); + if (status=="save" && !yellow.config.userRestrictions && this.paneAction!="delete") this.action("send"); + if (status=="help") window.open(this.getText("HelpUrl", "yellow"), "_blank"); + if (status=="markdown") window.open(this.getText("MarkdownUrl", "yellow"), "_blank"); + if (status=="format" || status=="heading" || status=="list" || status=="emojiawesome" || status=="fontawesome") { + this.showPopup("yellow-popup-"+status, status); + } else { + this.hidePopup(this.popupId); + } + }, + + // Update toolbar + updateToolbar: function(status, name) { + if (status) { + var element = document.getElementById("yellow-toolbar-"+status); + if (element) yellow.toolbox.addClass(element, name); + } else { + var elements = document.getElementsByClassName(name); + for (var i=0, l=elements.length; i<l; i++) { + yellow.toolbox.removeClass(elements[i], name); + } + } + }, + + // Create popup + createPopup: function(popupId) { + if (yellow.config.debug) console.log("yellow.edit.createPopup id:"+popupId); + var elementPopup = document.createElement("div"); + elementPopup.className = "yellow-popup"; + elementPopup.setAttribute("id", popupId); + elementPopup.style.display = "none"; + var elementDiv = document.createElement("div"); + elementDiv.setAttribute("id", popupId+"-content"); + switch (popupId) { + case "yellow-popup-format": + elementDiv.innerHTML = + "<ul class=\"yellow-dropdown yellow-dropdown-menu\">"+ + "<li><a href=\"#\" id=\"yellow-popup-format-h1\" data-action=\"toolbar\" data-status=\"h1\">"+this.getText("ToolbarH1")+"</a></li>"+ + "<li><a href=\"#\" id=\"yellow-popup-format-h2\" data-action=\"toolbar\" data-status=\"h2\">"+this.getText("ToolbarH2")+"</a></li>"+ + "<li><a href=\"#\" id=\"yellow-popup-format-h3\" data-action=\"toolbar\" data-status=\"h3\">"+this.getText("ToolbarH3")+"</a></li>"+ + "<li><a href=\"#\" id=\"yellow-popup-format-paragraph\" data-action=\"toolbar\" data-status=\"paragraph\">"+this.getText("ToolbarParagraph")+"</a></li>"+ + "<li><a href=\"#\" id=\"yellow-popup-format-pre\" data-action=\"toolbar\" data-status=\"pre\">"+this.getText("ToolbarPre")+"</a></li>"+ + "<li><a href=\"#\" id=\"yellow-popup-format-quote\" data-action=\"toolbar\" data-status=\"quote\">"+this.getText("ToolbarQuote")+"</a></li>"+ + "</ul>"; + break; + case "yellow-popup-heading": + elementDiv.innerHTML = + "<ul class=\"yellow-dropdown yellow-dropdown-menu\">"+ + "<li><a href=\"#\" id=\"yellow-popup-heading-h1\" data-action=\"toolbar\" data-status=\"h1\">"+this.getText("ToolbarH1")+"</a></li>"+ + "<li><a href=\"#\" id=\"yellow-popup-heading-h2\" data-action=\"toolbar\" data-status=\"h2\">"+this.getText("ToolbarH2")+"</a></li>"+ + "<li><a href=\"#\" id=\"yellow-popup-heading-h3\" data-action=\"toolbar\" data-status=\"h3\">"+this.getText("ToolbarH3")+"</a></li>"+ + "</ul>"; + break; + case "yellow-popup-list": + elementDiv.innerHTML = + "<ul class=\"yellow-dropdown yellow-dropdown-menu\">"+ + "<li><a href=\"#\" id=\"yellow-popup-list-ul\" data-action=\"toolbar\" data-status=\"ul\">"+this.getText("ToolbarUl")+"</a></li>"+ + "<li><a href=\"#\" id=\"yellow-popup-list-ol\" data-action=\"toolbar\" data-status=\"ol\">"+this.getText("ToolbarOl")+"</a></li>"+ + "</ul>"; + break; + case "yellow-popup-emojiawesome": + var rawDataEmojis = ""; + if (yellow.config.emojiawesomeToolbarButtons && yellow.config.emojiawesomeToolbarButtons!="none") { + var tokens = yellow.config.emojiawesomeToolbarButtons.split(" "); + for (var i=0; i<tokens.length; i++) { + var token = tokens[i].replace(/[\:]/g,""); + var className = token.replace("+1", "plus1").replace("-1", "minus1").replace(/_/g, "-"); + rawDataEmojis += "<li><a href=\"#\" id=\"yellow-popup-list-"+yellow.toolbox.encodeHtml(token)+"\" data-action=\"toolbar\" data-status=\"text\" data-args=\":"+yellow.toolbox.encodeHtml(token)+":\"><i class=\"ea ea-"+yellow.toolbox.encodeHtml(className)+"\"></i></a></li>"; + } + } + elementDiv.innerHTML = "<ul class=\"yellow-dropdown yellow-dropdown-menu\">"+rawDataEmojis+"</ul>"; + break; + case "yellow-popup-fontawesome": + var rawDataIcons = ""; + if (yellow.config.fontawesomeToolbarButtons && yellow.config.fontawesomeToolbarButtons!="none") { + var tokens = yellow.config.fontawesomeToolbarButtons.split(" "); + for (var i=0; i<tokens.length; i++) { + var token = tokens[i].replace(/[\:]/g,""); + rawDataIcons += "<li><a href=\"#\" id=\"yellow-popup-list-"+yellow.toolbox.encodeHtml(token)+"\" data-action=\"toolbar\" data-status=\"text\" data-args=\":"+yellow.toolbox.encodeHtml(token)+":\"><i class=\"fa "+yellow.toolbox.encodeHtml(token)+"\"></i></a></li>"; + } + } + elementDiv.innerHTML = "<ul class=\"yellow-dropdown yellow-dropdown-menu\">"+rawDataIcons+"</ul>"; + break; + } + elementPopup.appendChild(elementDiv); + yellow.toolbox.insertAfter(elementPopup, document.getElementsByTagName("body")[0].firstChild); + this.bindActions(elementPopup); + }, + + // Show or hide popup + showPopup: function(popupId, status) { + if (this.popupId!=popupId) { + this.hidePopup(this.popupId); + if (!document.getElementById(popupId)) this.createPopup(popupId); + var element = document.getElementById(popupId); + if (yellow.config.debug) console.log("yellow.edit.showPopup id:"+popupId); + yellow.toolbox.setVisible(element, true); + this.popupId = popupId; + this.updateToolbar(status, "yellow-toolbar-selected"); + var elementParent = document.getElementById("yellow-toolbar-"+status); + var popupLeft = yellow.toolbox.getOuterLeft(elementParent); + var popupTop = yellow.toolbox.getOuterTop(elementParent) + yellow.toolbox.getOuterHeight(elementParent) - 1; + yellow.toolbox.setOuterLeft(document.getElementById(popupId), popupLeft); + yellow.toolbox.setOuterTop(document.getElementById(popupId), popupTop); + } else { + this.hidePopup(this.popupId, true); + } + }, + + // Hide popup + hidePopup: function(popupId, fadeout) { + var element = document.getElementById(popupId); + if (yellow.toolbox.isVisible(element)) { + yellow.toolbox.setVisible(element, false, fadeout); + this.popupId = 0; + this.updateToolbar(0, "yellow-toolbar-selected"); + } + }, + + // Show or hide preview + showPreview: function(elementText, elementPreview) { + if (!yellow.toolbox.isVisible(elementPreview)) { + var thisObject = this; + var formData = new FormData(); + formData.append("action", "preview"); + formData.append("csrftoken", this.getCookie("csrftoken")); + formData.append("rawdataedit", elementText.value); + formData.append("rawdataendofline", yellow.page.rawDataEndOfLine); + var request = new XMLHttpRequest(); + request.open("POST", window.location.pathname, true); + request.onload = function() { if (this.status==200) thisObject.showPreviewDone.call(thisObject, elementText, elementPreview, this.responseText); }; + request.send(formData); + } else { + this.showPreviewDone(elementText, elementPreview, ""); + } + }, + + // Preview done + showPreviewDone: function(elementText, elementPreview, responseText) { + var showPreview = responseText.length!=0; + yellow.toolbox.setVisible(elementText, !showPreview); + yellow.toolbox.setVisible(elementPreview, showPreview); + if (showPreview) { + this.updateToolbar("preview", "yellow-toolbar-checked"); + elementPreview.innerHTML = responseText; + dispatchEvent(new Event("load")); + } else { + this.updateToolbar(0, "yellow-toolbar-checked"); + elementText.focus(); + } + }, + + // Show file dialog and trigger upload + showFileDialog: function() { + var element = document.createElement("input"); + element.setAttribute("id", "yellow-file-dialog"); + element.setAttribute("type", "file"); + element.setAttribute("accept", yellow.config.editUploadExtensions); + element.setAttribute("multiple", "multiple"); + yellow.toolbox.addEvent(element, "change", yellow.onDrop); + element.click(); + }, + + // Upload file + uploadFile: function(elementText, file) { + var extension = (file.name.lastIndexOf(".")!=-1 ? file.name.substring(file.name.lastIndexOf("."), file.name.length) : "").toLowerCase(); + var extensions = yellow.config.editUploadExtensions.split(/\s*,\s*/); + if (file.size<=yellow.config.serverFileSizeMax && extensions.indexOf(extension)!=-1) { + var text = this.getText("UploadProgress")+"\u200b"; + yellow.editor.setMarkdown(elementText, text, "insert"); + var thisObject = this; + var formData = new FormData(); + formData.append("action", "upload"); + formData.append("csrftoken", this.getCookie("csrftoken")); + formData.append("file", file); + var request = new XMLHttpRequest(); + request.open("POST", window.location.pathname, true); + request.onload = function() { if (this.status==200) { thisObject.uploadFileDone.call(thisObject, elementText, this.responseText); } else { thisObject.uploadFileError.call(thisObject, elementText, this.responseText); } }; + request.send(formData); + } + }, + + // Upload done + uploadFileDone: function(elementText, responseText) { + var result = JSON.parse(responseText); + if (result) { + var textOld = this.getText("UploadProgress")+"\u200b"; + var textNew; + if (result.location.substring(0, yellow.config.imageLocation.length)==yellow.config.imageLocation) { + textNew = "[image "+result.location.substring(yellow.config.imageLocation.length)+"]"; + } else { + textNew = "[link]("+result.location+")"; + } + yellow.editor.replace(elementText, textOld, textNew); + } + }, + + // Upload error + uploadFileError: function(elementText, responseText) { + var result = JSON.parse(responseText); + if (result) { + var textOld = this.getText("UploadProgress")+"\u200b"; + var textNew = "["+result.error+"]"; + yellow.editor.replace(elementText, textOld, textNew); + } + }, - // Bind actions to links - bindActions: function(element) - { - var elements = element.getElementsByTagName("a"); - for(var i=0, l=elements.length; i<l; i++) - { - if(elements[i].getAttribute("data-action")) elements[i].onclick = yellow.onClickAction; - if(elements[i].getAttribute("data-action")=="toolbar") elements[i].onmousedown = function(e) { e.preventDefault() }; - } - }, - - // Return action - getAction: function(paneId, paneAction) - { - var action = ""; - if(paneId=="yellow-pane-edit") - { - switch(paneAction) - { - case "create": action = "create"; break; - case "edit": action = document.getElementById("yellow-pane-edit-text").value.length!=0 ? "edit" : "delete"; break; - case "delete": action = "delete"; break; - } - if(yellow.page.statusCode==434 && paneAction!="delete") action = "create"; - } - return action; - }, - - // Return request string - getRequest: function(key, prefix) - { - if(!prefix) prefix = "request"; - key = prefix + yellow.toolbox.toUpperFirst(key); - return (key in yellow.page) ? yellow.page[key] : ""; - }, + // Bind actions to links + bindActions: function(element) { + var elements = element.getElementsByTagName("a"); + for (var i=0, l=elements.length; i<l; i++) { + if (elements[i].getAttribute("data-action")) elements[i].onclick = yellow.onClickAction; + if (elements[i].getAttribute("data-action")=="toolbar") elements[i].onmousedown = function(e) { e.preventDefault(); }; + } + }, + + // Return action + getAction: function(paneId, paneAction) { + var action = ""; + if (paneId=="yellow-pane-edit") { + switch (paneAction) { + case "create": action = "create"; break; + case "edit": action = document.getElementById("yellow-pane-edit-text").value.length!=0 ? "edit" : "delete"; break; + case "delete": action = "delete"; break; + } + if (yellow.page.statusCode==434 && paneAction!="delete") action = "create"; + } + return action; + }, + + // Return request string + getRequest: function(key, prefix) { + if (!prefix) prefix = "request"; + key = prefix + yellow.toolbox.toUpperFirst(key); + return (key in yellow.page) ? yellow.page[key] : ""; + }, - // Return text string - 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 text string + 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 cookie string - getCookie: function(name) - { - return yellow.toolbox.getCookie(name); - }, + // Return cookie string + getCookie: function(name) { + return yellow.toolbox.getCookie(name); + }, - // Check if plugin exists - isPlugin: function(name) - { - return name in yellow.config.serverPlugins; - } + // Check if plugin exists + isPlugin: function(name) { + return name in yellow.config.serverPlugins; + } }; -yellow.editor = -{ - // Set Markdown formatting - setMarkdown: function(element, prefix, type, toggle, callback) - { - var information = this.getMarkdownInformation(element, prefix, type); - var selectionStart = (information.type.indexOf("block")!=-1) ? information.top : information.start; - var selectionEnd = (information.type.indexOf("block")!=-1) ? information.bottom : information.end; - if(information.found && toggle) information.type = information.type.replace("insert", "remove"); - if(information.type=="remove-fenced-block" || information.type=="remove-inline") - { - selectionStart -= information.prefix.length; selectionEnd += information.prefix.length; - } - var text = information.text; - var textSelectionBefore = text.substring(0, selectionStart); - var textSelection = text.substring(selectionStart, selectionEnd); - var textSelectionAfter = text.substring(selectionEnd, text.length); - var textSelectionNew, selectionStartNew, selectionEndNew; - switch(information.type) - { - case "insert-multiline-block": - textSelectionNew = this.getMarkdownMultilineBlock(textSelection, information); - selectionStartNew = information.start + this.getMarkdownDifference(textSelection, textSelectionNew, true); - selectionEndNew = information.end + this.getMarkdownDifference(textSelection, textSelectionNew); - if(information.start==information.top && information.start!=information.end) selectionStartNew = information.top; - if(information.end==information.top && information.start!=information.end) selectionEndNew = information.top; - break; - case "remove-multiline-block": - textSelectionNew = this.getMarkdownMultilineBlock(textSelection, information); - selectionStartNew = information.start + this.getMarkdownDifference(textSelection, textSelectionNew, true); - selectionEndNew = information.end + this.getMarkdownDifference(textSelection, textSelectionNew); - if(selectionStartNew<=information.top) selectionStartNew = information.top; - if(selectionEndNew<=information.top) selectionEndNew = information.top; - break; - case "insert-fenced-block": - textSelectionNew = this.getMarkdownFencedBlock(textSelection, information); - selectionStartNew = information.start + information.prefix.length; - selectionEndNew = information.end + this.getMarkdownDifference(textSelection, textSelectionNew) - information.prefix.length; - break; - case "remove-fenced-block": - textSelectionNew = this.getMarkdownFencedBlock(textSelection, information); - selectionStartNew = information.start - information.prefix.length; - selectionEndNew = information.end + this.getMarkdownDifference(textSelection, textSelectionNew) + information.prefix.length; - break; - case "insert-inline": - textSelectionNew = information.prefix + textSelection + information.prefix; - selectionStartNew = information.start + information.prefix.length; - selectionEndNew = information.end + information.prefix.length; - break; - case "remove-inline": - textSelectionNew = text.substring(information.start, information.end); - selectionStartNew = information.start - information.prefix.length; - selectionEndNew = information.end - information.prefix.length; - break; - case "insert": - textSelectionNew = callback ? callback(textSelection, information) : information.prefix; - selectionStartNew = information.start + textSelectionNew.length; - selectionEndNew = selectionStartNew; - } - if(textSelection!=textSelectionNew || selectionStart!=selectionStartNew || selectionEnd!=selectionEndNew) - { - element.focus(); - element.setSelectionRange(selectionStart, selectionEnd); - document.execCommand("insertText", false, textSelectionNew); - element.value = textSelectionBefore + textSelectionNew + textSelectionAfter; - element.setSelectionRange(selectionStartNew, selectionEndNew); - } - if(yellow.config.debug) console.log("yellow.editor.setMarkdown type:"+information.type); - }, - - // Return Markdown formatting information - getMarkdownInformation: function(element, prefix, type) - { - var text = element.value; - var start = element.selectionStart; - var end = element.selectionEnd; - var top = start, bottom = end; - while(text.charAt(top-1)!="\n" && top>0) top--; - if(bottom==top && bottom<text.length) bottom++; - while(text.charAt(bottom-1)!="\n" && bottom<text.length) bottom++; - if(type=="insert-autodetect") - { - if(text.substring(start, end).indexOf("\n")!=-1) - { - type = "insert-fenced-block"; prefix = "```\n"; - } else { - type = "insert-inline"; prefix = "`"; - } - } - var found = false; - if(type.indexOf("multiline-block")!=-1) - { - if(text.substring(top, top+prefix.length)==prefix) found = true; - } else if(type.indexOf("fenced-block")!=-1) { - if(text.substring(top-prefix.length, top)==prefix && text.substring(bottom, bottom+prefix.length)==prefix) - { - found = true; - } - } else { - if(text.substring(start-prefix.length, start)==prefix && text.substring(end, end+prefix.length)==prefix) - { - if(prefix=="*") - { - var lettersBefore = 0, lettersAfter = 0; - for(var index=start-1; text.charAt(index)=="*"; index--) lettersBefore++; - for(var index=end; text.charAt(index)=="*"; index++) lettersAfter++; - found = lettersBefore!=2 && lettersAfter!=2; - } else { - found = true; - } - } - } - return { "text":text, "prefix":prefix, "type":type, "start":start, "end":end, "top":top, "bottom":bottom, "found":found }; - }, - - // Return Markdown length difference - getMarkdownDifference: function(textSelection, textSelectionNew, firstTextLine) - { - var textSelectionLength, textSelectionLengthNew; - if(firstTextLine) - { - var position = textSelection.indexOf("\n"); - var positionNew = textSelectionNew.indexOf("\n"); - textSelectionLength = position!=-1 ? position+1 : textSelection.length+1; - textSelectionLengthNew = positionNew!=-1 ? positionNew+1 : textSelectionNew.length+1; - } else { - var position = textSelection.indexOf("\n"); - var positionNew = textSelectionNew.indexOf("\n"); - textSelectionLength = position!=-1 ? textSelection.length : textSelection.length+1; - textSelectionLengthNew = positionNew!=-1 ? textSelectionNew.length : textSelectionNew.length+1; - } - return textSelectionLengthNew - textSelectionLength; - }, - - // Return Markdown for multiline block - getMarkdownMultilineBlock: function(textSelection, information) - { - var textSelectionNew = ""; - var lines = yellow.toolbox.getTextLines(textSelection); - for(var i=0; i<lines.length; i++) - { - var matches = lines[i].match(/^(\s*[\#\*\-\>\s]+)?(\s+\[.\]|\s*\d+\.)?[ \t]+/); - if(matches) - { - textSelectionNew += lines[i].substring(matches[0].length); - } else { - textSelectionNew += lines[i]; - } - } - textSelection = textSelectionNew; - if(information.type.indexOf("remove")==-1) - { - textSelectionNew = ""; - var linePrefix = information.prefix; - lines = yellow.toolbox.getTextLines(textSelection.length!=0 ? textSelection : "\n"); - for(var i=0; i<lines.length; i++) - { - textSelectionNew += linePrefix+lines[i]; - if(information.prefix=="1. ") - { - var matches = linePrefix.match(/^(\d+)\.\s/); - if(matches) linePrefix = (parseInt(matches[1])+1)+". "; - } - } - textSelection = textSelectionNew; - } - return textSelection; - }, - - // Return Markdown for fenced block - getMarkdownFencedBlock: function(textSelection, information) - { - var textSelectionNew = ""; - var lines = yellow.toolbox.getTextLines(textSelection); - for(var i=0; i<lines.length; i++) - { - var matches = lines[i].match(/^```/); - if(!matches) textSelectionNew += lines[i]; - } - textSelection = textSelectionNew; - if(information.type.indexOf("remove")==-1) - { - if(textSelection.length==0) textSelection = "\n"; - textSelection = information.prefix + textSelection + information.prefix; - } - return textSelection; - }, - - // Return Markdown for link - getMarkdownLink: function(textSelection, information) - { - return textSelection.length!=0 ? information.prefix.replace("link", textSelection) : information.prefix; - }, - - // Set meta data - setMetaData: function(element, key, value, toggle) - { - var information = this.getMetaDataInformation(element, key); - if(information.bottom!=0) - { - var selectionStart = information.found ? information.start : information.bottom; - var selectionEnd = information.found ? information.end : information.bottom; - var text = information.text; - var textSelectionBefore = text.substring(0, selectionStart); - var textSelection = text.substring(selectionStart, selectionEnd); - var textSelectionAfter = text.substring(selectionEnd, text.length); - var textSelectionNew = yellow.toolbox.toUpperFirst(key)+": "+value+"\n"; - if(information.found && information.value==value && toggle) textSelectionNew = ""; - var selectionStartNew = selectionStart; - var selectionEndNew = selectionStart + textSelectionNew.trim().length; - element.focus(); - element.setSelectionRange(selectionStart, selectionEnd); - document.execCommand("insertText", false, textSelectionNew); - element.value = textSelectionBefore + textSelectionNew + textSelectionAfter; - element.setSelectionRange(selectionStartNew, selectionEndNew); - element.scrollTop = 0; - if(yellow.config.debug) console.log("yellow.editor.setMetaData key:"+key); - } - }, - - // Return meta data information - getMetaDataInformation: function(element, key) - { - var text = element.value; - var value = ""; - var start = 0, end = 0, top = 0, bottom = 0; - var found = false; - var parts = text.match(/^(\xEF\xBB\xBF)?(\-\-\-[\r\n]+)([\s\S]+?)\-\-\-[\r\n]+/); - if(parts) - { - key = yellow.toolbox.toLowerFirst(key); - start = end = top = ((parts[1] ? parts[1] : "")+parts[2]).length; - bottom = ((parts[1] ? parts[1] : "")+parts[2]+parts[3]).length; - var lines = yellow.toolbox.getTextLines(parts[3]); - for(var i=0; i<lines.length; i++) - { - var matches = lines[i].match(/^\s*(.*?)\s*:\s*(.*?)\s*$/); - if(matches && yellow.toolbox.toLowerFirst(matches[1])==key && matches[2].length!=0) - { - value = matches[2]; - end = start + lines[i].length; - found = true; - break; - } - start = end = start + lines[i].length; - } - } - return { "text":text, "value":value, "start":start, "end":end, "top":top, "bottom":bottom, "found":found }; - }, - - // Replace text - replace: function(element, textOld, textNew) - { - var text = element.value; - var selectionStart = element.selectionStart; - var selectionEnd = element.selectionEnd; - var selectionStartFound = text.indexOf(textOld); - var selectionEndFound = selectionStartFound + textOld.length; - if(selectionStartFound!=-1) - { - var selectionStartNew = selectionStart<selectionStartFound ? selectionStart : selectionStart+textNew.length-textOld.length; - var selectionEndNew = selectionEnd<selectionEndFound ? selectionEnd : selectionEnd+textNew.length-textOld.length; - var textBefore = text.substring(0, selectionStartFound); - var textAfter = text.substring(selectionEndFound, text.length); - if(textOld!=textNew) - { - element.focus(); - element.setSelectionRange(selectionStartFound, selectionEndFound); - document.execCommand("insertText", false, textNew); - element.value = textBefore + textNew + textAfter; - element.setSelectionRange(selectionStartNew, selectionEndNew); - } - } - }, - - // Undo changes - undo: function() - { - document.execCommand("undo"); - }, +yellow.editor = { - // Redo changes - redo: function() - { - document.execCommand("redo"); - } + // Set Markdown formatting + setMarkdown: function(element, prefix, type, toggle, callback) { + var information = this.getMarkdownInformation(element, prefix, type); + var selectionStart = (information.type.indexOf("block")!=-1) ? information.top : information.start; + var selectionEnd = (information.type.indexOf("block")!=-1) ? information.bottom : information.end; + if (information.found && toggle) information.type = information.type.replace("insert", "remove"); + if (information.type=="remove-fenced-block" || information.type=="remove-inline") { + selectionStart -= information.prefix.length; selectionEnd += information.prefix.length; + } + var text = information.text; + var textSelectionBefore = text.substring(0, selectionStart); + var textSelection = text.substring(selectionStart, selectionEnd); + var textSelectionAfter = text.substring(selectionEnd, text.length); + var textSelectionNew, selectionStartNew, selectionEndNew; + switch (information.type) { + case "insert-multiline-block": + textSelectionNew = this.getMarkdownMultilineBlock(textSelection, information); + selectionStartNew = information.start + this.getMarkdownDifference(textSelection, textSelectionNew, true); + selectionEndNew = information.end + this.getMarkdownDifference(textSelection, textSelectionNew); + if (information.start==information.top && information.start!=information.end) selectionStartNew = information.top; + if (information.end==information.top && information.start!=information.end) selectionEndNew = information.top; + break; + case "remove-multiline-block": + textSelectionNew = this.getMarkdownMultilineBlock(textSelection, information); + selectionStartNew = information.start + this.getMarkdownDifference(textSelection, textSelectionNew, true); + selectionEndNew = information.end + this.getMarkdownDifference(textSelection, textSelectionNew); + if (selectionStartNew<=information.top) selectionStartNew = information.top; + if (selectionEndNew<=information.top) selectionEndNew = information.top; + break; + case "insert-fenced-block": + textSelectionNew = this.getMarkdownFencedBlock(textSelection, information); + selectionStartNew = information.start + information.prefix.length; + selectionEndNew = information.end + this.getMarkdownDifference(textSelection, textSelectionNew) - information.prefix.length; + break; + case "remove-fenced-block": + textSelectionNew = this.getMarkdownFencedBlock(textSelection, information); + selectionStartNew = information.start - information.prefix.length; + selectionEndNew = information.end + this.getMarkdownDifference(textSelection, textSelectionNew) + information.prefix.length; + break; + case "insert-inline": + textSelectionNew = information.prefix + textSelection + information.prefix; + selectionStartNew = information.start + information.prefix.length; + selectionEndNew = information.end + information.prefix.length; + break; + case "remove-inline": + textSelectionNew = text.substring(information.start, information.end); + selectionStartNew = information.start - information.prefix.length; + selectionEndNew = information.end - information.prefix.length; + break; + case "insert": + textSelectionNew = callback ? callback(textSelection, information) : information.prefix; + selectionStartNew = information.start + textSelectionNew.length; + selectionEndNew = selectionStartNew; + } + if (textSelection!=textSelectionNew || selectionStart!=selectionStartNew || selectionEnd!=selectionEndNew) { + element.focus(); + element.setSelectionRange(selectionStart, selectionEnd); + document.execCommand("insertText", false, textSelectionNew); + element.value = textSelectionBefore + textSelectionNew + textSelectionAfter; + element.setSelectionRange(selectionStartNew, selectionEndNew); + } + if (yellow.config.debug) console.log("yellow.editor.setMarkdown type:"+information.type); + }, + + // Return Markdown formatting information + getMarkdownInformation: function(element, prefix, type) { + var text = element.value; + var start = element.selectionStart; + var end = element.selectionEnd; + var top = start, bottom = end; + while (text.charAt(top-1)!="\n" && top>0) top--; + if (bottom==top && bottom<text.length) bottom++; + while (text.charAt(bottom-1)!="\n" && bottom<text.length) bottom++; + if (type=="insert-autodetect") { + if (text.substring(start, end).indexOf("\n")!=-1) { + type = "insert-fenced-block"; prefix = "```\n"; + } else { + type = "insert-inline"; prefix = "`"; + } + } + var found = false; + if (type.indexOf("multiline-block")!=-1) { + if (text.substring(top, top+prefix.length)==prefix) found = true; + } else if (type.indexOf("fenced-block")!=-1) { + if (text.substring(top-prefix.length, top)==prefix && text.substring(bottom, bottom+prefix.length)==prefix) { + found = true; + } + } else { + if (text.substring(start-prefix.length, start)==prefix && text.substring(end, end+prefix.length)==prefix) { + if (prefix=="*") { + var lettersBefore = 0, lettersAfter = 0; + for (var index=start-1; text.charAt(index)=="*"; index--) lettersBefore++; + for (var index=end; text.charAt(index)=="*"; index++) lettersAfter++; + found = lettersBefore!=2 && lettersAfter!=2; + } else { + found = true; + } + } + } + return { "text":text, "prefix":prefix, "type":type, "start":start, "end":end, "top":top, "bottom":bottom, "found":found }; + }, + + // Return Markdown length difference + getMarkdownDifference: function(textSelection, textSelectionNew, firstTextLine) { + var textSelectionLength, textSelectionLengthNew; + if (firstTextLine) { + var position = textSelection.indexOf("\n"); + var positionNew = textSelectionNew.indexOf("\n"); + textSelectionLength = position!=-1 ? position+1 : textSelection.length+1; + textSelectionLengthNew = positionNew!=-1 ? positionNew+1 : textSelectionNew.length+1; + } else { + var position = textSelection.indexOf("\n"); + var positionNew = textSelectionNew.indexOf("\n"); + textSelectionLength = position!=-1 ? textSelection.length : textSelection.length+1; + textSelectionLengthNew = positionNew!=-1 ? textSelectionNew.length : textSelectionNew.length+1; + } + return textSelectionLengthNew - textSelectionLength; + }, + + // Return Markdown for multiline block + getMarkdownMultilineBlock: function(textSelection, information) { + var textSelectionNew = ""; + var lines = yellow.toolbox.getTextLines(textSelection); + for (var i=0; i<lines.length; i++) { + var matches = lines[i].match(/^(\s*[\#\*\-\>\s]+)?(\s+\[.\]|\s*\d+\.)?[ \t]+/); + if (matches) { + textSelectionNew += lines[i].substring(matches[0].length); + } else { + textSelectionNew += lines[i]; + } + } + textSelection = textSelectionNew; + if (information.type.indexOf("remove")==-1) { + textSelectionNew = ""; + var linePrefix = information.prefix; + lines = yellow.toolbox.getTextLines(textSelection.length!=0 ? textSelection : "\n"); + for (var i=0; i<lines.length; i++) { + textSelectionNew += linePrefix+lines[i]; + if (information.prefix=="1. ") { + var matches = linePrefix.match(/^(\d+)\.\s/); + if (matches) linePrefix = (parseInt(matches[1])+1)+". "; + } + } + textSelection = textSelectionNew; + } + return textSelection; + }, + + // Return Markdown for fenced block + getMarkdownFencedBlock: function(textSelection, information) { + var textSelectionNew = ""; + var lines = yellow.toolbox.getTextLines(textSelection); + for (var i=0; i<lines.length; i++) { + var matches = lines[i].match(/^```/); + if (!matches) textSelectionNew += lines[i]; + } + textSelection = textSelectionNew; + if (information.type.indexOf("remove")==-1) { + if (textSelection.length==0) textSelection = "\n"; + textSelection = information.prefix + textSelection + information.prefix; + } + return textSelection; + }, + + // Return Markdown for link + getMarkdownLink: function(textSelection, information) { + return textSelection.length!=0 ? information.prefix.replace("link", textSelection) : information.prefix; + }, + + // Set meta data + setMetaData: function(element, key, value, toggle) { + var information = this.getMetaDataInformation(element, key); + if (information.bottom!=0) { + var selectionStart = information.found ? information.start : information.bottom; + var selectionEnd = information.found ? information.end : information.bottom; + var text = information.text; + var textSelectionBefore = text.substring(0, selectionStart); + var textSelection = text.substring(selectionStart, selectionEnd); + var textSelectionAfter = text.substring(selectionEnd, text.length); + var textSelectionNew = yellow.toolbox.toUpperFirst(key)+": "+value+"\n"; + if (information.found && information.value==value && toggle) textSelectionNew = ""; + var selectionStartNew = selectionStart; + var selectionEndNew = selectionStart + textSelectionNew.trim().length; + element.focus(); + element.setSelectionRange(selectionStart, selectionEnd); + document.execCommand("insertText", false, textSelectionNew); + element.value = textSelectionBefore + textSelectionNew + textSelectionAfter; + element.setSelectionRange(selectionStartNew, selectionEndNew); + element.scrollTop = 0; + if (yellow.config.debug) console.log("yellow.editor.setMetaData key:"+key); + } + }, + + // Return meta data information + getMetaDataInformation: function(element, key) { + var text = element.value; + var value = ""; + var start = 0, end = 0, top = 0, bottom = 0; + var found = false; + var parts = text.match(/^(\xEF\xBB\xBF)?(\-\-\-[\r\n]+)([\s\S]+?)\-\-\-[\r\n]+/); + if (parts) { + key = yellow.toolbox.toLowerFirst(key); + start = end = top = ((parts[1] ? parts[1] : "")+parts[2]).length; + bottom = ((parts[1] ? parts[1] : "")+parts[2]+parts[3]).length; + var lines = yellow.toolbox.getTextLines(parts[3]); + for (var i=0; i<lines.length; i++) { + var matches = lines[i].match(/^\s*(.*?)\s*:\s*(.*?)\s*$/); + if (matches && yellow.toolbox.toLowerFirst(matches[1])==key && matches[2].length!=0) { + value = matches[2]; + end = start + lines[i].length; + found = true; + break; + } + start = end = start + lines[i].length; + } + } + return { "text":text, "value":value, "start":start, "end":end, "top":top, "bottom":bottom, "found":found }; + }, + + // Replace text + replace: function(element, textOld, textNew) { + var text = element.value; + var selectionStart = element.selectionStart; + var selectionEnd = element.selectionEnd; + var selectionStartFound = text.indexOf(textOld); + var selectionEndFound = selectionStartFound + textOld.length; + if (selectionStartFound!=-1) { + var selectionStartNew = selectionStart<selectionStartFound ? selectionStart : selectionStart+textNew.length-textOld.length; + var selectionEndNew = selectionEnd<selectionEndFound ? selectionEnd : selectionEnd+textNew.length-textOld.length; + var textBefore = text.substring(0, selectionStartFound); + var textAfter = text.substring(selectionEndFound, text.length); + if (textOld!=textNew) { + element.focus(); + element.setSelectionRange(selectionStartFound, selectionEndFound); + document.execCommand("insertText", false, textNew); + element.value = textBefore + textNew + textAfter; + element.setSelectionRange(selectionStartNew, selectionEndNew); + } + } + }, + + // Undo changes + undo: function() { + document.execCommand("undo"); + }, + + // Redo changes + redo: function() { + document.execCommand("redo"); + } }; -yellow.toolbox = -{ - // Insert element before reference element - insertBefore: function(element, elementReference) - { - elementReference.parentNode.insertBefore(element, elementReference); - }, +yellow.toolbox = { + + // Insert element before reference element + insertBefore: function(element, elementReference) { + elementReference.parentNode.insertBefore(element, elementReference); + }, - // Insert element after reference element - insertAfter: function(element, elementReference) - { - elementReference.parentNode.insertBefore(element, elementReference.nextSibling); - }, + // Insert element after reference element + insertAfter: function(element, elementReference) { + elementReference.parentNode.insertBefore(element, elementReference.nextSibling); + }, - // Add element class - addClass: function(element, name) - { - element.classList.add(name); - }, - - // Remove element class - removeClass: function(element, name) - { - element.classList.remove(name); - }, + // Add element class + addClass: function(element, name) { + element.classList.add(name); + }, + + // Remove element class + removeClass: function(element, name) { + element.classList.remove(name); + }, - // Add attribute information - addValue: function(selector, name, value) - { - var element = document.querySelector(selector); - element.setAttribute(name, element.getAttribute(name) + value); - }, + // Add attribute information + addValue: function(selector, name, value) { + var element = document.querySelector(selector); + element.setAttribute(name, element.getAttribute(name) + value); + }, - // Remove attribute information - removeValue: function(selector, name, value) - { - var element = document.querySelector(selector); - element.setAttribute(name, element.getAttribute(name).replace(value, "")); - }, - - // Add event handler - addEvent: function(element, type, handler) - { - element.addEventListener(type, handler, false); - }, - - // Remove event handler - removeEvent: function(element, type, handler) - { - element.removeEventListener(type, handler, false); - }, - - // Return shortcut from keyboard event, alphanumeric only - getEventShortcut: function(e) - { - var shortcut = ""; - if(e.keyCode>=48 && e.keyCode<=90) - { - shortcut += (e.ctrlKey ? "ctrl+" : "")+(e.metaKey ? "meta+" : "")+(e.altKey ? "alt+" : "")+(e.shiftKey ? "shift+" : ""); - shortcut += String.fromCharCode(e.keyCode).toLowerCase(); - } - return shortcut; - }, - - // Return element width in pixel - getWidth: function(element) - { - return element.offsetWidth - this.getBoxSize(element).width; - }, - - // Return element height in pixel - getHeight: function(element) - { - return element.offsetHeight - this.getBoxSize(element).height; - }, - - // Set element width in pixel, including padding and border - setOuterWidth: function(element, width) - { - element.style.width = Math.max(0, width - this.getBoxSize(element).width) + "px"; - }, - - // Set element height in pixel, including padding and border - setOuterHeight: function(element, height) - { - element.style.height = Math.max(0, height - this.getBoxSize(element).height) + "px"; - }, - - // Return element width in pixel, including padding and border - getOuterWidth: function(element, includeMargin) - { - var width = element.offsetWidth; - if(includeMargin) width += this.getMarginSize(element).width; - return width; - }, + // Remove attribute information + removeValue: function(selector, name, value) { + var element = document.querySelector(selector); + element.setAttribute(name, element.getAttribute(name).replace(value, "")); + }, + + // Add event handler + addEvent: function(element, type, handler) { + element.addEventListener(type, handler, false); + }, + + // Remove event handler + removeEvent: function(element, type, handler) { + element.removeEventListener(type, handler, false); + }, + + // Return shortcut from keyboard event, alphanumeric only + getEventShortcut: function(e) { + var shortcut = ""; + if (e.keyCode>=48 && e.keyCode<=90) { + shortcut += (e.ctrlKey ? "ctrl+" : "")+(e.metaKey ? "meta+" : "")+(e.altKey ? "alt+" : "")+(e.shiftKey ? "shift+" : ""); + shortcut += String.fromCharCode(e.keyCode).toLowerCase(); + } + return shortcut; + }, + + // Return element width in pixel + getWidth: function(element) { + return element.offsetWidth - this.getBoxSize(element).width; + }, + + // Return element height in pixel + getHeight: function(element) { + return element.offsetHeight - this.getBoxSize(element).height; + }, + + // Set element width in pixel, including padding and border + setOuterWidth: function(element, width) { + element.style.width = Math.max(0, width - this.getBoxSize(element).width) + "px"; + }, + + // Set element height in pixel, including padding and border + setOuterHeight: function(element, height) { + element.style.height = Math.max(0, height - this.getBoxSize(element).height) + "px"; + }, + + // Return element width in pixel, including padding and border + getOuterWidth: function(element, includeMargin) { + var width = element.offsetWidth; + if (includeMargin) width += this.getMarginSize(element).width; + return width; + }, - // Return element height in pixel, including padding and border - getOuterHeight: function(element, includeMargin) - { - var height = element.offsetHeight; - if(includeMargin) height += this.getMarginSize(element).height; - return height; - }, - - // Set element left position in pixel - setOuterLeft: function(element, left) - { - element.style.left = Math.max(0, left) + "px"; - }, - - // Set element top position in pixel - setOuterTop: function(element, top) - { - element.style.top = Math.max(0, top) + "px"; - }, - - // Return element left position in pixel - getOuterLeft: function(element) - { - return element.getBoundingClientRect().left + window.pageXOffset; - }, - - // Return element top position in pixel - getOuterTop: function(element) - { - return element.getBoundingClientRect().top + window.pageYOffset; - }, - - // Return window width in pixel - getWindowWidth: function() - { - return window.innerWidth; - }, - - // Return window height in pixel - getWindowHeight: function() - { - return window.innerHeight; - }, - - // Return element CSS property - getStyle: function(element, property) - { - return window.getComputedStyle(element).getPropertyValue(property); - }, - - // Return element CSS padding and border - getBoxSize: function(element) - { - var paddingLeft = parseFloat(this.getStyle(element, "padding-left")) || 0; - var paddingRight = parseFloat(this.getStyle(element, "padding-right")) || 0; - var borderLeft = parseFloat(this.getStyle(element, "border-left-width")) || 0; - var borderRight = parseFloat(this.getStyle(element, "border-right-width")) || 0; - var width = paddingLeft + paddingRight + borderLeft + borderRight; - var paddingTop = parseFloat(this.getStyle(element, "padding-top")) || 0; - var paddingBottom = parseFloat(this.getStyle(element, "padding-bottom")) || 0; - var borderTop = parseFloat(this.getStyle(element, "border-top-width")) || 0; - var borderBottom = parseFloat(this.getStyle(element, "border-bottom-width")) || 0; - var height = paddingTop + paddingBottom + borderTop + borderBottom; - return { "width":width, "height":height }; - }, - - // Return element CSS margin - getMarginSize: function(element) - { - var marginLeft = parseFloat(this.getStyle(element, "margin-left")) || 0; - var marginRight = parseFloat(this.getStyle(element, "margin-right")) || 0; - var width = marginLeft + marginRight; - var marginTop = parseFloat(this.getStyle(element, "margin-top")) || 0; - var marginBottom = parseFloat(this.getStyle(element, "margin-bottom")) || 0; - var height = marginTop + marginBottom; - return { "width":width, "height":height }; - }, - - // Set element visibility - setVisible: function(element, show, fadeout) - { - if(fadeout && !show) - { - var opacity = 1; - function renderFrame() - { - opacity -= .1; - if(opacity<=0) - { - element.style.opacity = "initial"; - element.style.display = "none"; - } else { - element.style.opacity = opacity; - requestAnimationFrame(renderFrame); - } - } - renderFrame(); - } else { - element.style.display = show ? "block" : "none"; - } - }, + // Return element height in pixel, including padding and border + getOuterHeight: function(element, includeMargin) { + var height = element.offsetHeight; + if (includeMargin) height += this.getMarginSize(element).height; + return height; + }, + + // Set element left position in pixel + setOuterLeft: function(element, left) { + element.style.left = Math.max(0, left) + "px"; + }, + + // Set element top position in pixel + setOuterTop: function(element, top) { + element.style.top = Math.max(0, top) + "px"; + }, + + // Return element left position in pixel + getOuterLeft: function(element) { + return element.getBoundingClientRect().left + window.pageXOffset; + }, + + // Return element top position in pixel + getOuterTop: function(element) { + return element.getBoundingClientRect().top + window.pageYOffset; + }, + + // Return window width in pixel + getWindowWidth: function() { + return window.innerWidth; + }, + + // Return window height in pixel + getWindowHeight: function() { + return window.innerHeight; + }, + + // Return element CSS property + getStyle: function(element, property) { + return window.getComputedStyle(element).getPropertyValue(property); + }, + + // Return element CSS padding and border + getBoxSize: function(element) { + var paddingLeft = parseFloat(this.getStyle(element, "padding-left")) || 0; + var paddingRight = parseFloat(this.getStyle(element, "padding-right")) || 0; + var borderLeft = parseFloat(this.getStyle(element, "border-left-width")) || 0; + var borderRight = parseFloat(this.getStyle(element, "border-right-width")) || 0; + var width = paddingLeft + paddingRight + borderLeft + borderRight; + var paddingTop = parseFloat(this.getStyle(element, "padding-top")) || 0; + var paddingBottom = parseFloat(this.getStyle(element, "padding-bottom")) || 0; + var borderTop = parseFloat(this.getStyle(element, "border-top-width")) || 0; + var borderBottom = parseFloat(this.getStyle(element, "border-bottom-width")) || 0; + var height = paddingTop + paddingBottom + borderTop + borderBottom; + return { "width":width, "height":height }; + }, + + // Return element CSS margin + getMarginSize: function(element) { + var marginLeft = parseFloat(this.getStyle(element, "margin-left")) || 0; + var marginRight = parseFloat(this.getStyle(element, "margin-right")) || 0; + var width = marginLeft + marginRight; + var marginTop = parseFloat(this.getStyle(element, "margin-top")) || 0; + var marginBottom = parseFloat(this.getStyle(element, "margin-bottom")) || 0; + var height = marginTop + marginBottom; + return { "width":width, "height":height }; + }, + + // Set element visibility + setVisible: function(element, show, fadeout) { + if (fadeout && !show) { + var opacity = 1; + function renderFrame() { + opacity -= .1; + if (opacity<=0) { + element.style.opacity = "initial"; + element.style.display = "none"; + } else { + element.style.opacity = opacity; + requestAnimationFrame(renderFrame); + } + } + renderFrame(); + } else { + element.style.display = show ? "block" : "none"; + } + }, - // Check if element exists and is visible - isVisible: function(element) - { - return element && element.style.display!="none"; - }, - - // Convert first letter to lowercase - toLowerFirst: function(string) - { - return string.charAt(0).toLowerCase()+string.slice(1); - }, + // Check if element exists and is visible + isVisible: function(element) { + return element && element.style.display!="none"; + }, + + // Convert first letter to lowercase + toLowerFirst: function(string) { + return string.charAt(0).toLowerCase()+string.slice(1); + }, - // Convert first letter to uppercase - toUpperFirst: function(string) - { - return string.charAt(0).toUpperCase()+string.slice(1); - }, - - // Return lines from text string, including newline - getTextLines: function(string) - { - var lines = string.split("\n"); - for(var i=0; i<lines.length; i++) lines[i] = lines[i]+"\n"; - if(string.length==0 || string.charAt(string.length-1)=="\n") lines.pop(); - return lines; - }, - - // Return cookie string - getCookie: function(name) - { - var matches = document.cookie.match("(^|; )"+name+"=([^;]+)"); - return matches ? unescape(matches[2]) : ""; - }, - - // Encode HTML special characters - encodeHtml: function(string) - { - return string - .replace(/&/g, "&amp;") - .replace(/</g, "&lt;") - .replace(/>/g, "&gt;") - .replace(/"/g, "&quot;"); - }, - - // Submit form with post method - submitForm: function(args) - { - var elementForm = document.createElement("form"); - elementForm.setAttribute("method", "post"); - for(var key in args) - { - if(!args.hasOwnProperty(key)) continue; - var elementInput = document.createElement("input"); - elementInput.setAttribute("type", "hidden"); - elementInput.setAttribute("name", key); - elementInput.setAttribute("value", args[key]); - elementForm.appendChild(elementInput); - } - document.body.appendChild(elementForm); - elementForm.submit(); - } + // Convert first letter to uppercase + toUpperFirst: function(string) { + return string.charAt(0).toUpperCase()+string.slice(1); + }, + + // Return lines from text string, including newline + getTextLines: function(string) { + var lines = string.split("\n"); + for (var i=0; i<lines.length; i++) lines[i] = lines[i]+"\n"; + if (string.length==0 || string.charAt(string.length-1)=="\n") lines.pop(); + return lines; + }, + + // Return cookie string + getCookie: function(name) { + var matches = document.cookie.match("(^|; )"+name+"=([^;]+)"); + return matches ? unescape(matches[2]) : ""; + }, + + // Encode HTML special characters + encodeHtml: function(string) { + return string + .replace(/&/g, "&amp;") + .replace(/</g, "&lt;") + .replace(/>/g, "&gt;") + .replace(/"/g, "&quot;"); + }, + + // Submit form with post method + submitForm: function(args) { + var elementForm = document.createElement("form"); + elementForm.setAttribute("method", "post"); + for (var key in args) { + if (!args.hasOwnProperty(key)) continue; + var elementInput = document.createElement("input"); + elementInput.setAttribute("type", "hidden"); + elementInput.setAttribute("name", key); + elementInput.setAttribute("value", args[key]); + elementForm.appendChild(elementInput); + } + document.body.appendChild(elementForm); + elementForm.submit(); + } }; yellow.edit.intervalId = setInterval("yellow.onLoad()", 1); diff --git a/system/plugins/edit.php b/system/plugins/edit.php @@ -3,2035 +3,1777 @@ // Copyright (c) 2013-2018 Datenstrom, https://datenstrom.se // This file may be used and distributed under the terms of the public license. -class YellowEdit -{ - const VERSION = "0.7.27"; - var $yellow; //access to API - var $response; //web response - var $users; //user accounts - var $merge; //text merge +class YellowEdit { + const VERSION = "0.7.27"; + public $yellow; //access to API + public $response; //web response + public $users; //user accounts + public $merge; //text merge - // Handle initialisation - function onLoad($yellow) - { - $this->yellow = $yellow; - $this->response = new YellowResponse($yellow); - $this->users = new YellowUsers($yellow); - $this->merge = new YellowMerge($yellow); - $this->yellow->config->setDefault("editLocation", "/edit/"); - $this->yellow->config->setDefault("editUploadNewLocation", "/media/@group/@filename"); - $this->yellow->config->setDefault("editUploadExtensions", ".gif, .jpg, .pdf, .png, .svg, .tgz, .zip"); - $this->yellow->config->setDefault("editKeyboardShortcuts", "ctrl+b bold, ctrl+i italic, ctrl+e code, ctrl+k link, ctrl+s save, ctrl+shift+p preview"); - $this->yellow->config->setDefault("editToolbarButtons", "auto"); - $this->yellow->config->setDefault("editEndOfLine", "auto"); - $this->yellow->config->setDefault("editUserFile", "user.ini"); - $this->yellow->config->setDefault("editUserPasswordMinLength", "8"); - $this->yellow->config->setDefault("editUserHashAlgorithm", "bcrypt"); - $this->yellow->config->setDefault("editUserHashCost", "10"); - $this->yellow->config->setDefault("editUserHome", "/"); - $this->yellow->config->setDefault("editLoginRestrictions", "0"); - $this->yellow->config->setDefault("editLoginSessionTimeout", "2592000"); - $this->yellow->config->setDefault("editBruteForceProtection", "25"); - $this->users->load($this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile")); - } + // Handle initialisation + public function onLoad($yellow) { + $this->yellow = $yellow; + $this->response = new YellowResponse($yellow); + $this->users = new YellowUsers($yellow); + $this->merge = new YellowMerge($yellow); + $this->yellow->config->setDefault("editLocation", "/edit/"); + $this->yellow->config->setDefault("editUploadNewLocation", "/media/@group/@filename"); + $this->yellow->config->setDefault("editUploadExtensions", ".gif, .jpg, .pdf, .png, .svg, .tgz, .zip"); + $this->yellow->config->setDefault("editKeyboardShortcuts", "ctrl+b bold, ctrl+i italic, ctrl+e code, ctrl+k link, ctrl+s save, ctrl+shift+p preview"); + $this->yellow->config->setDefault("editToolbarButtons", "auto"); + $this->yellow->config->setDefault("editEndOfLine", "auto"); + $this->yellow->config->setDefault("editUserFile", "user.ini"); + $this->yellow->config->setDefault("editUserPasswordMinLength", "8"); + $this->yellow->config->setDefault("editUserHashAlgorithm", "bcrypt"); + $this->yellow->config->setDefault("editUserHashCost", "10"); + $this->yellow->config->setDefault("editUserHome", "/"); + $this->yellow->config->setDefault("editLoginRestrictions", "0"); + $this->yellow->config->setDefault("editLoginSessionTimeout", "2592000"); + $this->yellow->config->setDefault("editBruteForceProtection", "25"); + $this->users->load($this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile")); + } - // Handle startup - function onStartup($update) - { - if($update) - { - $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); - $fileData = $this->yellow->toolbox->readFile($fileNameUser); - foreach($this->yellow->toolbox->getTextLines($fileData) as $line) - { - preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); - if(!empty($matches[1]) && !empty($matches[2]) && $matches[1][0]!='#') - { - list($hash, $name, $language, $status, $stamp, $modified, $errors, $pending, $home) = explode(',', $matches[2]); - if($errors=="none") { $home=$pending; $pending=$errors; $errors=$modified; $modified=$stamp; $stamp=""; } //TODO: remove later - if(strlenb($stamp)!=20) $stamp=$this->users->createStamp(); //TODO: remove later, converts old file format - if($status!="active" && $status!="inactive") { unset($this->users->users[$matches[1]]); continue; } - $pending = "none"; - $this->users->set($matches[1], $hash, $name, $language, $status, $stamp, $modified, $errors, $pending, $home); - $fileDataNew .= "$matches[1]: $hash,$name,$language,$status,$stamp,$modified,$errors,$pending,$home\n"; - } else { - $fileDataNew .= $line; - } - } - if($fileData!=$fileDataNew) $this->yellow->toolbox->createFile($fileNameUser, $fileDataNew); - } - } - - // Handle request - function onRequest($scheme, $address, $base, $location, $fileName) - { - $statusCode = 0; - if($this->checkRequest($location)) - { - $scheme = $this->yellow->config->get("serverScheme"); - $address = $this->yellow->config->get("serverAddress"); - $base = rtrim($this->yellow->config->get("serverBase").$this->yellow->config->get("editLocation"), '/'); - list($scheme, $address, $base, $location, $fileName) = $this->yellow->getRequestInformation($scheme, $address, $base); - $this->yellow->page->setRequestInformation($scheme, $address, $base, $location, $fileName); - $statusCode = $this->processRequest($scheme, $address, $base, $location, $fileName); - } - return $statusCode; - } - - // Handle page meta data parsing - function onParseMeta($page) - { - if($page==$this->yellow->page && $this->response->isActive()) - { - if($this->response->isUser()) - { - if(empty($this->response->rawDataSource)) $this->response->rawDataSource = $page->rawData; - if(empty($this->response->rawDataEdit)) $this->response->rawDataEdit = $page->rawData; - if(empty($this->response->rawDataEndOfLine)) $this->response->rawDataEndOfLine = $this->response->getEndOfLine($page->rawData); - if($page->statusCode==434) $this->response->rawDataEdit = $this->response->getRawDataNew($page->location); - } - if(empty($this->response->language)) $this->response->language = $page->get("language"); - if(empty($this->response->action)) $this->response->action = $this->response->isUser() ? "none" : "login"; - if(empty($this->response->status)) $this->response->status = "none"; - if($this->response->status=="error") $this->response->action = "error"; - } - } - - // Handle page content parsing of custom block - function onParseContentBlock($page, $name, $text, $shortcut) - { - $output = null; - if($name=="edit" && $shortcut) - { - $editText = "$name $text"; - if(substru($text, 0, 2)=="- ") $editText = trim(substru($text, 2)); - $output = "<a href=\"".$page->get("pageEdit")."\">".htmlspecialchars($editText)."</a>"; - } - return $output; - } - - // Handle page extra HTML data - function onExtra($name) - { - $output = null; - if($name=="header" && $this->response->isActive()) - { - $pluginLocation = $this->yellow->config->get("serverBase").$this->yellow->config->get("pluginLocation"); - $output = "<link rel=\"stylesheet\" type=\"text/css\" media=\"all\" href=\"{$pluginLocation}edit.css\" />\n"; - $output .= "<script type=\"text/javascript\" src=\"{$pluginLocation}edit.js\"></script>\n"; - $output .= "<script type=\"text/javascript\">\n"; - $output .= "// <![CDATA[\n"; - $output .= "yellow.page = ".json_encode($this->response->getPageData()).";\n"; - $output .= "yellow.config = ".json_encode($this->response->getConfigData()).";\n"; - $output .= "yellow.text = ".json_encode($this->response->getTextData()).";\n"; - $output .= "// ]]>\n"; - $output .= "</script>\n"; - } - return $output; - } - - // Handle command - function onCommand($args) - { - list($command) = $args; - switch($command) - { - case "user": $statusCode = $this->userCommand($args); break; - default: $statusCode = 0; - } - return $statusCode; - } - - // Handle command help - function onCommandHelp() - { - return "user [option email password name]\n"; - } + // Handle startup + public function onStartup($update) { + if ($update) { + $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); + $fileData = $this->yellow->toolbox->readFile($fileNameUser); + foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) { + preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); + if (!empty($matches[1]) && !empty($matches[2]) && $matches[1][0]!="#") { + list($hash, $name, $language, $status, $stamp, $modified, $errors, $pending, $home) = explode(",", $matches[2]); + if ($errors=="none") { $home=$pending; $pending=$errors; $errors=$modified; $modified=$stamp; $stamp=""; } //TODO: remove later + if (strlenb($stamp)!=20) $stamp=$this->users->createStamp(); //TODO: remove later, converts old file format + if ($status!="active" && $status!="inactive") { + unset($this->users->users[$matches[1]]); + continue; + } + $pending = "none"; + $this->users->set($matches[1], $hash, $name, $language, $status, $stamp, $modified, $errors, $pending, $home); + $fileDataNew .= "$matches[1]: $hash,$name,$language,$status,$stamp,$modified,$errors,$pending,$home\n"; + } else { + $fileDataNew .= $line; + } + } + if ($fileData!=$fileDataNew) $this->yellow->toolbox->createFile($fileNameUser, $fileDataNew); + } + } + + // Handle request + public function onRequest($scheme, $address, $base, $location, $fileName) { + $statusCode = 0; + if ($this->checkRequest($location)) { + $scheme = $this->yellow->config->get("serverScheme"); + $address = $this->yellow->config->get("serverAddress"); + $base = rtrim($this->yellow->config->get("serverBase").$this->yellow->config->get("editLocation"), "/"); + list($scheme, $address, $base, $location, $fileName) = $this->yellow->getRequestInformation($scheme, $address, $base); + $this->yellow->page->setRequestInformation($scheme, $address, $base, $location, $fileName); + $statusCode = $this->processRequest($scheme, $address, $base, $location, $fileName); + } + return $statusCode; + } + + // Handle page meta data parsing + public function onParseMeta($page) { + if ($page==$this->yellow->page && $this->response->isActive()) { + if ($this->response->isUser()) { + if (empty($this->response->rawDataSource)) $this->response->rawDataSource = $page->rawData; + if (empty($this->response->rawDataEdit)) $this->response->rawDataEdit = $page->rawData; + if (empty($this->response->rawDataEndOfLine)) $this->response->rawDataEndOfLine = $this->response->getEndOfLine($page->rawData); + if ($page->statusCode==434) $this->response->rawDataEdit = $this->response->getRawDataNew($page->location); + } + if (empty($this->response->language)) $this->response->language = $page->get("language"); + if (empty($this->response->action)) $this->response->action = $this->response->isUser() ? "none" : "login"; + if (empty($this->response->status)) $this->response->status = "none"; + if ($this->response->status=="error") $this->response->action = "error"; + } + } + + // Handle page content parsing of custom block + public function onParseContentBlock($page, $name, $text, $shortcut) { + $output = null; + if ($name=="edit" && $shortcut) { + $editText = "$name $text"; + if (substru($text, 0, 2)=="- ") $editText = trim(substru($text, 2)); + $output = "<a href=\"".$page->get("pageEdit")."\">".htmlspecialchars($editText)."</a>"; + } + return $output; + } + + // Handle page extra HTML data + public function onExtra($name) { + $output = null; + if ($name=="header" && $this->response->isActive()) { + $pluginLocation = $this->yellow->config->get("serverBase").$this->yellow->config->get("pluginLocation"); + $output = "<link rel=\"stylesheet\" type=\"text/css\" media=\"all\" href=\"{$pluginLocation}edit.css\" />\n"; + $output .= "<script type=\"text/javascript\" src=\"{$pluginLocation}edit.js\"></script>\n"; + $output .= "<script type=\"text/javascript\">\n"; + $output .= "// <![CDATA[\n"; + $output .= "yellow.page = ".json_encode($this->response->getPageData()).";\n"; + $output .= "yellow.config = ".json_encode($this->response->getConfigData()).";\n"; + $output .= "yellow.text = ".json_encode($this->response->getTextData()).";\n"; + $output .= "// ]]>\n"; + $output .= "</script>\n"; + } + return $output; + } + + // Handle command + public function onCommand($args) { + list($command) = $args; + switch ($command) { + case "user": $statusCode = $this->userCommand($args); break; + default: $statusCode = 0; + } + return $statusCode; + } + + // Handle command help + public function onCommandHelp() { + return "user [option email password name]\n"; + } - // Update user account - function userCommand($args) - { - list($command, $option) = $args; - switch($option) - { - case "": $statusCode = $this->userShow($args); break; - case "add": $statusCode = $this->userAdd($args); break; - case "change": $statusCode = $this->userChange($args); break; - case "remove": $statusCode = $this->userRemove($args); break; - default: $statusCode = 400; echo "Yellow $command: Invalid arguments\n"; - } - return $statusCode; - } - - // Show user accounts - function userShow($args) - { - list($command) = $args; - foreach($this->users->getData() as $line) echo "$line\n"; - if(!$this->users->getNumber()) echo "Yellow $command: No user accounts\n"; - return 200; - } - - // Add user account - function userAdd($args) - { - $status = "ok"; - list($command, $option, $email, $password, $name) = $args; - if(empty($email) || empty($password)) $status = $this->response->status = "incomplete"; - if($status=="ok") $status = $this->getUserAccount($email, $password, "add"); - if($status=="ok" && $this->users->isTaken($email)) $status = "taken"; - switch($status) - { - case "incomplete": echo "ERROR updating configuration: Please enter email and password!\n"; break; - case "invalid": echo "ERROR updating configuration: Please enter a valid email!\n"; break; - case "taken": echo "ERROR updating configuration: Please enter a different email!\n"; break; - case "weak": echo "ERROR updating configuration: Please enter a different password!\n"; break; - } - if($status=="ok") - { - $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); - $status = $this->users->save($fileNameUser, $email, $password, $name, "", "active") ? "ok" : "error"; - if($status=="error") echo "ERROR updating configuration: Can't write file '$fileNameUser'!\n"; - } - if($status=="ok") - { - $algorithm = $this->yellow->config->get("editUserHashAlgorithm"); - $status = substru($this->users->getHash($email), 0, 10)!="error-hash" ? "ok" : "error"; - if($status=="error") echo "ERROR updating configuration: Hash algorithm '$algorithm' not supported!\n"; - } - $statusCode = $status=="ok" ? 200 : 500; - echo "Yellow $command: User account ".($statusCode!=200 ? "not " : "")."added\n"; - return $statusCode; - } - - // Change user account - function userChange($args) - { - $status = "ok"; - list($command, $option, $email, $password, $name) = $args; - 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"; - switch($status) - { - case "invalid": echo "ERROR updating configuration: Please enter a valid email!\n"; break; - case "unknown": echo "ERROR updating configuration: Can't find email '$email'!\n"; break; - case "weak": echo "ERROR updating configuration: Please enter a different password!\n"; break; - } - if($status=="ok") - { - $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); - $status = $this->users->save($fileNameUser, $email, $password, $name) ? "ok" : "error"; - if($status=="error") echo "ERROR updating configuration: Can't write file '$fileNameUser'!\n"; - } - $statusCode = $status=="ok" ? 200 : 500; - echo "Yellow $command: User account ".($statusCode!=200 ? "not " : "")."changed\n"; - return $statusCode; - } + // Update user account + public function userCommand($args) { + list($command, $option) = $args; + switch ($option) { + case "": $statusCode = $this->userShow($args); break; + case "add": $statusCode = $this->userAdd($args); break; + case "change": $statusCode = $this->userChange($args); break; + case "remove": $statusCode = $this->userRemove($args); break; + default: $statusCode = 400; echo "Yellow $command: Invalid arguments\n"; + } + return $statusCode; + } + + // Show user accounts + public function userShow($args) { + list($command) = $args; + foreach ($this->users->getData() as $line) { + echo "$line\n"; + } + if (!$this->users->getNumber()) echo "Yellow $command: No user accounts\n"; + return 200; + } + + // Add user account + public function userAdd($args) { + $status = "ok"; + list($command, $option, $email, $password, $name) = $args; + if (empty($email) || empty($password)) $status = $this->response->status = "incomplete"; + if ($status=="ok") $status = $this->getUserAccount($email, $password, "add"); + if ($status=="ok" && $this->users->isTaken($email)) $status = "taken"; + switch ($status) { + case "incomplete": echo "ERROR updating configuration: Please enter email and password!\n"; break; + case "invalid": echo "ERROR updating configuration: Please enter a valid email!\n"; break; + case "taken": echo "ERROR updating configuration: Please enter a different email!\n"; break; + case "weak": echo "ERROR updating configuration: Please enter a different password!\n"; break; + } + if ($status=="ok") { + $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); + $status = $this->users->save($fileNameUser, $email, $password, $name, "", "active") ? "ok" : "error"; + if ($status=="error") echo "ERROR updating configuration: Can't write file '$fileNameUser'!\n"; + } + if ($status=="ok") { + $algorithm = $this->yellow->config->get("editUserHashAlgorithm"); + $status = substru($this->users->getHash($email), 0, 10)!="error-hash" ? "ok" : "error"; + if ($status=="error") echo "ERROR updating configuration: Hash algorithm '$algorithm' not supported!\n"; + } + $statusCode = $status=="ok" ? 200 : 500; + echo "Yellow $command: User account ".($statusCode!=200 ? "not " : "")."added\n"; + return $statusCode; + } + + // Change user account + public function userChange($args) { + $status = "ok"; + list($command, $option, $email, $password, $name) = $args; + 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"; + switch ($status) { + case "invalid": echo "ERROR updating configuration: Please enter a valid email!\n"; break; + case "unknown": echo "ERROR updating configuration: Can't find email '$email'!\n"; break; + case "weak": echo "ERROR updating configuration: Please enter a different password!\n"; break; + } + if ($status=="ok") { + $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); + $status = $this->users->save($fileNameUser, $email, $password, $name) ? "ok" : "error"; + if ($status=="error") echo "ERROR updating configuration: Can't write file '$fileNameUser'!\n"; + } + $statusCode = $status=="ok" ? 200 : 500; + echo "Yellow $command: User account ".($statusCode!=200 ? "not " : "")."changed\n"; + return $statusCode; + } - // Remove user account - function userRemove($args) - { - $status = "ok"; - list($command, $option, $email) = $args; - if(empty($email)) $status = $this->response->status = "invalid"; - if($status=="ok") $status = $this->getUserAccount($email, "", "remove"); - if($status=="ok" && !$this->users->isExisting($email)) $status = "unknown"; - switch($status) - { - case "invalid": echo "ERROR updating configuration: Please enter a valid email!\n"; break; - case "unknown": echo "ERROR updating configuration: Can't find email '$email'!\n"; break; - } - if($status=="ok") - { - $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); - $status = $this->users->remove($fileNameUser, $email) ? "ok" : "error"; - if($status=="error") echo "ERROR updating configuration: Can't write file '$fileNameUser'!\n"; - } - $statusCode = $status=="ok" ? 200 : 500; - echo "Yellow $command: User account ".($statusCode!=200 ? "not " : "")."removed\n"; - return $statusCode; - } - - // Process request - function processRequest($scheme, $address, $base, $location, $fileName) - { - $statusCode = 0; - if($this->checkUserAuth($scheme, $address, $base, $location, $fileName)) - { - switch($_REQUEST["action"]) - { - case "": $statusCode = $this->processRequestShow($scheme, $address, $base, $location, $fileName); break; - case "login": $statusCode = $this->processRequestLogin($scheme, $address, $base, $location, $fileName); break; - case "logout": $statusCode = $this->processRequestLogout($scheme, $address, $base, $location, $fileName); break; - case "settings": $statusCode = $this->processRequestSettings($scheme, $address, $base, $location, $fileName); break; - case "version": $statusCode = $this->processRequestVersion($scheme, $address, $base, $location, $fileName); break; - case "update": $statusCode = $this->processRequestUpdate($scheme, $address, $base, $location, $fileName); break; - case "quit": $statusCode = $this->processRequestQuit($scheme, $address, $base, $location, $fileName); break; - case "create": $statusCode = $this->processRequestCreate($scheme, $address, $base, $location, $fileName); break; - case "edit": $statusCode = $this->processRequestEdit($scheme, $address, $base, $location, $fileName); break; - case "delete": $statusCode = $this->processRequestDelete($scheme, $address, $base, $location, $fileName); break; - case "preview": $statusCode = $this->processRequestPreview($scheme, $address, $base, $location, $fileName); break; - case "upload": $statusCode = $this->processRequestUpload($scheme, $address, $base, $location, $fileName); break; - } - } else if($this->checkUserUnauth($scheme, $address, $base, $location, $fileName)) { - $this->yellow->lookup->requestHandler = "core"; - switch($_REQUEST["action"]) - { - case "": $statusCode = $this->processRequestShow($scheme, $address, $base, $location, $fileName); break; - case "signup": $statusCode = $this->processRequestSignup($scheme, $address, $base, $location, $fileName); break; - case "forgot": $statusCode = $this->processRequestForgot($scheme, $address, $base, $location, $fileName); break; - case "confirm": $statusCode = $this->processRequestConfirm($scheme, $address, $base, $location, $fileName); break; - case "approve": $statusCode = $this->processRequestApprove($scheme, $address, $base, $location, $fileName); break; - case "recover": $statusCode = $this->processRequestRecover($scheme, $address, $base, $location, $fileName); break; - case "reactivate": $statusCode = $this->processRequestReactivate($scheme, $address, $base, $location, $fileName); break; - case "verify": $statusCode = $this->processRequestVerify($scheme, $address, $base, $location, $fileName); break; - case "change": $statusCode = $this->processRequestChange($scheme, $address, $base, $location, $fileName); break; - case "remove": $statusCode = $this->processRequestRemove($scheme, $address, $base, $location, $fileName); break; - } - } - $this->checkUserFailed($scheme, $address, $base, $location, $fileName); - return $statusCode; - } - - // Process request to show file - function processRequestShow($scheme, $address, $base, $location, $fileName) - { - $statusCode = 0; - if(is_readable($fileName)) - { - $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); - } else { - if($this->yellow->lookup->isRedirectLocation($location)) - { - $location = $this->yellow->lookup->isFileLocation($location) ? "$location/" : "/".$this->yellow->getRequestLanguage()."/"; - $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); - $statusCode = $this->yellow->sendStatus(301, $location); - } else { - $this->yellow->page->error($this->response->isUserRestrictions() ? 404 : 434); - $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); - } - } - return $statusCode; - } + // Remove user account + public function userRemove($args) { + $status = "ok"; + list($command, $option, $email) = $args; + if (empty($email)) $status = $this->response->status = "invalid"; + if ($status=="ok") $status = $this->getUserAccount($email, "", "remove"); + if ($status=="ok" && !$this->users->isExisting($email)) $status = "unknown"; + switch ($status) { + case "invalid": echo "ERROR updating configuration: Please enter a valid email!\n"; break; + case "unknown": echo "ERROR updating configuration: Can't find email '$email'!\n"; break; + } + if ($status=="ok") { + $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); + $status = $this->users->remove($fileNameUser, $email) ? "ok" : "error"; + if ($status=="error") echo "ERROR updating configuration: Can't write file '$fileNameUser'!\n"; + } + $statusCode = $status=="ok" ? 200 : 500; + echo "Yellow $command: User account ".($statusCode!=200 ? "not " : "")."removed\n"; + return $statusCode; + } + + // Process request + public function processRequest($scheme, $address, $base, $location, $fileName) { + $statusCode = 0; + if ($this->checkUserAuth($scheme, $address, $base, $location, $fileName)) { + switch ($_REQUEST["action"]) { + case "": $statusCode = $this->processRequestShow($scheme, $address, $base, $location, $fileName); break; + case "login": $statusCode = $this->processRequestLogin($scheme, $address, $base, $location, $fileName); break; + case "logout": $statusCode = $this->processRequestLogout($scheme, $address, $base, $location, $fileName); break; + case "settings": $statusCode = $this->processRequestSettings($scheme, $address, $base, $location, $fileName); break; + case "version": $statusCode = $this->processRequestVersion($scheme, $address, $base, $location, $fileName); break; + case "update": $statusCode = $this->processRequestUpdate($scheme, $address, $base, $location, $fileName); break; + case "quit": $statusCode = $this->processRequestQuit($scheme, $address, $base, $location, $fileName); break; + case "create": $statusCode = $this->processRequestCreate($scheme, $address, $base, $location, $fileName); break; + case "edit": $statusCode = $this->processRequestEdit($scheme, $address, $base, $location, $fileName); break; + case "delete": $statusCode = $this->processRequestDelete($scheme, $address, $base, $location, $fileName); break; + case "preview": $statusCode = $this->processRequestPreview($scheme, $address, $base, $location, $fileName); break; + case "upload": $statusCode = $this->processRequestUpload($scheme, $address, $base, $location, $fileName); break; + } + } elseif ($this->checkUserUnauth($scheme, $address, $base, $location, $fileName)) { + $this->yellow->lookup->requestHandler = "core"; + switch ($_REQUEST["action"]) { + case "": $statusCode = $this->processRequestShow($scheme, $address, $base, $location, $fileName); break; + case "signup": $statusCode = $this->processRequestSignup($scheme, $address, $base, $location, $fileName); break; + case "forgot": $statusCode = $this->processRequestForgot($scheme, $address, $base, $location, $fileName); break; + case "confirm": $statusCode = $this->processRequestConfirm($scheme, $address, $base, $location, $fileName); break; + case "approve": $statusCode = $this->processRequestApprove($scheme, $address, $base, $location, $fileName); break; + case "recover": $statusCode = $this->processRequestRecover($scheme, $address, $base, $location, $fileName); break; + case "reactivate": $statusCode = $this->processRequestReactivate($scheme, $address, $base, $location, $fileName); break; + case "verify": $statusCode = $this->processRequestVerify($scheme, $address, $base, $location, $fileName); break; + case "change": $statusCode = $this->processRequestChange($scheme, $address, $base, $location, $fileName); break; + case "remove": $statusCode = $this->processRequestRemove($scheme, $address, $base, $location, $fileName); break; + } + } + $this->checkUserFailed($scheme, $address, $base, $location, $fileName); + return $statusCode; + } + + // Process request to show file + public function processRequestShow($scheme, $address, $base, $location, $fileName) { + $statusCode = 0; + if (is_readable($fileName)) { + $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); + } else { + if ($this->yellow->lookup->isRedirectLocation($location)) { + $location = $this->yellow->lookup->isFileLocation($location) ? "$location/" : "/".$this->yellow->getRequestLanguage()."/"; + $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); + $statusCode = $this->yellow->sendStatus(301, $location); + } else { + $this->yellow->page->error($this->response->isUserRestrictions() ? 404 : 434); + $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); + } + } + return $statusCode; + } - // Process request for user login - function processRequestLogin($scheme, $address, $base, $location, $fileName) - { - $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); - if($this->users->save($fileNameUser, $this->response->userEmail)) - { - $home = $this->users->getHome($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); - } else { - $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $home); - $statusCode = $this->yellow->sendStatus(302, $location); - } - } else { - $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); - $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); - } - return $statusCode; - } - - // Process request for user logout - function processRequestLogout($scheme, $address, $base, $location, $fileName) - { - $this->response->userEmail = ""; - $this->response->destroyCookies($scheme, $address, $base); - $location = $this->yellow->lookup->normaliseUrl( - $this->yellow->config->get("serverScheme"), - $this->yellow->config->get("serverAddress"), - $this->yellow->config->get("serverBase"), $location); - $statusCode = $this->yellow->sendStatus(302, $location); - return $statusCode; - } + // Process request for user login + public function processRequestLogin($scheme, $address, $base, $location, $fileName) { + $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); + if ($this->users->save($fileNameUser, $this->response->userEmail)) { + $home = $this->users->getHome($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); + } else { + $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $home); + $statusCode = $this->yellow->sendStatus(302, $location); + } + } else { + $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); + $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); + } + return $statusCode; + } + + // Process request for user logout + public function processRequestLogout($scheme, $address, $base, $location, $fileName) { + $this->response->userEmail = ""; + $this->response->destroyCookies($scheme, $address, $base); + $location = $this->yellow->lookup->normaliseUrl( + $this->yellow->config->get("serverScheme"), + $this->yellow->config->get("serverAddress"), + $this->yellow->config->get("serverBase"), + $location); + $statusCode = $this->yellow->sendStatus(302, $location); + return $statusCode; + } - // Process request for user signup - function processRequestSignup($scheme, $address, $base, $location, $fileName) - { - $this->response->action = "signup"; - $this->response->status = "ok"; - $name = trim(preg_replace("/[^\pL\d\-\. ]/u", "-", $_REQUEST["name"])); - $email = trim($_REQUEST["email"]); - $password = trim($_REQUEST["password"]); - $consent = trim($_REQUEST["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->isLoginRestrictions()) $this->response->status = "next"; - if($this->response->status=="ok" && $this->users->isTaken($email)) $this->response->status = "next"; - if($this->response->status=="ok") - { - $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); - $this->response->status = $this->users->save($fileNameUser, $email, $password, $name, "", "unconfirmed") ? "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->config->get("editUserHashAlgorithm"); - $this->response->status = substru($this->users->getHash($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") - { - $this->response->status = $this->response->sendMail($scheme, $address, $base, $email, "confirm") ? "next" : "error"; - if($this->response->status=="error") $this->yellow->page->error(500, "Can't send email on this server!"); - } - $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); - return $statusCode; - } - - // Process request to confirm user signup - function processRequestConfirm($scheme, $address, $base, $location, $fileName) - { - $this->response->action = "confirm"; - $this->response->status = "ok"; - $email = $_REQUEST["email"]; - $this->response->status = $this->getUserStatus($email, $_REQUEST["action"]); - if($this->response->status=="ok") - { - $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); - $this->response->status = $this->users->save($fileNameUser, $email, "", "", "", "unapproved") ? "ok" : "error"; - if($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); - } - if($this->response->status=="ok") - { - $this->response->status = $this->response->sendMail($scheme, $address, $base, $email, "approve") ? "done" : "error"; - if($this->response->status=="error") $this->yellow->page->error(500, "Can't send email on this server!"); - } - $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); - return $statusCode; - } - - // Process request to approve user signup - function processRequestApprove($scheme, $address, $base, $location, $fileName) - { - $this->response->action = "approve"; - $this->response->status = "ok"; - $email = $_REQUEST["email"]; - $this->response->status = $this->getUserStatus($email, $_REQUEST["action"]); - if($this->response->status=="ok") - { - $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); - $this->response->status = $this->users->save($fileNameUser, $email, "", "", "", "active") ? "ok" : "error"; - if($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); - } - if($this->response->status=="ok") - { - $this->response->status = $this->response->sendMail($scheme, $address, $base, $email, "welcome") ? "done" : "error"; - if($this->response->status=="error") $this->yellow->page->error(500, "Can't send email on this server!"); - } - $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); - return $statusCode; - } + // Process request for user signup + public function processRequestSignup($scheme, $address, $base, $location, $fileName) { + $this->response->action = "signup"; + $this->response->status = "ok"; + $name = trim(preg_replace("/[^\pL\d\-\. ]/u", "-", $_REQUEST["name"])); + $email = trim($_REQUEST["email"]); + $password = trim($_REQUEST["password"]); + $consent = trim($_REQUEST["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->isLoginRestrictions()) $this->response->status = "next"; + if ($this->response->status=="ok" && $this->users->isTaken($email)) $this->response->status = "next"; + if ($this->response->status=="ok") { + $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); + $this->response->status = $this->users->save($fileNameUser, $email, $password, $name, "", "unconfirmed") ? "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->config->get("editUserHashAlgorithm"); + $this->response->status = substru($this->users->getHash($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") { + $this->response->status = $this->response->sendMail($scheme, $address, $base, $email, "confirm") ? "next" : "error"; + if ($this->response->status=="error") $this->yellow->page->error(500, "Can't send email on this server!"); + } + $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); + return $statusCode; + } + + // Process request to confirm user signup + public function processRequestConfirm($scheme, $address, $base, $location, $fileName) { + $this->response->action = "confirm"; + $this->response->status = "ok"; + $email = $_REQUEST["email"]; + $this->response->status = $this->getUserStatus($email, $_REQUEST["action"]); + if ($this->response->status=="ok") { + $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); + $this->response->status = $this->users->save($fileNameUser, $email, "", "", "", "unapproved") ? "ok" : "error"; + if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); + } + if ($this->response->status=="ok") { + $this->response->status = $this->response->sendMail($scheme, $address, $base, $email, "approve") ? "done" : "error"; + if ($this->response->status=="error") $this->yellow->page->error(500, "Can't send email on this server!"); + } + $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); + return $statusCode; + } + + // Process request to approve user signup + public function processRequestApprove($scheme, $address, $base, $location, $fileName) { + $this->response->action = "approve"; + $this->response->status = "ok"; + $email = $_REQUEST["email"]; + $this->response->status = $this->getUserStatus($email, $_REQUEST["action"]); + if ($this->response->status=="ok") { + $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); + $this->response->status = $this->users->save($fileNameUser, $email, "", "", "", "active") ? "ok" : "error"; + if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); + } + if ($this->response->status=="ok") { + $this->response->status = $this->response->sendMail($scheme, $address, $base, $email, "welcome") ? "done" : "error"; + if ($this->response->status=="error") $this->yellow->page->error(500, "Can't send email on this server!"); + } + $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); + return $statusCode; + } - // Process request for forgotten password - function processRequestForgot($scheme, $address, $base, $location, $fileName) - { - $this->response->action = "forgot"; - $this->response->status = "ok"; - $email = trim($_REQUEST["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->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!"); - } - $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); - return $statusCode; - } - - // Process request to recover password - function processRequestRecover($scheme, $address, $base, $location, $fileName) - { - $this->response->action = "recover"; - $this->response->status = "ok"; - $email = trim($_REQUEST["email"]); - $password = trim($_REQUEST["password"]); - $this->response->status = $this->getUserStatus($email, $_REQUEST["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") - { - $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); - $this->response->status = $this->users->save($fileNameUser, $email, $password) ? "ok" : "error"; - if($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); - } - if($this->response->status=="ok") - { - $this->response->destroyCookies($scheme, $address, $base); - $this->response->status = "done"; - } - } - $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); - return $statusCode; - } - - // Process request to reactivate account - function processRequestReactivate($scheme, $address, $base, $location, $fileName) - { - $this->response->action = "reactivate"; - $this->response->status = "ok"; - $email = $_REQUEST["email"]; - $this->response->status = $this->getUserStatus($email, $_REQUEST["action"]); - if($this->response->status=="ok") - { - $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); - $this->response->status = $this->users->save($fileNameUser, $email, "", "", "", "active") ? "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); - return $statusCode; - } - - // Process request to change settings - function processRequestSettings($scheme, $address, $base, $location, $fileName) - { - $this->response->action = "settings"; - $this->response->status = "ok"; - $email = trim($_REQUEST["email"]); - $emailSource = $this->response->userEmail; - $password = trim($_REQUEST["password"]); - $name = trim(preg_replace("/[^\pL\d\-\. ]/u", "-", $_REQUEST["name"])); - $language = trim($_REQUEST["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" && $email!=$emailSource) - { - $pending = $emailSource; - $home = $this->users->getHome($emailSource); - $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); - $this->response->status = $this->users->save($fileNameUser, $email, "no", $name, $language, "unverified", "", "", "", $pending, $home) ? "ok" : "error"; - if($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); - } - if($this->response->status=="ok") - { - $pending = $email.':'.(empty($password) ? $this->users->getHash($emailSource) : $this->users->createHash($password)); - $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); - $this->response->status = $this->users->save($fileNameUser, $emailSource, "", $name, $language, "", "", "", "", $pending) ? "ok" : "error"; - if($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); - } - if($this->response->status=="ok") - { - $action = $email!=$emailSource ? "verify" : "change"; - $this->response->status = $this->response->sendMail($scheme, $address, $base, $email, $action) ? "next" : "error"; - if($this->response->status=="error") $this->yellow->page->error(500, "Can't send email on this server!"); - } - } else { - if($this->response->status=="ok") - { - $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); - $this->response->status = $this->users->save($fileNameUser, $email, "", $name, $language) ? "done" : "error"; - if($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); - } - } - if($this->response->status=="done") - { - $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); - $statusCode = $this->yellow->sendStatus(303, $location); - } else { - $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); - } - return $statusCode; - } + // Process request for forgotten password + public function processRequestForgot($scheme, $address, $base, $location, $fileName) { + $this->response->action = "forgot"; + $this->response->status = "ok"; + $email = trim($_REQUEST["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->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!"); + } + $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); + return $statusCode; + } + + // Process request to recover password + public function processRequestRecover($scheme, $address, $base, $location, $fileName) { + $this->response->action = "recover"; + $this->response->status = "ok"; + $email = trim($_REQUEST["email"]); + $password = trim($_REQUEST["password"]); + $this->response->status = $this->getUserStatus($email, $_REQUEST["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") { + $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); + $this->response->status = $this->users->save($fileNameUser, $email, $password) ? "ok" : "error"; + if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); + } + if ($this->response->status=="ok") { + $this->response->destroyCookies($scheme, $address, $base); + $this->response->status = "done"; + } + } + $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); + return $statusCode; + } + + // Process request to reactivate account + public function processRequestReactivate($scheme, $address, $base, $location, $fileName) { + $this->response->action = "reactivate"; + $this->response->status = "ok"; + $email = $_REQUEST["email"]; + $this->response->status = $this->getUserStatus($email, $_REQUEST["action"]); + if ($this->response->status=="ok") { + $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); + $this->response->status = $this->users->save($fileNameUser, $email, "", "", "", "active") ? "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); + return $statusCode; + } + + // Process request to change settings + public function processRequestSettings($scheme, $address, $base, $location, $fileName) { + $this->response->action = "settings"; + $this->response->status = "ok"; + $email = trim($_REQUEST["email"]); + $emailSource = $this->response->userEmail; + $password = trim($_REQUEST["password"]); + $name = trim(preg_replace("/[^\pL\d\-\. ]/u", "-", $_REQUEST["name"])); + $language = trim($_REQUEST["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" && $email!=$emailSource) { + $pending = $emailSource; + $home = $this->users->getHome($emailSource); + $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); + $this->response->status = $this->users->save($fileNameUser, $email, "no", $name, $language, "unverified", "", "", "", $pending, $home) ? "ok" : "error"; + if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); + } + if ($this->response->status=="ok") { + $pending = $email.":".(empty($password) ? $this->users->getHash($emailSource) : $this->users->createHash($password)); + $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); + $this->response->status = $this->users->save($fileNameUser, $emailSource, "", $name, $language, "", "", "", "", $pending) ? "ok" : "error"; + if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); + } + if ($this->response->status=="ok") { + $action = $email!=$emailSource ? "verify" : "change"; + $this->response->status = $this->response->sendMail($scheme, $address, $base, $email, $action) ? "next" : "error"; + if ($this->response->status=="error") $this->yellow->page->error(500, "Can't send email on this server!"); + } + } else { + if ($this->response->status=="ok") { + $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); + $this->response->status = $this->users->save($fileNameUser, $email, "", $name, $language) ? "done" : "error"; + if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); + } + } + if ($this->response->status=="done") { + $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); + $statusCode = $this->yellow->sendStatus(303, $location); + } else { + $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); + } + return $statusCode; + } - // Process request to verify email - function processRequestVerify($scheme, $address, $base, $location, $fileName) - { - $this->response->action = "verify"; - $this->response->status = "ok"; - $email = $emailSource = $_REQUEST["email"]; - $this->response->status = $this->getUserStatus($email, $_REQUEST["action"]); - if($this->response->status=="ok") - { - $emailSource = $this->users->getPending($email); - if($this->users->getStatus($emailSource)!="active") $this->response->status = "done"; - } - if($this->response->status=="ok") - { - $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); - $this->response->status = $this->users->save($fileNameUser, $email, "", "", "", "unchanged") ? "ok" : "error"; - if($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); - } - if($this->response->status=="ok") - { - $this->response->status = $this->response->sendMail($scheme, $address, $base, $emailSource, "change") ? "done" : "error"; - if($this->response->status=="error") $this->yellow->page->error(500, "Can't send email on this server!"); - } - $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); - return $statusCode; - } - - // Process request to change email or password - function processRequestChange($scheme, $address, $base, $location, $fileName) - { - $this->response->action = "change"; - $this->response->status = "ok"; - $email = $emailSource = trim($_REQUEST["email"]); - $this->response->status = $this->getUserStatus($email, $_REQUEST["action"]); - if($this->response->status=="ok") - { - list($email, $hash) = explode(':', $this->users->getPending($email), 2); - if(!$this->users->isExisting($email) || empty($hash)) $this->response->status = "done"; - } - if($this->response->status=="ok") - { - $this->users->users[$email]["hash"] = $hash; - $this->users->users[$email]["pending"] = "none"; - $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); - $this->response->status = $this->users->save($fileNameUser, $email, "", "", "", "active") ? "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->config->get("configDir").$this->yellow->config->get("editUserFile"); - $this->response->status = $this->users->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") - { - $this->response->destroyCookies($scheme, $address, $base); - $this->response->status = "done"; - } - $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); - return $statusCode; - } - - // Process request to show software version - function processRequestVersion($scheme, $address, $base, $location, $fileName) - { - $this->response->action = "version"; - $this->response->status = "ok"; - if($this->yellow->plugins->isExisting("update")) - { - list($statusCodeCurrent, $dataCurrent) = $this->yellow->plugins->get("update")->getSoftwareVersion(); - list($statusCodeLatest, $dataLatest) = $this->yellow->plugins->get("update")->getSoftwareVersion(true); - list($statusCodeModified, $dataModified) = $this->yellow->plugins->get("update")->getSoftwareModified(); - $statusCode = max($statusCodeCurrent, $statusCodeLatest, $statusCodeModified); - if($this->response->isUserWebmaster()) - { - foreach($dataCurrent as $key=>$value) - { - if(strnatcasecmp($dataCurrent[$key], $dataLatest[$key])<0) - { - ++$updates; - $rawData = htmlspecialchars("$key $dataLatest[$key]")."<br />\n"; - $this->response->rawDataOutput .= $rawData; - } - } - if($updates==0) - { - foreach($dataCurrent as $key=>$value) - { - if(!is_null($dataModified[$key]) && !is_null($dataLatest[$key])) - { - $rawData = $this->yellow->text->getTextHtml("editVersionUpdateModified", $this->response->language)." - <a href=\"#\" data-action=\"update\" data-status=\"update\" data-args=\"".$this->yellow->toolbox->normaliseArgs("option:force/feature:$key")."\">".$this->yellow->text->getTextHtml("editVersionUpdateForce", $this->response->language)."</a><br />\n"; - $rawData = preg_replace("/@software/i", htmlspecialchars("$key $dataLatest[$key]"), $rawData); - $this->response->rawDataOutput .= $rawData; - } - } - } - $this->response->status = $updates ? "updates" : "done"; - } else { - foreach($dataCurrent as $key=>$value) - { - if(strnatcasecmp($dataCurrent[$key], $dataLatest[$key])<0) ++$updates; - } - $this->response->status = $updates ? "warning" : "done"; - } - if($statusCode!=200) $this->response->status = "error"; - } - $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); - return $statusCode; - } - - // Process request to update website - function processRequestUpdate($scheme, $address, $base, $location, $fileName) - { - $statusCode = 0; - if($this->yellow->plugins->isExisting("update") && $this->response->isUserWebmaster()) - { - $option = trim($_REQUEST["option"]); - $feature = trim($_REQUEST["feature"]); - $statusCode = $this->yellow->command("update", $option, $feature); - if($statusCode==200) - { - $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); - $statusCode = $this->yellow->sendStatus(303, $location); - } - } - return $statusCode; - } - - // Process request to quit account - function processRequestQuit($scheme, $address, $base, $location, $fileName) - { - $this->response->action = "quit"; - $this->response->status = "ok"; - $name = trim($_REQUEST["name"]); - $email = $this->response->userEmail; - if(empty($name)) $this->response->status = "none"; - if($this->response->status=="ok" && $name!=$this->users->getName($email)) $this->response->status = "mismatch"; - if($this->response->status=="ok") $this->response->status = $this->getUserAccount($email, "", $this->response->action); - 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!"); - } - $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); - return $statusCode; - } - - // Process request to remove account - function processRequestRemove($scheme, $address, $base, $location, $fileName) - { - $this->response->action = "remove"; - $this->response->status = "ok"; - $email = $_REQUEST["email"]; - $this->response->status = $this->getUserStatus($email, $_REQUEST["action"]); - if($this->response->status=="ok") - { - $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); - $this->response->status = $this->users->save($fileNameUser, $email, "", "", "", "removed") ? "ok" : "error"; - if($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); - } - 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->config->get("configDir").$this->yellow->config->get("editUserFile"); - $this->response->status = $this->users->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") - { - $this->response->destroyCookies($scheme, $address, $base); - $this->response->status = "done"; - } - $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); - return $statusCode; - } - - // Process request to create page - function processRequestCreate($scheme, $address, $base, $location, $fileName) - { - $statusCode = 0; - if(!$this->response->isUserRestrictions() && !empty($_REQUEST["rawdataedit"])) - { - $this->response->rawDataSource = $_REQUEST["rawdatasource"]; - $this->response->rawDataEdit = $_REQUEST["rawdatasource"]; - $this->response->rawDataEndOfLine = $_REQUEST["rawdataendofline"]; - $rawData = $_REQUEST["rawdataedit"]; - $page = $this->response->getPageNew($scheme, $address, $base, $location, $fileName, $rawData, $this->response->getEndOfLine()); - if(!$page->isError()) - { - if($this->yellow->toolbox->createFile($page->fileName, $page->rawData, true)) - { - $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $page->location); - $statusCode = $this->yellow->sendStatus(303, $location); - } else { - $this->yellow->page->error(500, "Can't write file '$page->fileName'!"); - $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); - } - } else { - $this->yellow->page->error(500, $page->get("pageError")); - $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); - } - } - return $statusCode; - } - - // Process request to edit page - function processRequestEdit($scheme, $address, $base, $location, $fileName) - { - $statusCode = 0; - if(!$this->response->isUserRestrictions() && !empty($_REQUEST["rawdataedit"])) - { - $this->response->rawDataSource = $_REQUEST["rawdatasource"]; - $this->response->rawDataEdit = $_REQUEST["rawdataedit"]; - $this->response->rawDataEndOfLine = $_REQUEST["rawdataendofline"]; - $rawDataFile = $this->yellow->toolbox->readFile($fileName); - $page = $this->response->getPageEdit($scheme, $address, $base, $location, $fileName, - $this->response->rawDataSource, $this->response->rawDataEdit, $rawDataFile, $this->response->rawDataEndOfLine); - if(!$page->isError()) - { - if($this->yellow->toolbox->renameFile($fileName, $page->fileName, true) && - $this->yellow->toolbox->createFile($page->fileName, $page->rawData)) - { - $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $page->location); - $statusCode = $this->yellow->sendStatus(303, $location); - } else { - $this->yellow->page->error(500, "Can't write file '$page->fileName'!"); - $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); - } - } else { - $this->yellow->page->error(500, $page->get("pageError")); - $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); - } - } - return $statusCode; - } + // Process request to verify email + public function processRequestVerify($scheme, $address, $base, $location, $fileName) { + $this->response->action = "verify"; + $this->response->status = "ok"; + $email = $emailSource = $_REQUEST["email"]; + $this->response->status = $this->getUserStatus($email, $_REQUEST["action"]); + if ($this->response->status=="ok") { + $emailSource = $this->users->getPending($email); + if ($this->users->getStatus($emailSource)!="active") $this->response->status = "done"; + } + if ($this->response->status=="ok") { + $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); + $this->response->status = $this->users->save($fileNameUser, $email, "", "", "", "unchanged") ? "ok" : "error"; + if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); + } + if ($this->response->status=="ok") { + $this->response->status = $this->response->sendMail($scheme, $address, $base, $emailSource, "change") ? "done" : "error"; + if ($this->response->status=="error") $this->yellow->page->error(500, "Can't send email on this server!"); + } + $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); + return $statusCode; + } + + // Process request to change email or password + public function processRequestChange($scheme, $address, $base, $location, $fileName) { + $this->response->action = "change"; + $this->response->status = "ok"; + $email = $emailSource = trim($_REQUEST["email"]); + $this->response->status = $this->getUserStatus($email, $_REQUEST["action"]); + if ($this->response->status=="ok") { + list($email, $hash) = explode(":", $this->users->getPending($email), 2); + if (!$this->users->isExisting($email) || empty($hash)) $this->response->status = "done"; + } + if ($this->response->status=="ok") { + $this->users->users[$email]["hash"] = $hash; + $this->users->users[$email]["pending"] = "none"; + $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); + $this->response->status = $this->users->save($fileNameUser, $email, "", "", "", "active") ? "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->config->get("configDir").$this->yellow->config->get("editUserFile"); + $this->response->status = $this->users->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") { + $this->response->destroyCookies($scheme, $address, $base); + $this->response->status = "done"; + } + $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); + return $statusCode; + } + + // Process request to show software version + public function processRequestVersion($scheme, $address, $base, $location, $fileName) { + $this->response->action = "version"; + $this->response->status = "ok"; + if ($this->yellow->plugins->isExisting("update")) { + list($statusCodeCurrent, $dataCurrent) = $this->yellow->plugins->get("update")->getSoftwareVersion(); + list($statusCodeLatest, $dataLatest) = $this->yellow->plugins->get("update")->getSoftwareVersion(true); + list($statusCodeModified, $dataModified) = $this->yellow->plugins->get("update")->getSoftwareModified(); + $statusCode = max($statusCodeCurrent, $statusCodeLatest, $statusCodeModified); + if ($this->response->isUserWebmaster()) { + foreach ($dataCurrent as $key=>$value) { + if (strnatcasecmp($dataCurrent[$key], $dataLatest[$key])<0) { + ++$updates; + $rawData = htmlspecialchars("$key $dataLatest[$key]")."<br />\n"; + $this->response->rawDataOutput .= $rawData; + } + } + if ($updates==0) { + foreach ($dataCurrent as $key=>$value) { + if (!is_null($dataModified[$key]) && !is_null($dataLatest[$key])) { + $rawData = $this->yellow->text->getTextHtml("editVersionUpdateModified", $this->response->language)." - <a href=\"#\" data-action=\"update\" data-status=\"update\" data-args=\"".$this->yellow->toolbox->normaliseArgs("option:force/feature:$key")."\">".$this->yellow->text->getTextHtml("editVersionUpdateForce", $this->response->language)."</a><br />\n"; + $rawData = preg_replace("/@software/i", htmlspecialchars("$key $dataLatest[$key]"), $rawData); + $this->response->rawDataOutput .= $rawData; + } + } + } + $this->response->status = $updates ? "updates" : "done"; + } else { + foreach ($dataCurrent as $key=>$value) { + if (strnatcasecmp($dataCurrent[$key], $dataLatest[$key])<0) ++$updates; + } + $this->response->status = $updates ? "warning" : "done"; + } + if ($statusCode!=200) $this->response->status = "error"; + } + $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); + return $statusCode; + } + + // Process request to update website + public function processRequestUpdate($scheme, $address, $base, $location, $fileName) { + $statusCode = 0; + if ($this->yellow->plugins->isExisting("update") && $this->response->isUserWebmaster()) { + $option = trim($_REQUEST["option"]); + $feature = trim($_REQUEST["feature"]); + $statusCode = $this->yellow->command("update", $option, $feature); + if ($statusCode==200) { + $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); + $statusCode = $this->yellow->sendStatus(303, $location); + } + } + return $statusCode; + } + + // Process request to quit account + public function processRequestQuit($scheme, $address, $base, $location, $fileName) { + $this->response->action = "quit"; + $this->response->status = "ok"; + $name = trim($_REQUEST["name"]); + $email = $this->response->userEmail; + if (empty($name)) $this->response->status = "none"; + if ($this->response->status=="ok" && $name!=$this->users->getName($email)) $this->response->status = "mismatch"; + if ($this->response->status=="ok") $this->response->status = $this->getUserAccount($email, "", $this->response->action); + 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!"); + } + $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); + return $statusCode; + } + + // Process request to remove account + public function processRequestRemove($scheme, $address, $base, $location, $fileName) { + $this->response->action = "remove"; + $this->response->status = "ok"; + $email = $_REQUEST["email"]; + $this->response->status = $this->getUserStatus($email, $_REQUEST["action"]); + if ($this->response->status=="ok") { + $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); + $this->response->status = $this->users->save($fileNameUser, $email, "", "", "", "removed") ? "ok" : "error"; + if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); + } + 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->config->get("configDir").$this->yellow->config->get("editUserFile"); + $this->response->status = $this->users->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") { + $this->response->destroyCookies($scheme, $address, $base); + $this->response->status = "done"; + } + $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); + return $statusCode; + } + + // Process request to create page + public function processRequestCreate($scheme, $address, $base, $location, $fileName) { + $statusCode = 0; + if (!$this->response->isUserRestrictions() && !empty($_REQUEST["rawdataedit"])) { + $this->response->rawDataSource = $_REQUEST["rawdatasource"]; + $this->response->rawDataEdit = $_REQUEST["rawdatasource"]; + $this->response->rawDataEndOfLine = $_REQUEST["rawdataendofline"]; + $rawData = $_REQUEST["rawdataedit"]; + $page = $this->response->getPageNew($scheme, $address, $base, $location, $fileName, $rawData, $this->response->getEndOfLine()); + if (!$page->isError()) { + if ($this->yellow->toolbox->createFile($page->fileName, $page->rawData, true)) { + $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $page->location); + $statusCode = $this->yellow->sendStatus(303, $location); + } else { + $this->yellow->page->error(500, "Can't write file '$page->fileName'!"); + $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); + } + } else { + $this->yellow->page->error(500, $page->get("pageError")); + $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); + } + } + return $statusCode; + } + + // Process request to edit page + public function processRequestEdit($scheme, $address, $base, $location, $fileName) { + $statusCode = 0; + if (!$this->response->isUserRestrictions() && !empty($_REQUEST["rawdataedit"])) { + $this->response->rawDataSource = $_REQUEST["rawdatasource"]; + $this->response->rawDataEdit = $_REQUEST["rawdataedit"]; + $this->response->rawDataEndOfLine = $_REQUEST["rawdataendofline"]; + $rawDataFile = $this->yellow->toolbox->readFile($fileName); + $page = $this->response->getPageEdit($scheme, $address, $base, $location, $fileName, + $this->response->rawDataSource, $this->response->rawDataEdit, $rawDataFile, $this->response->rawDataEndOfLine); + if (!$page->isError()) { + if ($this->yellow->toolbox->renameFile($fileName, $page->fileName, true) && + $this->yellow->toolbox->createFile($page->fileName, $page->rawData)) { + $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $page->location); + $statusCode = $this->yellow->sendStatus(303, $location); + } else { + $this->yellow->page->error(500, "Can't write file '$page->fileName'!"); + $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); + } + } else { + $this->yellow->page->error(500, $page->get("pageError")); + $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); + } + } + return $statusCode; + } - // Process request to delete page - function processRequestDelete($scheme, $address, $base, $location, $fileName) - { - $statusCode = 0; - if(!$this->response->isUserRestrictions() && is_file($fileName)) - { - $this->response->rawDataSource = $_REQUEST["rawdatasource"]; - $this->response->rawDataEdit = $_REQUEST["rawdatasource"]; - $this->response->rawDataEndOfLine = $_REQUEST["rawdataendofline"]; - $rawDataFile = $this->yellow->toolbox->readFile($fileName); - $page = $this->response->getPageDelete($scheme, $address, $base, $location, $fileName, - $rawDataFile, $this->response->rawDataEndOfLine); - if(!$page->isError()) - { - if($this->yellow->lookup->isFileLocation($location)) - { - if($this->yellow->toolbox->deleteFile($fileName, $this->yellow->config->get("trashDir"))) - { - $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); - $statusCode = $this->yellow->sendStatus(303, $location); - } else { - $this->yellow->page->error(500, "Can't delete file '$fileName'!"); - $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); - } - } else { - if($this->yellow->toolbox->deleteDirectory(dirname($fileName), $this->yellow->config->get("trashDir"))) - { - $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); - $statusCode = $this->yellow->sendStatus(303, $location); - } else { - $this->yellow->page->error(500, "Can't delete file '$fileName'!"); - $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); - } - } - } else { - $this->yellow->page->error(500, $page->get("pageError")); - $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); - } - } - return $statusCode; - } + // Process request to delete page + public function processRequestDelete($scheme, $address, $base, $location, $fileName) { + $statusCode = 0; + if (!$this->response->isUserRestrictions() && is_file($fileName)) { + $this->response->rawDataSource = $_REQUEST["rawdatasource"]; + $this->response->rawDataEdit = $_REQUEST["rawdatasource"]; + $this->response->rawDataEndOfLine = $_REQUEST["rawdataendofline"]; + $rawDataFile = $this->yellow->toolbox->readFile($fileName); + $page = $this->response->getPageDelete($scheme, $address, $base, $location, $fileName, + $rawDataFile, $this->response->rawDataEndOfLine); + if (!$page->isError()) { + if ($this->yellow->lookup->isFileLocation($location)) { + if ($this->yellow->toolbox->deleteFile($fileName, $this->yellow->config->get("trashDir"))) { + $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); + $statusCode = $this->yellow->sendStatus(303, $location); + } else { + $this->yellow->page->error(500, "Can't delete file '$fileName'!"); + $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); + } + } else { + if ($this->yellow->toolbox->deleteDirectory(dirname($fileName), $this->yellow->config->get("trashDir"))) { + $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); + $statusCode = $this->yellow->sendStatus(303, $location); + } else { + $this->yellow->page->error(500, "Can't delete file '$fileName'!"); + $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); + } + } + } else { + $this->yellow->page->error(500, $page->get("pageError")); + $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); + } + } + return $statusCode; + } - // Process request to show preview - function processRequestPreview($scheme, $address, $base, $location, $fileName) - { - $page = $this->response->getPagePreview($scheme, $address, $base, $location, $fileName, - $_REQUEST["rawdataedit"], $_REQUEST["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"; - } - return $statusCode; - } - - // Process request to upload file - function processRequestUpload($scheme, $address, $base, $location, $fileName) - { - $data = array(); - $fileNameTemp = $_FILES["file"]["tmp_name"]; - $fileNameShort = preg_replace("/[^\pL\d\-\.]/u", "-", basename($_FILES["file"]["name"])); - $fileSizeMax = $this->yellow->toolbox->getNumberBytes(ini_get("upload_max_filesize")); - $extension = strtoloweru(($pos = strrposu($fileNameShort, '.')) ? substru($fileNameShort, $pos) : ""); - $extensions = preg_split("/\s*,\s*/", $this->yellow->config->get("editUploadExtensions")); - if(!$this->response->isUserRestrictions() && is_uploaded_file($fileNameTemp) && - filesize($fileNameTemp)<=$fileSizeMax && in_array($extension, $extensions)) - { - $file = $this->response->getFileUpload($scheme, $address, $base, $location, $fileNameTemp, $fileNameShort); - if(!$file->isError() && $this->yellow->toolbox->copyFile($fileNameTemp, $file->fileName, true)) - { - $data["location"] = $file->getLocation(); - } else { - $data["error"] = "Can't write file '$file->fileName'!"; - } - } else { - $data["error"] = "Can't write file '$fileNameShort'!"; - } - $statusCode = $this->yellow->sendData(is_null($data["error"]) ? 200 : 500, json_encode($data), "a.json", false); - return $statusCode; - } - - // Check request - function checkRequest($location) - { - $locationLength = strlenu($this->yellow->config->get("editLocation")); - $this->response->active = substru($location, 0, $locationLength)==$this->yellow->config->get("editLocation"); - return $this->response->isActive(); - } - - // Check user authentication - function checkUserAuth($scheme, $address, $base, $location, $fileName) - { - if($this->isRequestSameSite("POST", $scheme, $address) || $_REQUEST["action"]=="") - { - if($_REQUEST["action"]=="login") - { - $email = $_REQUEST["email"]; - $password = $_REQUEST["password"]; - if($this->users->checkAuthLogin($email, $password)) - { - $this->response->createCookies($scheme, $address, $base, $email); - $this->response->userEmail = $email; - $this->response->userRestrictions = $this->getUserRestrictions($email, $location, $fileName); - $this->response->language = $this->getUserLanguage($email); - } else { - $this->response->userFailedError = "login"; - $this->response->userFailedEmail = $email; - $this->response->userFailedExpire = PHP_INT_MAX; - } - } else if(isset($_COOKIE["authtoken"]) && isset($_COOKIE["csrftoken"])) { - if($this->users->checkAuthToken($_COOKIE["authtoken"], $_COOKIE["csrftoken"], $_POST["csrftoken"], $_REQUEST["action"]=="")) - { - $this->response->userEmail = $email = $this->users->getAuthEmail($_COOKIE["authtoken"]); - $this->response->userRestrictions = $this->getUserRestrictions($email, $location, $fileName); - $this->response->language = $this->getUserLanguage($email); - } else { - $this->response->userFailedError = "auth"; - $this->response->userFailedEmail = $this->users->getAuthEmail($_COOKIE["authtoken"]); - $this->response->userFailedExpire = $this->users->getAuthExpire($_COOKIE["authtoken"]); - } - } - } - return $this->response->isUser(); - } + // Process request to show preview + public function processRequestPreview($scheme, $address, $base, $location, $fileName) { + $page = $this->response->getPagePreview($scheme, $address, $base, $location, $fileName, + $_REQUEST["rawdataedit"], $_REQUEST["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"; + } + return $statusCode; + } + + // Process request to upload file + public function processRequestUpload($scheme, $address, $base, $location, $fileName) { + $data = array(); + $fileNameTemp = $_FILES["file"]["tmp_name"]; + $fileNameShort = preg_replace("/[^\pL\d\-\.]/u", "-", basename($_FILES["file"]["name"])); + $fileSizeMax = $this->yellow->toolbox->getNumberBytes(ini_get("upload_max_filesize")); + $extension = strtoloweru(($pos = strrposu($fileNameShort, ".")) ? substru($fileNameShort, $pos) : ""); + $extensions = preg_split("/\s*,\s*/", $this->yellow->config->get("editUploadExtensions")); + if (!$this->response->isUserRestrictions() && is_uploaded_file($fileNameTemp) && + filesize($fileNameTemp)<=$fileSizeMax && in_array($extension, $extensions)) { + $file = $this->response->getFileUpload($scheme, $address, $base, $location, $fileNameTemp, $fileNameShort); + if (!$file->isError() && $this->yellow->toolbox->copyFile($fileNameTemp, $file->fileName, true)) { + $data["location"] = $file->getLocation(); + } else { + $data["error"] = "Can't write file '$file->fileName'!"; + } + } else { + $data["error"] = "Can't write file '$fileNameShort'!"; + } + $statusCode = $this->yellow->sendData(is_null($data["error"]) ? 200 : 500, json_encode($data), "a.json", false); + return $statusCode; + } + + // Check request + public function checkRequest($location) { + $locationLength = strlenu($this->yellow->config->get("editLocation")); + $this->response->active = substru($location, 0, $locationLength)==$this->yellow->config->get("editLocation"); + return $this->response->isActive(); + } + + // Check user authentication + public function checkUserAuth($scheme, $address, $base, $location, $fileName) { + if ($this->isRequestSameSite("POST", $scheme, $address) || $_REQUEST["action"]=="") { + if ($_REQUEST["action"]=="login") { + $email = $_REQUEST["email"]; + $password = $_REQUEST["password"]; + if ($this->users->checkAuthLogin($email, $password)) { + $this->response->createCookies($scheme, $address, $base, $email); + $this->response->userEmail = $email; + $this->response->userRestrictions = $this->getUserRestrictions($email, $location, $fileName); + $this->response->language = $this->getUserLanguage($email); + } else { + $this->response->userFailedError = "login"; + $this->response->userFailedEmail = $email; + $this->response->userFailedExpire = PHP_INT_MAX; + } + } elseif (isset($_COOKIE["authtoken"]) && isset($_COOKIE["csrftoken"])) { + if ($this->users->checkAuthToken($_COOKIE["authtoken"], $_COOKIE["csrftoken"], $_POST["csrftoken"], $_REQUEST["action"]=="")) { + $this->response->userEmail = $email = $this->users->getAuthEmail($_COOKIE["authtoken"]); + $this->response->userRestrictions = $this->getUserRestrictions($email, $location, $fileName); + $this->response->language = $this->getUserLanguage($email); + } else { + $this->response->userFailedError = "auth"; + $this->response->userFailedEmail = $this->users->getAuthEmail($_COOKIE["authtoken"]); + $this->response->userFailedExpire = $this->users->getAuthExpire($_COOKIE["authtoken"]); + } + } + } + return $this->response->isUser(); + } - // Check user without authentication - function checkUserUnauth($scheme, $address, $base, $location, $fileName) - { - $ok = false; - if($_REQUEST["action"]=="" || $_REQUEST["action"]=="signup" || $_REQUEST["action"]=="forgot") - { - $ok = true; - } else if(isset($_REQUEST["actiontoken"])) { - if($this->users->checkActionToken($_REQUEST["actiontoken"], $_REQUEST["email"], $_REQUEST["action"], $_REQUEST["expire"])) - { - $ok = true; - $this->response->language = $this->getUserLanguage($_REQUEST["email"]); - } else { - $this->response->userFailedError = "action"; - $this->response->userFailedEmail = $_REQUEST["email"]; - $this->response->userFailedExpire = $_REQUEST["expire"]; - } - } - return $ok; - } + // Check user without authentication + public function checkUserUnauth($scheme, $address, $base, $location, $fileName) { + $ok = false; + if ($_REQUEST["action"]=="" || $_REQUEST["action"]=="signup" || $_REQUEST["action"]=="forgot") { + $ok = true; + } elseif (isset($_REQUEST["actiontoken"])) { + if ($this->users->checkActionToken($_REQUEST["actiontoken"], $_REQUEST["email"], $_REQUEST["action"], $_REQUEST["expire"])) { + $ok = true; + $this->response->language = $this->getUserLanguage($_REQUEST["email"]); + } else { + $this->response->userFailedError = "action"; + $this->response->userFailedEmail = $_REQUEST["email"]; + $this->response->userFailedExpire = $_REQUEST["expire"]; + } + } + return $ok; + } - // Check user failed - function checkUserFailed($scheme, $address, $base, $location, $fileName) - { - if(!empty($this->response->userFailedError)) - { - if($this->response->userFailedExpire>time() && $this->users->isExisting($this->response->userFailedEmail)) - { - $email = $this->response->userFailedEmail; - $modified = $this->users->getModified($email); - $errors = $this->users->getErrors($email)+1; - $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); - $status = $this->users->save($fileNameUser, $email, "", "", "", "", "", $modified, $errors) ? "ok" : "error"; - if($status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); - if($errors==$this->yellow->config->get("editBruteForceProtection")) - { - $statusBeforeProtection = $this->users->getStatus($email); - $statusAfterProtection = ($statusBeforeProtection=="active" || $statusBeforeProtection=="inactive") ? "inactive" : "failed"; - if($status=="ok") - { - $status = $this->users->save($fileNameUser, $email, "", "", "", $statusAfterProtection, "", $modified, $errors) ? "ok" : "error"; - if($status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); - } - if($status=="ok" && $statusBeforeProtection=="active") - { - $status = $this->response->sendMail($scheme, $address, $base, $email, "reactivate") ? "done" : "error"; - if($status=="error") $this->yellow->page->error(500, "Can't send email on this server!"); - } - } - } - if($this->response->userFailedError=="login" || $this->response->userFailedError=="auth") - { - $this->response->destroyCookies($scheme, $address, $base); - $this->response->status = "error"; - $this->yellow->page->error(430); - } else { - $this->response->status = "error"; - $this->yellow->page->error(500, "Link has expired!"); - } - } - } - - // Return user status changes - function getUserStatus($email, $action) - { - switch($action) - { - case "confirm": $statusExpected = "unconfirmed"; break; - case "approve": $statusExpected = "unapproved"; break; - case "recover": $statusExpected = "active"; break; - case "reactivate": $statusExpected = "inactive"; break; - case "verify": $statusExpected = "unverified"; break; - case "change": $statusExpected = "active"; break; - case "remove": $statusExpected = "active"; break; - } - return $this->users->getStatus($email)==$statusExpected ? "ok" : "done"; - } + // 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)) { + $email = $this->response->userFailedEmail; + $modified = $this->users->getModified($email); + $errors = $this->users->getErrors($email)+1; + $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); + $status = $this->users->save($fileNameUser, $email, "", "", "", "", "", $modified, $errors) ? "ok" : "error"; + if ($status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); + if ($errors==$this->yellow->config->get("editBruteForceProtection")) { + $statusBeforeProtection = $this->users->getStatus($email); + $statusAfterProtection = ($statusBeforeProtection=="active" || $statusBeforeProtection=="inactive") ? "inactive" : "failed"; + if ($status=="ok") { + $status = $this->users->save($fileNameUser, $email, "", "", "", $statusAfterProtection, "", $modified, $errors) ? "ok" : "error"; + if ($status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); + } + if ($status=="ok" && $statusBeforeProtection=="active") { + $status = $this->response->sendMail($scheme, $address, $base, $email, "reactivate") ? "done" : "error"; + if ($status=="error") $this->yellow->page->error(500, "Can't send email on this server!"); + } + } + } + if ($this->response->userFailedError=="login" || $this->response->userFailedError=="auth") { + $this->response->destroyCookies($scheme, $address, $base); + $this->response->status = "error"; + $this->yellow->page->error(430); + } else { + $this->response->status = "error"; + $this->yellow->page->error(500, "Link has expired!"); + } + } + } + + // Return user status changes + public function getUserStatus($email, $action) { + switch ($action) { + case "confirm": $statusExpected = "unconfirmed"; break; + case "approve": $statusExpected = "unapproved"; break; + case "recover": $statusExpected = "active"; break; + case "reactivate": $statusExpected = "inactive"; break; + case "verify": $statusExpected = "unverified"; break; + case "change": $statusExpected = "active"; break; + case "remove": $statusExpected = "active"; break; + } + return $this->users->getStatus($email)==$statusExpected ? "ok" : "done"; + } - // Return user account changes - function getUserAccount($email, $password, $action) - { - $status = null; - foreach($this->yellow->plugins->plugins as $key=>$value) - { - if(method_exists($value["obj"], "onEditUserAccount")) - { - $status = $value["obj"]->onEditUserAccount($email, $password, $action, $this->users); - if(!is_null($status)) break; - } - } - if(is_null($status)) - { - $status = "ok"; - if(!empty($password) && strlenu($password)<$this->yellow->config->get("editUserPasswordMinLength")) $status = "weak"; - if(!empty($email) && !filter_var($email, FILTER_VALIDATE_EMAIL)) $status = "invalid"; - } - return $status; - } - - // Return user restrictions - function getUserRestrictions($email, $location, $fileName) - { - $userRestrictions = null; - foreach($this->yellow->plugins->plugins as $key=>$value) - { - if(method_exists($value["obj"], "onEditUserRestrictions")) - { - $userRestrictions = $value["obj"]->onEditUserRestrictions($email, $location, $fileName, $this->users); - if(!is_null($userRestrictions)) break; - } - } - if(is_null($userRestrictions)) - { - $userRestrictions = substru($location, 0, strlenu($this->users->getHome($email)))!=$this->users->getHome($email); - $userRestrictions |= empty($fileName) || strlenu(dirname($fileName))>128 || strlenu(basename($fileName))>128; - } - return $userRestrictions; - } - - // Return user language - function getUserLanguage($email) - { - $language = $this->users->getLanguage($email); - if(!$this->yellow->text->isLanguage($language)) $language = $this->yellow->config->get("language"); - return $language; - } - - // Check if request came from same site - function isRequestSameSite($method, $scheme, $address) - { - if(preg_match("#^(\w+)://([^/]+)(.*)$#", $_SERVER["HTTP_REFERER"], $matches)) $origin = "$matches[1]://$matches[2]"; - if(isset($_SERVER["HTTP_ORIGIN"])) $origin = $_SERVER["HTTP_ORIGIN"]; - return $_SERVER["REQUEST_METHOD"]==$method && $origin=="$scheme://$address"; - } + // Return user account changes + public function getUserAccount($email, $password, $action) { + $status = null; + foreach ($this->yellow->plugins->plugins as $key=>$value) { + if (method_exists($value["obj"], "onEditUserAccount")) { + $status = $value["obj"]->onEditUserAccount($email, $password, $action, $this->users); + if (!is_null($status)) break; + } + } + if (is_null($status)) { + $status = "ok"; + if (!empty($password) && strlenu($password)<$this->yellow->config->get("editUserPasswordMinLength")) $status = "weak"; + if (!empty($email) && !filter_var($email, FILTER_VALIDATE_EMAIL)) $status = "invalid"; + } + return $status; + } + + // Return user restrictions + public function getUserRestrictions($email, $location, $fileName) { + $userRestrictions = null; + foreach ($this->yellow->plugins->plugins as $key=>$value) { + if (method_exists($value["obj"], "onEditUserRestrictions")) { + $userRestrictions = $value["obj"]->onEditUserRestrictions($email, $location, $fileName, $this->users); + if (!is_null($userRestrictions)) break; + } + } + if (is_null($userRestrictions)) { + $userRestrictions = substru($location, 0, strlenu($this->users->getHome($email)))!=$this->users->getHome($email); + $userRestrictions |= empty($fileName) || strlenu(dirname($fileName))>128 || strlenu(basename($fileName))>128; + } + return $userRestrictions; + } + + // Return user language + public function getUserLanguage($email) { + $language = $this->users->getLanguage($email); + if (!$this->yellow->text->isLanguage($language)) $language = $this->yellow->config->get("language"); + return $language; + } + + // Check if request came from same site + public function isRequestSameSite($method, $scheme, $address) { + if (preg_match("#^(\w+)://([^/]+)(.*)$#", $_SERVER["HTTP_REFERER"], $matches)) $origin = "$matches[1]://$matches[2]"; + if (isset($_SERVER["HTTP_ORIGIN"])) $origin = $_SERVER["HTTP_ORIGIN"]; + return $_SERVER["REQUEST_METHOD"]==$method && $origin=="$scheme://$address"; + } } - -class YellowResponse -{ - var $yellow; //access to API - var $plugin; //access to plugin - var $active; //location is active? (boolean) - var $userEmail; //user email - var $userRestrictions; //user can change page? (boolean) - var $userFailedError; //error of failed authentication - var $userFailedEmail; //email of failed authentication - var $userFailedExpire; //expiration time of failed authentication - var $rawDataSource; //raw data of page for comparison - var $rawDataEdit; //raw data of page for editing - var $rawDataOutput; //raw data of dynamic output - var $rawDataEndOfLine; //end of line format for raw data - var $language; //response language - var $action; //response action - var $status; //response status - - function __construct($yellow) - { - $this->yellow = $yellow; - $this->plugin = $yellow->plugins->get("edit"); - } - - // Return new page - function getPageNew($scheme, $address, $base, $location, $fileName, $rawData, $endOfLine) - { - $page = new YellowPage($this->yellow); - $page->setRequestInformation($scheme, $address, $base, $location, $fileName); - $page->parseData($this->normaliseLines($rawData, $endOfLine), false, 0); - $this->editContentFile($page, "create"); - if($this->yellow->lookup->isFileLocation($location) || $this->yellow->pages->find($page->location)) - { - $page->location = $this->getPageNewLocation($page->rawData, $page->location, $page->get("pageNewLocation")); - $page->fileName = $this->yellow->lookup->findFileNew($page->location, $page->get("published")); - while($this->yellow->pages->find($page->location) || empty($page->fileName)) - { - $rawData = $this->yellow->toolbox->setMetaData($page->rawData, "title", $this->getTitleNext($page->rawData)); - $page->rawData = $this->normaliseLines($rawData, $endOfLine); - $page->location = $this->getPageNewLocation($page->rawData, $page->location, $page->get("pageNewLocation")); - $page->fileName = $this->yellow->lookup->findFileNew($page->location, $page->get("published")); - if(++$pageCounter>999) break; - } - if($this->yellow->pages->find($page->location) || empty($page->fileName)) - { - $page->error(500, "Page '".$page->get("title")."' is not possible!"); - } - } else { - $page->fileName = $this->yellow->lookup->findFileNew($page->location); - } - if($this->plugin->getUserRestrictions($this->userEmail, $page->location, $page->fileName)) - { - $page->error(500, "Page '".$page->get("title")."' is restricted!"); - } - return $page; - } - - // Return modified page - function getPageEdit($scheme, $address, $base, $location, $fileName, $rawDataSource, $rawDataEdit, $rawDataFile, $endOfLine) - { - $page = new YellowPage($this->yellow); - $page->setRequestInformation($scheme, $address, $base, $location, $fileName); - $rawData = $this->plugin->merge->merge( - $this->normaliseLines($rawDataSource, $endOfLine), - $this->normaliseLines($rawDataEdit, $endOfLine), - $this->normaliseLines($rawDataFile, $endOfLine)); - $page->parseData($this->normaliseLines($rawData, $endOfLine), false, 0); - $this->editContentFile($page, "edit"); - if(empty($page->rawData)) $page->error(500, "Page has been modified by someone else!"); - if($this->yellow->lookup->isFileLocation($location) && !$page->isError()) - { - $pageSource = new YellowPage($this->yellow); - $pageSource->setRequestInformation($scheme, $address, $base, $location, $fileName); - $pageSource->parseData($this->normaliseLines($rawDataSource, $endOfLine), false, 0); - if(substrb($pageSource->rawData, 0, $pageSource->metaDataOffsetBytes) != - substrb($page->rawData, 0, $page->metaDataOffsetBytes)) - { - $page->location = $this->getPageNewLocation($page->rawData, $page->location, $page->get("pageNewLocation")); - $page->fileName = $this->yellow->lookup->findFileNew($page->location, $page->get("published")); - if($page->location!=$pageSource->location) - { - if(!$this->yellow->lookup->isFileLocation($page->location) || empty($page->fileName)) - { - $page->error(500, "Page '".$page->get("title")."' is not possible!"); - } else if($this->yellow->pages->find($page->location)) { - $page->error(500, "Page '".$page->get("title")."' already exists!"); - } - } - } - } - if($this->plugin->getUserRestrictions($this->userEmail, $page->location, $page->fileName)) - { - $page->error(500, "Page '".$page->get("title")."' is restricted!"); - } - return $page; - } - - // Return deleted page - function getPageDelete($scheme, $address, $base, $location, $fileName, $rawData, $endOfLine) - { - $page = new YellowPage($this->yellow); - $page->setRequestInformation($scheme, $address, $base, $location, $fileName); - $page->parseData($this->normaliseLines($rawData, $endOfLine), false, 0); - $this->editContentFile($page, "delete"); - if($this->plugin->getUserRestrictions($this->userEmail, $page->location, $page->fileName)) - { - $page->error(500, "Page '".$page->get("title")."' is restricted!"); - } - return $page; - } + +class YellowResponse { + public $yellow; //access to API + public $plugin; //access to plugin + public $active; //location is active? (boolean) + public $userEmail; //user email + public $userRestrictions; //user can change page? (boolean) + public $userFailedError; //error of failed authentication + public $userFailedEmail; //email of failed authentication + public $userFailedExpire; //expiration time of failed authentication + public $rawDataSource; //raw data of page for comparison + public $rawDataEdit; //raw data of page for editing + public $rawDataOutput; //raw data of dynamic output + public $rawDataEndOfLine; //end of line format for raw data + public $language; //response language + public $action; //response action + public $status; //response status + + public function __construct($yellow) { + $this->yellow = $yellow; + $this->plugin = $yellow->plugins->get("edit"); + } + + // Return new page + public function getPageNew($scheme, $address, $base, $location, $fileName, $rawData, $endOfLine) { + $page = new YellowPage($this->yellow); + $page->setRequestInformation($scheme, $address, $base, $location, $fileName); + $page->parseData($this->normaliseLines($rawData, $endOfLine), false, 0); + $this->editContentFile($page, "create"); + if ($this->yellow->lookup->isFileLocation($location) || $this->yellow->pages->find($page->location)) { + $page->location = $this->getPageNewLocation($page->rawData, $page->location, $page->get("pageNewLocation")); + $page->fileName = $this->yellow->lookup->findFileNew($page->location, $page->get("published")); + while ($this->yellow->pages->find($page->location) || empty($page->fileName)) { + $rawData = $this->yellow->toolbox->setMetaData($page->rawData, "title", $this->getTitleNext($page->rawData)); + $page->rawData = $this->normaliseLines($rawData, $endOfLine); + $page->location = $this->getPageNewLocation($page->rawData, $page->location, $page->get("pageNewLocation")); + $page->fileName = $this->yellow->lookup->findFileNew($page->location, $page->get("published")); + if (++$pageCounter>999) break; + } + if ($this->yellow->pages->find($page->location) || empty($page->fileName)) { + $page->error(500, "Page '".$page->get("title")."' is not possible!"); + } + } else { + $page->fileName = $this->yellow->lookup->findFileNew($page->location); + } + if ($this->plugin->getUserRestrictions($this->userEmail, $page->location, $page->fileName)) { + $page->error(500, "Page '".$page->get("title")."' is restricted!"); + } + return $page; + } + + // Return modified page + public function getPageEdit($scheme, $address, $base, $location, $fileName, $rawDataSource, $rawDataEdit, $rawDataFile, $endOfLine) { + $page = new YellowPage($this->yellow); + $page->setRequestInformation($scheme, $address, $base, $location, $fileName); + $rawData = $this->plugin->merge->merge( + $this->normaliseLines($rawDataSource, $endOfLine), + $this->normaliseLines($rawDataEdit, $endOfLine), + $this->normaliseLines($rawDataFile, $endOfLine)); + $page->parseData($this->normaliseLines($rawData, $endOfLine), false, 0); + $this->editContentFile($page, "edit"); + if (empty($page->rawData)) $page->error(500, "Page has been modified by someone else!"); + if ($this->yellow->lookup->isFileLocation($location) && !$page->isError()) { + $pageSource = new YellowPage($this->yellow); + $pageSource->setRequestInformation($scheme, $address, $base, $location, $fileName); + $pageSource->parseData($this->normaliseLines($rawDataSource, $endOfLine), false, 0); + if (substrb($pageSource->rawData, 0, $pageSource->metaDataOffsetBytes) != + substrb($page->rawData, 0, $page->metaDataOffsetBytes)) { + $page->location = $this->getPageNewLocation($page->rawData, $page->location, $page->get("pageNewLocation")); + $page->fileName = $this->yellow->lookup->findFileNew($page->location, $page->get("published")); + if ($page->location!=$pageSource->location) { + if (!$this->yellow->lookup->isFileLocation($page->location) || empty($page->fileName)) { + $page->error(500, "Page '".$page->get("title")."' is not possible!"); + } elseif ($this->yellow->pages->find($page->location)) { + $page->error(500, "Page '".$page->get("title")."' already exists!"); + } + } + } + } + if ($this->plugin->getUserRestrictions($this->userEmail, $page->location, $page->fileName)) { + $page->error(500, "Page '".$page->get("title")."' is restricted!"); + } + return $page; + } + + // Return deleted page + public function getPageDelete($scheme, $address, $base, $location, $fileName, $rawData, $endOfLine) { + $page = new YellowPage($this->yellow); + $page->setRequestInformation($scheme, $address, $base, $location, $fileName); + $page->parseData($this->normaliseLines($rawData, $endOfLine), false, 0); + $this->editContentFile($page, "delete"); + if ($this->plugin->getUserRestrictions($this->userEmail, $page->location, $page->fileName)) { + $page->error(500, "Page '".$page->get("title")."' is restricted!"); + } + return $page; + } - // Return preview page - function getPagePreview($scheme, $address, $base, $location, $fileName, $rawData, $endOfLine) - { - $page = new YellowPage($this->yellow); - $page->setRequestInformation($scheme, $address, $base, $location, $fileName); - $page->parseData($this->normaliseLines($rawData, $endOfLine), false, 200); - $this->yellow->text->setLanguage($page->get("language")); - $page->set("pageClass", "page-preview"); - $page->set("pageClass", $page->get("pageClass")." template-".$page->get("template")); - $output = "<div class=\"".$page->getHtml("pageClass")."\"><div class=\"content\">"; - if($this->yellow->config->get("editToolbarButtons")!="none") - { - $output .= "<h1>".$page->getHtml("titleContent")."</h1>\n"; - } - $output .= $page->getContent(); - $output .= "</div></div>"; - $page->setOutput($output); - return $page; - } - - // Return uploaded file - function getFileUpload($scheme, $address, $base, $pageLocation, $fileNameTemp, $fileNameShort) - { - $file = new YellowPage($this->yellow); - $file->setRequestInformation($scheme, $address, $base, "/".$fileNameTemp, $fileNameTemp); - $file->parseData(null, false, 0); - $file->set("fileNameShort", $fileNameShort); - $this->editMediaFile($file, "upload"); - $file->location = $this->getFileNewLocation($fileNameShort, $pageLocation, $file->get("fileNewLocation")); - $file->fileName = substru($file->location, 1); - while(is_file($file->fileName)) - { - $fileNameShort = $this->getFileNext(basename($file->fileName)); - $file->location = $this->getFileNewLocation($fileNameShort, $pageLocation, $file->get("fileNewLocation")); - $file->fileName = substru($file->location, 1); - if(++$fileCounter>999) break; - } - if(is_file($file->fileName)) $file->error(500, "File '".$file->get("fileNameShort")."' is not possible!"); - return $file; - } + // Return preview page + public function getPagePreview($scheme, $address, $base, $location, $fileName, $rawData, $endOfLine) { + $page = new YellowPage($this->yellow); + $page->setRequestInformation($scheme, $address, $base, $location, $fileName); + $page->parseData($this->normaliseLines($rawData, $endOfLine), false, 200); + $this->yellow->text->setLanguage($page->get("language")); + $page->set("pageClass", "page-preview"); + $page->set("pageClass", $page->get("pageClass")." template-".$page->get("template")); + $output = "<div class=\"".$page->getHtml("pageClass")."\"><div class=\"content\">"; + if ($this->yellow->config->get("editToolbarButtons")!="none") $output .= "<h1>".$page->getHtml("titleContent")."</h1>\n"; + $output .= $page->getContent(); + $output .= "</div></div>"; + $page->setOutput($output); + return $page; + } + + // Return uploaded file + public function getFileUpload($scheme, $address, $base, $pageLocation, $fileNameTemp, $fileNameShort) { + $file = new YellowPage($this->yellow); + $file->setRequestInformation($scheme, $address, $base, "/".$fileNameTemp, $fileNameTemp); + $file->parseData(null, false, 0); + $file->set("fileNameShort", $fileNameShort); + $this->editMediaFile($file, "upload"); + $file->location = $this->getFileNewLocation($fileNameShort, $pageLocation, $file->get("fileNewLocation")); + $file->fileName = substru($file->location, 1); + while (is_file($file->fileName)) { + $fileNameShort = $this->getFileNext(basename($file->fileName)); + $file->location = $this->getFileNewLocation($fileNameShort, $pageLocation, $file->get("fileNewLocation")); + $file->fileName = substru($file->location, 1); + if (++$fileCounter>999) break; + } + if (is_file($file->fileName)) $file->error(500, "File '".$file->get("fileNameShort")."' is not possible!"); + return $file; + } - // Return page data including status information - function getPageData() - { - $data = array(); - if($this->isUser()) - { - $data["title"] = $this->yellow->toolbox->getMetaData($this->rawDataEdit, "title"); - $data["rawDataSource"] = $this->rawDataSource; - $data["rawDataEdit"] = $this->rawDataEdit; - $data["rawDataNew"] = $this->getRawDataNew(); - $data["rawDataOutput"] = strval($this->rawDataOutput); - $data["rawDataEndOfLine"] = $this->rawDataEndOfLine; - $data["scheme"] = $this->yellow->page->scheme; - $data["address"] = $this->yellow->page->address; - $data["base"] = $this->yellow->page->base; - $data["location"] = $this->yellow->page->location; - $data["parserSafeMode"] = $this->yellow->page->parserSafeMode; - } - if($this->action!="none") $data = array_merge($data, $this->getRequestData()); - $data["action"] = $this->action; - $data["status"] = $this->status; - $data["statusCode"] = $this->yellow->page->statusCode; - return $data; - } - - // Return configuration data including user information - function getConfigData() - { - $data = $this->yellow->config->getData("", "Location"); - if($this->isUser()) - { - $data["userEmail"] = $this->userEmail; - $data["userName"] = $this->plugin->users->getName($this->userEmail); - $data["userLanguage"] = $this->plugin->users->getLanguage($this->userEmail); - $data["userStatus"] = $this->plugin->users->getStatus($this->userEmail); - $data["userHome"] = $this->plugin->users->getHome($this->userEmail); - $data["userRestrictions"] = intval($this->isUserRestrictions()); - $data["userWebmaster"] = intval($this->isUserWebmaster()); - $data["serverScheme"] = $this->yellow->config->get("serverScheme"); - $data["serverAddress"] = $this->yellow->config->get("serverAddress"); - $data["serverBase"] = $this->yellow->config->get("serverBase"); - $data["serverFileSizeMax"] = $this->yellow->toolbox->getNumberBytes(ini_get("upload_max_filesize")); - $data["serverVersion"] = "Datenstrom Yellow ".YellowCore::VERSION; - $data["serverPlugins"] = array(); - foreach($this->yellow->plugins->plugins as $key=>$value) - { - $data["serverPlugins"][$key] = $value["plugin"]; - } - $data["serverLanguages"] = array(); - foreach($this->yellow->text->getLanguages() as $language) - { - $data["serverLanguages"][$language] = $this->yellow->text->getTextHtml("languageDescription", $language); - } - $data["editUploadExtensions"] = $this->yellow->config->get("editUploadExtensions"); - $data["editKeyboardShortcuts"] = $this->yellow->config->get("editKeyboardShortcuts"); - $data["editToolbarButtons"] = $this->getToolbarButtons("edit"); - $data["emojiawesomeToolbarButtons"] = $this->getToolbarButtons("emojiawesome"); - $data["fontawesomeToolbarButtons"] = $this->getToolbarButtons("fontawesome"); - } else { - $data["editLoginEmail"] = $this->yellow->page->get("editLoginEmail"); - $data["editLoginPassword"] = $this->yellow->page->get("editLoginPassword"); - $data["editLoginRestrictions"] = intval($this->isLoginRestrictions()); - } - if(defined("DEBUG") && DEBUG>=1) $data["debug"] = DEBUG; - return $data; - } - - // Return request strings - function getRequestData() - { - $data = array(); - foreach($_REQUEST as $key=>$value) - { - if($key=="password" || $key=="authtoken" || $key=="csrftoken" || $key=="actiontoken" || substru($key, 0, 7)=="rawdata") continue; - $data["request".ucfirst($key)] = trim($value); - } - return $data; - } - - // Return text strings - function getTextData() - { - $textLanguage = $this->yellow->text->getData("language", $this->language); - $textEdit = $this->yellow->text->getData("edit", $this->language); - $textYellow = $this->yellow->text->getData("yellow", $this->language); - return array_merge($textLanguage, $textEdit, $textYellow); - } - - // Return toolbar buttons - function getToolbarButtons($name) - { - if($name=="edit") - { - $toolbarButtons = $this->yellow->config->get("editToolbarButtons"); - if($toolbarButtons=="auto") - { - $toolbarButtons = ""; - if($this->yellow->plugins->isExisting("markdown")) $toolbarButtons = "preview, format, bold, italic, code, list, link, file"; - if($this->yellow->plugins->isExisting("emojiawesome")) $toolbarButtons .= ", emojiawesome"; - if($this->yellow->plugins->isExisting("fontawesome")) $toolbarButtons .= ", fontawesome"; - if($this->yellow->plugins->isExisting("draft")) $toolbarButtons .= ", draft"; - if($this->yellow->plugins->isExisting("markdown")) $toolbarButtons .= ", markdown"; - } - } else { - $toolbarButtons = $this->yellow->config->get("{$name}ToolbarButtons"); - } - return $toolbarButtons; - } - - // Return end of line format - function getEndOfLine($rawData = "") - { - $endOfLine = $this->yellow->config->get("editEndOfLine"); - if($endOfLine=="auto") - { - $rawData = empty($rawData) ? PHP_EOL : substru($rawData, 0, 4096); - $endOfLine = strposu($rawData, "\r")===false ? "lf" : "crlf"; - } - return $endOfLine; - } - - // Return raw data for new page - function getRawDataNew($location = "") - { - foreach($this->yellow->pages->path($this->yellow->page->location)->reverse() as $page) - { - if($page->isExisting("templateNew")) - { - $name = $this->yellow->lookup->normaliseName($page->get("templateNew")); - $fileName = strreplaceu("(.*)", $name, $this->yellow->config->get("configDir").$this->yellow->config->get("newFile")); - if(is_file($fileName)) break; - } - } - if(!is_file($fileName)) - { - $name = $this->yellow->lookup->normaliseName($this->yellow->config->get("template")); - $fileName = strreplaceu("(.*)", $name, $this->yellow->config->get("configDir").$this->yellow->config->get("newFile")); - } - $rawData = $this->yellow->toolbox->readFile($fileName); - $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->plugin->users->getName($this->userEmail), " "), $rawData); - $rawData = preg_replace("/@username/i", $this->plugin->users->getName($this->userEmail), $rawData); - $rawData = preg_replace("/@userlanguage/i", $this->plugin->users->getLanguage($this->userEmail), $rawData); - if(!empty($location)) - { - $rawData = $this->yellow->toolbox->setMetaData($rawData, "title", $this->yellow->toolbox->createTextTitle($location)); - } - return $rawData; - } - - // Return location for new/modified page - function getPageNewLocation($rawData, $pageLocation, $pageNewLocation) - { - $location = empty($pageNewLocation) ? "@title" : $pageNewLocation; - $location = preg_replace("/@title/i", $this->getPageNewTitle($rawData), $location); - $location = preg_replace("/@timestamp/i", $this->getPageNewData($rawData, "published", true, "U"), $location); - $location = preg_replace("/@date/i", $this->getPageNewData($rawData, "published", true, "Y-m-d"), $location); - $location = preg_replace("/@year/i", $this->getPageNewData($rawData, "published", true, "Y"), $location); - $location = preg_replace("/@month/i", $this->getPageNewData($rawData, "published", true, "m"), $location); - $location = preg_replace("/@day/i", $this->getPageNewData($rawData, "published", true, "d"), $location); - $location = preg_replace("/@tag/i", $this->getPageNewData($rawData, "tag", true), $location); - $location = preg_replace("/@author/i", $this->getPageNewData($rawData, "author", true), $location); - if(!preg_match("/^\//", $location)) - { - $location = $this->yellow->lookup->getDirectoryLocation($pageLocation).$location; - } - return $location; - } - - // Return title for new/modified page - function getPageNewTitle($rawData) - { - $title = $this->yellow->toolbox->getMetaData($rawData, "title"); - $titleSlug = $this->yellow->toolbox->getMetaData($rawData, "titleSlug"); - $value = empty($titleSlug) ? $title : $titleSlug; - $value = $this->yellow->lookup->normaliseName($value, true, false, true); - return trim(preg_replace("/-+/", "-", $value), "-"); - } - - // Return data for new/modified page - function getPageNewData($rawData, $key, $filterFirst = false, $dateFormat = "") - { - $value = $this->yellow->toolbox->getMetaData($rawData, $key); - if($filterFirst && preg_match("/^(.*?)\,(.*)$/", $value, $matches)) $value = $matches[1]; - if(!empty($dateFormat)) $value = date($dateFormat, strtotime($value)); - if(strempty($value)) $value = "none"; - $value = $this->yellow->lookup->normaliseName($value, true, false, true); - return trim(preg_replace("/-+/", "-", $value), "-"); - } + // Return page data including status information + public function getPageData() { + $data = array(); + if ($this->isUser()) { + $data["title"] = $this->yellow->toolbox->getMetaData($this->rawDataEdit, "title"); + $data["rawDataSource"] = $this->rawDataSource; + $data["rawDataEdit"] = $this->rawDataEdit; + $data["rawDataNew"] = $this->getRawDataNew(); + $data["rawDataOutput"] = strval($this->rawDataOutput); + $data["rawDataEndOfLine"] = $this->rawDataEndOfLine; + $data["scheme"] = $this->yellow->page->scheme; + $data["address"] = $this->yellow->page->address; + $data["base"] = $this->yellow->page->base; + $data["location"] = $this->yellow->page->location; + $data["parserSafeMode"] = $this->yellow->page->parserSafeMode; + } + if ($this->action!="none") $data = array_merge($data, $this->getRequestData()); + $data["action"] = $this->action; + $data["status"] = $this->status; + $data["statusCode"] = $this->yellow->page->statusCode; + return $data; + } + + // Return configuration data including user information + public function getConfigData() { + $data = $this->yellow->config->getData("", "Location"); + if ($this->isUser()) { + $data["userEmail"] = $this->userEmail; + $data["userName"] = $this->plugin->users->getName($this->userEmail); + $data["userLanguage"] = $this->plugin->users->getLanguage($this->userEmail); + $data["userStatus"] = $this->plugin->users->getStatus($this->userEmail); + $data["userHome"] = $this->plugin->users->getHome($this->userEmail); + $data["userRestrictions"] = intval($this->isUserRestrictions()); + $data["userWebmaster"] = intval($this->isUserWebmaster()); + $data["serverScheme"] = $this->yellow->config->get("serverScheme"); + $data["serverAddress"] = $this->yellow->config->get("serverAddress"); + $data["serverBase"] = $this->yellow->config->get("serverBase"); + $data["serverFileSizeMax"] = $this->yellow->toolbox->getNumberBytes(ini_get("upload_max_filesize")); + $data["serverVersion"] = "Datenstrom Yellow ".YellowCore::VERSION; + $data["serverPlugins"] = array(); + foreach ($this->yellow->plugins->plugins as $key=>$value) { + $data["serverPlugins"][$key] = $value["plugin"]; + } + $data["serverLanguages"] = array(); + foreach ($this->yellow->text->getLanguages() as $language) { + $data["serverLanguages"][$language] = $this->yellow->text->getTextHtml("languageDescription", $language); + } + $data["editUploadExtensions"] = $this->yellow->config->get("editUploadExtensions"); + $data["editKeyboardShortcuts"] = $this->yellow->config->get("editKeyboardShortcuts"); + $data["editToolbarButtons"] = $this->getToolbarButtons("edit"); + $data["emojiawesomeToolbarButtons"] = $this->getToolbarButtons("emojiawesome"); + $data["fontawesomeToolbarButtons"] = $this->getToolbarButtons("fontawesome"); + } else { + $data["editLoginEmail"] = $this->yellow->page->get("editLoginEmail"); + $data["editLoginPassword"] = $this->yellow->page->get("editLoginPassword"); + $data["editLoginRestrictions"] = intval($this->isLoginRestrictions()); + } + if (defined("DEBUG") && DEBUG>=1) $data["debug"] = DEBUG; + return $data; + } + + // Return request strings + public function getRequestData() { + $data = array(); + foreach ($_REQUEST as $key=>$value) { + if ($key=="password" || $key=="authtoken" || $key=="csrftoken" || $key=="actiontoken" || substru($key, 0, 7)=="rawdata") continue; + $data["request".ucfirst($key)] = trim($value); + } + return $data; + } + + // Return text strings + public function getTextData() { + $textLanguage = $this->yellow->text->getData("language", $this->language); + $textEdit = $this->yellow->text->getData("edit", $this->language); + $textYellow = $this->yellow->text->getData("yellow", $this->language); + return array_merge($textLanguage, $textEdit, $textYellow); + } + + // Return toolbar buttons + public function getToolbarButtons($name) { + if ($name=="edit") { + $toolbarButtons = $this->yellow->config->get("editToolbarButtons"); + if ($toolbarButtons=="auto") { + $toolbarButtons = ""; + if ($this->yellow->plugins->isExisting("markdown")) $toolbarButtons = "preview, format, bold, italic, code, list, link, file"; + if ($this->yellow->plugins->isExisting("emojiawesome")) $toolbarButtons .= ", emojiawesome"; + if ($this->yellow->plugins->isExisting("fontawesome")) $toolbarButtons .= ", fontawesome"; + if ($this->yellow->plugins->isExisting("draft")) $toolbarButtons .= ", draft"; + if ($this->yellow->plugins->isExisting("markdown")) $toolbarButtons .= ", markdown"; + } + } else { + $toolbarButtons = $this->yellow->config->get("{$name}ToolbarButtons"); + } + return $toolbarButtons; + } + + // Return end of line format + public function getEndOfLine($rawData = "") { + $endOfLine = $this->yellow->config->get("editEndOfLine"); + if ($endOfLine=="auto") { + $rawData = empty($rawData) ? PHP_EOL : substru($rawData, 0, 4096); + $endOfLine = strposu($rawData, "\r")===false ? "lf" : "crlf"; + } + return $endOfLine; + } + + // Return raw data for new page + public function getRawDataNew($location = "") { + foreach ($this->yellow->pages->path($this->yellow->page->location)->reverse() as $page) { + if ($page->isExisting("templateNew")) { + $name = $this->yellow->lookup->normaliseName($page->get("templateNew")); + $fileName = strreplaceu("(.*)", $name, $this->yellow->config->get("configDir").$this->yellow->config->get("newFile")); + if (is_file($fileName)) break; + } + } + if (!is_file($fileName)) { + $name = $this->yellow->lookup->normaliseName($this->yellow->config->get("template")); + $fileName = strreplaceu("(.*)", $name, $this->yellow->config->get("configDir").$this->yellow->config->get("newFile")); + } + $rawData = $this->yellow->toolbox->readFile($fileName); + $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->plugin->users->getName($this->userEmail), " "), $rawData); + $rawData = preg_replace("/@username/i", $this->plugin->users->getName($this->userEmail), $rawData); + $rawData = preg_replace("/@userlanguage/i", $this->plugin->users->getLanguage($this->userEmail), $rawData); + if (!empty($location)) { + $rawData = $this->yellow->toolbox->setMetaData($rawData, "title", $this->yellow->toolbox->createTextTitle($location)); + } + return $rawData; + } + + // Return location for new/modified page + public function getPageNewLocation($rawData, $pageLocation, $pageNewLocation) { + $location = empty($pageNewLocation) ? "@title" : $pageNewLocation; + $location = preg_replace("/@title/i", $this->getPageNewTitle($rawData), $location); + $location = preg_replace("/@timestamp/i", $this->getPageNewData($rawData, "published", true, "U"), $location); + $location = preg_replace("/@date/i", $this->getPageNewData($rawData, "published", true, "Y-m-d"), $location); + $location = preg_replace("/@year/i", $this->getPageNewData($rawData, "published", true, "Y"), $location); + $location = preg_replace("/@month/i", $this->getPageNewData($rawData, "published", true, "m"), $location); + $location = preg_replace("/@day/i", $this->getPageNewData($rawData, "published", true, "d"), $location); + $location = preg_replace("/@tag/i", $this->getPageNewData($rawData, "tag", true), $location); + $location = preg_replace("/@author/i", $this->getPageNewData($rawData, "author", true), $location); + if (!preg_match("/^\//", $location)) { + $location = $this->yellow->lookup->getDirectoryLocation($pageLocation).$location; + } + return $location; + } + + // Return title for new/modified page + public function getPageNewTitle($rawData) { + $title = $this->yellow->toolbox->getMetaData($rawData, "title"); + $titleSlug = $this->yellow->toolbox->getMetaData($rawData, "titleSlug"); + $value = empty($titleSlug) ? $title : $titleSlug; + $value = $this->yellow->lookup->normaliseName($value, true, false, true); + return trim(preg_replace("/-+/", "-", $value), "-"); + } + + // Return data for new/modified page + public function getPageNewData($rawData, $key, $filterFirst = false, $dateFormat = "") { + $value = $this->yellow->toolbox->getMetaData($rawData, $key); + if ($filterFirst && preg_match("/^(.*?)\,(.*)$/", $value, $matches)) $value = $matches[1]; + if (!empty($dateFormat)) $value = date($dateFormat, strtotime($value)); + if (strempty($value)) $value = "none"; + $value = $this->yellow->lookup->normaliseName($value, true, false, true); + return trim(preg_replace("/-+/", "-", $value), "-"); + } - // Return location for new file - function getFileNewLocation($fileNameShort, $pageLocation, $fileNewLocation) - { - $location = empty($fileNewLocation) ? $this->yellow->config->get("editUploadNewLocation") : $fileNewLocation; - $location = preg_replace("/@timestamp/i", time(), $location); - $location = preg_replace("/@type/i", $this->yellow->toolbox->getFileType($fileNameShort), $location); - $location = preg_replace("/@group/i", $this->getFileNewGroup($fileNameShort), $location); - $location = preg_replace("/@folder/i", $this->getFileNewFolder($pageLocation), $location); - $location = preg_replace("/@filename/i", strtoloweru($fileNameShort), $location); - if(!preg_match("/^\//", $location)) - { - $location = $this->yellow->config->get("mediaLocation").$location; - } - return $location; - } - - // Return group for new file - function getFileNewGroup($fileNameShort) - { - $path = $this->yellow->config->get("mediaDir"); - $fileType = $this->yellow->toolbox->getFileType($fileNameShort); - $fileName = $this->yellow->config->get(preg_match("/(gif|jpg|png|svg)$/", $fileType) ? "imageDir" : "downloadDir").$fileNameShort; - preg_match("#^$path(.+?)\/#", $fileName, $matches); - return strtoloweru($matches[1]); - } + // Return location for new file + public function getFileNewLocation($fileNameShort, $pageLocation, $fileNewLocation) { + $location = empty($fileNewLocation) ? $this->yellow->config->get("editUploadNewLocation") : $fileNewLocation; + $location = preg_replace("/@timestamp/i", time(), $location); + $location = preg_replace("/@type/i", $this->yellow->toolbox->getFileType($fileNameShort), $location); + $location = preg_replace("/@group/i", $this->getFileNewGroup($fileNameShort), $location); + $location = preg_replace("/@folder/i", $this->getFileNewFolder($pageLocation), $location); + $location = preg_replace("/@filename/i", strtoloweru($fileNameShort), $location); + if (!preg_match("/^\//", $location)) { + $location = $this->yellow->config->get("mediaLocation").$location; + } + return $location; + } + + // Return group for new file + public function getFileNewGroup($fileNameShort) { + $path = $this->yellow->config->get("mediaDir"); + $fileType = $this->yellow->toolbox->getFileType($fileNameShort); + $fileName = $this->yellow->config->get(preg_match("/(gif|jpg|png|svg)$/", $fileType) ? "imageDir" : "downloadDir").$fileNameShort; + preg_match("#^$path(.+?)\/#", $fileName, $matches); + return strtoloweru($matches[1]); + } - // Return folder for new file - function getFileNewFolder($pageLocation) - { - $parentTopLocation = $this->yellow->pages->getParentTopLocation($pageLocation); - if($parentTopLocation==$this->yellow->pages->getHomeLocation($pageLocation)) $parentTopLocation .= "home"; - return strtoloweru(trim($parentTopLocation, '/')); - } - - // Return next title - function getTitleNext($rawData) - { - preg_match("/^(.*?)(\d*)$/", $this->yellow->toolbox->getMetaData($rawData, "title"), $matches); - $titleText = $matches[1]; - $titleNumber = strempty($matches[2]) ? " 2" : $matches[2]+1; - return $titleText.$titleNumber; - } - - // Return next file name - function getFileNext($fileNameShort) - { - preg_match("/^(.*?)(\d*)(\..*?)?$/", $fileNameShort, $matches); - $fileText = $matches[1]; - $fileNumber = strempty($matches[2]) ? "-2" : $matches[2]+1; - $fileExtension = $matches[3]; - return $fileText.$fileNumber.$fileExtension; - } + // Return folder for new file + public function getFileNewFolder($pageLocation) { + $parentTopLocation = $this->yellow->pages->getParentTopLocation($pageLocation); + if ($parentTopLocation==$this->yellow->pages->getHomeLocation($pageLocation)) $parentTopLocation .= "home"; + return strtoloweru(trim($parentTopLocation, "/")); + } + + // Return next title + public function getTitleNext($rawData) { + preg_match("/^(.*?)(\d*)$/", $this->yellow->toolbox->getMetaData($rawData, "title"), $matches); + $titleText = $matches[1]; + $titleNumber = strempty($matches[2]) ? " 2" : $matches[2]+1; + return $titleText.$titleNumber; + } + + // Return next file name + public function getFileNext($fileNameShort) { + preg_match("/^(.*?)(\d*)(\..*?)?$/", $fileNameShort, $matches); + $fileText = $matches[1]; + $fileNumber = strempty($matches[2]) ? "-2" : $matches[2]+1; + $fileExtension = $matches[3]; + return $fileText.$fileNumber.$fileExtension; + } - // Normalise text lines, convert line endings - 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; - } - - // Create browser cookies - function createCookies($scheme, $address, $base, $email) - { - $expire = time() + $this->yellow->config->get("editLoginSessionTimeout"); - $authToken = $this->plugin->users->createAuthToken($email, $expire); - $csrfToken = $this->plugin->users->createCsrfToken(); - setcookie("authtoken", $authToken, $expire, "$base/", "", $scheme=="https", true); - setcookie("csrftoken", $csrfToken, $expire, "$base/", "", $scheme=="https", false); - } - - // Destroy browser cookies - function destroyCookies($scheme, $address, $base) - { - setcookie("authtoken", "", 1, "$base/", "", $scheme=="https", true); - setcookie("csrftoken", "", 1, "$base/", "", $scheme=="https", false); - } - - // Send mail to user - function sendMail($scheme, $address, $base, $email, $action) - { - if($action=="welcome" || $action=="goodbye") - { - $url = "$scheme://$address$base/"; - } else { - $expire = time() + 60*60*24; - $actionToken = $this->plugin->users->createActionToken($email, $action, $expire); - $url = "$scheme://$address$base"."/action:$action/email:$email/expire:$expire/actiontoken:$actionToken/"; - } - if($action=="approve") - { - $account = $email; - $name = $this->yellow->config->get("author"); - $email = $this->yellow->config->get("email"); - } else { - $account = $email; - $name = $this->plugin->users->getName($email); - } - $language = $this->plugin->users->getLanguage($email); - if(!$this->yellow->text->isLanguage($language)) $language = $this->yellow->config->get("language"); - $sitename = $this->yellow->config->get("sitename"); - $prefix = "edit".ucfirst($action); - $message = $this->yellow->text->getText("{$prefix}Message", $language); - $message = strreplaceu("\\n", "\n", $message); - $message = preg_replace("/@useraccount/i", $account, $message); - $message = preg_replace("/@usershort/i", strtok($name, " "), $message); - $message = preg_replace("/@username/i", $name, $message); - $message = preg_replace("/@userlanguage/i", $language, $message); - $mailTo = mb_encode_mimeheader("$name")." <$email>"; - $mailSubject = mb_encode_mimeheader($this->yellow->text->getText("{$prefix}Subject", $language)); - $mailHeaders = mb_encode_mimeheader("From: $sitename")." <noreply>\r\n"; - $mailHeaders .= mb_encode_mimeheader("X-Request-Url: $scheme://$address$base")."\r\n"; - $mailHeaders .= "Mime-Version: 1.0\r\n"; - $mailHeaders .= "Content-Type: text/plain; charset=utf-8\r\n"; - $mailMessage = "$message\r\n\r\n$url\r\n-- \r\n$sitename"; - return mail($mailTo, $mailSubject, $mailMessage, $mailHeaders); - } - - // Change content file - function editContentFile($page, $action) - { - if(!$page->isError()) - { - foreach($this->yellow->plugins->plugins as $key=>$value) - { - if(method_exists($value["obj"], "onEditContentFile")) $value["obj"]->onEditContentFile($page, $action); - } - } - } + // 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; + } + + // Create browser cookies + public function createCookies($scheme, $address, $base, $email) { + $expire = time() + $this->yellow->config->get("editLoginSessionTimeout"); + $authToken = $this->plugin->users->createAuthToken($email, $expire); + $csrfToken = $this->plugin->users->createCsrfToken(); + setcookie("authtoken", $authToken, $expire, "$base/", "", $scheme=="https", true); + setcookie("csrftoken", $csrfToken, $expire, "$base/", "", $scheme=="https", false); + } + + // Destroy browser cookies + public function destroyCookies($scheme, $address, $base) { + setcookie("authtoken", "", 1, "$base/", "", $scheme=="https", true); + setcookie("csrftoken", "", 1, "$base/", "", $scheme=="https", false); + } + + // Send mail to user + public function sendMail($scheme, $address, $base, $email, $action) { + if ($action=="welcome" || $action=="goodbye") { + $url = "$scheme://$address$base/"; + } else { + $expire = time() + 60*60*24; + $actionToken = $this->plugin->users->createActionToken($email, $action, $expire); + $url = "$scheme://$address$base"."/action:$action/email:$email/expire:$expire/actiontoken:$actionToken/"; + } + if ($action=="approve") { + $account = $email; + $name = $this->yellow->config->get("author"); + $email = $this->yellow->config->get("email"); + } else { + $account = $email; + $name = $this->plugin->users->getName($email); + } + $language = $this->plugin->users->getLanguage($email); + if (!$this->yellow->text->isLanguage($language)) $language = $this->yellow->config->get("language"); + $sitename = $this->yellow->config->get("sitename"); + $prefix = "edit".ucfirst($action); + $message = $this->yellow->text->getText("{$prefix}Message", $language); + $message = strreplaceu("\\n", "\n", $message); + $message = preg_replace("/@useraccount/i", $account, $message); + $message = preg_replace("/@usershort/i", strtok($name, " "), $message); + $message = preg_replace("/@username/i", $name, $message); + $message = preg_replace("/@userlanguage/i", $language, $message); + $mailTo = mb_encode_mimeheader("$name")." <$email>"; + $mailSubject = mb_encode_mimeheader($this->yellow->text->getText("{$prefix}Subject", $language)); + $mailHeaders = mb_encode_mimeheader("From: $sitename")." <noreply>\r\n"; + $mailHeaders .= mb_encode_mimeheader("X-Request-Url: $scheme://$address$base")."\r\n"; + $mailHeaders .= "Mime-Version: 1.0\r\n"; + $mailHeaders .= "Content-Type: text/plain; charset=utf-8\r\n"; + $mailMessage = "$message\r\n\r\n$url\r\n-- \r\n$sitename"; + return mail($mailTo, $mailSubject, $mailMessage, $mailHeaders); + } + + // Change content file + public function editContentFile($page, $action) { + if (!$page->isError()) { + foreach ($this->yellow->plugins->plugins as $key=>$value) { + if (method_exists($value["obj"], "onEditContentFile")) $value["obj"]->onEditContentFile($page, $action); + } + } + } - // Change media file - function editMediaFile($file, $action) - { - if(!$file->isError()) - { - foreach($this->yellow->plugins->plugins as $key=>$value) - { - if(method_exists($value["obj"], "onEditMediaFile")) $value["obj"]->onEditMediaFile($file, $action); - } - } - } - - // Check if active - function isActive() - { - return $this->active; - } - - // Check if user is logged in - function isUser() - { - return !empty($this->userEmail); - } - - // Check if user has restrictions - function isUserRestrictions() - { - return empty($this->userEmail) || $this->userRestrictions; - } - - // Check if user is webmaster - function isUserWebmaster() - { - return !empty($this->userEmail) && $this->userEmail==$this->yellow->config->get("email"); - } - - // Check if login has restrictions - function isLoginRestrictions() - { - return $this->yellow->config->get("editLoginRestrictions"); - } + // Change media file + public function editMediaFile($file, $action) { + if (!$file->isError()) { + foreach ($this->yellow->plugins->plugins as $key=>$value) { + if (method_exists($value["obj"], "onEditMediaFile")) $value["obj"]->onEditMediaFile($file, $action); + } + } + } + + // 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 has restrictions + public function isUserRestrictions() { + return empty($this->userEmail) || $this->userRestrictions; + } + + // Check if user is webmaster + public function isUserWebmaster() { + return !empty($this->userEmail) && $this->userEmail==$this->yellow->config->get("email"); + } + + // Check if login has restrictions + public function isLoginRestrictions() { + return $this->yellow->config->get("editLoginRestrictions"); + } } -class YellowUsers -{ - var $yellow; //access to API - var $users; //registered users - - function __construct($yellow) - { - $this->yellow = $yellow; - $this->users = array(); - } +class YellowUsers { + public $yellow; //access to API + public $users; //registered users + + public function __construct($yellow) { + $this->yellow = $yellow; + $this->users = array(); + } - // Load users from file - function load($fileName) - { - if(defined("DEBUG") && DEBUG>=2) echo "YellowUsers::load file:$fileName<br/>\n"; - $fileData = $this->yellow->toolbox->readFile($fileName); - foreach($this->yellow->toolbox->getTextLines($fileData) as $line) - { - if(preg_match("/^\#/", $line)) continue; - preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); - if(!empty($matches[1]) && !empty($matches[2])) - { - list($hash, $name, $language, $status, $stamp, $modified, $errors, $pending, $home) = explode(',', $matches[2]); - $this->set($matches[1], $hash, $name, $language, $status, $stamp, $modified, $errors, $pending, $home); - if(defined("DEBUG") && DEBUG>=3) echo "YellowUsers::load email:$matches[1]<br/>\n"; - } - } - } + // Load users from file + public function load($fileName) { + if (defined("DEBUG") && DEBUG>=2) echo "YellowUsers::load file:$fileName<br/>\n"; + $fileData = $this->yellow->toolbox->readFile($fileName); + foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) { + if (preg_match("/^\#/", $line)) continue; + preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); + if (!empty($matches[1]) && !empty($matches[2])) { + list($hash, $name, $language, $status, $stamp, $modified, $errors, $pending, $home) = explode(",", $matches[2]); + $this->set($matches[1], $hash, $name, $language, $status, $stamp, $modified, $errors, $pending, $home); + if (defined("DEBUG") && DEBUG>=3) echo "YellowUsers::load email:$matches[1]<br/>\n"; + } + } + } - // Save user to file - function save($fileName, $email, $password = "", $name = "", $language = "", $status = "", $stamp = "", $modified = "", $errors = "", $pending = "", $home = "") - { - if(!empty($password)) $hash = $this->createHash($password); - if($this->isExisting($email)) - { - $email = strreplaceu(',', '-', $email); - $hash = strreplaceu(',', '-', empty($hash) ? $this->users[$email]["hash"] : $hash); - $name = strreplaceu(',', '-', empty($name) ? $this->users[$email]["name"] : $name); - $language = strreplaceu(',', '-', empty($language) ? $this->users[$email]["language"] : $language); - $status = strreplaceu(',', '-', empty($status) ? $this->users[$email]["status"] : $status); - $stamp = strreplaceu(',', '-', empty($stamp) ? $this->users[$email]["stamp"] : $stamp); - $modified = strreplaceu(',', '-', empty($modified) ? time() : $modified); - $errors = strreplaceu(',', '-', empty($errors) ? "0" : $errors); - $pending = strreplaceu(',', '-', empty($pending) ? $this->users[$email]["pending"] : $pending); - $home = strreplaceu(',', '-', empty($home) ? $this->users[$email]["home"] : $home); - } else { - $email = strreplaceu(',', '-', empty($email) ? "none" : $email); - $hash = strreplaceu(',', '-', empty($hash) ? "none" : $hash); - $name = strreplaceu(',', '-', empty($name) ? $this->yellow->config->get("sitename") : $name); - $language = strreplaceu(',', '-', empty($language) ? $this->yellow->config->get("language") : $language); - $status = strreplaceu(',', '-', empty($status) ? "active" : $status); - $stamp = strreplaceu(',', '-', empty($stamp) ? $this->createStamp() : $stamp); - $modified = strreplaceu(',', '-', empty($modified) ? time() : $modified); - $errors = strreplaceu(',', '-', empty($errors) ? "0" : $errors); - $pending = strreplaceu(',', '-', empty($pending) ? "none" : $pending); - $home = strreplaceu(',', '-', empty($home) ? $this->yellow->config->get("editUserHome") : $home); - } - $this->set($email, $hash, $name, $language, $status, $stamp, $modified, $errors, $pending, $home); - $fileData = $this->yellow->toolbox->readFile($fileName); - foreach($this->yellow->toolbox->getTextLines($fileData) as $line) - { - preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); - if(!empty($matches[1]) && $matches[1]==$email) - { - $fileDataNew .= "$email: $hash,$name,$language,$status,$stamp,$modified,$errors,$pending,$home\n"; - $found = true; - } else { - $fileDataNew .= $line; - } - } - if(!$found) $fileDataNew .= "$email: $hash,$name,$language,$status,$stamp,$modified,$errors,$pending,$home\n"; - return $this->yellow->toolbox->createFile($fileName, $fileDataNew); - } - - // Remove user from file - function remove($fileName, $email) - { - unset($this->users[$email]); - $fileData = $this->yellow->toolbox->readFile($fileName); - foreach($this->yellow->toolbox->getTextLines($fileData) as $line) - { - preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); - if(!empty($matches[1]) && !empty($matches[2]) && $matches[1]!=$email) - { - $fileDataNew .= $line; - } - } - return $this->yellow->toolbox->createFile($fileName, $fileDataNew); - } - - // Set user data - function set($email, $hash, $name, $language, $status, $stamp, $modified, $errors, $pending, $home) - { - $this->users[$email] = array(); - $this->users[$email]["email"] = $email; - $this->users[$email]["hash"] = $hash; - $this->users[$email]["name"] = $name; - $this->users[$email]["language"] = $language; - $this->users[$email]["status"] = $status; - $this->users[$email]["stamp"] = $stamp; - $this->users[$email]["modified"] = $modified; - $this->users[$email]["errors"] = $errors; - $this->users[$email]["pending"] = $pending; - $this->users[$email]["home"] = $home; - } - - // Check user authentication from email and password - function checkAuthLogin($email, $password) - { - $algorithm = $this->yellow->config->get("editUserHashAlgorithm"); - return $this->isExisting($email) && $this->users[$email]["status"]=="active" && - $this->yellow->toolbox->verifyHash($password, $algorithm, $this->users[$email]["hash"]); - } + // Save user to file + public function save($fileName, $email, $password = "", $name = "", $language = "", $status = "", $stamp = "", $modified = "", $errors = "", $pending = "", $home = "") { + if (!empty($password)) $hash = $this->createHash($password); + if ($this->isExisting($email)) { + $email = strreplaceu(",", "-", $email); + $hash = strreplaceu(",", "-", empty($hash) ? $this->users[$email]["hash"] : $hash); + $name = strreplaceu(",", "-", empty($name) ? $this->users[$email]["name"] : $name); + $language = strreplaceu(",", "-", empty($language) ? $this->users[$email]["language"] : $language); + $status = strreplaceu(",", "-", empty($status) ? $this->users[$email]["status"] : $status); + $stamp = strreplaceu(",", "-", empty($stamp) ? $this->users[$email]["stamp"] : $stamp); + $modified = strreplaceu(",", "-", empty($modified) ? time() : $modified); + $errors = strreplaceu(",", "-", empty($errors) ? "0" : $errors); + $pending = strreplaceu(",", "-", empty($pending) ? $this->users[$email]["pending"] : $pending); + $home = strreplaceu(",", "-", empty($home) ? $this->users[$email]["home"] : $home); + } else { + $email = strreplaceu(",", "-", empty($email) ? "none" : $email); + $hash = strreplaceu(",", "-", empty($hash) ? "none" : $hash); + $name = strreplaceu(",", "-", empty($name) ? $this->yellow->config->get("sitename") : $name); + $language = strreplaceu(",", "-", empty($language) ? $this->yellow->config->get("language") : $language); + $status = strreplaceu(",", "-", empty($status) ? "active" : $status); + $stamp = strreplaceu(",", "-", empty($stamp) ? $this->createStamp() : $stamp); + $modified = strreplaceu(",", "-", empty($modified) ? time() : $modified); + $errors = strreplaceu(",", "-", empty($errors) ? "0" : $errors); + $pending = strreplaceu(",", "-", empty($pending) ? "none" : $pending); + $home = strreplaceu(",", "-", empty($home) ? $this->yellow->config->get("editUserHome") : $home); + } + $this->set($email, $hash, $name, $language, $status, $stamp, $modified, $errors, $pending, $home); + $fileData = $this->yellow->toolbox->readFile($fileName); + foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) { + preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); + if (!empty($matches[1]) && $matches[1]==$email) { + $fileDataNew .= "$email: $hash,$name,$language,$status,$stamp,$modified,$errors,$pending,$home\n"; + $found = true; + } else { + $fileDataNew .= $line; + } + } + if (!$found) $fileDataNew .= "$email: $hash,$name,$language,$status,$stamp,$modified,$errors,$pending,$home\n"; + return $this->yellow->toolbox->createFile($fileName, $fileDataNew); + } + + // Remove user from file + public function remove($fileName, $email) { + unset($this->users[$email]); + $fileData = $this->yellow->toolbox->readFile($fileName); + foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) { + preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); + if (!empty($matches[1]) && !empty($matches[2]) && $matches[1]!=$email) $fileDataNew .= $line; + } + return $this->yellow->toolbox->createFile($fileName, $fileDataNew); + } + + // Set user data + public function set($email, $hash, $name, $language, $status, $stamp, $modified, $errors, $pending, $home) { + $this->users[$email] = array(); + $this->users[$email]["email"] = $email; + $this->users[$email]["hash"] = $hash; + $this->users[$email]["name"] = $name; + $this->users[$email]["language"] = $language; + $this->users[$email]["status"] = $status; + $this->users[$email]["stamp"] = $stamp; + $this->users[$email]["modified"] = $modified; + $this->users[$email]["errors"] = $errors; + $this->users[$email]["pending"] = $pending; + $this->users[$email]["home"] = $home; + } + + // Check user authentication from email and password + public function checkAuthLogin($email, $password) { + $algorithm = $this->yellow->config->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 - function checkAuthToken($authToken, $csrfTokenExpected, $csrfTokenReceived, $ignoreCsrfToken) - { - $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) || $ignoreCsrfToken); - } - - // Check action token - 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 - function createAuthToken($email, $expire) - { - $signature = $this->yellow->toolbox->createHash($this->users[$email]["hash"]."auth".$expire, "sha256"); - if(empty($signature)) $signature = "padd"."error-hash-algorithm-sha256"; - return substrb($signature, 4).$this->getStamp($email).dechex($expire); - } - - // Create action token - function createActionToken($email, $action, $expire) - { - $signature = $this->yellow->toolbox->createHash($this->users[$email]["hash"].$action.$expire, "sha256"); - if(empty($signature)) $signature = "padd"."error-hash-algorithm-sha256"; - return substrb($signature, 4); - } - - // Create CSRF token - function createCsrfToken() - { - return $this->yellow->toolbox->createSalt(64); - } - - // Create password hash - function createHash($password) - { - $algorithm = $this->yellow->config->get("editUserHashAlgorithm"); - $cost = $this->yellow->config->get("editUserHashCost"); - $hash = $this->yellow->toolbox->createHash($password, $algorithm, $cost); - if(empty($hash)) $hash = "error-hash-algorithm-$algorithm"; - return $hash; - } - - // Create user stamp - function createStamp() - { - $stamp = $this->yellow->toolbox->createSalt(20); - while($this->getAuthEmail("none", $stamp)) $stamp = $this->yellow->toolbox->createSalt(20); - return $stamp; - } - - // Return user email from authentication, timing attack safe email lookup - function getAuthEmail($authToken, $stamp = "") - { - if(empty($stamp)) $stamp = substrb($authToken, 96, 20); - foreach($this->users as $key=>$value) - { - if($this->yellow->toolbox->verifyToken($value["stamp"], $stamp)) $email = $key; - } - return $email; - } - - // Return expiration time from authentication - function getAuthExpire($authToken) - { - return hexdec(substrb($authToken, 96+20)); - } - - // Return user hash - function getHash($email) - { - return $this->isExisting($email) ? $this->users[$email]["hash"] : ""; - } - - // Return user name - function getName($email) - { - return $this->isExisting($email) ? $this->users[$email]["name"] : ""; - } + // Check user authentication from tokens + public function checkAuthToken($authToken, $csrfTokenExpected, $csrfTokenReceived, $ignoreCsrfToken) { + $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) || $ignoreCsrfToken); + } + + // 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"); + if (empty($signature)) $signature = "padd"."error-hash-algorithm-sha256"; + return substrb($signature, 4).$this->getStamp($email).dechex($expire); + } + + // Create action token + public function createActionToken($email, $action, $expire) { + $signature = $this->yellow->toolbox->createHash($this->users[$email]["hash"].$action.$expire, "sha256"); + if (empty($signature)) $signature = "padd"."error-hash-algorithm-sha256"; + return substrb($signature, 4); + } + + // Create CSRF token + public function createCsrfToken() { + return $this->yellow->toolbox->createSalt(64); + } + + // Create password hash + public function createHash($password) { + $algorithm = $this->yellow->config->get("editUserHashAlgorithm"); + $cost = $this->yellow->config->get("editUserHashCost"); + $hash = $this->yellow->toolbox->createHash($password, $algorithm, $cost); + if (empty($hash)) $hash = "error-hash-algorithm-$algorithm"; + return $hash; + } + + // Create user stamp + public function createStamp() { + $stamp = $this->yellow->toolbox->createSalt(20); + while ($this->getAuthEmail("none", $stamp)) { + $stamp = $this->yellow->toolbox->createSalt(20); + } + return $stamp; + } + + // Return user email from authentication, timing attack safe email lookup + public function getAuthEmail($authToken, $stamp = "") { + if (empty($stamp)) $stamp = substrb($authToken, 96, 20); + foreach ($this->users as $key=>$value) { + if ($this->yellow->toolbox->verifyToken($value["stamp"], $stamp)) $email = $key; + } + return $email; + } + + // Return expiration time from authentication + public function getAuthExpire($authToken) { + return hexdec(substrb($authToken, 96+20)); + } + + // Return user hash + public function getHash($email) { + return $this->isExisting($email) ? $this->users[$email]["hash"] : ""; + } + + // Return user name + public function getName($email) { + return $this->isExisting($email) ? $this->users[$email]["name"] : ""; + } - // Return user language - function getLanguage($email) - { - return $this->isExisting($email) ? $this->users[$email]["language"] : ""; - } - - // Return user status - function getStatus($email) - { - return $this->isExisting($email) ? $this->users[$email]["status"] : ""; - } - - // Return user stamp - function getStamp($email) - { - return $this->isExisting($email) ? $this->users[$email]["stamp"] : ""; - } - - // Return user modified - function getModified($email) - { - return $this->isExisting($email) ? $this->users[$email]["modified"] : ""; - } + // Return user language + public function getLanguage($email) { + return $this->isExisting($email) ? $this->users[$email]["language"] : ""; + } + + // Return user status + public function getStatus($email) { + return $this->isExisting($email) ? $this->users[$email]["status"] : ""; + } + + // Return user stamp + public function getStamp($email) { + return $this->isExisting($email) ? $this->users[$email]["stamp"] : ""; + } + + // Return user modified + public function getModified($email) { + return $this->isExisting($email) ? $this->users[$email]["modified"] : ""; + } - // Return user errors - function getErrors($email) - { - return $this->isExisting($email) ? $this->users[$email]["errors"] : ""; - } + // Return user errors + public function getErrors($email) { + return $this->isExisting($email) ? $this->users[$email]["errors"] : ""; + } - // Return user pending - function getPending($email) - { - return $this->isExisting($email) ? $this->users[$email]["pending"] : ""; - } - - // Return user home - function getHome($email) - { - return $this->isExisting($email) ? $this->users[$email]["home"] : ""; - } - - // Return number of users - function getNumber() - { - return count($this->users); - } + // Return user pending + public function getPending($email) { + return $this->isExisting($email) ? $this->users[$email]["pending"] : ""; + } + + // Return user home + public function getHome($email) { + return $this->isExisting($email) ? $this->users[$email]["home"] : ""; + } + + // Return number of users + public function getNumber() { + return count($this->users); + } - // Return user data - function getData() - { - $data = array(); - foreach($this->users as $key=>$value) - { - $name = $value["name"]; if(preg_match("/\s/", $name)) $name = "\"$name\""; - $status = $value["status"]; if(preg_match("/\s/", $status)) $status = "\"$status\""; - $data[$key] = "$value[email] $name $status"; - } - uksort($data, "strnatcasecmp"); - return $data; - } - - // Check if user is taken - function isTaken($email) - { - $taken = false; - if($this->isExisting($email)) - { - $status = $this->users[$email]["status"]; - $reserved = $this->users[$email]["modified"] + 60*60*24; - if($status=="active" || $status=="inactive" || $reserved>time()) $taken = true; - } - return $taken; - } - - // Check if user exists - function isExisting($email) - { - return !is_null($this->users[$email]); - } + // Return user data + public function getData() { + $data = array(); + foreach ($this->users as $key=>$value) { + $name = $value["name"]; + $status = $value["status"]; + if (preg_match("/\s/", $name)) $name = "\"$name\""; + if (preg_match("/\s/", $status)) $status = "\"$status\""; + $data[$key] = "$value[email] $name $status"; + } + 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 = $this->users[$email]["modified"] + 60*60*24; + if ($status=="active" || $status=="inactive" || $reserved>time()) $taken = true; + } + return $taken; + } + + // Check if user exists + public function isExisting($email) { + return !is_null($this->users[$email]); + } } - -class YellowMerge -{ - var $yellow; //access to API - const ADD = '+'; //merge types - const MODIFY = '*'; - const REMOVE = '-'; - const SAME = ' '; - - function __construct($yellow) - { - $this->yellow = $yellow; - } - - // Merge text, null if not possible - function merge($textSource, $textMine, $textYours, $showDiff = false) - { - if($textMine!=$textYours) - { - $diffMine = $this->buildDiff($textSource, $textMine); - $diffYours = $this->buildDiff($textSource, $textYours); - $diff = $this->mergeDiff($diffMine, $diffYours); - $output = $this->getOutput($diff, $showDiff); - } else { - $output = $textMine; - } - return $output; - } - - // Build differences to common source - function buildDiff($textSource, $textOther) - { - $diff = array(); - $lastRemove = -1; - $textStart = 0; - $textSource = $this->yellow->toolbox->getTextLines($textSource); - $textOther = $this->yellow->toolbox->getTextLines($textOther); - $sourceEnd = $sourceSize = count($textSource); - $otherEnd = $otherSize = count($textOther); - while($textStart<$sourceEnd && $textStart<$otherEnd && $textSource[$textStart]==$textOther[$textStart]) ++$textStart; - while($textStart<$sourceEnd && $textStart<$otherEnd && $textSource[$sourceEnd-1]==$textOther[$otherEnd-1]) - { - --$sourceEnd; --$otherEnd; - } - for($pos=0; $pos<$textStart; ++$pos) array_push($diff, array(YellowMerge::SAME, $textSource[$pos], false)); - $lcs = $this->buildDiffLCS($textSource, $textOther, $textStart, $sourceEnd-$textStart, $otherEnd-$textStart); - for($x=0,$y=0,$xEnd=$otherEnd-$textStart,$yEnd=$sourceEnd-$textStart; $x<$xEnd || $y<$yEnd;) - { - $max = $lcs[$y][$x]; - if($y<$yEnd && $lcs[$y+1][$x]==$max) - { - array_push($diff, array(YellowMerge::REMOVE, $textSource[$textStart+$y], false)); - if($lastRemove==-1) $lastRemove = count($diff)-1; - ++$y; - continue; - } - if($x<$xEnd && $lcs[$y][$x+1]==$max) - { - if($lastRemove==-1 || $diff[$lastRemove][0]!=YellowMerge::REMOVE) - { - array_push($diff, array(YellowMerge::ADD, $textOther[$textStart+$x], false)); - $lastRemove = -1; - } else { - $diff[$lastRemove] = array(YellowMerge::MODIFY, $textOther[$textStart+$x], false); - ++$lastRemove; if(count($diff)==$lastRemove) $lastRemove = -1; - } - ++$x; - continue; - } - array_push($diff, array(YellowMerge::SAME, $textSource[$textStart+$y], false)); - $lastRemove = -1; - ++$x; - ++$y; - } - for($pos=$sourceEnd;$pos<$sourceSize; ++$pos) array_push($diff, array(YellowMerge::SAME, $textSource[$pos], false)); - return $diff; - } - - // Build longest common subsequence - function buildDiffLCS($textSource, $textOther, $textStart, $yEnd, $xEnd) - { - $lcs = array_fill(0, $yEnd+1, array_fill(0, $xEnd+1, 0)); - for($y=$yEnd-1; $y>=0; --$y) - { - for($x=$xEnd-1; $x>=0; --$x) - { - if($textSource[$textStart+$y]==$textOther[$textStart+$x]) - { - $lcs[$y][$x] = $lcs[$y+1][$x+1]+1; - } else { - $lcs[$y][$x] = max($lcs[$y][$x+1], $lcs[$y+1][$x]); - } - } - } - return $lcs; - } - - // Merge differences - function mergeDiff($diffMine, $diffYours) - { - $diff = array(); - $posMine = $posYours = 0; - while($posMine<count($diffMine) && $posYours<count($diffYours)) - { - $typeMine = $diffMine[$posMine][0]; - $typeYours = $diffYours[$posYours][0]; - if($typeMine==YellowMerge::SAME) - { - array_push($diff, $diffYours[$posYours]); - } else if($typeYours==YellowMerge::SAME) { - array_push($diff, $diffMine[$posMine]); - } else if($typeMine==YellowMerge::ADD && $typeYours==YellowMerge::ADD) { - $this->mergeConflict($diff, $diffMine[$posMine], $diffYours[$posYours], false); - } else if($typeMine==YellowMerge::MODIFY && $typeYours==YellowMerge::MODIFY) { - $this->mergeConflict($diff, $diffMine[$posMine], $diffYours[$posYours], false); - } else if($typeMine==YellowMerge::REMOVE && $typeYours==YellowMerge::REMOVE) { - array_push($diff, $diffMine[$posMine]); - } else if($typeMine==YellowMerge::ADD) { - array_push($diff, $diffMine[$posMine]); - } else if($typeYours==YellowMerge::ADD) { - array_push($diff, $diffYours[$posYours]); - } else { - $this->mergeConflict($diff, $diffMine[$posMine], $diffYours[$posYours], true); - } - if(defined("DEBUG") && DEBUG>=2) echo "YellowMerge::mergeDiff $typeMine $typeYours pos:$posMine\t$posYours<br/>\n"; - if($typeMine==YellowMerge::ADD || $typeYours==YellowMerge::ADD) - { - if($typeMine==YellowMerge::ADD) ++$posMine; - if($typeYours==YellowMerge::ADD) ++$posYours; - } else { - ++$posMine; - ++$posYours; - } - } - for(;$posMine<count($diffMine); ++$posMine) - { - array_push($diff, $diffMine[$posMine]); - $typeMine = $diffMine[$posMine][0]; $typeYours = ' '; - if(defined("DEBUG") && DEBUG>=2) echo "YellowMerge::mergeDiff $typeMine $typeYours pos:$posMine\t$posYours<br/>\n"; - } - for(;$posYours<count($diffYours); ++$posYours) - { - array_push($diff, $diffYours[$posYours]); - $typeYours = $diffYours[$posYours][0]; $typeMine = ' '; - if(defined("DEBUG") && DEBUG>=2) echo "YellowMerge::mergeDiff $typeMine $typeYours pos:$posMine\t$posYours<br/>\n"; - } - return $diff; - } - - // Merge potential conflict - function mergeConflict(&$diff, $diffMine, $diffYours, $conflict) - { - if(!$conflict && $diffMine[1]==$diffYours[1]) - { - array_push($diff, $diffMine); - } else { - array_push($diff, array($diffMine[0], $diffMine[1], true)); - array_push($diff, array($diffYours[0], $diffYours[1], true)); - } - } - - // Return merged text, null if not possible - function getOutput($diff, $showDiff = false) - { - $output = ""; - if(!$showDiff) - { - for($i=0; $i<count($diff); ++$i) - { - if($diff[$i][0]!=YellowMerge::REMOVE) $output .= $diff[$i][1]; - $conflict |= $diff[$i][2]; - } - } else { - for($i=0; $i<count($diff); ++$i) - { - $output .= $diff[$i][2] ? "! " : $diff[$i][0].' '; - $output .= $diff[$i][1]; - } - } - return !$conflict ? $output : null; - } + +class YellowMerge { + public $yellow; //access to API + const ADD = "+"; //merge types + const MODIFY = "*"; + const REMOVE = "-"; + const SAME = " "; + + public function __construct($yellow) { + $this->yellow = $yellow; + } + + // Merge text, null if not possible + public function merge($textSource, $textMine, $textYours, $showDiff = false) { + if ($textMine!=$textYours) { + $diffMine = $this->buildDiff($textSource, $textMine); + $diffYours = $this->buildDiff($textSource, $textYours); + $diff = $this->mergeDiff($diffMine, $diffYours); + $output = $this->getOutput($diff, $showDiff); + } else { + $output = $textMine; + } + return $output; + } + + // Build differences to common source + public function buildDiff($textSource, $textOther) { + $diff = array(); + $lastRemove = -1; + $textStart = 0; + $textSource = $this->yellow->toolbox->getTextLines($textSource); + $textOther = $this->yellow->toolbox->getTextLines($textOther); + $sourceEnd = $sourceSize = count($textSource); + $otherEnd = $otherSize = count($textOther); + while ($textStart<$sourceEnd && $textStart<$otherEnd && $textSource[$textStart]==$textOther[$textStart]) { + ++$textStart; + } + while ($textStart<$sourceEnd && $textStart<$otherEnd && $textSource[$sourceEnd-1]==$textOther[$otherEnd-1]) { + --$sourceEnd; + --$otherEnd; + } + for ($pos=0; $pos<$textStart; ++$pos) { + array_push($diff, array(YellowMerge::SAME, $textSource[$pos], false)); + } + $lcs = $this->buildDiffLCS($textSource, $textOther, $textStart, $sourceEnd-$textStart, $otherEnd-$textStart); + for ($x=0,$y=0,$xEnd=$otherEnd-$textStart,$yEnd=$sourceEnd-$textStart; $x<$xEnd || $y<$yEnd;) { + $max = $lcs[$y][$x]; + if ($y<$yEnd && $lcs[$y+1][$x]==$max) { + array_push($diff, array(YellowMerge::REMOVE, $textSource[$textStart+$y], false)); + if ($lastRemove==-1) $lastRemove = count($diff)-1; + ++$y; + continue; + } + if ($x<$xEnd && $lcs[$y][$x+1]==$max) { + if ($lastRemove==-1 || $diff[$lastRemove][0]!=YellowMerge::REMOVE) { + array_push($diff, array(YellowMerge::ADD, $textOther[$textStart+$x], false)); + $lastRemove = -1; + } else { + $diff[$lastRemove] = array(YellowMerge::MODIFY, $textOther[$textStart+$x], false); + ++$lastRemove; + if (count($diff)==$lastRemove) $lastRemove = -1; + } + ++$x; + continue; + } + array_push($diff, array(YellowMerge::SAME, $textSource[$textStart+$y], false)); + $lastRemove = -1; + ++$x; + ++$y; + } + for ($pos=$sourceEnd;$pos<$sourceSize; ++$pos) { + array_push($diff, array(YellowMerge::SAME, $textSource[$pos], false)); + } + return $diff; + } + + // Build longest common subsequence + public function buildDiffLCS($textSource, $textOther, $textStart, $yEnd, $xEnd) { + $lcs = array_fill(0, $yEnd+1, array_fill(0, $xEnd+1, 0)); + for ($y=$yEnd-1; $y>=0; --$y) { + for ($x=$xEnd-1; $x>=0; --$x) { + if ($textSource[$textStart+$y]==$textOther[$textStart+$x]) { + $lcs[$y][$x] = $lcs[$y+1][$x+1]+1; + } else { + $lcs[$y][$x] = max($lcs[$y][$x+1], $lcs[$y+1][$x]); + } + } + } + return $lcs; + } + + // Merge differences + public function mergeDiff($diffMine, $diffYours) { + $diff = array(); + $posMine = $posYours = 0; + while ($posMine<count($diffMine) && $posYours<count($diffYours)) { + $typeMine = $diffMine[$posMine][0]; + $typeYours = $diffYours[$posYours][0]; + if ($typeMine==YellowMerge::SAME) { + array_push($diff, $diffYours[$posYours]); + } elseif ($typeYours==YellowMerge::SAME) { + array_push($diff, $diffMine[$posMine]); + } elseif ($typeMine==YellowMerge::ADD && $typeYours==YellowMerge::ADD) { + $this->mergeConflict($diff, $diffMine[$posMine], $diffYours[$posYours], false); + } elseif ($typeMine==YellowMerge::MODIFY && $typeYours==YellowMerge::MODIFY) { + $this->mergeConflict($diff, $diffMine[$posMine], $diffYours[$posYours], false); + } elseif ($typeMine==YellowMerge::REMOVE && $typeYours==YellowMerge::REMOVE) { + array_push($diff, $diffMine[$posMine]); + } elseif ($typeMine==YellowMerge::ADD) { + array_push($diff, $diffMine[$posMine]); + } elseif ($typeYours==YellowMerge::ADD) { + array_push($diff, $diffYours[$posYours]); + } else { + $this->mergeConflict($diff, $diffMine[$posMine], $diffYours[$posYours], true); + } + if (defined("DEBUG") && DEBUG>=2) echo "YellowMerge::mergeDiff $typeMine $typeYours pos:$posMine\t$posYours<br/>\n"; + if ($typeMine==YellowMerge::ADD || $typeYours==YellowMerge::ADD) { + if ($typeMine==YellowMerge::ADD) ++$posMine; + if ($typeYours==YellowMerge::ADD) ++$posYours; + } else { + ++$posMine; + ++$posYours; + } + } + for (;$posMine<count($diffMine); ++$posMine) { + array_push($diff, $diffMine[$posMine]); + $typeMine = $diffMine[$posMine][0]; + $typeYours = " "; + if (defined("DEBUG") && DEBUG>=2) echo "YellowMerge::mergeDiff $typeMine $typeYours pos:$posMine\t$posYours<br/>\n"; + } + for (;$posYours<count($diffYours); ++$posYours) { + array_push($diff, $diffYours[$posYours]); + $typeYours = $diffYours[$posYours][0]; + $typeMine = " "; + if (defined("DEBUG") && DEBUG>=2) echo "YellowMerge::mergeDiff $typeMine $typeYours pos:$posMine\t$posYours<br/>\n"; + } + return $diff; + } + + // Merge potential conflict + public function mergeConflict(&$diff, $diffMine, $diffYours, $conflict) { + if (!$conflict && $diffMine[1]==$diffYours[1]) { + array_push($diff, $diffMine); + } else { + array_push($diff, array($diffMine[0], $diffMine[1], true)); + array_push($diff, array($diffYours[0], $diffYours[1], true)); + } + } + + // Return merged text, null if not possible + public function getOutput($diff, $showDiff = false) { + $output = ""; + if (!$showDiff) { + for ($i=0; $i<count($diff); ++$i) { + if ($diff[$i][0]!=YellowMerge::REMOVE) $output .= $diff[$i][1]; + $conflict |= $diff[$i][2]; + } + } else { + for ($i=0; $i<count($diff); ++$i) { + $output .= $diff[$i][2] ? "! " : $diff[$i][0]." "; + $output .= $diff[$i][1]; + } + } + return !$conflict ? $output : null; + } } $yellow->plugins->register("edit", "YellowEdit", YellowEdit::VERSION); -?> diff --git a/system/plugins/image.php b/system/plugins/image.php @@ -3,194 +3,168 @@ // Copyright (c) 2013-2017 Datenstrom, https://datenstrom.se // This file may be used and distributed under the terms of the public license. -class YellowImage -{ - const VERSION = "0.7.3"; - var $yellow; //access to API - var $graphicsLibrary; //graphics library support? (boolean) +class YellowImage { + const VERSION = "0.7.3"; + public $yellow; //access to API + public $graphicsLibrary; //graphics library support? (boolean) - // Handle initialisation - function onLoad($yellow) - { - $this->yellow = $yellow; - $this->yellow->config->setDefault("imageThumbnailLocation", "/media/thumbnails/"); - $this->yellow->config->setDefault("imageThumbnailDir", "media/thumbnails/"); - $this->yellow->config->setDefault("imageThumbnailJpgQuality", 80); - $this->yellow->config->setDefault("imageAlt", "Image"); - $this->graphicsLibrary = $this->isGraphicsLibrary(); - } + // Handle initialisation + public function onLoad($yellow) { + $this->yellow = $yellow; + $this->yellow->config->setDefault("imageThumbnailLocation", "/media/thumbnails/"); + $this->yellow->config->setDefault("imageThumbnailDir", "media/thumbnails/"); + $this->yellow->config->setDefault("imageThumbnailJpgQuality", 80); + $this->yellow->config->setDefault("imageAlt", "Image"); + $this->graphicsLibrary = $this->isGraphicsLibrary(); + } - // Handle page content parsing of custom block - function onParseContentBlock($page, $name, $text, $shortcut) - { - $output = null; - if($name=="image" && $shortcut) - { - if(!$this->graphicsLibrary) - { - $this->yellow->page->error(500, "Plugin 'image' requires GD library with gif/jpg/png support!"); - return $output; - } - list($name, $alt, $style, $width, $height) = $this->yellow->toolbox->getTextArgs($text); - if(!preg_match("/^\w+:/", $name)) - { - if(empty($alt)) $alt = $this->yellow->config->get("imageAlt"); - if(empty($width)) $width = "100%"; - if(empty($height)) $height = $width; - list($src, $width, $height) = $this->getImageInfo($this->yellow->config->get("imageDir").$name, $width, $height); - } else { - if(empty($alt)) $alt = $this->yellow->config->get("imageAlt"); - $src = $this->yellow->lookup->normaliseUrl("", "", "", $name); - $width = $height = 0; - } - $output = "<img src=\"".htmlspecialchars($src)."\""; - if($width && $height) $output .= " width=\"".htmlspecialchars($width)."\" height=\"".htmlspecialchars($height)."\""; - if(!empty($alt)) $output .= " alt=\"".htmlspecialchars($alt)."\" title=\"".htmlspecialchars($alt)."\""; - if(!empty($style)) $output .= " class=\"".htmlspecialchars($style)."\""; - $output .= " />"; - } - return $output; - } - - // Handle command - function onCommand($args) - { - list($command) = $args; - switch($command) - { - case "clean": $statusCode = $this->cleanCommand($args); break; - default: $statusCode = 0; - } - return $statusCode; - } + // Handle page content parsing of custom block + public function onParseContentBlock($page, $name, $text, $shortcut) { + $output = null; + if ($name=="image" && $shortcut) { + if (!$this->graphicsLibrary) { + $this->yellow->page->error(500, "Plugin 'image' requires GD library with gif/jpg/png support!"); + return $output; + } + list($name, $alt, $style, $width, $height) = $this->yellow->toolbox->getTextArgs($text); + if (!preg_match("/^\w+:/", $name)) { + if (empty($alt)) $alt = $this->yellow->config->get("imageAlt"); + if (empty($width)) $width = "100%"; + if (empty($height)) $height = $width; + list($src, $width, $height) = $this->getImageInfo($this->yellow->config->get("imageDir").$name, $width, $height); + } else { + if (empty($alt)) $alt = $this->yellow->config->get("imageAlt"); + $src = $this->yellow->lookup->normaliseUrl("", "", "", $name); + $width = $height = 0; + } + $output = "<img src=\"".htmlspecialchars($src)."\""; + if ($width && $height) $output .= " width=\"".htmlspecialchars($width)."\" height=\"".htmlspecialchars($height)."\""; + if (!empty($alt)) $output .= " alt=\"".htmlspecialchars($alt)."\" title=\"".htmlspecialchars($alt)."\""; + if (!empty($style)) $output .= " class=\"".htmlspecialchars($style)."\""; + $output .= " />"; + } + return $output; + } + + // Handle command + public function onCommand($args) { + list($command) = $args; + switch ($command) { + case "clean": $statusCode = $this->cleanCommand($args); break; + default: $statusCode = 0; + } + return $statusCode; + } - // Clean thumbnails - function cleanCommand($args) - { - $statusCode = 0; - list($command, $path) = $args; - if($path=="all") - { - $path = $this->yellow->config->get("imageThumbnailDir"); - foreach($this->yellow->toolbox->getDirectoryEntries($path, "/.*/", false, false) as $entry) - { - if(!$this->yellow->toolbox->deleteFile($entry)) $statusCode = 500; - } - if($statusCode==500) echo "ERROR cleaning thumbnails: Can't delete files in directory '$path'!\n"; - } - return $statusCode; - } + // Clean thumbnails + public function cleanCommand($args) { + $statusCode = 0; + list($command, $path) = $args; + if ($path=="all") { + $path = $this->yellow->config->get("imageThumbnailDir"); + foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/.*/", false, false) as $entry) { + if (!$this->yellow->toolbox->deleteFile($entry)) $statusCode = 500; + } + if ($statusCode==500) echo "ERROR cleaning thumbnails: Can't delete files in directory '$path'!\n"; + } + return $statusCode; + } - // Return image info, create thumbnail on demand - function getImageInfo($fileName, $widthOutput, $heightOutput) - { - $fileNameShort = substru($fileName, strlenu($this->yellow->config->get("imageDir"))); - list($widthInput, $heightInput, $type) = $this->yellow->toolbox->detectImageInfo($fileName); - $widthOutput = $this->convertValueAndUnit($widthOutput, $widthInput); - $heightOutput = $this->convertValueAndUnit($heightOutput, $heightInput); - if(($widthInput==$widthOutput && $heightInput==$heightOutput) || $type=="svg") - { - $src = $this->yellow->config->get("serverBase").$this->yellow->config->get("imageLocation").$fileNameShort; - $width = $widthOutput; $height = $heightOutput; - } else { - $fileNameThumb = ltrim(str_replace(array("/", "\\", "."), "-", dirname($fileNameShort)."/".pathinfo($fileName, PATHINFO_FILENAME)), "-"); - $fileNameThumb .= "-".$widthOutput."x".$heightOutput; - $fileNameThumb .= ".".pathinfo($fileName, PATHINFO_EXTENSION); - $fileNameOutput = $this->yellow->config->get("imageThumbnailDir").$fileNameThumb; - if($this->isFileNotUpdated($fileName, $fileNameOutput)) - { - $image = $this->loadImage($fileName, $type); - $image = $this->resizeImage($image, $widthInput, $heightInput, $widthOutput, $heightOutput); - if(!$this->saveImage($image, $fileNameOutput, $type) || - !$this->yellow->toolbox->modifyFile($fileNameOutput, $this->yellow->toolbox->getFileModified($fileName))) - { - $this->yellow->page->error(500, "Can't write file '$fileNameOutput'!"); - } - } - $src = $this->yellow->config->get("serverBase").$this->yellow->config->get("imageThumbnailLocation").$fileNameThumb; - list($width, $height) = $this->yellow->toolbox->detectImageInfo($fileNameOutput); - } - return array($src, $width, $height); - } + // Return image info, create thumbnail on demand + public function getImageInfo($fileName, $widthOutput, $heightOutput) { + $fileNameShort = substru($fileName, strlenu($this->yellow->config->get("imageDir"))); + list($widthInput, $heightInput, $type) = $this->yellow->toolbox->detectImageInfo($fileName); + $widthOutput = $this->convertValueAndUnit($widthOutput, $widthInput); + $heightOutput = $this->convertValueAndUnit($heightOutput, $heightInput); + if (($widthInput==$widthOutput && $heightInput==$heightOutput) || $type=="svg") { + $src = $this->yellow->config->get("serverBase").$this->yellow->config->get("imageLocation").$fileNameShort; + $width = $widthOutput; + $height = $heightOutput; + } else { + $fileNameThumb = ltrim(str_replace(array("/", "\\", "."), "-", dirname($fileNameShort)."/".pathinfo($fileName, PATHINFO_FILENAME)), "-"); + $fileNameThumb .= "-".$widthOutput."x".$heightOutput; + $fileNameThumb .= ".".pathinfo($fileName, PATHINFO_EXTENSION); + $fileNameOutput = $this->yellow->config->get("imageThumbnailDir").$fileNameThumb; + if ($this->isFileNotUpdated($fileName, $fileNameOutput)) { + $image = $this->loadImage($fileName, $type); + $image = $this->resizeImage($image, $widthInput, $heightInput, $widthOutput, $heightOutput); + if (!$this->saveImage($image, $fileNameOutput, $type) || + !$this->yellow->toolbox->modifyFile($fileNameOutput, $this->yellow->toolbox->getFileModified($fileName))) { + $this->yellow->page->error(500, "Can't write file '$fileNameOutput'!"); + } + } + $src = $this->yellow->config->get("serverBase").$this->yellow->config->get("imageThumbnailLocation").$fileNameThumb; + list($width, $height) = $this->yellow->toolbox->detectImageInfo($fileNameOutput); + } + return array($src, $width, $height); + } - // Load image from file - function loadImage($fileName, $type) - { - $image = false; - switch($type) - { - case "gif": $image = @imagecreatefromgif($fileName); break; - case "jpg": $image = @imagecreatefromjpeg($fileName); break; - case "png": $image = @imagecreatefrompng($fileName); break; - } - return $image; - } - - // Save image to file - function saveImage($image, $fileName, $type) - { - $ok = false; - switch($type) - { - case "gif": $ok = @imagegif($image, $fileName); break; - case "jpg": $ok = @imagejpeg($image, $fileName, $this->yellow->config->get("imageThumbnailJpgQuality")); break; - case "png": $ok = @imagepng($image, $fileName); break; - } - return $ok; - } + // Load image from file + public function loadImage($fileName, $type) { + $image = false; + switch ($type) { + case "gif": $image = @imagecreatefromgif($fileName); break; + case "jpg": $image = @imagecreatefromjpeg($fileName); break; + case "png": $image = @imagecreatefrompng($fileName); break; + } + return $image; + } + + // Save image to file + public function saveImage($image, $fileName, $type) { + $ok = false; + switch ($type) { + case "gif": $ok = @imagegif($image, $fileName); break; + case "jpg": $ok = @imagejpeg($image, $fileName, $this->yellow->config->get("imageThumbnailJpgQuality")); break; + case "png": $ok = @imagepng($image, $fileName); break; + } + return $ok; + } - // Create image from scratch - function createImage($width, $height) - { - $image = imagecreatetruecolor($width, $height); - imagealphablending($image, false); - imagesavealpha($image, true); - return $image; - } + // Create image from scratch + public function createImage($width, $height) { + $image = imagecreatetruecolor($width, $height); + imagealphablending($image, false); + imagesavealpha($image, true); + return $image; + } - // Resize image - function resizeImage($image, $widthInput, $heightInput, $widthOutput, $heightOutput) - { - $widthFit = $widthInput * ($heightOutput / $heightInput); - $heightFit = $heightInput * ($widthOutput / $widthInput); - $widthDiff = abs($widthOutput - $widthFit); - $heightDiff = abs($heightOutput - $heightFit); - $imageOutput = $this->createImage($widthOutput, $heightOutput); - if($heightFit>$heightOutput) - { - imagecopyresampled($imageOutput, $image, 0, $heightDiff/-2, 0, 0, $widthOutput, $heightFit, $widthInput, $heightInput); - } else { - imagecopyresampled($imageOutput, $image, $widthDiff/-2, 0, 0, 0, $widthFit, $heightOutput, $widthInput, $heightInput); - } - return $imageOutput; - } - - // Return value according to unit - function convertValueAndUnit($text, $valueBase) - { - $value = $unit = ""; - if(preg_match("/([\d\.]+)(\S*)/", $text, $matches)) - { - $value = $matches[1]; - $unit = $matches[2]; - if($unit=="%") $value = $valueBase * $value / 100; - } - return intval($value); - } + // Resize image + public function resizeImage($image, $widthInput, $heightInput, $widthOutput, $heightOutput) { + $widthFit = $widthInput * ($heightOutput / $heightInput); + $heightFit = $heightInput * ($widthOutput / $widthInput); + $widthDiff = abs($widthOutput - $widthFit); + $heightDiff = abs($heightOutput - $heightFit); + $imageOutput = $this->createImage($widthOutput, $heightOutput); + if ($heightFit>$heightOutput) { + imagecopyresampled($imageOutput, $image, 0, $heightDiff/-2, 0, 0, $widthOutput, $heightFit, $widthInput, $heightInput); + } else { + imagecopyresampled($imageOutput, $image, $widthDiff/-2, 0, 0, 0, $widthFit, $heightOutput, $widthInput, $heightInput); + } + return $imageOutput; + } + + // Return value according to unit + public function convertValueAndUnit($text, $valueBase) { + $value = $unit = ""; + if (preg_match("/([\d\.]+)(\S*)/", $text, $matches)) { + $value = $matches[1]; + $unit = $matches[2]; + if ($unit=="%") $value = $valueBase * $value / 100; + } + return intval($value); + } - // Check if file needs to be updated - function isFileNotUpdated($fileNameInput, $fileNameOutput) - { - return $this->yellow->toolbox->getFileModified($fileNameInput)!=$this->yellow->toolbox->getFileModified($fileNameOutput); - } + // Check if file needs to be updated + public function isFileNotUpdated($fileNameInput, $fileNameOutput) { + return $this->yellow->toolbox->getFileModified($fileNameInput)!=$this->yellow->toolbox->getFileModified($fileNameOutput); + } - // Check graphics library support - function isGraphicsLibrary() - { - return extension_loaded("gd") && function_exists("gd_info") && - ((imagetypes()&(IMG_GIF|IMG_JPG|IMG_PNG))==(IMG_GIF|IMG_JPG|IMG_PNG)); - } + // Check graphics library support + public function isGraphicsLibrary() { + return extension_loaded("gd") && function_exists("gd_info") && + ((imagetypes()&(IMG_GIF|IMG_JPG|IMG_PNG))==(IMG_GIF|IMG_JPG|IMG_PNG)); + } } $yellow->plugins->register("image", "YellowImage", YellowImage::VERSION); -?> diff --git a/system/plugins/language.php b/system/plugins/language.php @@ -3,10 +3,8 @@ // Copyright (c) 2013-2018 Datenstrom, https://datenstrom.se // This file may be used and distributed under the terms of the public license. -class YellowLanguage -{ - const VERSION = "0.7.13"; +class YellowLanguage { + const VERSION = "0.7.13"; } $yellow->plugins->register("language", "YellowLanguage", YellowLanguage::VERSION); -?> diff --git a/system/plugins/markdown.php b/system/plugins/markdown.php @@ -3,23 +3,20 @@ // Copyright (c) 2013-2018 Datenstrom, https://datenstrom.se // This file may be used and distributed under the terms of the public license. -class YellowMarkdown -{ - const VERSION = "0.6.8"; - var $yellow; //access to API - - // Handle initialisation - function onLoad($yellow) - { - $this->yellow = $yellow; - } - - // Handle page content parsing of raw format - function onParseContentRaw($page, $text) - { - $markdown = new YellowMarkdownExtraParser($this->yellow, $page); - return $markdown->transform($text); - } +class YellowMarkdown { + const VERSION = "0.6.8"; + public $yellow; //access to API + + // Handle initialisation + public function onLoad($yellow) { + $this->yellow = $yellow; + } + + // Handle page content parsing of raw format + public function onParseContentRaw($page, $text) { + $markdown = new YellowMarkdownExtraParser($this->yellow, $page); + return $markdown->transform($text); + } } // PHP Markdown Lib @@ -3734,154 +3731,138 @@ class MarkdownExtraParser extends MarkdownParser { } } } - + // Markdown extra parser extensions // Copyright (c) 2013-2018 Datenstrom - -class YellowMarkdownExtraParser extends MarkdownExtraParser -{ - var $yellow; //access to API - var $page; //access to page - var $idAttributes; //id attributes - - function __construct($yellow, $page) - { - $this->yellow = $yellow; - $this->page = $page; - $this->idAttributes = array(); - $this->no_markup = $page->parserSafeMode; - $this->url_filter_func = function($url) use ($yellow, $page) - { - return $yellow->lookup->normaliseLocation($url, $page->location, - $page->parserSafeMode && $page->statusCode==200); - }; - parent::__construct(); - } - // Return unique id attribute - function getIdAttribute($text) - { - $text = $this->yellow->lookup->normaliseName($text, true, false, true); - $text = trim(preg_replace("/-+/", "-", $text), "-"); - if(is_null($this->idAttributes[$text])) - { - $this->idAttributes[$text] = $text; - $attr = " id=\"$text\""; - } - return $attr; - } - - // Handle links - function doAutoLinks($text) - { - $text = preg_replace_callback("/<(\w+:[^\'\">\s]+)>/", array(&$this, "_doAutoLinks_url_callback"), $text); - $text = preg_replace_callback("/<([\w\-\.]+@[\w\-\.]+)>/", array(&$this, "_doAutoLinks_email_callback"), $text); - $text = preg_replace_callback("/\[\-\-(.*?)\-\-\]/", array(&$this, "_doAutoLinks_comment_callback"), $text); - $text = preg_replace_callback("/\[(\w+)(.*?)\]/", array(&$this, "_doAutoLinks_shortcut_callback"), $text); - $text = preg_replace_callback("/\:([\w\+\-\_]+)\:/", array(&$this, "_doAutoLinks_shortcode_callback"), $text); - $text = preg_replace_callback("/((http|https|ftp):\/\/\S+[^\'\"\,\.\;\:\s]+)/", array(&$this, "_doAutoLinks_url_callback"), $text); - $text = preg_replace_callback("/([\w\-\.]+@[\w\-\.]+\.[\w]{2,4})/", array(&$this, "_doAutoLinks_email_callback"), $text); - return $text; - } - - // Handle comments - function _doAutoLinks_comment_callback($matches) - { - $text = $matches[1]; - $output = "<!--".htmlspecialchars($text, ENT_NOQUOTES)."-->"; - if($text[0]=='-') $output = ""; - return $this->hashBlock($output); - } - - // Handle shortcuts - function _doAutoLinks_shortcut_callback($matches) - { - $output = $this->page->parseContentBlock($matches[1], trim($matches[2]), true); - if(is_null($output)) $output = htmlspecialchars($matches[0], ENT_NOQUOTES); - return substr($output, 0, 4)=="<div" ? $this->hashBlock(trim($output)) : $this->hashPart(trim($output)); - } - - // Handle shortcodes - function _doAutoLinks_shortcode_callback($matches) - { - $output = $this->page->parseContentBlock("", $matches[1], true); - if(is_null($output)) $output = htmlspecialchars($matches[0], ENT_NOQUOTES); - return $this->hashPart($output); - } - - // Handle fenced code blocks - function _doFencedCodeBlocks_callback($matches) - { - $text = $matches[4]; - $name = empty($matches[2]) ? "" : "$matches[2] $matches[3]"; - $output = $this->page->parseContentBlock($name, $text, false); - if(is_null($output)) - { - $attr = $this->doExtraAttributes("pre", ".$matches[2] $matches[3]"); - $output = "<pre$attr><code>".htmlspecialchars($text, ENT_NOQUOTES)."</code></pre>"; - } - return "\n\n".$this->hashBlock($output)."\n\n"; - } - - // Handle headers, text style - function _doHeaders_callback_setext($matches) - { - if($matches[3]=='-' && preg_match('{^- }', $matches[1])) return $matches[0]; - $text = $matches[1]; - $level = $matches[3]{0}=='=' ? 1 : 2; - $attr = $this->doExtraAttributes("h$level", $dummy =& $matches[2]); - if(empty($attr) && $level>=2 && $level<=3) $attr = $this->getIdAttribute($text); - $output = "<h$level$attr>".$this->runSpanGamut($text)."</h$level>"; - return "\n".$this->hashBlock($output)."\n\n"; - } - - // Handle headers, atx style - function _doHeaders_callback_atx($matches) - { - $text = $matches[2]; - $level = strlen($matches[1]); - $attr = $this->doExtraAttributes("h$level", $dummy =& $matches[3]); - if(empty($attr) && $level>=2 && $level<=3) $attr = $this->getIdAttribute($text); - $output = "<h$level$attr>".$this->runSpanGamut($text)."</h$level>"; - return "\n".$this->hashBlock($output)."\n\n"; - } - - // Handle inline links - function _doAnchors_inline_callback($matches) - { - $url = $matches[3]=="" ? $matches[4] : $matches[3]; - $text = $matches[2]; - $title = $matches[7]; - $attr = $this->doExtraAttributes("a", $dummy =& $matches[8]); - $output = "<a href=\"".$this->encodeURLAttribute($url)."\""; - if(!empty($title)) $output .= " title=\"".$this->encodeAttribute($title)."\""; - $output .= $attr; - $output .= ">".$this->runSpanGamut($text)."</a>"; - return $this->hashPart($output); - } - - // Handle inline images - function _doImages_inline_callback($matches) - { - $width = $height = 0; - $src = $matches[3]=="" ? $matches[4] : $matches[3]; - if(!preg_match("/^\w+:/", $src)) - { - list($width, $height) = $this->yellow->toolbox->detectImageInfo($this->yellow->config->get("imageDir").$src); - $src = $this->yellow->config->get("serverBase").$this->yellow->config->get("imageLocation").$src; - } - $alt = $matches[2]; - $title = $matches[7]=="" ? $matches[2] : $matches[7]; - $attr = $this->doExtraAttributes("img", $dummy =& $matches[8]); - $output = "<img src=\"".$this->encodeURLAttribute($src)."\""; - if($width && $height) $output .= " width=\"$width\" height=\"$height\""; - if(!empty($alt)) $output .= " alt=\"".$this->encodeAttribute($alt)."\""; - if(!empty($title)) $output .= " title=\"".$this->encodeAttribute($title)."\""; - $output .= $attr; - $output .= $this->empty_element_suffix; - return $this->hashPart($output); - } +class YellowMarkdownExtraParser extends MarkdownExtraParser { + public $yellow; //access to API + public $page; //access to page + public $idAttributes; //id attributes + + public function __construct($yellow, $page) { + $this->yellow = $yellow; + $this->page = $page; + $this->idAttributes = array(); + $this->no_markup = $page->parserSafeMode; + $this->url_filter_func = function($url) use ($yellow, $page) { + return $yellow->lookup->normaliseLocation($url, $page->location, + $page->parserSafeMode && $page->statusCode==200); + }; + parent::__construct(); + } + + // Return unique id attribute + public function getIdAttribute($text) { + $text = $this->yellow->lookup->normaliseName($text, true, false, true); + $text = trim(preg_replace("/-+/", "-", $text), "-"); + if (is_null($this->idAttributes[$text])) { + $this->idAttributes[$text] = $text; + $attr = " id=\"$text\""; + } + return $attr; + } + + // Handle links + public function doAutoLinks($text) { + $text = preg_replace_callback("/<(\w+:[^\'\">\s]+)>/", array(&$this, "_doAutoLinks_url_callback"), $text); + $text = preg_replace_callback("/<([\w\-\.]+@[\w\-\.]+)>/", array(&$this, "_doAutoLinks_email_callback"), $text); + $text = preg_replace_callback("/\[\-\-(.*?)\-\-\]/", array(&$this, "_doAutoLinks_comment_callback"), $text); + $text = preg_replace_callback("/\[(\w+)(.*?)\]/", array(&$this, "_doAutoLinks_shortcut_callback"), $text); + $text = preg_replace_callback("/\:([\w\+\-\_]+)\:/", array(&$this, "_doAutoLinks_shortcode_callback"), $text); + $text = preg_replace_callback("/((http|https|ftp):\/\/\S+[^\'\"\,\.\;\:\s]+)/", array(&$this, "_doAutoLinks_url_callback"), $text); + $text = preg_replace_callback("/([\w\-\.]+@[\w\-\.]+\.[\w]{2,4})/", array(&$this, "_doAutoLinks_email_callback"), $text); + return $text; + } + + // Handle comments + public function _doAutoLinks_comment_callback($matches) { + $text = $matches[1]; + $output = "<!--".htmlspecialchars($text, ENT_NOQUOTES)."-->"; + if ($text[0]=="-") $output = ""; + return $this->hashBlock($output); + } + + // Handle shortcuts + public function _doAutoLinks_shortcut_callback($matches) { + $output = $this->page->parseContentBlock($matches[1], trim($matches[2]), true); + if (is_null($output)) $output = htmlspecialchars($matches[0], ENT_NOQUOTES); + return substr($output, 0, 4)=="<div" ? $this->hashBlock(trim($output)) : $this->hashPart(trim($output)); + } + + // Handle shortcodes + public function _doAutoLinks_shortcode_callback($matches) { + $output = $this->page->parseContentBlock("", $matches[1], true); + if (is_null($output)) $output = htmlspecialchars($matches[0], ENT_NOQUOTES); + return $this->hashPart($output); + } + + // Handle fenced code blocks + public function _doFencedCodeBlocks_callback($matches) { + $text = $matches[4]; + $name = empty($matches[2]) ? "" : "$matches[2] $matches[3]"; + $output = $this->page->parseContentBlock($name, $text, false); + if (is_null($output)) { + $attr = $this->doExtraAttributes("pre", ".$matches[2] $matches[3]"); + $output = "<pre$attr><code>".htmlspecialchars($text, ENT_NOQUOTES)."</code></pre>"; + } + return "\n\n".$this->hashBlock($output)."\n\n"; + } + + // Handle headers, text style + public function _doHeaders_callback_setext($matches) { + if ($matches[3]=="-" && preg_match('{^- }', $matches[1])) return $matches[0]; + $text = $matches[1]; + $level = $matches[3]{0}=="=" ? 1 : 2; + $attr = $this->doExtraAttributes("h$level", $dummy =& $matches[2]); + if (empty($attr) && $level>=2 && $level<=3) $attr = $this->getIdAttribute($text); + $output = "<h$level$attr>".$this->runSpanGamut($text)."</h$level>"; + return "\n".$this->hashBlock($output)."\n\n"; + } + + // Handle headers, atx style + public function _doHeaders_callback_atx($matches) { + $text = $matches[2]; + $level = strlen($matches[1]); + $attr = $this->doExtraAttributes("h$level", $dummy =& $matches[3]); + if (empty($attr) && $level>=2 && $level<=3) $attr = $this->getIdAttribute($text); + $output = "<h$level$attr>".$this->runSpanGamut($text)."</h$level>"; + return "\n".$this->hashBlock($output)."\n\n"; + } + + // Handle inline links + public function _doAnchors_inline_callback($matches) { + $url = $matches[3]=="" ? $matches[4] : $matches[3]; + $text = $matches[2]; + $title = $matches[7]; + $attr = $this->doExtraAttributes("a", $dummy =& $matches[8]); + $output = "<a href=\"".$this->encodeURLAttribute($url)."\""; + if (!empty($title)) $output .= " title=\"".$this->encodeAttribute($title)."\""; + $output .= $attr; + $output .= ">".$this->runSpanGamut($text)."</a>"; + return $this->hashPart($output); + } + + // Handle inline images + public function _doImages_inline_callback($matches) { + $width = $height = 0; + $src = $matches[3]=="" ? $matches[4] : $matches[3]; + if (!preg_match("/^\w+:/", $src)) { + list($width, $height) = $this->yellow->toolbox->detectImageInfo($this->yellow->config->get("imageDir").$src); + $src = $this->yellow->config->get("serverBase").$this->yellow->config->get("imageLocation").$src; + } + $alt = $matches[2]; + $title = $matches[7]=="" ? $matches[2] : $matches[7]; + $attr = $this->doExtraAttributes("img", $dummy =& $matches[8]); + $output = "<img src=\"".$this->encodeURLAttribute($src)."\""; + if ($width && $height) $output .= " width=\"$width\" height=\"$height\""; + if (!empty($alt)) $output .= " alt=\"".$this->encodeAttribute($alt)."\""; + if (!empty($title)) $output .= " title=\"".$this->encodeAttribute($title)."\""; + $output .= $attr; + $output .= $this->empty_element_suffix; + return $this->hashPart($output); + } } - + $yellow->plugins->register("markdown", "YellowMarkdown", YellowMarkdown::VERSION); -?> + diff --git a/system/plugins/update-blog.installation b/system/plugins/update-blog.installation Binary files differ. diff --git a/system/plugins/update-wiki.installation b/system/plugins/update-wiki.installation Binary files differ. diff --git a/system/plugins/update.php b/system/plugins/update.php @@ -3,835 +3,698 @@ // Copyright (c) 2013-2018 Datenstrom, https://datenstrom.se // This file may be used and distributed under the terms of the public license. -class YellowUpdate -{ - const VERSION = "0.7.16"; - var $yellow; //access to API - var $updates; //number of updates - - // Handle initialisation - function onLoad($yellow) - { - $this->yellow = $yellow; - $this->yellow->config->setDefault("updatePluginsUrl", "https://github.com/datenstrom/yellow-plugins"); - $this->yellow->config->setDefault("updateThemesUrl", "https://github.com/datenstrom/yellow-themes"); - $this->yellow->config->setDefault("updateInformationFile", "update.ini"); - $this->yellow->config->setDefault("updateVersionFile", "version.ini"); - $this->yellow->config->setDefault("updateResourceFile", "resource.ini"); - } - - // Handle startup - function onStartup($update) - { - if($update) - { - $fileNameConfig = $this->yellow->config->get("configDir").$this->yellow->config->get("configFile"); - $fileData = $this->yellow->toolbox->readFile($fileNameConfig); - $configDefaults = new YellowDataCollection(); - $configDefaults->exchangeArray($this->yellow->config->configDefaults->getArrayCopy()); - foreach($this->yellow->toolbox->getTextLines($fileData) as $line) - { - preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); - if(!empty($matches[1]) && !is_null($configDefaults[$matches[1]])) unset($configDefaults[$matches[1]]); - if(!empty($matches[1]) && $matches[1][0]!='#' && is_null($this->yellow->config->configDefaults[$matches[1]])) - { - $fileDataNew .= "# $line"; - } else { - $fileDataNew .= $line; - } - } - unset($configDefaults["configFile"]); - foreach($configDefaults as $key=>$value) - { - $fileDataNew .= ucfirst($key).": $value\n"; - } - if($fileData!=$fileDataNew) $this->yellow->toolbox->createFile($fileNameConfig, $fileDataNew); - } - if($update) //TODO: remove later, converts old theme - { - $path = $this->yellow->config->get("themeDir"); - foreach($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.css$/", true, false) as $entry) - { - $fileNameAsset = $this->yellow->config->get("assetDir").basename($entry); - if(!is_file($fileNameAsset)) - { - $fileData = $this->yellow->toolbox->readFile($entry); - $fileData = preg_replace("#url\(assets/(.*?)\)#", "url($1)", $fileData); - $this->yellow->toolbox->createFile($fileNameAsset, $fileData); - } - $this->yellow->toolbox->deleteFile($entry, $this->yellow->config->get("trashDir")); - $_GET["clean-url"] = "theme-has-been-updated"; - } - } - if($update) //TODO: remove later, converts old error page - { - $fileName = $this->yellow->config->get("configDir")."page-error-500.txt"; - if(is_file($fileName)) - { - $fileData = $this->yellow->toolbox->readFile($fileName); - $fileDataNew = preg_replace("/@pageerror/", "[yellow error]", $fileData); - if($fileData!=$fileDataNew) $this->yellow->toolbox->createFile($fileName, $fileDataNew); - } - } - if($update) //TODO: remove later, converts new blog page - { - $fileName = $this->yellow->config->get("configDir")."page-new-blog.txt"; - if(is_file($fileName)) - { - $fileData = $this->yellow->toolbox->readFile($fileName); - $fileDataNew = $this->yellow->toolbox->setMetaData($fileData, "template", "blog"); - if($fileData!=$fileDataNew) $this->yellow->toolbox->createFile($fileName, $fileDataNew); - } - } - if($update) //TODO: remove later, converts new wiki page - { - $fileName = $this->yellow->config->get("configDir")."page-new-wiki.txt"; - if(is_file($fileName)) - { - $fileData = $this->yellow->toolbox->readFile($fileName); - $fileDataNew = $this->yellow->toolbox->setMetaData($fileData, "template", "wiki"); - if($fileData!=$fileDataNew) $this->yellow->toolbox->createFile($fileName, $fileDataNew); - } - } - if($update) //TODO: remove later, converts template settings - { - $valueDefault = $this->yellow->config->get("template"); - foreach($this->yellow->pages->index(true, true) as $page) - { - preg_match("/^.*\/(.+?)$/", dirname($page->fileName), $matches); - $value = $this->yellow->lookup->normaliseName($matches[1], true, false, true); - if(!is_file($this->yellow->config->get("templateDir").$value.".html")) $value = $valueDefault; - $pageTemplate = $this->yellow->toolbox->getMetaData($page->rawData, "template"); - $pagePublished = $this->yellow->toolbox->getMetaData($page->rawData, "published"); - if(empty($pagePublished) && $value=="blog") $value = $valueDefault; - if(empty($pageTemplate) && $value!=$valueDefault) - { - $rawDataNew = $this->yellow->toolbox->setMetaData($page->rawData, "template", $value); - if($page->rawData!=$rawDataNew) $this->yellow->toolbox->createFile($page->fileName, $rawDataNew); - } - } - foreach($this->yellow->pages->index(true, true)->filter("template", "blogpages") as $page) - { - $rawDataNew = $this->yellow->toolbox->setMetaData($page->rawData, "templateNew", "blog"); - if($page->rawData!=$rawDataNew) $this->yellow->toolbox->createFile($page->fileName, $rawDataNew); - } - foreach($this->yellow->pages->index(true, true)->filter("template", "wikipages") as $page) - { - $rawDataNew = $this->yellow->toolbox->setMetaData($page->rawData, "templateNew", "wiki"); - if($page->rawData!=$rawDataNew) $this->yellow->toolbox->createFile($page->fileName, $rawDataNew); - } - $this->yellow->pages = new YellowPages($this->yellow); - } - } - - // Handle request - function onRequest($scheme, $address, $base, $location, $fileName) - { - $statusCode = 0; - if($this->yellow->config->get("installationMode")) - { - $statusCode = $this->processRequestInstallationMode($scheme, $address, $base, $location, $fileName); - } else { - $statusCode = $this->processRequestInstallationPending($scheme, $address, $base, $location, $fileName); - } - return $statusCode; - } - - // Handle command - function onCommand($args) - { - list($command) = $args; - switch($command) - { - case "clean": $statusCode = $this->cleanCommand($args); break; - case "update": $statusCode = $this->updateCommand($args); break; - default: $statusCode = $this->processCommandInstallationPending($args); break; - } - return $statusCode; - } - - // Handle command help - function onCommandHelp() - { - return "update [option feature]"; - } - - // Clean downloads - function cleanCommand($args) - { - $statusCode = 0; - list($command, $path) = $args; - if($path=="all") - { - $path = $this->yellow->config->get("pluginDir"); - $regex = "/^.*\\".$this->yellow->config->get("downloadExtension")."$/"; - foreach($this->yellow->toolbox->getDirectoryEntries($path, $regex, false, false) as $entry) - { - if(!$this->yellow->toolbox->deleteFile($entry)) $statusCode = 500; - } - if($statusCode==500) echo "ERROR cleaning downloads: Can't delete files in directory '$path'!\n"; - } - return $statusCode; - } - - // Update website - function updateCommand($args) - { - list($command, $option, $feature) = $args; - if(empty($option) || $option=="normal" || $option=="force") - { - $force = $option=="force"; - list($statusCode, $data) = $this->detectSoftware($force, $feature); - if($statusCode!=200 || !empty($data)) - { - $this->updates = 0; - if($statusCode==200) $statusCode = $this->downloadSoftware($data); - if($statusCode==200) $statusCode = $this->updateSoftware($force); - if($statusCode>=400) echo "ERROR updating files: ".$this->yellow->page->get("pageError")."\n"; - echo "Yellow $command: Website ".($statusCode!=200 ? "not " : "")."updated"; - echo ", $this->updates update".($this->updates!=1 ? 's' : '')." installed\n"; - } else { - echo "Your website is up to date\n"; - } - } else { - $statusCode = 400; - echo "Yellow $command: Invalid arguments\n"; - } - return $statusCode; - } - - // Detect software - function detectSoftware($force, $feature) - { - $data = array(); - list($statusCodeCurrent, $dataCurrent) = $this->getSoftwareVersion(); - list($statusCodeLatest, $dataLatest) = $this->getSoftwareVersion(true, true); - list($statusCodeModified, $dataModified) = $this->getSoftwareModified(); - $statusCode = max($statusCodeCurrent, $statusCodeLatest, $statusCodeModified); - if(empty($feature)) - { - foreach($dataCurrent as $key=>$value) - { - list($version) = explode(',', $dataLatest[$key]); - if(strnatcasecmp($dataCurrent[$key], $version)<0) $data[$key] = $dataLatest[$key]; - if(!is_null($dataModified[$key]) && !empty($version) && $force) $data[$key] = $dataLatest[$key]; - } - } else { - foreach($dataCurrent as $key=>$value) - { - list($version) = explode(',', $dataLatest[$key]); - if(strtoloweru($key)==strtoloweru($feature) && !empty($version)) - { - $data[$key] = $dataLatest[$key]; - $dataModified = array_intersect_key($dataModified, $data); - break; - } - } - if(empty($data)) - { - $statusCode = 500; - $this->yellow->page->error($statusCode, "Can't find feature '$feature'!"); - } - } - if($statusCode==200) - { - foreach(array_merge($dataModified, $data) as $key=>$value) - { - list($version) = explode(',', $value); - if(is_null($dataModified[$key]) || $force) - { - echo "$key $version\n"; - } else { - echo "$key $version has been modified - Force update\n"; - } - } - } - return array($statusCode, $data); - } - - // Download software - function downloadSoftware($data) - { - $statusCode = 200; - $path = $this->yellow->config->get("pluginDir"); - $fileExtension = $this->yellow->config->get("downloadExtension"); - foreach($data as $key=>$value) - { - $fileName = $path.$this->yellow->lookup->normaliseName($key, true, false, true).".zip"; - list($version, $url) = explode(',', $value); - list($statusCode, $fileData) = $this->getSoftwareFile($url); - if(empty($fileData) || !$this->yellow->toolbox->createFile($fileName.$fileExtension, $fileData)) - { - $statusCode = 500; - $this->yellow->page->error($statusCode, "Can't write file '$fileName'!"); - break; - } - } - if($statusCode==200) - { - foreach($data as $key=>$value) - { - $fileName = $path.$this->yellow->lookup->normaliseName($key, true, false, true).".zip"; - if(!$this->yellow->toolbox->renameFile($fileName.$fileExtension, $fileName)) - { - $statusCode = 500; - $this->yellow->page->error($statusCode, "Can't write file '$fileName'!"); - } - } - } - return $statusCode; - } +class YellowUpdate { + const VERSION = "0.7.16"; + public $yellow; //access to API + public $updates; //number of updates + + // Handle initialisation + public function onLoad($yellow) { + $this->yellow = $yellow; + $this->yellow->config->setDefault("updatePluginsUrl", "https://github.com/datenstrom/yellow-plugins"); + $this->yellow->config->setDefault("updateThemesUrl", "https://github.com/datenstrom/yellow-themes"); + $this->yellow->config->setDefault("updateInformationFile", "update.ini"); + $this->yellow->config->setDefault("updateVersionFile", "version.ini"); + $this->yellow->config->setDefault("updateResourceFile", "resource.ini"); + } + + // Handle startup + public function onStartup($update) { + if ($update) { + $fileNameConfig = $this->yellow->config->get("configDir").$this->yellow->config->get("configFile"); + $fileData = $this->yellow->toolbox->readFile($fileNameConfig); + $configDefaults = new YellowDataCollection(); + $configDefaults->exchangeArray($this->yellow->config->configDefaults->getArrayCopy()); + foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) { + preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); + if (!empty($matches[1]) && !is_null($configDefaults[$matches[1]])) unset($configDefaults[$matches[1]]); + if (!empty($matches[1]) && $matches[1][0]!="#" && is_null($this->yellow->config->configDefaults[$matches[1]])) { + $fileDataNew .= "# $line"; + } else { + $fileDataNew .= $line; + } + } + unset($configDefaults["configFile"]); + foreach ($configDefaults as $key=>$value) { + $fileDataNew .= ucfirst($key).": $value\n"; + } + if ($fileData!=$fileDataNew) $this->yellow->toolbox->createFile($fileNameConfig, $fileDataNew); + } + if ($update) { //TODO: remove later, converts old theme + $path = $this->yellow->config->get("themeDir"); + foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.css$/", true, false) as $entry) { + $fileNameAsset = $this->yellow->config->get("assetDir").basename($entry); + if (!is_file($fileNameAsset)) { + $fileData = $this->yellow->toolbox->readFile($entry); + $fileData = preg_replace("#url\(assets/(.*?)\)#", "url($1)", $fileData); + $this->yellow->toolbox->createFile($fileNameAsset, $fileData); + } + $this->yellow->toolbox->deleteFile($entry, $this->yellow->config->get("trashDir")); + $_GET["clean-url"] = "theme-has-been-updated"; + } + } + if ($update) { //TODO: remove later, converts old error page + $fileName = $this->yellow->config->get("configDir")."page-error-500.txt"; + if (is_file($fileName)) { + $fileData = $this->yellow->toolbox->readFile($fileName); + $fileDataNew = preg_replace("/@pageerror/", "[yellow error]", $fileData); + if ($fileData!=$fileDataNew) $this->yellow->toolbox->createFile($fileName, $fileDataNew); + } + } + if ($update) { //TODO: remove later, converts new blog page + $fileName = $this->yellow->config->get("configDir")."page-new-blog.txt"; + if (is_file($fileName)) { + $fileData = $this->yellow->toolbox->readFile($fileName); + $fileDataNew = $this->yellow->toolbox->setMetaData($fileData, "template", "blog"); + if ($fileData!=$fileDataNew) $this->yellow->toolbox->createFile($fileName, $fileDataNew); + } + } + if ($update) { //TODO: remove later, converts new wiki page + $fileName = $this->yellow->config->get("configDir")."page-new-wiki.txt"; + if (is_file($fileName)) { + $fileData = $this->yellow->toolbox->readFile($fileName); + $fileDataNew = $this->yellow->toolbox->setMetaData($fileData, "template", "wiki"); + if ($fileData!=$fileDataNew) $this->yellow->toolbox->createFile($fileName, $fileDataNew); + } + } + if ($update) { //TODO: remove later, converts template settings + $valueDefault = $this->yellow->config->get("template"); + foreach ($this->yellow->pages->index(true, true) as $page) { + preg_match("/^.*\/(.+?)$/", dirname($page->fileName), $matches); + $value = $this->yellow->lookup->normaliseName($matches[1], true, false, true); + if (!is_file($this->yellow->config->get("templateDir").$value.".html")) $value = $valueDefault; + $pageTemplate = $this->yellow->toolbox->getMetaData($page->rawData, "template"); + $pagePublished = $this->yellow->toolbox->getMetaData($page->rawData, "published"); + if (empty($pagePublished) && $value=="blog") $value = $valueDefault; + if (empty($pageTemplate) && $value!=$valueDefault) { + $rawDataNew = $this->yellow->toolbox->setMetaData($page->rawData, "template", $value); + if ($page->rawData!=$rawDataNew) $this->yellow->toolbox->createFile($page->fileName, $rawDataNew); + } + } + foreach ($this->yellow->pages->index(true, true)->filter("template", "blogpages") as $page) { + $rawDataNew = $this->yellow->toolbox->setMetaData($page->rawData, "templateNew", "blog"); + if ($page->rawData!=$rawDataNew) $this->yellow->toolbox->createFile($page->fileName, $rawDataNew); + } + foreach ($this->yellow->pages->index(true, true)->filter("template", "wikipages") as $page) { + $rawDataNew = $this->yellow->toolbox->setMetaData($page->rawData, "templateNew", "wiki"); + if ($page->rawData!=$rawDataNew) $this->yellow->toolbox->createFile($page->fileName, $rawDataNew); + } + $this->yellow->pages = new YellowPages($this->yellow); + } + } + + // Handle request + public function onRequest($scheme, $address, $base, $location, $fileName) { + $statusCode = 0; + if ($this->yellow->config->get("installationMode")) { + $statusCode = $this->processRequestInstallationMode($scheme, $address, $base, $location, $fileName); + } else { + $statusCode = $this->processRequestInstallationPending($scheme, $address, $base, $location, $fileName); + } + return $statusCode; + } + + // Handle command + public function onCommand($args) { + list($command) = $args; + switch ($command) { + case "clean": $statusCode = $this->cleanCommand($args); break; + case "update": $statusCode = $this->updateCommand($args); break; + default: $statusCode = $this->processCommandInstallationPending($args); break; + } + return $statusCode; + } + + // Handle command help + public function onCommandHelp() { + return "update [option feature]"; + } + + // Clean downloads + public function cleanCommand($args) { + $statusCode = 0; + list($command, $path) = $args; + if ($path=="all") { + $path = $this->yellow->config->get("pluginDir"); + $regex = "/^.*\\".$this->yellow->config->get("downloadExtension")."$/"; + foreach ($this->yellow->toolbox->getDirectoryEntries($path, $regex, false, false) as $entry) { + if (!$this->yellow->toolbox->deleteFile($entry)) $statusCode = 500; + } + if ($statusCode==500) echo "ERROR cleaning downloads: Can't delete files in directory '$path'!\n"; + } + return $statusCode; + } + + // Update website + public function updateCommand($args) { + list($command, $option, $feature) = $args; + if (empty($option) || $option=="normal" || $option=="force") { + $force = $option=="force"; + list($statusCode, $data) = $this->detectSoftware($force, $feature); + if ($statusCode!=200 || !empty($data)) { + $this->updates = 0; + if ($statusCode==200) $statusCode = $this->downloadSoftware($data); + if ($statusCode==200) $statusCode = $this->updateSoftware($force); + if ($statusCode>=400) echo "ERROR updating files: ".$this->yellow->page->get("pageError")."\n"; + echo "Yellow $command: Website ".($statusCode!=200 ? "not " : "")."updated"; + echo ", $this->updates update".($this->updates!=1 ? "s" : "")." installed\n"; + } else { + echo "Your website is up to date\n"; + } + } else { + $statusCode = 400; + echo "Yellow $command: Invalid arguments\n"; + } + return $statusCode; + } + + // Detect software + public function detectSoftware($force, $feature) { + $data = array(); + list($statusCodeCurrent, $dataCurrent) = $this->getSoftwareVersion(); + list($statusCodeLatest, $dataLatest) = $this->getSoftwareVersion(true, true); + list($statusCodeModified, $dataModified) = $this->getSoftwareModified(); + $statusCode = max($statusCodeCurrent, $statusCodeLatest, $statusCodeModified); + if (empty($feature)) { + foreach ($dataCurrent as $key=>$value) { + list($version) = explode(",", $dataLatest[$key]); + if (strnatcasecmp($dataCurrent[$key], $version)<0) $data[$key] = $dataLatest[$key]; + if (!is_null($dataModified[$key]) && !empty($version) && $force) $data[$key] = $dataLatest[$key]; + } + } else { + foreach ($dataCurrent as $key=>$value) { + list($version) = explode(",", $dataLatest[$key]); + if (strtoloweru($key)==strtoloweru($feature) && !empty($version)) { + $data[$key] = $dataLatest[$key]; + $dataModified = array_intersect_key($dataModified, $data); + break; + } + } + if (empty($data)) { + $statusCode = 500; + $this->yellow->page->error($statusCode, "Can't find feature '$feature'!"); + } + } + if ($statusCode==200) { + foreach (array_merge($dataModified, $data) as $key=>$value) { + list($version) = explode(",", $value); + if (is_null($dataModified[$key]) || $force) { + echo "$key $version\n"; + } else { + echo "$key $version has been modified - Force update\n"; + } + } + } + return array($statusCode, $data); + } + + // Download software + public function downloadSoftware($data) { + $statusCode = 200; + $path = $this->yellow->config->get("pluginDir"); + $fileExtension = $this->yellow->config->get("downloadExtension"); + foreach ($data as $key=>$value) { + $fileName = $path.$this->yellow->lookup->normaliseName($key, true, false, true).".zip"; + list($version, $url) = explode(",", $value); + list($statusCode, $fileData) = $this->getSoftwareFile($url); + if (empty($fileData) || !$this->yellow->toolbox->createFile($fileName.$fileExtension, $fileData)) { + $statusCode = 500; + $this->yellow->page->error($statusCode, "Can't write file '$fileName'!"); + break; + } + } + if ($statusCode==200) { + foreach ($data as $key=>$value) { + $fileName = $path.$this->yellow->lookup->normaliseName($key, true, false, true).".zip"; + if (!$this->yellow->toolbox->renameFile($fileName.$fileExtension, $fileName)) { + $statusCode = 500; + $this->yellow->page->error($statusCode, "Can't write file '$fileName'!"); + } + } + } + return $statusCode; + } - // Update software - function updateSoftware($force = false) - { - $statusCode = 200; - if(function_exists("opcache_reset")) opcache_reset(); - $path = $this->yellow->config->get("pluginDir"); - foreach($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.zip$/", true, false) as $entry) - { - $statusCode = max($statusCode, $this->updateSoftwareArchive($entry, $force)); - if(!$this->yellow->toolbox->deleteFile($entry)) - { - $statusCode = 500; - $this->yellow->page->error($statusCode, "Can't delete file '$entry'!"); - } - } - $path = $this->yellow->config->get("themeDir"); - foreach($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.zip$/", true, false) as $entry) - { - $statusCode = max($statusCode, $this->updateSoftwareArchive($entry, $force)); - if(!$this->yellow->toolbox->deleteFile($entry)) - { - $statusCode = 500; - $this->yellow->page->error($statusCode, "Can't delete file '$entry'!"); - } - } - return $statusCode; - } + // Update software + public function updateSoftware($force = false) { + $statusCode = 200; + if (function_exists("opcache_reset")) opcache_reset(); + $path = $this->yellow->config->get("pluginDir"); + foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.zip$/", true, false) as $entry) { + $statusCode = max($statusCode, $this->updateSoftwareArchive($entry, $force)); + if (!$this->yellow->toolbox->deleteFile($entry)) { + $statusCode = 500; + $this->yellow->page->error($statusCode, "Can't delete file '$entry'!"); + } + } + $path = $this->yellow->config->get("themeDir"); + foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.zip$/", true, false) as $entry) { + $statusCode = max($statusCode, $this->updateSoftwareArchive($entry, $force)); + if (!$this->yellow->toolbox->deleteFile($entry)) { + $statusCode = 500; + $this->yellow->page->error($statusCode, "Can't delete file '$entry'!"); + } + } + return $statusCode; + } - // Update software from archive - function updateSoftwareArchive($path, $force = false) - { - $statusCode = 200; - $zip = new ZipArchive(); - if($zip->open($path)===true) - { - if(defined("DEBUG") && DEBUG>=2) echo "YellowUpdate::updateSoftwareArchive file:$path<br/>\n"; - if(preg_match("#^(.*\/).*?$#", $zip->getNameIndex(0), $matches)) $pathBase = $matches[1]; - $fileData = $zip->getFromName($pathBase.$this->yellow->config->get("updateInformationFile")); - foreach($this->yellow->toolbox->getTextLines($fileData) as $line) - { - preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); - if(!empty($matches[1]) && !empty($matches[2])) - { - list($dummy, $entry) = explode('/', $matches[1], 2); - list($fileName, $flags) = explode(',', $matches[2], 2); - if($dummy[0]!='Y') $fileName = $matches[1]; //TODO: remove later, converts old file format - if(is_file($fileName)) { $lastPublished = filemtime($fileName); break; } - } - } - foreach($this->yellow->toolbox->getTextLines($fileData) as $line) - { - preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); - if(lcfirst($matches[1])=="plugin" || lcfirst($matches[1])=="theme") $software = $matches[2]; - if(lcfirst($matches[1])=="published") $modified = strtotime($matches[2]); - if(!empty($matches[1]) && !empty($matches[2]) && strposu($matches[1], '/')) - { - list($dummy, $entry) = explode('/', $matches[1], 2); - list($fileName, $flags) = explode(',', $matches[2], 2); - if($dummy[0]!='Y') //TODO: remove later, converts old file format - { - list($entry, $flags) = explode(',', $matches[2], 2); - $fileName = $matches[1]; - } - $fileData = $zip->getFromName($pathBase.$entry); - $lastModified = $this->yellow->toolbox->getFileModified($fileName); - $statusCode = $this->updateSoftwareFile($fileName, $fileData, $modified, $lastModified, $lastPublished, $flags, $force, $software); - if($statusCode!=200) break; - } - } - $zip->close(); - if($statusCode==200) $statusCode = $this->updateSoftwareMultiLanguage($software); - if($statusCode==200) $statusCode = $this->updateSoftwareNotification($software); - ++$this->updates; - } else { - $statusCode = 500; - $this->yellow->page->error(500, "Can't open file '$path'!"); - } - return $statusCode; - } - - // Update software file - function updateSoftwareFile($fileName, $fileData, $modified, $lastModified, $lastPublished, $flags, $force, $software) - { - $statusCode = 200; - $fileName = $this->yellow->toolbox->normaliseTokens($fileName); - if($this->yellow->lookup->isValidFile($fileName) && !empty($software)) - { - $create = $update = $delete = false; - 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("/careful/i", $flags) && is_file($fileName) && $lastModified!=$lastPublished && !$force) $update = false; - if(preg_match("/optional/i", $flags) && $this->isSoftwareExisting($software)) $create = $update = $delete = false; - if($create) - { - if(!$this->yellow->toolbox->createFile($fileName, $fileData, true) || - !$this->yellow->toolbox->modifyFile($fileName, $modified)) - { - $statusCode = 500; - $this->yellow->page->error($statusCode, "Can't write file '$fileName'!"); - } - } - if($update) - { - if(!$this->yellow->toolbox->deleteFile($fileName, $this->yellow->config->get("trashDir")) || - !$this->yellow->toolbox->createFile($fileName, $fileData) || - !$this->yellow->toolbox->modifyFile($fileName, $modified)) - { - $statusCode = 500; - $this->yellow->page->error($statusCode, "Can't write file '$fileName'!"); - } - } - if($delete) - { - if(!$this->yellow->toolbox->deleteFile($fileName, $this->yellow->config->get("trashDir"))) - { - $statusCode = 500; - $this->yellow->page->error($statusCode, "Can't delete file '$fileName'!"); - } - } - if(defined("DEBUG") && DEBUG>=2) - { - $debug = "action:".($create ? "create" : "").($update ? "update" : "").($delete ? "delete" : ""); - if(!$create && !$update && !$delete) $debug = "action:none"; - echo "YellowUpdate::updateSoftwareFile file:$fileName $debug<br/>\n"; - } - } - return $statusCode; - } + // Update software from archive + public function updateSoftwareArchive($path, $force = false) { + $statusCode = 200; + $zip = new ZipArchive(); + if ($zip->open($path)===true) { + if (defined("DEBUG") && DEBUG>=2) echo "YellowUpdate::updateSoftwareArchive file:$path<br/>\n"; + if (preg_match("#^(.*\/).*?$#", $zip->getNameIndex(0), $matches)) $pathBase = $matches[1]; + $fileData = $zip->getFromName($pathBase.$this->yellow->config->get("updateInformationFile")); + foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) { + preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); + if (!empty($matches[1]) && !empty($matches[2])) { + list($dummy, $entry) = explode("/", $matches[1], 2); + list($fileName, $flags) = explode(",", $matches[2], 2); + if ($dummy[0]!="Y") $fileName = $matches[1]; //TODO: remove later, converts old file format + if (is_file($fileName)) { + $lastPublished = filemtime($fileName); + break; + } + } + } + foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) { + preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); + if (lcfirst($matches[1])=="plugin" || lcfirst($matches[1])=="theme") $software = $matches[2]; + if (lcfirst($matches[1])=="published") $modified = strtotime($matches[2]); + if (!empty($matches[1]) && !empty($matches[2]) && strposu($matches[1], "/")) { + list($dummy, $entry) = explode("/", $matches[1], 2); + list($fileName, $flags) = explode(",", $matches[2], 2); + if ($dummy[0]!="Y") { //TODO: remove later, converts old file format + list($entry, $flags) = explode(",", $matches[2], 2); + $fileName = $matches[1]; + } + $fileData = $zip->getFromName($pathBase.$entry); + $lastModified = $this->yellow->toolbox->getFileModified($fileName); + $statusCode = $this->updateSoftwareFile($fileName, $fileData, $modified, $lastModified, $lastPublished, $flags, $force, $software); + if ($statusCode!=200) break; + } + } + $zip->close(); + if ($statusCode==200) $statusCode = $this->updateSoftwareMultiLanguage($software); + if ($statusCode==200) $statusCode = $this->updateSoftwareNotification($software); + ++$this->updates; + } else { + $statusCode = 500; + $this->yellow->page->error(500, "Can't open file '$path'!"); + } + return $statusCode; + } + + // Update software file + public function updateSoftwareFile($fileName, $fileData, $modified, $lastModified, $lastPublished, $flags, $force, $software) { + $statusCode = 200; + $fileName = $this->yellow->toolbox->normaliseTokens($fileName); + if ($this->yellow->lookup->isValidFile($fileName) && !empty($software)) { + $create = $update = $delete = false; + 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("/careful/i", $flags) && is_file($fileName) && $lastModified!=$lastPublished && !$force) $update = false; + if (preg_match("/optional/i", $flags) && $this->isSoftwareExisting($software)) $create = $update = $delete = false; + if ($create) { + if (!$this->yellow->toolbox->createFile($fileName, $fileData, true) || + !$this->yellow->toolbox->modifyFile($fileName, $modified)) { + $statusCode = 500; + $this->yellow->page->error($statusCode, "Can't write file '$fileName'!"); + } + } + if ($update) { + if (!$this->yellow->toolbox->deleteFile($fileName, $this->yellow->config->get("trashDir")) || + !$this->yellow->toolbox->createFile($fileName, $fileData) || + !$this->yellow->toolbox->modifyFile($fileName, $modified)) { + $statusCode = 500; + $this->yellow->page->error($statusCode, "Can't write file '$fileName'!"); + } + } + if ($delete) { + if (!$this->yellow->toolbox->deleteFile($fileName, $this->yellow->config->get("trashDir"))) { + $statusCode = 500; + $this->yellow->page->error($statusCode, "Can't delete file '$fileName'!"); + } + } + if (defined("DEBUG") && DEBUG>=2) { + $debug = "action:".($create ? "create" : "").($update ? "update" : "").($delete ? "delete" : ""); + if (!$create && !$update && !$delete) $debug = "action:none"; + echo "YellowUpdate::updateSoftwareFile file:$fileName $debug<br/>\n"; + } + } + return $statusCode; + } - // Update software for multiple languages - function updateSoftwareMultiLanguage($software) - { - $statusCode = 200; - if($this->yellow->config->get("multiLanguageMode") && !$this->isSoftwareExisting($software)) - { - $pathsSource = $pathsTarget = array(); - $pathBase = $this->yellow->config->get("contentDir"); - $fileExtension = $this->yellow->config->get("contentExtension"); - $fileRegex = "/^.*\\".$fileExtension."$/"; - foreach($this->yellow->toolbox->getDirectoryEntries($pathBase, "/.*/", true, true) as $entry) - { - if(count($this->yellow->toolbox->getDirectoryEntries($entry, $fileRegex, false, false))) - { - array_push($pathsSource, $entry."/"); - } else if(count($this->yellow->toolbox->getDirectoryEntries($entry, "/.*/", false, true))) { - array_push($pathsTarget, $entry."/"); - } - } - if(count($pathsSource) && count($pathsTarget)) - { - foreach($pathsSource as $pathSource) - { - foreach($pathsTarget as $pathTarget) - { - $fileNames = $this->yellow->toolbox->getDirectoryEntriesRecursive($pathSource, "/.*/", false, false); - foreach($fileNames as $fileName) - { - $modified = $this->yellow->toolbox->getFileModified($fileName); - $fileNameTarget = $pathTarget.substru($fileName, strlenu($pathBase)); - if(!is_file($fileNameTarget)) - { - if(!$this->yellow->toolbox->copyFile($fileName, $fileNameTarget, true) || - !$this->yellow->toolbox->modifyFile($fileNameTarget, $modified)) - { - $statusCode = 500; - $this->yellow->page->error(500, "Can't write file '$fileNameTarget'!"); - } - } - if(defined("DEBUG") && DEBUG>=2) echo "YellowUpdate::updateSoftwareNew file:$fileNameTarget<br/>\n"; - } - } - if(!$this->yellow->toolbox->deleteDirectory($pathSource)) - { - $statusCode = 500; - $this->yellow->page->error(500, "Can't delete path '$pathSource'!"); - } - } - } - } - return $statusCode; - } - - // Update software notification for next startup - function updateSoftwareNotification($software) - { - $statusCode = 200; - $startupUpdate = $this->yellow->config->get("startupUpdate"); - if($startupUpdate=="none") $startupUpdate = "YellowUpdate"; - if($software!="YellowUpdate") $startupUpdate .= ",$software"; - $fileNameConfig = $this->yellow->config->get("configDir").$this->yellow->config->get("configFile"); - if(!$this->yellow->config->save($fileNameConfig, array("startupUpdate" => $startupUpdate))) - { - $statusCode = 500; - $this->yellow->page->error(500, "Can't write file '$fileNameConfig'!"); - } - return $statusCode; - } - - // Update installation features - function updateInstallationFeatures($feature) - { - $statusCode = 200; - $path = $this->yellow->config->get("pluginDir"); - $regex = "/^.*\.installation$/"; - foreach($this->yellow->toolbox->getDirectoryEntries($path, $regex, true, false) as $entry) - { - if(preg_match("/^(.*?)-(.*?)\./", basename($entry), $matches)) - { - if(strtoloweru($matches[2])==strtoloweru($feature)) - { - $statusCode = max($statusCode, $this->updateSoftwareArchive($entry)); - break; - } - } - } - if($statusCode==200) - { - foreach($this->yellow->toolbox->getDirectoryEntries($path, $regex, true, false) as $entry) - { - $this->yellow->toolbox->deleteFile($entry); - } - } - return $statusCode; - } - - // Update installation page - function updateInstallationPage($fileName, $name, $language) - { - $statusCode = 200; - if($language!="en") - { - $fileData = strreplaceu("\r\n", "\n", $this->yellow->toolbox->readFile($fileName)); - $rawDataOld = strreplaceu("\\n", "\n", $this->yellow->text->getText("editInstallation{$name}Page", "en")); - $rawDataNew = strreplaceu("\\n", "\n", $this->yellow->text->getText("editInstallation{$name}Page", $language)); - if(!$this->yellow->toolbox->createFile($fileName, strreplaceu($rawDataOld, $rawDataNew, $fileData))) - { - $statusCode = 500; - $this->yellow->page->error($statusCode, "Can't write file '$fileName'!"); - } - } - return $statusCode; - } - - // Process command to install pending software - function processCommandInstallationPending($args) - { - $statusCode = 0; - if($this->isSoftwarePending()) - { - $statusCode = $this->updateSoftware(); - if($statusCode!=200) echo "ERROR updating files: ".$this->yellow->page->get("pageError")."\n"; - echo "Yellow has ".($statusCode!=200 ? "not " : "")."been updated: Please run command again\n"; - } - return $statusCode; - } - - // Process request to install pending software - function processRequestInstallationPending($scheme, $address, $base, $location, $fileName) - { - $statusCode = 0; - if($this->yellow->lookup->isContentFile($fileName) && !$this->yellow->isCommandLine() && $this->isSoftwarePending()) - { - $statusCode = $this->updateSoftware(); - if($statusCode==200) - { - $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); - $statusCode = $this->yellow->sendStatus(303, $location); - } - } - return $statusCode; - } - - // Process request to install website - function processRequestInstallationMode($scheme, $address, $base, $location, $fileName) - { - $statusCode = 0; - if($this->yellow->lookup->isContentFile($fileName) && !$this->yellow->isCommandLine()) - { - $this->yellow->pages->pages["root/"] = array(); - $this->yellow->page = new YellowPage($this->yellow); - $this->yellow->page->setRequestInformation($scheme, $address, $base, $location, $fileName); - $this->yellow->page->parseData($this->getRawDataInstallation(), false, 404); - $this->yellow->page->parserSafeMode = false; - $this->yellow->page->parseContent(); - $name = trim(preg_replace("/[^\pL\d\-\. ]/u", "-", $_REQUEST["name"])); - $email = trim($_REQUEST["email"]); - $password = trim($_REQUEST["password"]); - $language = trim($_REQUEST["language"]); - $feature = trim($_REQUEST["feature"]); - $status = trim($_REQUEST["status"]); - if($status=="install") - { - $serverVersion = $this->yellow->toolbox->getServerVersion(true); - $status = $this->checkServerRewrite($scheme, $address, $base, $location, $fileName) ? "ok" : "error"; - if($status=="error") $this->yellow->page->error(500, "Rewrite module not working on $serverVersion web server!"); - } - if($status=="ok") - { - if(!empty($email) && !empty($password) && $this->yellow->plugins->isExisting("edit")) - { - $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); - $status = $this->yellow->plugins->get("edit")->users->save($fileNameUser, $email, $password, $name, $language) ? "ok" : "error"; - if($status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); - } - } - if($status=="ok") - { - if(!empty($feature)) - { - $status = $this->updateInstallationFeatures($feature)==200 ? "ok" : "error"; - if($status=="error") $this->yellow->page->error(500, "Can't install feature '$feature'!"); - } - } - if($status=="ok") - { - $fileNameHome = $this->yellow->lookup->findFileFromLocation("/"); - $status = $this->updateInstallationPage($fileNameHome, "Home", $language)==200 ? "ok" : "error"; - if($status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameHome'!"); - } - if($status=="ok") - { - $fileNameAbout = $this->yellow->lookup->findFileFromLocation("/about/"); - $status = $this->updateInstallationPage($fileNameAbout, "About", $language)==200 ? "ok" : "error"; - if($status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameAbout'!"); - } - if($status=="ok") - { - if($this->yellow->config->get("sitename")=="Yellow") $_REQUEST["sitename"] = $name; - $fileNameConfig = $this->yellow->config->get("configDir").$this->yellow->config->get("configFile"); - $status = $this->yellow->config->save($fileNameConfig, $this->getConfigData()) ? "done" : "error"; - if($status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameConfig'!"); - } - if($status=="done") - { - $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); - $statusCode = $this->yellow->sendStatus(303, $location); - } else { - $statusCode = $this->yellow->sendPage(); - } - } - return $statusCode; - } - - // Check web server rewrite - function checkServerRewrite($scheme, $address, $base, $location, $fileName) - { - $curlHandle = curl_init(); - $location = $this->yellow->config->get("assetLocation").$this->yellow->page->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).")"; - curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($curlHandle, CURLOPT_CONNECTTIMEOUT, 30); - $rawData = curl_exec($curlHandle); - $statusCode = curl_getinfo($curlHandle, CURLINFO_HTTP_CODE); - curl_close($curlHandle); - return !empty($rawData) && $statusCode==200; - } - - // Return raw data for installation page - function getRawDataInstallation() - { - $language = $this->yellow->toolbox->detectBrowserLanguage($this->yellow->text->getLanguages(), $this->yellow->config->get("language")); - $fileName = strreplaceu("(.*)", "installation", $this->yellow->config->get("configDir").$this->yellow->config->get("newFile")); - $rawData = $this->yellow->toolbox->readFile($fileName); - if(empty($rawData)) - { - $this->yellow->text->setLanguage($language); - $rawData = "---\nTitle:".$this->yellow->text->get("editInstallationTitle")."\nLanguage:$language\nNavigation:navigation\n---\n"; - $rawData .= "<form class=\"installation-form\" action=\"".$this->yellow->page->getLocation(true)."\" method=\"post\">\n"; - $rawData .= "<p><label for=\"name\">".$this->yellow->text->get("editSignupName")."</label><br /><input class=\"form-control\" type=\"text\" maxlength=\"64\" name=\"name\" id=\"name\" 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>"; - 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 />"; - } - $rawData .= "</p>\n"; - } - if(count($this->getInstallationFeatures())>1) - { - $rawData .= "<p>".$this->yellow->text->get("editInstallationFeature")."<p>"; - foreach($this->getInstallationFeatures() as $feature) - { - $checked = $feature=="website" ? " checked=\"checked\"" : ""; - $rawData .= "<label for=\"$feature\"><input type=\"radio\" name=\"feature\" id=\"$feature\" value=\"$feature\"$checked> ".ucfirst($feature)."</label><br />"; - } - $rawData .= "</p>\n"; - } - $rawData .= "<input class=\"btn\" type=\"submit\" value=\"".$this->yellow->text->get("editOkButton")."\" />\n"; - $rawData .= "<input type=\"hidden\" name=\"status\" value=\"install\" />\n"; - $rawData .= "</form>\n"; - } - return $rawData; - } - - // Return configuration data - function getConfigData() - { - $data = array(); - foreach($_REQUEST as $key=>$value) - { - if(!$this->yellow->config->isExisting($key)) continue; - $data[$key] = trim($value); - } - $data["timezone"] = $this->yellow->toolbox->getTimezone(); - $data["staticUrl"] = $this->yellow->toolbox->getServerUrl(); - $data["installationMode"] = "0"; - return $data; - } + // Update software for multiple languages + public function updateSoftwareMultiLanguage($software) { + $statusCode = 200; + if ($this->yellow->config->get("multiLanguageMode") && !$this->isSoftwareExisting($software)) { + $pathsSource = $pathsTarget = array(); + $pathBase = $this->yellow->config->get("contentDir"); + $fileExtension = $this->yellow->config->get("contentExtension"); + $fileRegex = "/^.*\\".$fileExtension."$/"; + foreach ($this->yellow->toolbox->getDirectoryEntries($pathBase, "/.*/", true, true) as $entry) { + if (count($this->yellow->toolbox->getDirectoryEntries($entry, $fileRegex, false, false))) { + array_push($pathsSource, $entry."/"); + } elseif (count($this->yellow->toolbox->getDirectoryEntries($entry, "/.*/", false, true))) { + array_push($pathsTarget, $entry."/"); + } + } + if (count($pathsSource) && count($pathsTarget)) { + foreach ($pathsSource as $pathSource) { + foreach ($pathsTarget as $pathTarget) { + $fileNames = $this->yellow->toolbox->getDirectoryEntriesRecursive($pathSource, "/.*/", false, false); + foreach ($fileNames as $fileName) { + $modified = $this->yellow->toolbox->getFileModified($fileName); + $fileNameTarget = $pathTarget.substru($fileName, strlenu($pathBase)); + if (!is_file($fileNameTarget)) { + if (!$this->yellow->toolbox->copyFile($fileName, $fileNameTarget, true) || + !$this->yellow->toolbox->modifyFile($fileNameTarget, $modified)) { + $statusCode = 500; + $this->yellow->page->error(500, "Can't write file '$fileNameTarget'!"); + } + } + if (defined("DEBUG") && DEBUG>=2) echo "YellowUpdate::updateSoftwareNew file:$fileNameTarget<br/>\n"; + } + } + if (!$this->yellow->toolbox->deleteDirectory($pathSource)) { + $statusCode = 500; + $this->yellow->page->error(500, "Can't delete path '$pathSource'!"); + } + } + } + } + return $statusCode; + } + + // Update software notification for next startup + public function updateSoftwareNotification($software) { + $statusCode = 200; + $startupUpdate = $this->yellow->config->get("startupUpdate"); + if ($startupUpdate=="none") $startupUpdate = "YellowUpdate"; + if ($software!="YellowUpdate") $startupUpdate .= ",$software"; + $fileNameConfig = $this->yellow->config->get("configDir").$this->yellow->config->get("configFile"); + if (!$this->yellow->config->save($fileNameConfig, array("startupUpdate" => $startupUpdate))) { + $statusCode = 500; + $this->yellow->page->error(500, "Can't write file '$fileNameConfig'!"); + } + return $statusCode; + } + + // Update installation features + public function updateInstallationFeatures($feature) { + $statusCode = 200; + $path = $this->yellow->config->get("pluginDir"); + $regex = "/^.*\.installation$/"; + foreach ($this->yellow->toolbox->getDirectoryEntries($path, $regex, true, false) as $entry) { + if (preg_match("/^(.*?)-(.*?)\./", basename($entry), $matches)) { + if (strtoloweru($matches[2])==strtoloweru($feature)) { + $statusCode = max($statusCode, $this->updateSoftwareArchive($entry)); + break; + } + } + } + if ($statusCode==200) { + foreach ($this->yellow->toolbox->getDirectoryEntries($path, $regex, true, false) as $entry) { + $this->yellow->toolbox->deleteFile($entry); + } + } + return $statusCode; + } + + // Update installation page + public function updateInstallationPage($fileName, $name, $language) { + $statusCode = 200; + if ($language!="en") { + $fileData = strreplaceu("\r\n", "\n", $this->yellow->toolbox->readFile($fileName)); + $rawDataOld = strreplaceu("\\n", "\n", $this->yellow->text->getText("editInstallation{$name}Page", "en")); + $rawDataNew = strreplaceu("\\n", "\n", $this->yellow->text->getText("editInstallation{$name}Page", $language)); + if (!$this->yellow->toolbox->createFile($fileName, strreplaceu($rawDataOld, $rawDataNew, $fileData))) { + $statusCode = 500; + $this->yellow->page->error($statusCode, "Can't write file '$fileName'!"); + } + } + return $statusCode; + } + + // Process command to install pending software + public function processCommandInstallationPending($args) { + $statusCode = 0; + if ($this->isSoftwarePending()) { + $statusCode = $this->updateSoftware(); + if ($statusCode!=200) echo "ERROR updating files: ".$this->yellow->page->get("pageError")."\n"; + echo "Yellow has ".($statusCode!=200 ? "not " : "")."been updated: Please run command again\n"; + } + return $statusCode; + } + + // Process request to install pending software + public function processRequestInstallationPending($scheme, $address, $base, $location, $fileName) { + $statusCode = 0; + if ($this->yellow->lookup->isContentFile($fileName) && !$this->yellow->isCommandLine() && $this->isSoftwarePending()) { + $statusCode = $this->updateSoftware(); + if ($statusCode==200) { + $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); + $statusCode = $this->yellow->sendStatus(303, $location); + } + } + return $statusCode; + } + + // Process request to install website + public function processRequestInstallationMode($scheme, $address, $base, $location, $fileName) { + $statusCode = 0; + if ($this->yellow->lookup->isContentFile($fileName) && !$this->yellow->isCommandLine()) { + $this->yellow->pages->pages["root/"] = array(); + $this->yellow->page = new YellowPage($this->yellow); + $this->yellow->page->setRequestInformation($scheme, $address, $base, $location, $fileName); + $this->yellow->page->parseData($this->getRawDataInstallation(), false, 404); + $this->yellow->page->parserSafeMode = false; + $this->yellow->page->parseContent(); + $name = trim(preg_replace("/[^\pL\d\-\. ]/u", "-", $_REQUEST["name"])); + $email = trim($_REQUEST["email"]); + $password = trim($_REQUEST["password"]); + $language = trim($_REQUEST["language"]); + $feature = trim($_REQUEST["feature"]); + $status = trim($_REQUEST["status"]); + if ($status=="install") { + $serverVersion = $this->yellow->toolbox->getServerVersion(true); + $status = $this->checkServerRewrite($scheme, $address, $base, $location, $fileName) ? "ok" : "error"; + if ($status=="error") $this->yellow->page->error(500, "Rewrite module not working on $serverVersion web server!"); + } + if ($status=="ok") { + if (!empty($email) && !empty($password) && $this->yellow->plugins->isExisting("edit")) { + $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); + $status = $this->yellow->plugins->get("edit")->users->save($fileNameUser, $email, $password, $name, $language) ? "ok" : "error"; + if ($status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); + } + } + if ($status=="ok") { + if (!empty($feature)) { + $status = $this->updateInstallationFeatures($feature)==200 ? "ok" : "error"; + if ($status=="error") $this->yellow->page->error(500, "Can't install feature '$feature'!"); + } + } + if ($status=="ok") { + $fileNameHome = $this->yellow->lookup->findFileFromLocation("/"); + $status = $this->updateInstallationPage($fileNameHome, "Home", $language)==200 ? "ok" : "error"; + if ($status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameHome'!"); + } + if ($status=="ok") { + $fileNameAbout = $this->yellow->lookup->findFileFromLocation("/about/"); + $status = $this->updateInstallationPage($fileNameAbout, "About", $language)==200 ? "ok" : "error"; + if ($status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameAbout'!"); + } + if ($status=="ok") { + if ($this->yellow->config->get("sitename")=="Yellow") $_REQUEST["sitename"] = $name; + $fileNameConfig = $this->yellow->config->get("configDir").$this->yellow->config->get("configFile"); + $status = $this->yellow->config->save($fileNameConfig, $this->getConfigData()) ? "done" : "error"; + if ($status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameConfig'!"); + } + if ($status=="done") { + $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); + $statusCode = $this->yellow->sendStatus(303, $location); + } else { + $statusCode = $this->yellow->sendPage(); + } + } + return $statusCode; + } + + // Check web server rewrite + public function checkServerRewrite($scheme, $address, $base, $location, $fileName) { + $curlHandle = curl_init(); + $location = $this->yellow->config->get("assetLocation").$this->yellow->page->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).")"; + curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($curlHandle, CURLOPT_CONNECTTIMEOUT, 30); + $rawData = curl_exec($curlHandle); + $statusCode = curl_getinfo($curlHandle, CURLINFO_HTTP_CODE); + curl_close($curlHandle); + return !empty($rawData) && $statusCode==200; + } + + // Return raw data for installation page + public function getRawDataInstallation() { + $language = $this->yellow->toolbox->detectBrowserLanguage($this->yellow->text->getLanguages(), $this->yellow->config->get("language")); + $fileName = strreplaceu("(.*)", "installation", $this->yellow->config->get("configDir").$this->yellow->config->get("newFile")); + $rawData = $this->yellow->toolbox->readFile($fileName); + if (empty($rawData)) { + $this->yellow->text->setLanguage($language); + $rawData = "---\nTitle:".$this->yellow->text->get("editInstallationTitle")."\nLanguage:$language\nNavigation:navigation\n---\n"; + $rawData .= "<form class=\"installation-form\" action=\"".$this->yellow->page->getLocation(true)."\" method=\"post\">\n"; + $rawData .= "<p><label for=\"name\">".$this->yellow->text->get("editSignupName")."</label><br /><input class=\"form-control\" type=\"text\" maxlength=\"64\" name=\"name\" id=\"name\" 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>"; + 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 />"; + } + $rawData .= "</p>\n"; + } + if (count($this->getInstallationFeatures())>1) { + $rawData .= "<p>".$this->yellow->text->get("editInstallationFeature")."<p>"; + foreach ($this->getInstallationFeatures() as $feature) { + $checked = $feature=="website" ? " checked=\"checked\"" : ""; + $rawData .= "<label for=\"$feature\"><input type=\"radio\" name=\"feature\" id=\"$feature\" value=\"$feature\"$checked> ".ucfirst($feature)."</label><br />"; + } + $rawData .= "</p>\n"; + } + $rawData .= "<input class=\"btn\" type=\"submit\" value=\"".$this->yellow->text->get("editOkButton")."\" />\n"; + $rawData .= "<input type=\"hidden\" name=\"status\" value=\"install\" />\n"; + $rawData .= "</form>\n"; + } + return $rawData; + } + + // Return configuration data + public function getConfigData() { + $data = array(); + foreach ($_REQUEST as $key=>$value) { + if (!$this->yellow->config->isExisting($key)) continue; + $data[$key] = trim($value); + } + $data["timezone"] = $this->yellow->toolbox->getTimezone(); + $data["staticUrl"] = $this->yellow->toolbox->getServerUrl(); + $data["installationMode"] = "0"; + return $data; + } - // Return installation features - function getInstallationFeatures() - { - $data = array("website"); - $path = $this->yellow->config->get("pluginDir"); - $regex = "/^.*\.installation$/"; - foreach($this->yellow->toolbox->getDirectoryEntries($path, $regex, true, false, false) as $entry) - { - if(preg_match("/^(.*?)-(.*?)\./", $entry, $matches)) - { - array_push($data, $matches[2]); - } - } - return $data; - } - - // Return software version - function getSoftwareVersion($latest = false, $rawFormat = false) - { - $data = array(); - if($latest) - { - $urlPlugins = $this->yellow->config->get("updatePluginsUrl")."/raw/master/".$this->yellow->config->get("updateVersionFile"); - $urlThemes = $this->yellow->config->get("updateThemesUrl")."/raw/master/".$this->yellow->config->get("updateVersionFile"); - list($statusCodePlugins, $fileDataPlugins) = $this->getSoftwareFile($urlPlugins); - list($statusCodeThemes, $fileDataThemes) = $this->getSoftwareFile($urlThemes); - $statusCode = max($statusCodePlugins, $statusCodeThemes); - if($statusCode==200) - { - foreach($this->yellow->toolbox->getTextLines($fileDataPlugins."\n".$fileDataThemes) as $line) - { - preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); - if(!empty($matches[1]) && !empty($matches[2])) - { - list($version) = explode(',', $matches[2]); - $data[$matches[1]] = $rawFormat ? $matches[2] : $version; - } - } - } - } else { - $statusCode = 200; - $data = array_merge($this->yellow->plugins->getData(), $this->yellow->themes->getData()); - } - return array($statusCode, $data); - } + // Return installation features + public function getInstallationFeatures() { + $data = array("website"); + $path = $this->yellow->config->get("pluginDir"); + $regex = "/^.*\.installation$/"; + foreach ($this->yellow->toolbox->getDirectoryEntries($path, $regex, true, false, false) as $entry) { + if (preg_match("/^(.*?)-(.*?)\./", $entry, $matches)) array_push($data, $matches[2]); + } + return $data; + } + + // Return software version + public function getSoftwareVersion($latest = false, $rawFormat = false) { + $data = array(); + if ($latest) { + $urlPlugins = $this->yellow->config->get("updatePluginsUrl")."/raw/master/".$this->yellow->config->get("updateVersionFile"); + $urlThemes = $this->yellow->config->get("updateThemesUrl")."/raw/master/".$this->yellow->config->get("updateVersionFile"); + list($statusCodePlugins, $fileDataPlugins) = $this->getSoftwareFile($urlPlugins); + list($statusCodeThemes, $fileDataThemes) = $this->getSoftwareFile($urlThemes); + $statusCode = max($statusCodePlugins, $statusCodeThemes); + if ($statusCode==200) { + foreach ($this->yellow->toolbox->getTextLines($fileDataPlugins."\n".$fileDataThemes) as $line) { + preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); + if (!empty($matches[1]) && !empty($matches[2])) { + list($version) = explode(",", $matches[2]); + $data[$matches[1]] = $rawFormat ? $matches[2] : $version; + } + } + } + } else { + $statusCode = 200; + $data = array_merge($this->yellow->plugins->getData(), $this->yellow->themes->getData()); + } + return array($statusCode, $data); + } - // Return software modification - function getSoftwareModified() - { - $data = array(); - $dataCurrent = array_merge($this->yellow->plugins->getData(), $this->yellow->themes->getData()); - $urlPlugins = $this->yellow->config->get("updatePluginsUrl")."/raw/master/".$this->yellow->config->get("updateResourceFile"); - $urlThemes = $this->yellow->config->get("updateThemesUrl")."/raw/master/".$this->yellow->config->get("updateResourceFile"); - list($statusCodePlugins, $fileDataPlugins) = $this->getSoftwareFile($urlPlugins); - list($statusCodeThemes, $fileDataThemes) = $this->getSoftwareFile($urlThemes); - $statusCode = max($statusCodePlugins, $statusCodeThemes); - if($statusCode==200) - { - foreach($this->yellow->toolbox->getTextLines($fileDataPlugins."\n".$fileDataThemes) as $line) - { - preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); - if(!empty($matches[1]) && !empty($matches[2])) - { - list($softwareNew) = explode('/', $matches[1]); - list($fileName, $flags) = explode(',', $matches[2], 2); - if($software!=$softwareNew) - { - $software = $softwareNew; - list($fileName, $flags) = explode(',', $matches[2], 2); - $lastPublished = $this->yellow->toolbox->getFileModified($fileName); - } - if($this->yellow->lookup->isValidFile($fileName) && !is_null($dataCurrent[$software])) - { - $lastModified = $this->yellow->toolbox->getFileModified($fileName); - if(preg_match("/update/i", $flags) && preg_match("/careful/i", $flags) && $lastModified!=$lastPublished) - { - $data[$software] = $dataCurrent[$software]; - if(defined("DEBUG") && DEBUG>=2) echo "YellowUpdate::getSoftwareModified detected file:$fileName<br/>\n"; - } - } - } - } - } - return array($statusCode, $data); - } - - // Return software file - function getSoftwareFile($url) - { - $urlRequest = $url; - if(preg_match("#^https://github.com/(.+)/raw/(.+)$#", $url, $matches)) - { - $urlRequest = "https://raw.githubusercontent.com/".$matches[1]."/".$matches[2]; - } - $curlHandle = curl_init(); - curl_setopt($curlHandle, CURLOPT_URL, $urlRequest); - curl_setopt($curlHandle, CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; DatenstromYellow/".YellowCore::VERSION."; SoftwareUpdater)"); - curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($curlHandle, CURLOPT_CONNECTTIMEOUT, 30); - $rawData = curl_exec($curlHandle); - $statusCode = curl_getinfo($curlHandle, CURLINFO_HTTP_CODE); - curl_close($curlHandle); - if($statusCode==200) - { - $fileData = $rawData; - } else if($statusCode==0) { - $statusCode = 500; - list($scheme, $address) = $this->yellow->lookup->getUrlInformation($url); - $this->yellow->page->error($statusCode, "Can't connect to server '$scheme://$address'!"); - } else { - $statusCode = 500; - $this->yellow->page->error($statusCode, "Can't download file '$url'!"); - } - if(defined("DEBUG") && DEBUG>=2) echo "YellowUpdate::getSoftwareFile status:$statusCode url:$url<br/>\n"; - return array($statusCode, $fileData); - } - - // Check if software pending - function isSoftwarePending() - { - $path = $this->yellow->config->get("pluginDir"); - $foundPlugins = count($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.zip$/", true, false))>0; - $path = $this->yellow->config->get("themeDir"); - $foundThemes = count($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.zip$/", true, false))>0; - return $foundPlugins || $foundThemes; - } + // Return software modification + public function getSoftwareModified() { + $data = array(); + $dataCurrent = array_merge($this->yellow->plugins->getData(), $this->yellow->themes->getData()); + $urlPlugins = $this->yellow->config->get("updatePluginsUrl")."/raw/master/".$this->yellow->config->get("updateResourceFile"); + $urlThemes = $this->yellow->config->get("updateThemesUrl")."/raw/master/".$this->yellow->config->get("updateResourceFile"); + list($statusCodePlugins, $fileDataPlugins) = $this->getSoftwareFile($urlPlugins); + list($statusCodeThemes, $fileDataThemes) = $this->getSoftwareFile($urlThemes); + $statusCode = max($statusCodePlugins, $statusCodeThemes); + if ($statusCode==200) { + foreach ($this->yellow->toolbox->getTextLines($fileDataPlugins."\n".$fileDataThemes) as $line) { + preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); + if (!empty($matches[1]) && !empty($matches[2])) { + list($softwareNew) = explode("/", $matches[1]); + list($fileName, $flags) = explode(",", $matches[2], 2); + if ($software!=$softwareNew) { + $software = $softwareNew; + list($fileName, $flags) = explode(",", $matches[2], 2); + $lastPublished = $this->yellow->toolbox->getFileModified($fileName); + } + if ($this->yellow->lookup->isValidFile($fileName) && !is_null($dataCurrent[$software])) { + $lastModified = $this->yellow->toolbox->getFileModified($fileName); + if (preg_match("/update/i", $flags) && preg_match("/careful/i", $flags) && $lastModified!=$lastPublished) { + $data[$software] = $dataCurrent[$software]; + if (defined("DEBUG") && DEBUG>=2) echo "YellowUpdate::getSoftwareModified detected file:$fileName<br/>\n"; + } + } + } + } + } + return array($statusCode, $data); + } + + // Return software file + public function getSoftwareFile($url) { + $urlRequest = $url; + if (preg_match("#^https://github.com/(.+)/raw/(.+)$#", $url, $matches)) $urlRequest = "https://raw.githubusercontent.com/".$matches[1]."/".$matches[2]; + $curlHandle = curl_init(); + curl_setopt($curlHandle, CURLOPT_URL, $urlRequest); + curl_setopt($curlHandle, CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; DatenstromYellow/".YellowCore::VERSION."; SoftwareUpdater)"); + curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($curlHandle, CURLOPT_CONNECTTIMEOUT, 30); + $rawData = curl_exec($curlHandle); + $statusCode = curl_getinfo($curlHandle, CURLINFO_HTTP_CODE); + curl_close($curlHandle); + if ($statusCode==200) { + $fileData = $rawData; + } elseif ($statusCode==0) { + $statusCode = 500; + list($scheme, $address) = $this->yellow->lookup->getUrlInformation($url); + $this->yellow->page->error($statusCode, "Can't connect to server '$scheme://$address'!"); + } else { + $statusCode = 500; + $this->yellow->page->error($statusCode, "Can't download file '$url'!"); + } + if (defined("DEBUG") && DEBUG>=2) echo "YellowUpdate::getSoftwareFile status:$statusCode url:$url<br/>\n"; + return array($statusCode, $fileData); + } + + // Check if software pending + public function isSoftwarePending() { + $path = $this->yellow->config->get("pluginDir"); + $foundPlugins = count($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.zip$/", true, false))>0; + $path = $this->yellow->config->get("themeDir"); + $foundThemes = count($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.zip$/", true, false))>0; + return $foundPlugins || $foundThemes; + } - // Check if software exists - function isSoftwareExisting($software) - { - $data = array_merge($this->yellow->plugins->getData(), $this->yellow->themes->getData()); - return !is_null($data[$software]); - } + // Check if software exists + public function isSoftwareExisting($software) { + $data = array_merge($this->yellow->plugins->getData(), $this->yellow->themes->getData()); + return !is_null($data[$software]); + } } - + $yellow->plugins->register("update", "YellowUpdate", YellowUpdate::VERSION, 1); -?> diff --git a/system/themes/assets/flatsite.css b/system/themes/assets/flatsite.css @@ -2,184 +2,542 @@ /* Copyright (c) 2013-2018 Datenstrom, https://datenstrom.se */ /* This file may be used and distributed under the terms of the public license. */ -html, body, div, form, pre, span, tr, th, td, img { margin:0; padding:0; border:0; vertical-align:baseline; } +html, body, div, form, pre, span, tr, th, td, img { + margin: 0; + padding: 0; + border: 0; + vertical-align: baseline; +} @font-face { - font-family:'Open Sans'; - font-style:normal; - font-weight:300; - src:url(opensans-light.woff) format('woff'); + font-family: "Open Sans"; + font-style: normal; + font-weight: 300; + src: url(opensans-light.woff) format("woff"); } @font-face { - font-family:'Open Sans'; - font-style:normal; - font-weight:400; - src:url(opensans-regular.woff) format('woff'); + font-family: "Open Sans"; + font-style: normal; + font-weight: 400; + src: url(opensans-regular.woff) format("woff"); } @font-face { - font-family:'Open Sans'; - font-style:normal; - font-weight:700; - src:url(opensans-bold.woff) format('woff'); + font-family: "Open Sans"; + font-style: normal; + font-weight: 700; + src: url(opensans-bold.woff) format("woff"); } body { - margin:1em; - background-color:#fff; color:#717171; - font-family:'Open Sans',Helvetica,sans-serif; - font-size:1em; - font-weight:300; - line-height:1.5; -} -h1, h2, h3, h4, h5, h6 { color:#111; font-weight:normal; } -h1 { font-size:2.0em; } -hr { height:1px; background:#ddd; border:0; } -strong { font-weight:bold; } -code { font-size:1.1em; } -a { color:#07d; text-decoration:none; } -a:hover { color:#07d; text-decoration:underline; } -.content h1:first-child, .content>*:first-child { margin-top:0; } -.content h1 a { color:#111; } -.content h1 a:hover { color:#111; text-decoration:none; } -.content img { max-width:100%; height:auto; } -.content form { margin:1em 0; } -.content table { border-spacing:0; border-collapse:collapse; } -.content th { text-align:left; padding:0.3em; } -.content td { text-align:left; padding:0.3em; border-top:1px solid #ddd; border-bottom:1px solid #ddd; } -.content blockquote { margin-left:0; padding-left:1em; border-left:.5em solid #0a0; } -.content blockquote blockquote { margin-left:-1.5em; border-left:.5em solid #fb0; } -.content blockquote blockquote blockquote { border-color:#d00; } -.content code, pre { font-family:Consolas,"Liberation Mono",Menlo,Courier,monospace; font-size:90%; } -.content code { padding:0.15em 0.4em; margin:0; background-color:#f7f7f7; border-radius:3px; } -.content pre>code { padding:0; margin:0; white-space:pre; background:transparent; border:0; font-size:inherit; } -.content pre { padding:1em; overflow:auto; line-height:1.45; background-color:#f7f7f7; border-radius:3px; } -.content .flexible { position:relative; padding-top:0; padding-bottom:56.25%; } -.content .flexible iframe { position:absolute; top:0; left:0; width:100%; height:100%; } -.content .stretchable ul { margin:0 -0.5em; padding:0; list-style:none; text-align:center; } -.content .stretchable li { margin:0; padding:1em 0; display:inline-block; text-align:center; vertical-align:top; } -.content .stretchable a { color:#717171; text-decoration:none; } -.content .task-list-item { list-style-type:none; } -.content .task-list-item input { margin:0 0.2em 0.25em -1.75em; vertical-align:middle; } -.content .toc { margin:0; padding:0; list-style:none; } -.content .entry-links .previous { margin-right:1em; } -.content .pagination .previous { margin-right:1em; } -.content .pagination { margin:1em 0; } -.content .left { float:left; margin:0 1em 0 0; } -.content .center { display:block; margin:0 auto; } -.content .right { float:right; margin:0 0 0 1em; } -.content .rounded { border-radius:4px; } + margin: 1em; + background-color: #fff; + color: #717171; + font-family: "Open Sans", Helvetica, sans-serif; + font-size: 1em; + font-weight: 300; + line-height: 1.5; +} +h1, +h2, +h3, +h4, +h5, +h6 { + color: #111; + font-weight: normal; +} +h1 { + font-size: 2em; +} +hr { + height: 1px; + background: #ddd; + border: 0; +} +strong { + font-weight: bold; +} +code { + font-size: 1.1em; +} +a { + color: #07d; + text-decoration: none; +} +a:hover { + color: #07d; + text-decoration: underline; +} + +/* Content */ + +.content h1:first-child, +.content > *:first-child { + margin-top: 0; +} +.content h1 a { + color: #111; +} +.content h1 a:hover { + color: #111; + text-decoration: none; +} +.content img { + max-width: 100%; + height: auto; +} +.content form { + margin: 1em 0; +} +.content table { + border-spacing: 0; + border-collapse: collapse; +} +.content th { + text-align: left; + padding: 0.3em; +} +.content td { + text-align: left; + padding: 0.3em; + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; +} +.content blockquote { + margin-left: 0; + padding-left: 1em; + border-left: 0.5em solid #0a0; +} +.content blockquote blockquote { + margin-left: -1.5em; + border-left: 0.5em solid #fb0; +} +.content blockquote blockquote blockquote { + border-color: #d00; +} +.content code, +.content pre { + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; + font-size: 90%; +} +.content code { + padding: 0.15em 0.4em; + margin: 0; + background-color: #f7f7f7; + border-radius: 3px; +} +.content pre > code { + padding: 0; + margin: 0; + white-space: pre; + background: transparent; + border: 0; + font-size: inherit; +} +.content pre { + padding: 1em; + overflow: auto; + line-height: 1.45; + background-color: #f7f7f7; + border-radius: 3px; +} +.content .flexible { + position: relative; + padding-top: 0; + padding-bottom: 56.25%; +} +.content .flexible iframe { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.content .stretchable ul { + margin: 0 -0.5em; + padding: 0; + list-style: none; + text-align: center; +} +.content .stretchable li { + margin: 0; + padding: 1em 0; + display: inline-block; + text-align: center; + vertical-align: top; +} +.content .stretchable a { + color: #717171; + text-decoration: none; +} +.content .task-list-item { + list-style-type: none; +} +.content .task-list-item input { + margin: 0 0.2em 0.25em -1.75em; + vertical-align: middle; +} +.content .toc { + margin: 0; + padding: 0; + list-style: none; +} +.content .entry-links .previous { + margin-right: 1em; +} +.content .pagination .previous { + margin-right: 1em; +} +.content .pagination { + margin: 1em 0; +} +.content .left { + float: left; + margin: 0 1em 0 0; +} +.content .center { + display: block; + margin: 0 auto; +} +.content .right { + float: right; + margin: 0 0 0 1em; +} +.content .rounded { + border-radius: 4px; +} /* Header */ -.header .sitename { display:block; float:left; } -.header .sitename h1 { margin:0; } -.header .sitename h1 a { color:#111; text-decoration:none; } -.header .sitename h2 { margin-top:0; color:#717171; font-size:1.0em; font-weight:300; } +.header .sitename { + display: block; + float: left; +} +.header .sitename h1 { + margin: 0; +} +.header .sitename h1 a { + color: #111; + text-decoration: none; +} +.header .sitename h2 { + margin-top: 0; + color: #717171; + font-size: 1em; + font-weight: 300; +} /* Navigation */ -.navigation { display:block; float:right; } -.navigation { margin-top:0.9em; margin-bottom:0.9em; line-height:2; } -.navigation a { padding:0 0.3em; } -.navigation ul { margin:0 -0.3em; padding:0; list-style:none; } -.navigation li { display:inline; } -.navigation-tree { display:block; float:right; } -.navigation-tree { margin-top:0.9em; margin-bottom:0.9em; line-height:2; } -.navigation-tree a { padding:0 0.3em; } -.navigation-tree ul { margin:0 -0.3em; padding:0; list-style:none; } -.navigation-tree li { display:inline; } -.navigation-tree ul li { display:inline-block; position:relative; cursor:pointer; margin:0; } -.navigation-tree ul li ul { padding:0.3em; position:absolute; width:13em; background:#fff; z-index:100; display:none; } -.navigation-tree ul li ul { border:1px solid #bbb; border-radius:4px; box-shadow:2px 4px 10px rgba(0, 0, 0, 0.2); } -.navigation-tree ul li ul li { display:block; } -.navigation-tree>ul>li:hover>ul { display:block; } -.navigation-banner { clear:both; } -.navigation-search { padding-bottom:1em; } -.navigation-search .search-form { position:relative; } -.navigation-search .search-text { font-family:inherit; font-size:inherit; font-weight:inherit; } -.navigation-search .search-text { padding:0.5em; border:1px solid #bbb; border-radius:4px; width:100%; box-sizing:border-box; } -.navigation-search .search-text { background-color:#fff; background-image:linear-gradient(to bottom, #fff, #fff); } -.navigation-search .search-button { position:absolute; top:0; right:0; } -.navigation-search .search-button { font-family:inherit; font-size:inherit; font-weight:inherit; } -.navigation-search .search-button { margin:5px; padding:0.3em; border:none; background-color:transparent; } +.navigation { + display: block; + float: right; +} +.navigation { + margin-top: 0.9em; + margin-bottom: 0.9em; + line-height: 2; +} +.navigation a { + padding: 0 0.3em; +} +.navigation ul { + margin: 0 -0.3em; + padding: 0; + list-style: none; +} +.navigation li { + display: inline; +} +.navigation-tree { + display: block; + float: right; +} +.navigation-tree { + margin-top: 0.9em; + margin-bottom: 0.9em; + line-height: 2; +} +.navigation-tree a { + padding: 0 0.3em; +} +.navigation-tree ul { + margin: 0 -0.3em; + padding: 0; + list-style: none; +} +.navigation-tree li { + display: inline; +} +.navigation-tree ul li { + display: inline-block; + position: relative; + cursor: pointer; + margin: 0; +} +.navigation-tree ul li ul { + padding: 0.3em; + position: absolute; + width: 13em; + background: #fff; + z-index: 100; + display: none; +} +.navigation-tree ul li ul { + border: 1px solid #bbb; + border-radius: 4px; + box-shadow: 2px 4px 10px rgba(0, 0, 0, 0.2); +} +.navigation-tree ul li ul li { + display: block; +} +.navigation-tree > ul > li:hover > ul { + display: block; +} +.navigation-banner { + clear: both; +} +.navigation-search { + padding-bottom: 1em; +} +.navigation-search .search-form { + position: relative; +} +.navigation-search .search-text { + font-family: inherit; + font-size: inherit; + font-weight: inherit; +} +.navigation-search .search-text { + padding: 0.5em; + border: 1px solid #bbb; + border-radius: 4px; + width: 100%; + box-sizing: border-box; +} +.navigation-search .search-text { + background-color: #fff; + background-image: linear-gradient(to bottom, #fff, #fff); +} +.navigation-search .search-button { + position: absolute; + top: 0; + right: 0; +} +.navigation-search .search-button { + font-family: inherit; + font-size: inherit; + font-weight: inherit; +} +.navigation-search .search-button { + margin: 5px; + padding: 0.3em; + border: none; + background-color: transparent; +} /* Footer */ -.footer { margin-top:2em; } -.footer .siteinfo a { color:#07d; } -.footer .siteinfo a:hover { color:#07d; text-decoration:underline; } -.footer .siteinfo a.language img { vertical-align:middle; margin-top:-5px; margin-right:0.75em; } -.footer .siteinfo-left { float:left; } -.footer .siteinfo-right { float:right; } -.footer .siteinfo-banner { clear:both; } +.footer { + margin-top: 2em; +} +.footer .siteinfo a { + color: #07d; +} +.footer .siteinfo a:hover { + color: #07d; + text-decoration: underline; +} +.footer .siteinfo a.language img { + vertical-align: middle; + margin-top: -5px; + margin-right: 0.75em; +} +.footer .siteinfo-left { + float: left; +} +.footer .siteinfo-right { + float: right; +} +.footer .siteinfo-banner { + clear: both; +} /* Sidebar */ -.with-sidebar .main { margin-right:15em; } -.with-sidebar .sidebar { float:right; width:13em; margin-top:3.2em; padding:2px; overflow:hidden; text-align:right; } -.with-sidebar .sidebar ul { padding:0; list-style:none; } -.with-sidebar .sidebar .search-form input { width:100%; box-sizing:border-box; } -.with-sidebar .content:after { content:""; display:table; clear:both; } +.with-sidebar .main { + margin-right: 15em; +} +.with-sidebar .sidebar { + float: right; + width: 13em; + margin-top: 3.2em; + padding: 2px; + overflow: hidden; + text-align: right; +} +.with-sidebar .sidebar ul { + padding: 0; + list-style: none; +} +.with-sidebar .sidebar .search-form input { + width: 100%; + box-sizing: border-box; +} +.with-sidebar .content:after { + content: ""; + display: table; + clear: both; +} /* Forms and buttons */ .form-control { - margin:0; padding:2px 4px; - display:inline-block; min-width:7em; - background-color:#fff; color:#555; - background-image:linear-gradient(to bottom, #fff, #fff); - border:1px solid #bbb; - border-radius:4px; - font-size:0.9em; font-family:inherit; font-weight:normal; line-height:normal; + margin: 0; + padding: 2px 4px; + display: inline-block; + min-width: 7em; + background-color: #fff; + color: #555; + background-image: linear-gradient(to bottom, #fff, #fff); + border: 1px solid #bbb; + border-radius: 4px; + font-size: 0.9em; + font-family: inherit; + font-weight: normal; + line-height: normal; } .btn { - margin:0; padding:4px 22px; - display:inline-block; min-width:7em; - background-color:#eaeaea; color:#333333; - background-image:linear-gradient(to bottom, #f8f8f8, #e1e1e1); - border:1px solid #bbb; - border-color:#c1c1c1 #c1c1c1 #aaaaaa; - border-radius:4px; - outline-offset:-2px; - font-size:0.9em; font-family:inherit; font-weight:normal; line-height:1; - text-align:center; text-decoration:none; - box-sizing:border-box; -} -.btn:hover, .btn:focus, .btn:active { - color:#333333; - background-image:none; - text-decoration:none; -} -.btn:active { box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.1); } + margin: 0; + padding: 4px 22px; + display: inline-block; + min-width: 7em; + background-color: #eaeaea; + color: #333333; + background-image: linear-gradient(to bottom, #f8f8f8, #e1e1e1); + border: 1px solid #bbb; + border-color: #c1c1c1 #c1c1c1 #aaaaaa; + border-radius: 4px; + outline-offset: -2px; + font-size: 0.9em; + font-family: inherit; + font-weight: normal; + line-height: 1; + text-align: center; + text-decoration: none; + box-sizing: border-box; +} +.btn:hover, +.btn:focus, +.btn:active { + color: #333333; + background-image: none; + text-decoration: none; +} +.btn:active { + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1); +} /* Misc */ -.template-default .content img.screenshot { margin:0 -0.5em; } -.template-language .content div.language { font-size:1.2em; text-align:left; width:9em; margin:0 auto; } -.template-language .content div.language p { margin:1.5em 0em; } -.template-language .content div.language img { vertical-align:middle; margin-top:-5px; margin-right:1.5em; } -.hljs-meta, .hljs-keyword, .hljs-literal { color:#b0b; } -.hljs-attr, .hljs-attribute, .hljs-selector-id, .hljs-selector-class, .hljs-selector-pseudo { color:#b0b; } -.hljs-type, .hljs-built_in, .hljs-builtin-name, .hljs-params { color:#b0b; } -.hljs-string { color:#717171; } -.hljs-symbol, .hljs-bullet, .hljs-link, .hljs-number { color:#717171; } +.template-default .content img.screenshot { + margin: 0 -0.5em; +} +.template-language .content div.language { + font-size: 1.2em; + text-align: left; + width: 9em; + margin: 0 auto; +} +.template-language .content div.language p { + margin: 1.5em 0em; +} +.template-language .content div.language img { + vertical-align: middle; + margin-top: -5px; + margin-right: 1.5em; +} +.hljs-meta, +.hljs-keyword, +.hljs-literal { + color: #b0b; +} +.hljs-attr, +.hljs-attribute, +.hljs-selector-id, +.hljs-selector-class, +.hljs-selector-pseudo { + color: #b0b; +} +.hljs-type, +.hljs-built_in, +.hljs-builtin-name, +.hljs-params { + color: #b0b; +} +.hljs-string { + color: #717171; +} +.hljs-symbol, +.hljs-bullet, +.hljs-link, +.hljs-number { + color: #717171; +} /* Responsive and print */ -.page { margin:0 auto; max-width:1000px; } +.page { + margin: 0 auto; + max-width: 1000px; +} -@media screen and (min-width:62em) { - body { width:60em; margin:1em auto; } - .page{ margin:0; max-width:none; } +@media screen and (min-width: 62em) { + body { + width: 60em; + margin: 1em auto; + } + .page { + margin: 0; + max-width: none; + } } -@media screen and (max-width:32em) { - body { margin:0.5em; font-size:0.9em; } - .header .sitename h1, .content h1, .content h2 { font-size:1.3em; } - .header .sitename h1, .header .sitename h2, .footer, .page { margin:0; padding:0; } - .header .sitename, .navigation, .navigation-tree { float:none; } - .navigation { margin-top:0.5em; margin-bottom:0.5em; } - .navigation-search { padding-bottom:1em; } - .with-sidebar .main { margin-right:0; } - .with-sidebar .sidebar { display:none; } +@media screen and (max-width: 32em) { + body { + margin: 0.5em; + font-size: 0.9em; + } + .header .sitename h1, + .content h1, + .content h2 { + font-size: 1.3em; + } + .header .sitename h1, + .header .sitename h2, + .footer, + .page { + margin: 0; + padding: 0; + } + .header .sitename, + .navigation, + .navigation-tree { + float: none; + } + .navigation { + margin-top: 0.5em; + margin-bottom: 0.5em; + } + .navigation-search { + padding-bottom: 1em; + } + .with-sidebar .main { + margin-right: 0; + } + .with-sidebar .sidebar { + display: none; + } } @media print { - .page { border:none !important; } + .page { + border: none !important; + } } diff --git a/system/themes/assets/flatsite.php b/system/themes/assets/flatsite.php @@ -3,10 +3,8 @@ // Copyright (c) 2013-2018 Datenstrom, https://datenstrom.se // This file may be used and distributed under the terms of the public license. -class YellowThemeFlatsite -{ - const VERSION = "0.7.6"; +class YellowThemeFlatsite { + const VERSION = "0.7.6"; } $yellow->themes->register("flatsite", "YellowThemeFlatsite", YellowThemeFlatsite::VERSION); -?>