mikuli.cz

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

commit 3c7c88ddf3aa96c9e00adfebde48600057ef2d1a
parent 6d06eb1f26f7f0a0de070d9105ca890236ad81fa
Author: markseu <mark2011@mayberg.se>
Date:   Sat, 17 Jun 2017 16:43:19 +0200

System update (better content handling)

Diffstat:
Msystem/config/config.ini | 18+++++++++---------
Msystem/config/page-error-500.txt | 4++--
Asystem/plugins/command.php | 465+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsystem/plugins/commandline.php | 465-------------------------------------------------------------------------------
Msystem/plugins/core.php | 354++++++++++++++++++++++++++++++++++++++++---------------------------------------
Asystem/plugins/edit.css | 129+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asystem/plugins/edit.js | 811+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asystem/plugins/edit.php | 1545+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msystem/plugins/language-de.txt | 158++++++++++++++++++++++++++++++++++++++++----------------------------------------
Msystem/plugins/language-en.txt | 158++++++++++++++++++++++++++++++++++++++++----------------------------------------
Msystem/plugins/language-fr.txt | 158++++++++++++++++++++++++++++++++++++++++----------------------------------------
Msystem/plugins/language.php | 2+-
Msystem/plugins/update.php | 95++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Dsystem/plugins/webinterface.css | 129-------------------------------------------------------------------------------
Dsystem/plugins/webinterface.js | 808-------------------------------------------------------------------------------
Dsystem/plugins/webinterface.php | 1510-------------------------------------------------------------------------------
Msystem/plugins/yellow-blog.zip.installation | 0
Msystem/plugins/yellow-wiki.zip.installation | 0
18 files changed, 3449 insertions(+), 3360 deletions(-)

diff --git a/system/config/config.ini b/system/config/config.ini @@ -54,12 +54,12 @@ UpdateThemesUrl: https://github.com/datenstrom/yellow-themes UpdateInformationFile: update.ini UpdateVersionFile: version.ini UpdateResourceFile: resource.ini -WebinterfaceLocation: /edit/ -WebinterfaceNewFile: page-new-(.*).txt -WebinterfaceMetaFilePrefix: published -WebinterfaceUserFile: user.ini -WebinterfaceUserPasswordMinLength: 4 -WebinterfaceUserHashAlgorithm: bcrypt -WebinterfaceUserHashCost: 10 -WebinterfaceUserStatus: active -WebinterfaceUserHome: / +EditLocation: /edit/ +EditNewFile: page-new-(.*).txt +EditUserFile: user.ini +EditUserPasswordMinLength: 4 +EditUserHashAlgorithm: bcrypt +EditUserHashCost: 10 +EditUserStatus: active +EditUserHome: / +EditLoginRestrictions: 0 diff --git a/system/config/page-error-500.txt b/system/config/page-error-500.txt @@ -1,4 +1,4 @@ --- Title: Server error --- -Something went wrong. @pageerror -\ No newline at end of file +Something went wrong. [yellow error] +\ No newline at end of file diff --git a/system/plugins/command.php b/system/plugins/command.php @@ -0,0 +1,465 @@ +<?php +// Command plugin, https://github.com/datenstrom/yellow-plugins/tree/master/command +// Copyright (c) 2013-2017 Datenstrom, https://datenstrom.se +// This file may be used and distributed under the terms of the public license. + +class YellowCommand +{ + const VERSION = "0.7.1"; + var $yellow; //access to API + var $files; //number of files + 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 "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 .= "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 files + function buildCommand($args) + { + $statusCode = 0; + list($command, $path, $location) = $args; + if(empty($location) || $location[0]=='/') + { + if($this->checkStaticConfig()) + { + $statusCode = $this->buildStatic($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' : ''); + echo ", status $statusCode\n"; + } else { + $statusCode = 400; + echo "Yellow $command: Invalid arguments\n"; + } + return $statusCode; + } + + // Build static files and additional locations + function buildStatic($path, $location) + { + $path = rtrim(empty($path) ? $this->yellow->config->get("staticDir") : $path, '/'); + $this->files = $this->errors = $statusCode = 0; + $this->locationsArgs = $this->locationsArgsPagination = array(); + if(empty($location)) + { + $statusCode = $this->cleanStatic($path, $location); + foreach($this->getContentLocations() as $location) + { + $statusCode = max($statusCode, $this->buildStaticFile($path, $location, true)); + } + foreach($this->locationsArgs as $location) + { + $statusCode = max($statusCode, $this->buildStaticFile($path, $location, true)); + } + foreach($this->locationsArgsPagination as $location) + { + 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; + } + } + 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)); + } else { + $statusCode = $this->buildStaticFile($path, $location); + } + 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"; + } + } + } + } + + // Clean static files + function cleanCommand($args) + { + $statusCode = 0; + list($command, $path, $location) = $args; + if(empty($location) || $location[0]=='/') + { + $statusCode = $this->cleanStatic($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 cleanStatic($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 { + $statusCode = $this->cleanStaticFile($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($path, $location) + { + $statusCode = 200; + $fileName = $this->getStaticFile($path, $location, $statusCode); + 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($command) = $args; + 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 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; + } + + // Return content locations + function getContentLocations() + { + $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") + { + 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 system locations + function getSystemLocations() + { + $locations = array(); + $regex = "/\.(css|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 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); + } +} + +$yellow->plugins->register("command", "YellowCommand", YellowCommand::VERSION); +?> diff --git a/system/plugins/commandline.php b/system/plugins/commandline.php @@ -1,464 +0,0 @@ -<?php -// Commandline plugin, https://github.com/datenstrom/yellow-plugins/tree/master/commandline -// Copyright (c) 2013-2017 Datenstrom, https://datenstrom.se -// This file may be used and distributed under the terms of the public license. - -class YellowCommandline -{ - const VERSION = "0.6.19"; - var $yellow; //access to API - var $files; //number of files - 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 "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 .= "clean [DIRECTORY LOCATION]\n"; - $help .= "version\n"; - return $help; - } - - // Show available commands - function helpCommand() - { - echo "Yellow ".YellowCore::VERSION."\n"; - $lineCounter = 0; - foreach($this->getCommandHelp() as $line) echo (++$lineCounter>1 ? " " : "Syntax: ")."yellow.php $line\n"; - return 200; - } - - // Build static files - function buildCommand($args) - { - $statusCode = 0; - list($command, $path, $location) = $args; - if(empty($location) || $location[0]=='/') - { - if($this->checkStaticConfig()) - { - $statusCode = $this->buildStatic($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' : ''); - echo ", status $statusCode\n"; - } else { - $statusCode = 400; - echo "Yellow $command: Invalid arguments\n"; - } - return $statusCode; - } - - // Build static files and additional locations - function buildStatic($path, $location) - { - $path = rtrim(empty($path) ? $this->yellow->config->get("staticDir") : $path, '/'); - $this->files = $this->errors = $statusCode = 0; - $this->locationsArgs = $this->locationsArgsPagination = array(); - if(empty($location)) - { - $statusCode = $this->cleanStatic($path, $location); - foreach($this->getContentLocations() as $location) - { - $statusCode = max($statusCode, $this->buildStaticFile($path, $location, true)); - } - foreach($this->locationsArgs as $location) - { - $statusCode = max($statusCode, $this->buildStaticFile($path, $location, true)); - } - foreach($this->locationsArgsPagination as $location) - { - 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; - } - } - 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)); - } else { - $statusCode = $this->buildStaticFile($path, $location); - } - 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 "YellowCommandline::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_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 "YellowCommandline::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 "YellowCommandline::analyseStaticFile detected location:$location<br/>\n"; - } - } - } - } - - // Clean static files - function cleanCommand($args) - { - $statusCode = 0; - list($command, $path, $location) = $args; - if(empty($location) || $location[0]=='/') - { - $statusCode = $this->cleanStatic($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 cleanStatic($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 { - $statusCode = $this->cleanStaticFile($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($path, $location) - { - $statusCode = 200; - $fileName = $this->getStaticFile($path, $location, $statusCode); - 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=="commandline") 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 "Yellow ".YellowCore::VERSION.", PHP ".PHP_VERSION.", $serverVersion\n"; - list($command) = $args; - 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 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; - } - - // Return content locations - function getContentLocations() - { - $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") - { - 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 system locations - function getSystemLocations() - { - $locations = array(); - $regex = "/\.(css|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 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); - } -} - -$yellow->plugins->register("commandline", "YellowCommandline", YellowCommandline::VERSION); -?> -\ No newline at end of file diff --git a/system/plugins/core.php b/system/plugins/core.php @@ -5,7 +5,7 @@ class YellowCore { - const VERSION = "0.6.9"; + const VERSION = "0.7.1"; var $page; //current page var $pages; //pages from file system var $files; //files from file system @@ -90,7 +90,7 @@ class YellowCore if(defined("DEBUG") && DEBUG>=2) { $serverVersion = $this->toolbox->getServerVersion(); - echo "Yellow ".YellowCore::VERSION.", PHP ".PHP_VERSION.", $serverVersion<br/>\n"; + echo "Datenstrom Yellow ".YellowCore::VERSION.", PHP ".PHP_VERSION.", $serverVersion<br/>\n"; } $this->config->load($this->config->get("configDir").$this->config->get("configFile")); $this->text->load($this->config->get("pluginDir").$this->config->get("textFile")); @@ -126,7 +126,6 @@ class YellowCore ob_start(); $statusCode = 0; $this->toolbox->timerStart($time); - $this->toolbox->normaliseRequest(); list($scheme, $address, $base, $location, $fileName) = $this->getRequestInformation(); $this->page->setRequestInformation($scheme, $address, $base, $location, $fileName); foreach($this->plugins->plugins as $key=>$value) @@ -393,7 +392,6 @@ class YellowPage var $base; //base location var $location; //page location var $fileName; //content file name - var $lastModified; //last modification date var $rawData; //raw data of page var $metaDataOffsetBytes; //meta data offset var $metaData; //meta data @@ -408,6 +406,7 @@ class YellowPage 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 function __construct($yellow) @@ -432,7 +431,6 @@ class YellowPage // Parse page data function parseData($rawData, $cacheable, $statusCode, $pageError = "") { - $this->lastModified = 0; $this->rawData = $rawData; $this->parser = null; $this->parserData = ""; @@ -441,6 +439,7 @@ class YellowPage $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); } @@ -463,14 +462,9 @@ class YellowPage 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("theme", $this->yellow->lookup->findNameFromFile($this->fileName, - $this->yellow->config->get("assetDir"), $this->yellow->config->get("theme"), ".css")); - $this->set("template", $this->yellow->lookup->findNameFromFile($this->fileName, - $this->yellow->config->get("templateDir"), $this->yellow->config->get("template"), ".html")); + $this->set("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->parseMetaData(array("sitename", "siteicon", "tagline", "author", "navigation", "sidebar", "parser")); + $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")); @@ -485,12 +479,12 @@ class YellowPage $this->yellow->config->get("serverScheme"), $this->yellow->config->get("serverAddress"), $this->yellow->config->get("serverBase"), - rtrim($this->yellow->config->get("webinterfaceLocation"), '/').$this->location)); - $this->set("pageFile", $this->yellow->lookup->normaliseFile($this->fileName)); + rtrim($this->yellow->config->get("editLocation"), '/').$this->location)); + $this->set("pageFile", $this->yellow->lookup->getPageFile($this->fileName)); } else { $this->set("type", $this->yellow->toolbox->getFileExtension($this->fileName)); $this->set("modified", date("Y-m-d H:i:s", $this->yellow->toolbox->getFileModified($this->fileName))); - $this->set("pageFile", $this->yellow->lookup->normaliseFile($this->fileName, true)); + $this->set("pageFile", $this->yellow->lookup->getPageFile($this->fileName, true)); } if(!empty($pageError)) $this->set("pageError", $pageError); foreach($this->yellow->plugins->plugins as $key=>$value) @@ -499,15 +493,15 @@ class YellowPage } } - // Parse page meta data from configuration and raw data - function parseMetaData($defaultKeys) + // 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]+\-\-\-[\r\n]+/s", $this->rawData, $parts)) + 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) @@ -535,7 +529,6 @@ class YellowPage $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 = preg_replace("/@pageError/i", $this->get("pageError"), $this->parserData); $this->parserData = $this->parser->onParseContentRaw($this, $this->parserData); foreach($this->yellow->plugins->plugins as $key=>$value) { @@ -548,7 +541,7 @@ class YellowPage } } else { $this->parserData = $this->getContent(true, $sizeMax); - $this->parserData = preg_replace("/@pageError/i", $this->get("pageError"), $this->parserData); + $this->parserData = preg_replace("/\[yellow error\]/i", $this->get("pageError"), $this->parserData); } if(!$this->isExisting("description")) { @@ -578,18 +571,16 @@ class YellowPage { if($name=="yellow" && $shortcut) { - $output = "Yellow ".YellowCore::VERSION; - if(!empty($text)) + $output = "Datenstrom Yellow ".YellowCore::VERSION; + if($text=="error") $output = $this->get("pageError"); + if($text=="version") { $output = "<span class=\"".htmlspecialchars($name)."\">\n"; - if($text=="version") + $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) { - $serverVersion = $this->yellow->toolbox->getServerVersion(); - $output .= "Yellow ".YellowCore::VERSION.", PHP ".PHP_VERSION.", $serverVersion<br />\n"; - foreach(array_merge($this->yellow->plugins->getData(), $this->yellow->themes->getData()) as $key=>$value) - { - $output .= htmlspecialchars("$key $value")."<br />\n"; - } + $output .= htmlspecialchars("$key $value")."<br />\n"; } $output .= "</span>\n"; if($this->parserSafeMode) $this->error(500, "Yellow '$text' is not available in safe mode!"); @@ -1430,7 +1421,7 @@ class YellowPages return $pages; } - // Return child pages recursively + // Return sub pages function getChildrenRecursive($location, $showInvisible = false, $levelMax = 0) { --$levelMax; @@ -1579,7 +1570,7 @@ class YellowFiles return $files; } - // Return child files recursively + // Return sub files function getChildrenRecursive($location, $showInvisible = false, $levelMax = 0) { --$levelMax; @@ -1642,11 +1633,6 @@ class YellowPlugins // Load plugins function load($path = "") { - if(count($this->yellow->config->config)==0) //TODO: remove later, backwards compability for old Yellow version - { - $this->yellow->load(); - return; - } $path = empty($path) ? $this->yellow->config->get("pluginDir") : $path; foreach($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.php$/", true, false) as $entry) { @@ -1769,6 +1755,12 @@ class YellowThemes } } + // Return theme + function get($name) + { + return $this->theme[$name]["obj"]; + } + // Return theme version function getData() { @@ -1841,11 +1833,11 @@ class YellowConfig foreach($this->yellow->toolbox->getTextLines($fileData) as $line) { preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); - $keyOriginal = $matches[1]; $keySearch = strtoloweru($matches[1]); $keyFound = ""; - foreach($config as $key=>$value) if(strtoloweru($key)==$keySearch) { $keyFound = $key; break; } + $keySearch = lcfirst($matches[1]); $keyFound = ""; + foreach($config as $key=>$value) if(lcfirst($key)==$keySearch) { $keyFound = $key; break; } if(!empty($keyFound)) { - $fileDataNew .= "$keyOriginal: $config[$keyFound]\n"; + $fileDataNew .= "$matches[1]: $config[$keyFound]\n"; unset($config[$keyFound]); } else { $fileDataNew .= $line; @@ -1853,7 +1845,7 @@ class YellowConfig } foreach($config as $key=>$value) { - $fileDataNew .= "$key: $value\n"; + $fileDataNew .= ucfirst($key).": $value\n"; } return $this->yellow->toolbox->createFile($fileName, $fileDataNew); } @@ -2074,7 +2066,7 @@ class YellowText class YellowLookup { - var $yellow; //access to API + var $yellow; //access to API var $requestHandler; //request handler name var $commandHandler; //command handler name var $snippetArgs; //snippet arguments @@ -2106,9 +2098,9 @@ class YellowLookup foreach($this->yellow->toolbox->getDirectoryEntries($path, "/.*/", true, true, false) as $entry) { if(empty($firstRoot)) { $firstRoot = $token = $entry; } - if($this->normaliseName($entry)==$root) { $token = $entry; break; } + if($this->normaliseToken($entry)==$root) { $token = $entry; break; } } - $pathRoot = $this->normaliseName($token)."/"; + $pathRoot = $this->normaliseToken($token)."/"; $path .= "$firstRoot/"; } if(!empty($pathHome)) @@ -2117,9 +2109,9 @@ class YellowLookup foreach($this->yellow->toolbox->getDirectoryEntries($path, "/.*/", true, true, false) as $entry) { if(empty($firstHome)) { $firstHome = $token = $entry; } - if($this->normaliseName($entry)==$home) { $token = $entry; break; } + if($this->normaliseToken($entry)==$home) { $token = $entry; break; } } - $pathHome = $this->normaliseName($token)."/"; + $pathHome = $this->normaliseToken($token)."/"; } return array($pathRoot, $pathHome); } @@ -2134,7 +2126,7 @@ class YellowLookup { foreach($this->yellow->toolbox->getDirectoryEntries($pathBase, "/.*/", true, true, false) as $entry) { - $token = $this->normaliseName($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"; @@ -2160,18 +2152,18 @@ class YellowLookup $tokens = explode('/', $fileName); if(!empty($pathRoot)) { - $token = $this->normaliseName($tokens[0]).'/'; + $token = $this->normaliseToken($tokens[0]).'/'; if($token!=$pathRoot) $location .= $token; array_shift($tokens); } for($i=0; $i<count($tokens)-1; ++$i) { - $token = $this->normaliseName($tokens[$i]).'/'; + $token = $this->normaliseToken($tokens[$i]).'/'; if($i || $token!=$pathHome) $location .= $token; } - $token = $this->normaliseName($tokens[$i]); - $fileFolder = $this->normaliseName($tokens[$i-1]).$fileExtension; - if($token!=$fileDefault && $token!=$fileFolder) $location .= $this->normaliseName($tokens[$i], true, true); + $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 { @@ -2199,40 +2191,40 @@ class YellowLookup if(!empty($pathRoot)) { $token = (count($tokens)>2) ? $tokens[1] : rtrim($pathRoot, '/'); - $path .= $this->findFileDirectory($path, $token, true, true, $found, $invalid); + $path .= $this->findFileDirectory($path, $token, "", true, true, $found, $invalid); } } else { if(!empty($pathRoot)) { if(count($tokens)>2) { - if($this->normaliseName($tokens[1])==$this->normaliseName($pathRoot)) $invalid = true; - $path .= $this->findFileDirectory($path, $tokens[1], false, true, $found, $invalid); + 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(!$found) $path .= $this->findFileDirectory($path, rtrim($pathRoot, '/'), "", true, true, $found, $invalid); } if(count($tokens)>2) { - if($this->normaliseName($tokens[1])==$this->normaliseName($pathHome)) $invalid = true; + 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); + $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); + $path .= $this->findFileDirectory($path, $tokens[0], "", true, true, $found, $invalid); } if(!$directory) { - if(!empty($tokens[$i])) + 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, true, false, $found, $invalid); + $path .= $this->findFileDirectory($path, $token, $fileExtension, false, true, $found, $invalid); } else { $path .= $this->findFileDefault($path, $fileDefault, $fileExtension, false); } @@ -2247,19 +2239,19 @@ class YellowLookup } // Return file or directory that matches token - function findFileDirectory($path, $token, $tokenFailback, $directory, &$found, &$invalid) + function findFileDirectory($path, $token, $fileExtension, $directory, $default, &$found, &$invalid) { - if($this->normaliseName($token)!=$token) $invalid = true; + 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->normaliseName($entry)==$token) { $token = $entry; $found = true; break; } + if($this->normaliseToken($entry, $fileExtension)==$token) { $token = $entry; $found = true; break; } } } if($directory) $token .= '/'; - return ($tokenFailback || $found) ? $token : ""; + return ($default || $found) ? $token : ""; } // Return default file in directory @@ -2268,17 +2260,68 @@ class YellowLookup $token = $fileDefault; if(!is_file($path."/".$fileDefault)) { - $fileFolder = $this->normaliseName(basename($path)).$fileExtension; + $fileFolder = $this->normaliseToken(basename($path), $fileExtension); $regex = "/^[\d\-\_\.]*($fileDefault|$fileFolder)$/"; foreach($this->yellow->toolbox->getDirectoryEntries($path, $regex, true, false, false) as $entry) { - if($this->normaliseName($entry)==$fileDefault) { $token = $entry; break; } - if($this->normaliseName($entry)==$fileFolder) { $token = $entry; break; } + 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) { @@ -2295,13 +2338,12 @@ class YellowLookup } if(!$this->isRootLocation($location)) { - $fileFolder = $this->normaliseName(basename($path)).$fileExtension; + $fileFolder = $this->normaliseToken(basename($path), $fileExtension); $regex = "/^.*\\".$fileExtension."$/"; foreach($this->yellow->toolbox->getDirectoryEntries($path, $regex, true, false, false) as $entry) { - if($this->normaliseName($entry)==$fileDefault) continue; - if($this->normaliseName($entry)==$fileFolder) continue; - if($this->normaliseName($entry, true, true)=="") continue; + if($this->normaliseToken($entry, $fileExtension)==$fileDefault) continue; + if($this->normaliseToken($entry, $fileExtension)==$fileFolder) continue; array_push($fileNames, $path.$entry); } } @@ -2318,48 +2360,13 @@ class YellowLookup if(!empty($pathRoot)) { $fileName = substru($fileName, strlenu($pathBase)); - if(preg_match("/^(.+?)\//", $fileName, $matches)) $name = $this->normaliseName($matches[1]); + if(preg_match("/^(.+?)\//", $fileName, $matches)) $name = $this->normaliseToken($matches[1]); if(strlenu($name)==2) $language = $name; } return $language; } - // Return theme/template name from file path - function findNameFromFile($fileName, $pathBase, $nameDefault, $fileExtension) - { - $name = ""; - if(preg_match("/^.*\/(.+?)$/", dirname($fileName), $matches)) $name = $this->normaliseName($matches[1]); - if(!is_file("$pathBase$name$fileExtension")) $name = $this->normaliseName($nameDefault); - return $name; - } - - // Return file path from config - function findFileFromConfig($fileName, $fileNameBase, $nameDefault) - { - $pathBase = $this->yellow->config->get("configDir"); - if(preg_match("/^.*\/(.+?)$/", dirname($fileName), $matches)) $name = $this->normaliseName($matches[1]); - $fileName = strreplaceu("(.*)", $name, $pathBase.$fileNameBase); - if(!is_file($fileName)) - { - $name = $this->normaliseName($nameDefault); - $fileName = strreplaceu("(.*)", $name, $pathBase.$fileNameBase); - } - return $fileName; - } - - // Return file path from title - function findFileFromTitle($titlePrefix, $titleText, $fileName, $fileDefault, $fileExtension) - { - preg_match("/^([\d\-\_\.]*)(.*)$/", $titlePrefix, $matches); - if(preg_match("/\d$/", $matches[1])) $matches[1] .= '-'; - $titleText = $this->normaliseName($titleText, false, false, true); - preg_match("/^([\d\-\_\.]*)(.*)$/", $matches[1].$titleText, $matches); - $fileNamePrefix = $matches[1]; - $fileNameText = empty($matches[2]) ? $fileDefault : $matches[2].$fileExtension; - return dirname($fileName)."/".$fileNamePrefix.$fileNameText; - } - - // Return file path for media location + // Return file path from media location function findFileFromMedia($location) { if($this->isFileLocation($location)) @@ -2373,7 +2380,7 @@ class YellowLookup return $fileName; } - // Return file path for system location + // Return file path from system location function findFileFromSystem($location) { if(preg_match("/\.(css|ico|js|jpg|png|svg|txt|woff|woff2)$/", $location)) @@ -2393,64 +2400,21 @@ class YellowLookup return $fileName; } - // Return file path for static file, if possible - function findFileStatic($location, $fileName, $cacheable) + // Normalise file/directory token + function normaliseToken($text, $fileExtension = "", $removeExtension = false) { - 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; + 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); } - // Return file path for new page - function findFilePageNew($fileName, $prefix = "") - { - $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] = (empty($prefix) ? "$number-" : $prefix).$tokens[$i]; - } - $tokens[$i] = $this->normaliseName($tokens[$i], false, false, true); - } - $path .= $tokens[$i]."/"; - } - $path .= $tokens[$i]; - return $path; - } - - // Normalise file/directory/other name - function normaliseName($text, $removePrefix = true, $removeExtension = false, $filterStrict = false) + // Normalise name + function normaliseName($text, $removePrefix = false, $removeExtension = false, $filterStrict = false) { if($removeExtension) $text = ($pos = strrposu($text, '.')) ? substru($text, 0, $pos) : $text; - if($removePrefix) if(preg_match("/^[\d\-\_\.]+(.*)$/", $text, $matches)) $text = $matches[1]; - if($filterStrict) $text = strreplaceu('.', '-', strtoloweru($text)); - return preg_replace("/[^\pL\d\-\_\.]/u", "-", rtrim($text, '/')); - } - - // Normalise content/media file name - function normaliseFile($fileName, $convertExtension = false) - { - $fileName = basename($fileName); - if($convertExtension) - { - $fileName = ($pos = strposu($fileName, '.')) ? substru($fileName, 0, $pos) : $fileName; - $fileName .= $this->yellow->config->get("contentExtension"); - } - return $fileName; + 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 @@ -2514,6 +2478,18 @@ class YellowLookup return array($scheme, $address, $base); } + // Return page file name + function getPageFile($fileName, $convertExtension = false) + { + $fileName = basename($fileName); + if($convertExtension) + { + $fileName = ($pos = strposu($fileName, '.')) ? substru($fileName, 0, $pos) : $fileName; + $fileName .= $this->yellow->config->get("contentExtension"); + } + return $fileName; + } + // Return directory location function getDirectoryLocation($location) { @@ -2799,17 +2775,6 @@ class YellowToolbox return isset($_SERVER["HTTP_IF_MODIFIED_SINCE"]) && $_SERVER["HTTP_IF_MODIFIED_SINCE"]==$lastModifiedFormatted; } - // Normalise request data, take care of magic quotes - function normaliseRequest() - { - if(get_magic_quotes_gpc()) - { - function stripArray($data) { return is_array($data) ? array_map("stripArray", $data) : stripslashes($data); } - $requestData = array(&$_GET, &$_POST, &$_COOKIE, &$_REQUEST); - foreach($requestData as &$data) $data = stripArray($data); - } - } - // Normalise path or location, take care of relative path tokens function normaliseTokens($text, $prependSlash = false) { @@ -3094,7 +3059,7 @@ class YellowToolbox return strtoloweru(($pos = strrposu($fileName, '.')) ? substru($fileName, $pos+1) : ""); } - // Return lines from text string, newline separated + // Return lines from text string, including newline function getTextLines($text) { $lines = array(); @@ -3269,6 +3234,47 @@ class YellowToolbox 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) { diff --git a/system/plugins/edit.css b/system/plugins/edit.css @@ -0,0 +1,129 @@ +/* Edit plugin, https://github.com/datenstrom/yellow-plugins/tree/master/edit */ +/* Copyright (c) 2013-2017 Datenstrom, https://datenstrom.se */ +/* This file may be used and distributed under the terms of the public license. */ + +.yellow-bar { position:relative; overflow:hidden; 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-body-modal-open { overflow:hidden; } + +.yellow-pane { + position:absolute; display:none; z-index:100; + margin:10px 0; 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; } +.yellow-pane p { margin:0.5em; } +.yellow-pane ul { list-style:none; margin:0 0.5em; padding:0; } +.yellow-pane div { overflow:hidden; } +.yellow-close { display:block; float:right; padding:0 0.5em; font-size:1.1em; 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; + 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; +} +.yellow-arrow:before { + border-color:rgba(187, 187, 187, 0); + border-bottom-color:#bbb; + border-width:11px; + margin-left:-11px; +} + +.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; +} +.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; +} +.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); } +.yellow-btn-delete { + background-color:#c33c35; color:#ffffff; + background-image:linear-gradient(to bottom, #ee5f5b, #bd362f); + border-color:#b13121 #b13121 #802020; +} +.yellow-btn-delete:hover, .yellow-btn-delete:focus, .yellow-btn-delete:active { color:#ffffff; } + +#yellow-pane-login { text-align:center; white-space:nowrap; } +#yellow-pane-login h1 { margin:0 1em; font-size:2em; } +#yellow-pane-login .yellow-form-control { width:15em; box-sizing:border-box; } +#yellow-pane-login .yellow-btn { width:15em; margin:1em 1em 0.5em 0; } +#yellow-pane-login-fields { width:15em; text-align:left; margin:0 auto; } +#yellow-pane-login-buttons { margin:0.5em 0; } +#yellow-pane-login-buttons p { margin:0; } + +#yellow-pane-signup { text-align:center; white-space:nowrap; } +#yellow-pane-signup h1 { margin:0 1em; font-size:2em; } +#yellow-pane-signup .yellow-form-control { width:15em; box-sizing:border-box; } +#yellow-pane-signup .yellow-btn { width:15em; margin:1em 1em 0.5em 0; } +#yellow-pane-signup-status { margin:0.5em 0; display:inline-block; } +#yellow-pane-signup-fields { width:15em; text-align:left; margin:0 auto; } +#yellow-pane-signup-buttons { margin-top:-0.5em; } + +#yellow-pane-recover { text-align:center; white-space:nowrap; } +#yellow-pane-recover h1 { margin:0 1em; font-size:2em; } +#yellow-pane-recover .yellow-form-control { width:15em; box-sizing:border-box; } +#yellow-pane-recover .yellow-btn { width:15em; margin:1em 1em 0.5em 0; } +#yellow-pane-recover-status { margin:0.5em 0; display:inline-block; } +#yellow-pane-recover-fields-first, #yellow-pane-recover-fields-second { width:15em; text-align:left; margin:0 auto; } +#yellow-pane-recover-buttons { margin-top:-0.5em; } + +#yellow-pane-settings { text-align:center; white-space:nowrap; } +#yellow-pane-settings h1 { margin:0 1em; font-size:2em; } +#yellow-pane-settings .yellow-form-control { width:15em; box-sizing:border-box; } +#yellow-pane-settings .yellow-btn { width:15em; margin:1em 1em 0.5em 0; } +#yellow-pane-settings-status { margin:0.5em 0; display:inline-block; } +#yellow-pane-settings-fields { width:15em; text-align:left; margin:0 auto; } +#yellow-pane-settings-buttons { margin-top:-0.5em; } + +#yellow-pane-version { text-align:center; white-space:nowrap; } +#yellow-pane-version h1 { margin:0 1em; font-size:2em; } +#yellow-pane-version .yellow-btn { width:15em; margin:1em 1em 0.5em 0; } +#yellow-pane-version-status { margin:0.5em 0; display:inline-block; } +#yellow-pane-version-fields { text-align:center; margin:0.5em 0; } +#yellow-pane-version-buttons { margin-top:-0.5em; } + +#yellow-pane-edit { } +#yellow-pane-edit h1 { margin:0 0 10px 0; font-size:2em; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; } +#yellow-pane-edit-page { padding:5px; outline:none; resize:none; } +#yellow-pane-edit-buttons { margin-top:5px; } +#yellow-pane-edit-buttons input { margin-right:10px; } +#yellow-pane-edit-help { float:right; } + +#yellow-pane-user { cursor:pointer; } +#yellow-pane-user a { text-decoration:none; } +#yellow-pane-user a:hover { text-decoration:underline; } diff --git a/system/plugins/edit.js b/system/plugins/edit.js @@ -0,0 +1,811 @@ +// Edit plugin, https://github.com/datenstrom/yellow-plugins/tree/master/edit +// Copyright (c) 2013-2017 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(); }, + onClick: function(e) { yellow.edit.hidePanesOnClick(yellow.toolbox.getEventElement(e)); }, + onKeydown: function(e) { yellow.edit.hidePanesOnKeydown(yellow.toolbox.getEventKeycode(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 + intervalId: 0, //timer interval ID + + // 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 "recover": this.showPane("yellow-pane-recover", action, status); break; + case "settings": this.showPane("yellow-pane-settings", action, status); break; + case "reconfirm": this.showPane("yellow-pane-settings", action, status); break; + case "change": this.showPane("yellow-pane-settings", action, status); break; + case "version": this.showPane("yellow-pane-version", action, status); break; + case "update": this.sendPane("yellow-pane-update", action, status, args); break; + case "create": this.showPane("yellow-pane-edit", action, status, true); break; + case "edit": this.showPane("yellow-pane-edit", action, status, true); break; + case "delete": this.showPane("yellow-pane-edit", action, status, true); break; + case "user": this.showPane("yellow-pane-user", action, status); break; + case "help": this.hidePane(this.paneId); location.href = this.getText("UserHelpUrl", "yellow"); break; + case "send": this.sendPane(this.paneId, this.paneAction); break; + case "close": this.hidePane(this.paneId); break; + } + }, + + // Load interface + 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); + } + }, + + // 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); + } + if(yellow.config.userName) + { + elementBar.innerHTML = + "<div class=\"yellow-bar-left\">"+ + "<a href=\"#\" onclick=\"yellow.action('edit'); return false;\" id=\"yellow-pane-edit-link\">"+this.getText("Edit")+"</a>"+ + "</div>"+ + "<div class=\"yellow-bar-right\">"+ + "<a href=\"#\" onclick=\"yellow.action('create'); return false;\" id=\"yellow-pane-create-link\">"+this.getText("Create")+"</a>"+ + "<a href=\"#\" onclick=\"yellow.action('delete'); return false;\" id=\"yellow-pane-delete-link\">"+this.getText("Delete")+"</a>"+ + "<a href=\"#\" onclick=\"yellow.action('user'); return false;\" id=\"yellow-pane-user-link\">"+yellow.toolbox.encodeHtml(yellow.config.userName)+"</a>"+ + "</div>"; + } + yellow.toolbox.insertBefore(elementBar, document.getElementsByTagName("body")[0].firstChild); + return 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, "keyup", yellow.onUpdate); + yellow.toolbox.addEvent(elementPane, "change", yellow.onUpdate); + } + 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.setAttribute("id", paneId+"-content"); + switch(paneId) + { + case "yellow-pane-login": + elementDiv.innerHTML = + "<form method=\"post\">"+ + "<a href=\"#\" onclick=\"yellow.action('close'); return false;\" class=\"yellow-close\">x</a>"+ + "<h1>"+this.getText("LoginTitle")+"</h1>"+ + "<div 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 id=\"yellow-pane-login-buttons\">"+ + "<p><a href=\"#\" onclick=\"yellow.action('recover'); return false;\" id=\"yellow-pane-login-recover\">"+this.getText("LoginRecover")+"</a><p>"+ + "<p><a href=\"#\" onclick=\"yellow.action('signup'); return false;\" id=\"yellow-pane-login-signup\">"+this.getText("LoginSignup")+"</a><p>"+ + "</div>"+ + "</form>"; + break; + case "yellow-pane-signup": + elementDiv.innerHTML = + "<form method=\"post\">"+ + "<a href=\"#\" onclick=\"yellow.action('close'); return false;\" class=\"yellow-close\">x</a>"+ + "<h1>"+this.getText("SignupTitle")+"</h1>"+ + "<div id=\"yellow-pane-signup-status\" class=\""+paneStatus+"\">"+this.getText(paneAction+"Status", "", paneStatus)+"</div>"+ + "<div 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 class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("SignupButton")+"\" /></p>"+ + "</div>"+ + "<div id=\"yellow-pane-signup-buttons\">"+ + "<p><input class=\"yellow-btn\" type=\"button\" onclick=\"yellow.action('close'); return false;\" value=\""+this.getText("OkButton")+"\" /></p>"+ + "</div>"+ + "</form>"; + break; + case "yellow-pane-recover": + elementDiv.innerHTML = + "<form method=\"post\">"+ + "<a href=\"#\" onclick=\"yellow.action('close'); return false;\" class=\"yellow-close\">x</a>"+ + "<h1>"+this.getText("RecoverTitle")+"</h1>"+ + "<div id=\"yellow-pane-recover-status\" class=\""+paneStatus+"\">"+this.getText(paneAction+"Status", "", paneStatus)+"</div>"+ + "<div id=\"yellow-pane-recover-fields-first\">"+ + "<input type=\"hidden\" name=\"action\" value=\"recover\" />"+ + "<p><label for=\"yellow-pane-recover-email\">"+this.getText("RecoverEmail")+"</label><br /><input class=\"yellow-form-control\" name=\"email\" id=\"yellow-pane-recover-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 id=\"yellow-pane-recover-fields-second\">"+ + "<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 id=\"yellow-pane-recover-buttons\">"+ + "<p><input class=\"yellow-btn\" type=\"button\" onclick=\"yellow.action('close'); return false;\" value=\""+this.getText("OkButton")+"\" /></p>"+ + "</div>"+ + "</form>"; + break; + case "yellow-pane-settings": + elementDiv.innerHTML = + "<form method=\"post\">"+ + "<a href=\"#\" onclick=\"yellow.action('close'); return false;\" class=\"yellow-close\">x</a>"+ + "<h1 id=\"yellow-pane-settings-title\">"+this.getText("SettingsTitle")+"</h1>"+ + "<div id=\"yellow-pane-settings-status\" class=\""+paneStatus+"\">"+this.getText(paneAction+"Status", "", paneStatus)+"</div>"+ + "<div id=\"yellow-pane-settings-fields\">"+ + "<input type=\"hidden\" name=\"action\" value=\"settings\" />"+ + "<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>"+this.getLanguages(paneId)+ + "<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("OkButton")+"\" /></p>"+ + "</div>"+ + "<div id=\"yellow-pane-settings-buttons\">"+ + "<p><input class=\"yellow-btn\" type=\"button\" onclick=\"yellow.action('close'); return false;\" value=\""+this.getText("OkButton")+"\" /></p>"+ + "</div>"+ + "</form>"; + break; + case "yellow-pane-version": + elementDiv.innerHTML = + "<form method=\"post\">"+ + "<a href=\"#\" onclick=\"yellow.action('close'); return false;\" class=\"yellow-close\">x</a>"+ + "<h1 id=\"yellow-pane-version-title\">"+yellow.toolbox.encodeHtml(yellow.config.serverVersion)+"</h1>"+ + "<div id=\"yellow-pane-version-status\" class=\""+paneStatus+"\">"+this.getText("VersionStatus", "", paneStatus)+"</div>"+ + "<div id=\"yellow-pane-version-fields\">"+yellow.page.rawDataOutput+"</div>"+ + "<div id=\"yellow-pane-version-buttons\">"+ + "<p><input class=\"yellow-btn\" type=\"button\" onclick=\"yellow.action('close'); return false;\" value=\""+this.getText("OkButton")+"\" /></p>"+ + "</div>"+ + "</form>"; + break; + case "yellow-pane-edit": + elementDiv.innerHTML = + "<form method=\"post\">"+ + "<a href=\"#\" onclick=\"yellow.action('close'); return false;\" class=\"yellow-close\">x</a>"+ + "<h1 id=\"yellow-pane-edit-title\">"+this.getText("Edit")+"</h1>"+ + "<textarea id=\"yellow-pane-edit-page\" class=\"yellow-form-control\" name=\"rawdataedit\"></textarea>"+ + "<div id=\"yellow-pane-edit-buttons\">"+ + "<input id=\"yellow-pane-edit-send\" class=\"yellow-btn\" type=\"button\" onclick=\"yellow.action('send'); return false;\" value=\""+this.getText("EditButton")+"\" />"+ + "<a href=\""+this.getText("MarkdownHelpUrl", "yellow")+"\" target=\"_blank\" id=\"yellow-pane-edit-help\">"+this.getText("MarkdownHelp")+"</a>" + + "</div>"+ + "</form>"; + break; + case "yellow-pane-user": + elementDiv.innerHTML = + "<p>"+yellow.toolbox.encodeHtml(yellow.config.userEmail)+"</p>"+ + "<p><a href=\"#\" onclick=\"yellow.action('settings'); return false;\">"+this.getText("SettingsTitle")+"</a></p>" + + "<p><a href=\"#\" onclick=\"yellow.action('help'); return false;\">"+this.getText("UserHelp")+"</a></p>" + + "<p><a href=\"#\" onclick=\"yellow.action('logout'); return false;\">"+this.getText("UserLogout")+"</a></p>"; + break; + } + elementPane.appendChild(elementDiv); + yellow.toolbox.insertAfter(elementPane, document.getElementsByTagName("body")[0].firstChild); + return 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" && paneStatus!="expired"; + 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-recover": + yellow.toolbox.setVisible(document.getElementById("yellow-pane-recover-fields-first"), showFields); + yellow.toolbox.setVisible(document.getElementById("yellow-pane-recover-fields-second"), showFields); + yellow.toolbox.setVisible(document.getElementById("yellow-pane-recover-buttons"), !showFields); + if(showFields) + { + if(this.getRequest("id")) + { + yellow.toolbox.setVisible(document.getElementById("yellow-pane-recover-fields-first"), false); + } else { + yellow.toolbox.setVisible(document.getElementById("yellow-pane-recover-fields-second"), false); + } + } + 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=\"#\" onclick=\"yellow.action('version'); return false;\">"+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" && yellow.config.userUpdate) + { + document.getElementById("yellow-pane-version-status").innerHTML = this.getText("VersionStatusCheck"); + document.getElementById("yellow-pane-version-fields").innerHTML = ""; + setTimeout("yellow.action('send');", 500); + } + if(paneStatus=="updates" && yellow.config.userWebmaster) + { + document.getElementById("yellow-pane-version-status").innerHTML = "<a href=\"#\" onclick=\"yellow.action('update'); return false;\">"+this.getText("VersionUpdateNormal")+"</a>"; + } + break; + case "yellow-pane-edit": + if(init) + { + var title; + var string = yellow.page.rawDataEdit; + switch(paneAction) + { + case "create": title = this.getText("CreateTitle"); string = yellow.page.rawDataNew; break; + case "edit": title = yellow.page.title ? yellow.page.title : this.getText("Edit"); break; + case "delete": title = this.getText("DeleteTitle"); break; + } + document.getElementById("yellow-pane-edit-title").innerHTML = yellow.toolbox.encodeHtml(title); + document.getElementById("yellow-pane-edit-page").value = string; + yellow.toolbox.setCursorPosition(document.getElementById("yellow-pane-edit-page"), 0); + } + paneAction = this.getPaneAction(paneId, paneAction); + var key, className, readOnly; + switch(paneAction) + { + case "create": key = "CreateButton"; className = "yellow-btn yellow-btn-create"; readOnly = false; break; + case "edit": key = "EditButton"; className = "yellow-btn yellow-btn-edit"; readOnly = false; break; + case "delete": key = "DeleteButton"; className = "yellow-btn yellow-btn-delete"; readOnly = false; break; + case "": key = "CancelButton"; className = "yellow-btn yellow-btn-cancel"; readOnly = true; break; + } + document.getElementById("yellow-pane-edit-send").value = this.getText(key); + document.getElementById("yellow-pane-edit-send").className = className; + document.getElementById("yellow-pane-edit-page").readOnly = readOnly; + break; + } + }, + + // Resize pane + resizePane: function(paneId, paneAction, paneStatus) + { + var elementBar = document.getElementById("yellow-bar"); + var paneLeft = yellow.toolbox.getOuterLeft(elementBar); + var paneTop = yellow.toolbox.getOuterTop(elementBar) + yellow.toolbox.getOuterHeight(elementBar); + var paneWidth = yellow.toolbox.getOuterWidth(elementBar); + var paneHeight = yellow.toolbox.getWindowHeight() - paneTop - yellow.toolbox.getOuterHeight(elementBar); + switch(paneId) + { + case "yellow-pane-login": + case "yellow-pane-signup": + case "yellow-pane-recover": + case "yellow-pane-settings": + case "yellow-pane-version": + 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); + yellow.toolbox.setOuterWidth(document.getElementById("yellow-pane-edit-page"), yellow.toolbox.getWidth(document.getElementById("yellow-pane-edit"))); + var height1 = yellow.toolbox.getHeight(document.getElementById("yellow-pane-edit")); + var height2 = yellow.toolbox.getOuterHeight(document.getElementById("yellow-pane-edit-content")); + var height3 = yellow.toolbox.getOuterHeight(document.getElementById("yellow-pane-edit-page")); + yellow.toolbox.setOuterHeight(document.getElementById("yellow-pane-edit-page"), height1 - height2 + height3); + 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); + yellow.toolbox.setOuterHeight(document.getElementById("yellow-pane-user"), paneHeight, true); + 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); + var element = document.getElementById(paneId); + if(!element) element = this.createPane(paneId, paneAction, paneStatus); + 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.resizePane(paneId, paneAction, paneStatus); + this.updatePane(paneId, paneAction, paneStatus, this.paneActionOld!=this.paneAction); + } + } else { + this.hidePane(this.paneId); + } + }, + + // Hide pane + hidePane: function(paneId) + { + var element = document.getElementById(paneId); + if(yellow.toolbox.isVisible(element)) + { + if(yellow.config.debug) console.log("yellow.edit.hidePane id:"+paneId); + 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); + this.paneId = 0; + this.paneActionOld = this.paneAction; + this.paneAction = 0; + this.paneStatus = 0; + } + }, + + // Hide all panes + hidePanes: function() + { + for(var element=document.getElementById("yellow-bar"); element; element=element.nextSibling) + { + if(element.className && element.className.indexOf("yellow-pane")>=0) + { + this.hidePane(element.getAttribute("id")); + } + } + }, + + // Hide all panes on mouse click outside + hidePanesOnClick: function(element) + { + for(;element; element=element.parentNode) + { + if(element.className) + { + if(element.className.indexOf("yellow-pane")>=0 || element.className.indexOf("yellow-bar-")>=0) return; + } + } + this.hidePanes(); + }, + + // Hide all panes on ESC key + hidePanesOnKeydown: function(keycode) + { + if(keycode==27) this.hidePanes(); + }, + + // Send pane + sendPane: function(paneId, paneAction, paneStatus, paneArgs) + { + if(yellow.config.debug) console.log("yellow.edit.sendPane id:"+paneId); + if(paneId=="yellow-pane-edit") + { + paneAction = this.getPaneAction(paneId, paneAction); + if(paneAction) + { + var args = {}; + args.action = paneAction; + args.rawdatasource = yellow.page.rawDataSource; + args.rawdataedit = document.getElementById("yellow-pane-edit-page").value; + yellow.toolbox.submitForm(args, true); + } else { + this.hidePane(paneId); + } + } else { + var args = {"action":paneAction}; + if(paneArgs) + { + var tokens = paneArgs.split('/'); + for(var i=0; i<tokens.length; i++) + { + var pair = tokens[i].split(/[:=]/); + if(!pair[0] || !pair[1]) continue; + args[pair[0]] = pair[1]; + } + } + yellow.toolbox.submitForm(args); + } + }, + + // Return pane action + getPaneAction: function(paneId, paneAction) + { + if(paneId=="yellow-pane-edit") + { + var string = document.getElementById("yellow-pane-edit-page").value; + var paneActionOld = paneAction; + switch(paneAction) + { + case "create": paneAction = "create"; break; + case "edit": paneAction = string ? "edit" : "delete"; break; + case "delete": paneAction = "delete"; break; + } + if(yellow.page.statusCode==424 && paneActionOld!="delete") paneAction = "create"; + if(yellow.config.userRestrictions) paneAction = ""; + } + return paneAction; + }, + + // Return language selection + getLanguages: function(paneId) + { + var languages = ""; + if(yellow.config.serverLanguages && yellow.toolbox.getLength(yellow.config.serverLanguages)>1) + { + languages += "<p>"; + for(var language in yellow.config.serverLanguages) + { + var checked = language==this.getRequest("language") ? " checked=\"checked\"" : ""; + languages += "<label for=\""+paneId+"-"+language+"\"><input type=\"radio\" name=\"language\" id=\""+paneId+"-"+language+"\" value=\""+language+"\""+checked+"> "+yellow.toolbox.encodeHtml(yellow.config.serverLanguages[language])+"</label><br />"; + } + languages += "</p>"; + } + return languages; + }, + + // Return request string + getRequest: function(key, prefix) + { + if(!prefix) prefix = "request"; + key = prefix + key.charAt(0).toUpperCase() + key.slice(1); + 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 + key.charAt(0).toUpperCase() + key.slice(1) + postfix.charAt(0).toUpperCase() + postfix.slice(1); + return (key in yellow.text) ? yellow.text[key] : "["+key+"]"; + } +}; + +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); + }, + + // Add element class + addClass: function(element, name) + { + var string = element.className + " " + name; + element.className = string.replace(/^\s+|\s+$/, ""); + }, + + // Remove element class + removeClass: function(element, name) + { + var string = (" " + element.className + " ").replace(" " + name + " ", " "); + element.className = string.replace(/^\s+|\s+$/, ""); + }, + + // 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) + { + if(element.addEventListener) element.addEventListener(type, handler, false); + else element.attachEvent("on"+type, handler); + }, + + // Remove event handler + removeEvent: function(element, type, handler) + { + if(element.removeEventListener) element.removeEventListener(type, handler, false); + else element.detachEvent("on"+type, handler); + }, + + // Return element of event + getEventElement: function(e) + { + e = e ? e : window.event; + return e.target ? e.target : e.srcElement; + }, + + // Return keycode of event + getEventKeycode: function(e) + { + e = e ? e : window.event; + return e.keyCode; + }, + + // Return element length + getLength: function(element) + { + return Object.keys ? Object.keys(element).length : 0; + }, + + // 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, setMax) + { + width -= this.getBoxSize(element).width; + if(setMax) + { + element.style.maxWidth = Math.max(0, width) + "px"; + } else { + element.style.width = Math.max(0, width) + "px"; + } + }, + + // Set element height in pixel, including padding and border + setOuterHeight: function(element, height, setMax) + { + height -= this.getBoxSize(element).height; + if(setMax) + { + element.style.maxHeight = Math.max(0, height) + "px"; + } else { + element.style.height = Math.max(0, 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) + { + var left = element.getBoundingClientRect().left; + return left + (window.pageXOffset || document.documentElement.scrollLeft); + }, + + // Return element top position in pixel + getOuterTop: function(element) + { + var top = element.getBoundingClientRect().top; + return top + (window.pageYOffset || document.documentElement.scrollTop); + }, + + // Return window width in pixel + getWindowWidth: function() + { + return window.innerWidth || document.documentElement.clientWidth; + }, + + // Return window height in pixel + getWindowHeight: function() + { + return window.innerHeight || document.documentElement.clientHeight; + }, + + // Return element CSS property + getStyle: function(element, property) + { + var string = ""; + if(window.getComputedStyle) + { + string = window.getComputedStyle(element, null).getPropertyValue(property); + } else { + property = property.replace(/\-(\w)/g, function(match, m) { return m.toUpperCase(); }); + string = element.currentStyle[property]; + } + return string; + }, + + // 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 input cursor position + setCursorPosition: function(element, pos) + { + if(element.setSelectionRange) + { + element.focus(); + element.setSelectionRange(pos, pos); + } else if(element.createTextRange) { + var range = element.createTextRange(); + range.move("character", pos); + range.select(); + } + }, + + // Get input cursor position + getCursorPosition: function(element) + { + var pos = 0; + if(element.setSelectionRange) + { + pos = element.selectionStart; + } else if(document.selection) { + var range = document.selection.createRange(); + var rangeDuplicate = range.duplicate(); + rangeDuplicate.moveToElementText(element); + rangeDuplicate.setEndPoint("EndToEnd", range); + pos = rangeDuplicate.text.length - range.text.length; + } + return pos; + }, + + // Set element visibility + setVisible: function(element, show) + { + element.style.display = show ? "block" : "none"; + }, + + // Check if element exists and is visible + isVisible: function(element) + { + return element && element.style.display!="none"; + }, + + // Encode newline characters + encodeNewline: function(string) + { + return string + .replace(/[%]/g, "%25") + .replace(/[\r]/g, "%0d") + .replace(/[\n]/g, "%0a"); + }, + + // 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, encodeNewline) + { + var elementForm = document.createElement("form"); + elementForm.setAttribute("method", "post"); + for(var key in args) + { + if(!args.hasOwnProperty(key)) continue; + var value = encodeNewline ? this.encodeNewline(args[key]) : args[key]; + var elementInput = document.createElement("input"); + elementInput.setAttribute("type", "hidden"); + elementInput.setAttribute("name", key); + elementInput.setAttribute("value", value); + 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 @@ -0,0 +1,1545 @@ +<?php +// Edit plugin, https://github.com/datenstrom/yellow-plugins/tree/master/edit +// Copyright (c) 2013-2017 Datenstrom, https://datenstrom.se +// This file may be used and distributed under the terms of the public license. + +class YellowEdit +{ + const VERSION = "0.7.1"; + var $yellow; //access to API + var $response; //web response + var $users; //user accounts + var $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("editNewFile", "page-new-(.*).txt"); + $this->yellow->config->setDefault("editUserFile", "user.ini"); + $this->yellow->config->setDefault("editUserPasswordMinLength", "4"); + $this->yellow->config->setDefault("editUserHashAlgorithm", "bcrypt"); + $this->yellow->config->setDefault("editUserHashCost", "10"); + $this->yellow->config->setDefault("editUserStatus", "active"); + $this->yellow->config->setDefault("editUserHome", "/"); + $this->yellow->config->setDefault("editLoginEmail", ""); + $this->yellow->config->setDefault("editLoginPassword", ""); + $this->yellow->config->setDefault("editLoginRestrictions", "0"); + $this->users->load($this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile")); + } + + // Handle startup + function onStartup($update) + { + if($update) $this->cleanCommand(array("clean", "all")); + } + + // 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($page->statusCode==424) $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 "clean": $statusCode = $this->cleanCommand($args); break; + case "user": $statusCode = $this->userCommand($args); break; + default: $statusCode = 0; + } + return $statusCode; + } + + // Handle command help + function onCommandHelp() + { + return "user [EMAIL PASSWORD NAME LANGUAGE]\n"; + } + + // Clean user accounts + function cleanCommand($args) + { + $statusCode = 0; + list($command, $path) = $args; + if($path=="all") + { + $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); + if(!$this->users->clean($fileNameUser)) $statusCode = 500; + if($statusCode==500) echo "ERROR cleaning configuration: Can't write file '$fileNameUser'!\n"; + } + return $statusCode; + } + + // Update user account + function userCommand($args) + { + $statusCode = 0; + list($command, $email, $password, $name, $language) = $args; + if(!empty($email) && !empty($password)) + { + $userExisting = $this->users->isExisting($email); + $status = $this->getUserAccount($email, $password, $command); + switch($status) + { + case "invalid": echo "ERROR updating configuration: Please enter a valid 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->update($fileNameUser, $email, $password, $name, $language, "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, 5)!="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 " : ""); + echo ($userExisting ? "updated" : "created")."\n"; + } else { + $statusCode = 200; + foreach($this->users->getData() as $line) echo "$line\n"; + if(!$this->users->getNumber()) echo "Yellow $command: No user accounts\n"; + } + return $statusCode; + } + + // Process request + function processRequest($scheme, $address, $base, $location, $fileName) + { + $statusCode = 0; + if($this->checkUser($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 "signup": $statusCode = $this->processRequestSignup($scheme, $address, $base, $location, $fileName); break; + case "confirm": $statusCode = $this->processRequestConfirm($scheme, $address, $base, $location, $fileName); break; + case "approve": $statusCode = $this->processRequestApprove($scheme, $address, $base, $location, $fileName); break; + case "recover": $statusCode = $this->processRequestRecover($scheme, $address, $base, $location, $fileName); break; + case "settings": $statusCode = $this->processRequestSettings($scheme, $address, $base, $location, $fileName); break; + case "reconfirm": $statusCode = $this->processRequestReconfirm($scheme, $address, $base, $location, $fileName); break; + case "change": $statusCode = $this->processRequestChange($scheme, $address, $base, $location, $fileName); break; + case "version": $statusCode = $this->processRequestVersion($scheme, $address, $base, $location, $fileName); break; + case "update": $statusCode = $this->processRequestUpdate($scheme, $address, $base, $location, $fileName); break; + case "create": $statusCode = $this->processRequestCreate($scheme, $address, $base, $location, $fileName); break; + case "edit": $statusCode = $this->processRequestEdit($scheme, $address, $base, $location, $fileName); break; + case "delete": $statusCode = $this->processRequestDelete($scheme, $address, $base, $location, $fileName); break; + } + } else { + $this->yellow->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 "confirm": $statusCode = $this->processRequestConfirm($scheme, $address, $base, $location, $fileName); break; + case "approve": $statusCode = $this->processRequestApprove($scheme, $address, $base, $location, $fileName); break; + case "recover": $statusCode = $this->processRequestRecover($scheme, $address, $base, $location, $fileName); break; + case "reconfirm": $statusCode = $this->processRequestReconfirm($scheme, $address, $base, $location, $fileName); break; + case "change": $statusCode = $this->processRequestChange($scheme, $address, $base, $location, $fileName); break; + } + if($this->response->action=="fail") $this->yellow->page->error(500, "Login failed, <a href=\"javascript:yellow.action('login');\">please log in</a>!"); + } + 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)) + { + $statusCode = 301; + $location = $this->yellow->lookup->isFileLocation($location) ? "$location/" : "/".$this->yellow->getRequestLanguage()."/"; + $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); + $this->yellow->sendStatus($statusCode, $location); + } else { + $statusCode = $this->response->isUserRestrictions() ? 404 : 424; + $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); + $this->yellow->page->error($statusCode); + } + } + return $statusCode; + } + + // Process request for user login + function processRequestLogin($scheme, $address, $base, $location, $fileName) + { + $statusCode = 0; + $home = $this->users->getHome($this->response->userEmail); + if(substru($location, 0, strlenu($home))==$home) + { + $statusCode = 303; + $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); + $this->yellow->sendStatus($statusCode, $location); + } else { + $statusCode = 302; + $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $home); + $this->yellow->sendStatus($statusCode, $location); + } + return $statusCode; + } + + // Process request for user logout + function processRequestLogout($scheme, $address, $base, $location, $fileName) + { + $statusCode = 302; + $this->response->userEmail = ""; + $this->response->destroyCookie($scheme, $address, $base); + $location = $this->yellow->lookup->normaliseUrl( + $this->yellow->config->get("serverScheme"), + $this->yellow->config->get("serverAddress"), + $this->yellow->config->get("serverBase"), $location); + $this->yellow->sendStatus($statusCode, $location); + return $statusCode; + } + + // Process request for user signup + function processRequestSignup($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"]); + if(empty($name) || empty($email) || empty($password)) $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->update($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") + { + $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->users->getResponseStatus($email, $_REQUEST["action"], $_REQUEST["expire"], $_REQUEST["id"]); + if($this->response->status=="ok") + { + $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); + $this->response->status = $this->users->update($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->users->getResponseStatus($email, $_REQUEST["action"], $_REQUEST["expire"], $_REQUEST["id"]); + if($this->response->status=="ok") + { + $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); + $this->response->status = $this->users->update($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 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"]); + if(empty($_REQUEST["id"])) + { + 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!"); + } + } else { + $this->response->status = $this->users->getResponseStatus($email, $_REQUEST["action"], $_REQUEST["expire"], $_REQUEST["id"]); + 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->update($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->userEmail = ""; + $this->response->destroyCookie($scheme, $address, $base); + $this->response->status = $this->response->sendMail($scheme, $address, $base, $email, "information") ? "done" : "error"; + if($this->response->status=="error") $this->yellow->page->error(500, "Can't send email on this server!"); + } + } + } + $statusCode = $this->yellow->processRequest($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; + $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); + $this->response->status = $this->users->update($fileNameUser, $email, "no", $name, $language, "unconfirmed", "", $pending) ? "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->update($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 ? "reconfirm" : "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->update($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") + { + $statusCode = 303; + $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); + $this->yellow->sendStatus($statusCode, $location); + } else { + $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); + } + return $statusCode; + } + + // Process request to reconfirm email + function processRequestReconfirm($scheme, $address, $base, $location, $fileName) + { + $this->response->action = "reconfirm"; + $this->response->status = "ok"; + $email = $emailSource = $_REQUEST["email"]; + $this->response->status = $this->users->getResponseStatus($email, $_REQUEST["action"], $_REQUEST["expire"], $_REQUEST["id"]); + 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->update($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 settings + function processRequestChange($scheme, $address, $base, $location, $fileName) + { + $this->response->action = "change"; + $this->response->status = "ok"; + $email = $emailSource = trim($_REQUEST["email"]); + $this->response->status = $this->users->getResponseStatus($email, $_REQUEST["action"], $_REQUEST["expire"], $_REQUEST["id"]); + 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" && $email!=$emailSource) + { + $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); + $this->users->users[$emailSource]["pending"] = "none"; + $this->response->status = $this->users->update($fileNameUser, $emailSource, "", "", "", "inactive") ? "ok" : "error"; + if($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); + } + 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->update($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->userEmail = ""; + $this->response->destroyCookie($scheme, $address, $base); + $this->response->status = $this->response->sendMail($scheme, $address, $base, $email, "information") ? "done" : "error"; + if($this->response->status=="error") $this->yellow->page->error(500, "Can't send email on this server!"); + } + $statusCode = $this->yellow->processRequest($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; + if(!empty($this->response->rawDataOutput)) $this->response->rawDataOutput .= "<br />\n"; + $this->response->rawDataOutput .= htmlspecialchars("$key $dataLatest[$key]"); + } + } + if($updates==0) + { + foreach($dataCurrent as $key=>$value) + { + if(!is_null($dataModified[$key]) && !is_null($dataLatest[$key])) + { + $rawData = $this->yellow->text->getTextHtml("editVersionUpdateModified", $this->response->language)." - <a href=\"#\" onclick=\"yellow.action('update','update','".$this->yellow->toolbox->normaliseArgs("option:force/feature:$key")."'); return false;\">".$this->yellow->text->getTextHtml("editVersionUpdateForce", $this->response->language)."</a>"; + $rawData = preg_replace("/@software/i", htmlspecialchars("$key $dataLatest[$key]"), $rawData); + if(!empty($this->response->rawDataOutput)) $this->response->rawDataOutput .= "<br />\n"; + $this->response->rawDataOutput .= $rawData; + } + } + } + } else { + foreach($dataCurrent as $key=>$value) + { + if(strnatcasecmp($dataCurrent[$key], $dataLatest[$key])<0) ++$updates; + } + } + $this->response->status = $updates ? "updates" : "done"; + if($statusCode!=200) $this->response->status = "error"; + } + $statusCode = $this->yellow->processRequest($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) + { + $statusCode = 303; + $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); + $this->yellow->sendStatus($statusCode, $location); + } + } + return $statusCode; + } + + // Process request to create page + function processRequestCreate($scheme, $address, $base, $location, $fileName) + { + $statusCode = 0; + if(!$this->response->isUserRestrictions() && !empty($_POST["rawdataedit"])) + { + $this->response->rawDataSource = $this->response->rawDataEdit = rawurldecode($_POST["rawdatasource"]); + $rawData = rawurldecode($_POST["rawdataedit"]); + $page = $this->response->getPageNew($scheme, $address, $base, $location, $fileName, $rawData); + if(!$page->isError()) + { + if($this->yellow->toolbox->createFile($page->fileName, $page->rawData, true)) + { + $statusCode = 303; + $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $page->location); + $this->yellow->sendStatus($statusCode, $location); + } else { + $statusCode = 500; + $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); + $this->yellow->page->error($statusCode, "Can't write file '$page->fileName'!"); + } + } else { + $statusCode = 500; + $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); + $this->yellow->page->error($statusCode, $page->get("pageError")); + } + } + return $statusCode; + } + + // Process request to edit page + function processRequestEdit($scheme, $address, $base, $location, $fileName) + { + $statusCode = 0; + if(!$this->response->isUserRestrictions() && !empty($_POST["rawdataedit"])) + { + $this->response->rawDataSource = rawurldecode($_POST["rawdatasource"]); + $this->response->rawDataEdit = rawurldecode($_POST["rawdataedit"]); + $page = $this->response->getPageEdit($scheme, $address, $base, $location, $fileName, + $this->response->rawDataSource, $this->response->rawDataEdit, $this->yellow->toolbox->readFile($fileName)); + if(!$page->isError()) + { + if($this->yellow->toolbox->renameFile($fileName, $page->fileName, true) && + $this->yellow->toolbox->createFile($page->fileName, $page->rawData)) + { + $statusCode = 303; + $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $page->location); + $this->yellow->sendStatus($statusCode, $location); + } else { + $statusCode = 500; + $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); + $this->yellow->page->error($statusCode, "Can't write file '$page->fileName'!"); + } + } else { + $statusCode = 500; + $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); + $this->yellow->page->error($statusCode, $page->get("pageError")); + } + } + 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 = $this->response->rawDataEdit = rawurldecode($_POST["rawdatasource"]); + $page = $this->response->getPageDelete($scheme, $address, $base, $location, $fileName, $this->response->rawDataSource); + if(!$page->isError()) + { + if($this->yellow->lookup->isFileLocation($location)) + { + if($this->yellow->toolbox->deleteFile($fileName, $this->yellow->config->get("trashDir"))) + { + $statusCode = 303; + $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); + $this->yellow->sendStatus($statusCode, $location); + } else { + $statusCode = 500; + $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); + $this->yellow->page->error($statusCode, "Can't delete file '$fileName'!"); + } + } else { + if($this->yellow->toolbox->deleteDirectory(dirname($fileName), $this->yellow->config->get("trashDir"))) + { + $statusCode = 303; + $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); + $this->yellow->sendStatus($statusCode, $location); + } else { + $statusCode = 500; + $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); + $this->yellow->page->error($statusCode, "Can't delete file '$fileName'!"); + } + } + } else { + $statusCode = 500; + $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); + $this->yellow->page->error($statusCode, $page->get("pageError")); + } + } + 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 + function checkUser($scheme, $address, $base, $location, $fileName) + { + if($_POST["action"]=="login") + { + $email = $_POST["email"]; + $password = $_POST["password"]; + if($this->users->checkUser($email, $password)) + { + $this->response->createCookie($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->action = "fail"; + } + } else if(isset($_COOKIE["login"])) { + list($email, $session) = explode(',', $_COOKIE["login"], 2); + if($this->users->checkCookie($email, $session)) + { + $this->response->userEmail = $email; + $this->response->userRestrictions = $this->getUserRestrictions($email, $location, $fileName); + $this->response->language = $this->getUserLanguage($email); + } else { + $this->response->action = "fail"; + } + } + return $this->response->isUser(); + } + + // 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; + } +} + +class YellowResponse +{ + var $yellow; //access to API + var $plugin; //access to plugin + var $userEmail; //user email + var $userRestrictions; //user can change page? (boolean) + var $active; //location is active? (boolean) + var $rawDataSource; //raw data of page for comparison + var $rawDataEdit; //raw data of page for editing + var $rawDataOutput; //raw data of dynamic output + 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) + { + $page = new YellowPage($this->yellow); + $page->setRequestInformation($scheme, $address, $base, $location, $fileName); + $page->parseData($rawData, false, 0); + $this->editContentFile($page, "create"); + if($this->yellow->lookup->isFileLocation($location) || $this->yellow->pages->find($page->location)) + { + $page->location = $this->getLocationNew($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)) + { + $page->rawData = $this->yellow->toolbox->setMetaData($page->rawData, "title", $this->getTitleNext($page->rawData)); + $page->location = $this->getLocationNew($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) + { + $page = new YellowPage($this->yellow); + $page->setRequestInformation($scheme, $address, $base, $location, $fileName); + $page->parseData($this->plugin->merge->merge($rawDataSource, $rawDataEdit, $rawDataFile), 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($rawDataSource, false, 0); + if(substrb($pageSource->rawData, 0, $pageSource->metaDataOffsetBytes) != + substrb($page->rawData, 0, $page->metaDataOffsetBytes)) + { + $page->location = $this->getLocationNew($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, $rawDataSource) + { + $page = new YellowPage($this->yellow); + $page->setRequestInformation($scheme, $address, $base, $location, $fileName); + $page->parseData($rawDataSource, 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 page data including login 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["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["userUpdate"] = intval($this->yellow->plugins->isExisting("update")); + $data["serverLanguages"] = array(); + foreach($this->yellow->text->getLanguages() as $language) + { + $data["serverLanguages"][$language] = $this->yellow->text->getTextHtml("languageDescription", $language); + } + $data["serverScheme"] = $this->yellow->config->get("serverScheme"); + $data["serverAddress"] = $this->yellow->config->get("serverAddress"); + $data["serverBase"] = $this->yellow->config->get("serverBase"); + $data["serverVersion"] = "Datenstrom Yellow ".YellowCore::VERSION; + } else { + $data["editLoginEmail"] = $this->yellow->config->get("editLoginEmail"); + $data["editLoginPassword"] = $this->yellow->config->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=="login" || $key=="password") 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 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("editNewFile")); + 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("editNewFile")); + } + $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 getLocationNew($rawData, $pageLocation, $pageNewLocation) + { + $location = empty($pageNewLocation) ? "@title" : $pageNewLocation; + $location = preg_replace("/@timestamp/i", $this->getLocationDataNew($rawData, "published", true, "U"), $location); + $location = preg_replace("/@date/i", $this->getLocationDataNew($rawData, "published", true, "Y-m-d"), $location); + $location = preg_replace("/@year/i", $this->getLocationDataNew($rawData, "published", true, "Y"), $location); + $location = preg_replace("/@month/i", $this->getLocationDataNew($rawData, "published", true, "m"), $location); + $location = preg_replace("/@day/i", $this->getLocationDataNew($rawData, "published", true, "d"), $location); + $location = preg_replace("/@tag/i", $this->getLocationDataNew($rawData, "tag", true), $location); + $location = preg_replace("/@author/i", $this->getLocationDataNew($rawData, "author", true), $location); + $location = preg_replace("/@title/i", $this->getLocationDataNew($rawData, "title"), $location); + if(!preg_match("/^\//", $location)) + { + $location = $this->yellow->lookup->getDirectoryLocation($pageLocation).$location; + } + return $location; + } + + // Return location data for new/modified page + function getLocationDataNew($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 title for next page + 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; + } + + // Create browser cookie + function createCookie($scheme, $address, $base, $email) + { + $session = $this->plugin->users->createSession($email); + setcookie("login", "$email,$session", time()+60*60*24*365, "$base/", "", $scheme=="https"); + } + + // Destroy browser cookie + function destroyCookie($scheme, $address, $base) + { + setcookie("login", "", time()-60*60, "$base/", "", $scheme=="https"); + } + + // Edit 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); + } + } + } + + // Send mail to user + function sendMail($scheme, $address, $base, $email, $action) + { + if($action=="welcome" || $action=="information") + { + $url = "$scheme://$address$base/"; + } else { + $expire = time()+60*60*24; + $id = $this->plugin->users->createRequestId($email, $action, $expire); + $url = "$scheme://$address$base"."/action:$action/email:$email/expire:$expire/id:$id/"; + } + 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 = 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 .= mb_encode_mimeheader("X-Remote-Addr: $_SERVER[REMOTE_ADDR]")."\r\n"; + $mailHeaders .= "Mime-Version: 1.0\r\n"; + $mailHeaders .= "Content-Type: text/plain; charset=utf-8\r\n"; + $mailMessage = "$message\r\n\r\n$url\r\n-- \r\n$sitename"; + return mail($mailTo, $mailSubject, $mailMessage, $mailHeaders); + } + + // 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"); + } +} + +class YellowUsers +{ + var $yellow; //access to API + var $users; //registered users + + 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, $modified, $pending, $home) = explode(',', $matches[2]); + $this->set($matches[1], $hash, $name, $language, $status, $modified, $pending, $home); + if(defined("DEBUG") && DEBUG>=3) echo "YellowUsers::load email:$matches[1]<br/>\n"; + } + } + } + + // Clean users in file + function clean($fileName) + { + $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])) + { + list($hash, $name, $language, $status, $modified, $pending, $home) = explode(',', $matches[2]); + if($status=="active" || $status=="inactive") + { + $pending = "none"; + $fileDataNew .= "$matches[1]: $hash,$name,$language,$status,$modified,$pending,$home\n"; + } + } else { + $fileDataNew .= $line; + } + } + return $this->yellow->toolbox->createFile($fileName, $fileDataNew); + } + + // Update users in file + function update($fileName, $email, $password = "", $name = "", $language = "", $status = "", $modified = "", $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); + $modified = strreplaceu(',', '-', empty($modified) ? time() : $modified); + $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) ? $this->yellow->config->get("editUserStatus") : $status); + $modified = strreplaceu(',', '-', empty($modified) ? time() : $modified); + $pending = strreplaceu(',', '-', empty($pending) ? "none" : $pending); + $home = strreplaceu(',', '-', empty($home) ? $this->yellow->config->get("editUserHome") : $home); + } + $this->set($email, $hash, $name, $language, $status, $modified, $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,$modified,$pending,$home\n"; + $found = true; + } else { + $fileDataNew .= $line; + } + } + if(!$found) $fileDataNew .= "$email: $hash,$name,$language,$status,$modified,$pending,$home\n"; + return $this->yellow->toolbox->createFile($fileName, $fileDataNew); + } + + // Set user data + function set($email, $hash, $name, $language, $status, $modified, $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]["modified"] = $modified; + $this->users[$email]["pending"] = $pending; + $this->users[$email]["home"] = $home; + } + + // Check user login from email and password + function checkUser($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 login from email and session + function checkCookie($email, $session) + { + return $this->isExisting($email) && $this->users[$email]["status"]=="active" && + $this->yellow->toolbox->verifyHash($this->users[$email]["hash"], "sha256", $session); + } + + // Create session + function createSession($email) + { + if($this->isExisting($email)) + { + $session = $this->yellow->toolbox->createHash($this->users[$email]["hash"], "sha256"); + if(empty($session)) $session = "error-hash-algorithm-sha256"; + } + return $session; + } + + // 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 request ID for action + function createRequestId($email, $action, $expire) + { + return $this->yellow->toolbox->createHash($this->users[$email]["hash"].$action.$expire, "sha256"); + } + + // Return response status for action + function getResponseStatus($email, $action, $expire, $id) + { + $status = "done"; + switch($action) + { + case "confirm": $statusExpected = "unconfirmed"; break; + case "reconfirm": $statusExpected = "unconfirmed"; break; + case "approve": $statusExpected = "unapproved"; break; + default: $statusExpected = "active"; break; + } + if($this->isExisting($email) && $this->users[$email]["status"]==$statusExpected && + $this->yellow->toolbox->verifyHash($this->users[$email]["hash"].$action.$expire, "sha256", $id)) + { + $status = "ok"; + } + if($expire<=time()) $status = "expired"; + return $status; + } + + // 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"] : ""; + } + + // 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 modified + function getModified($email = "") + { + return $this->isExisting($email) ? $this->users[$email]["modified"] : ""; + } + + // 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 data + function getData() + { + $data = array(); + foreach($this->users as $key=>$value) + { + $name = $value["name"]; if(preg_match("/\s/", $name)) $name = "\"$name\""; + $language = $value["language"]; if(preg_match("/\s/", $language)) $language = "\"$language\""; + $status = $value["status"]; if(preg_match("/\s/", $status)) $status = "\"$status\""; + $data[$key] = "$value[email] - $name $language $status"; + if($value["home"]!="/") $data[$key] .= " restrictions"; + } + 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]); + } +} + +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; + } +} + +$yellow->plugins->register("edit", "YellowEdit", YellowEdit::VERSION); +?> diff --git a/system/plugins/language-de.txt b/system/plugins/language-de.txt @@ -3,7 +3,7 @@ Language: de LanguageDescription: Deutsch LanguageTranslator: David Fehrmann -LanguageVersion: 0.6.16 +LanguageVersion: 0.7.1 BlogBy: von BlogFilter: Blog: @@ -25,6 +25,84 @@ DateFormatShort: F Y DateFormatMedium: d.m.Y DateFormatLong: d.m.Y H:i DateFormatTime: H:i +EditInstallationTitle: Hallo +EditInstallationFeature: Was willst du machen? +EditInstallationHomePage: Deine Webseite funktioniert!\n\nDu kannst [edit - diese Seite bearbeiten] oder einen Texteditor benutzen. +EditInstallationAboutPage: Diese Webseite ist erstellt mit [yellow]. [Weitere Informationen](https://developers.datenstrom.se/help/help-de). +EditLoginTitle: Willkommen +EditLoginEmail: E-Mail: +EditLoginPassword: Kennwort: +EditLoginRecover: Kennwort vergessen? +EditLoginSignup: Benutzerkonto erstellen? +EditLoginButton: Anmelden +EditSignupTitle: Benutzerkonto erstellen +EditSignupName: Name: +EditSignupEmail: E-Mail: +EditSignupPassword: Kennwort: +EditSignupButton: Erstellen +EditSignupStatusNone: Hier kannst du ein neues Benutzerkonto erstellen. +EditSignupStatusIncomplete: Bitte alle Felder ausfüllen. +EditSignupStatusInvalid: Bitte eine gültige E-Mail angeben. +EditSignupStatusWeak: Bitte ein anderes Kennwort angeben. +EditSignupStatusNext: Benutzerkonto wird erstellt, bitte überprüfe deine E-Mails. +EditRecoverTitle: Kennwort vergessen +EditRecoverEmail: E-Mail: +EditRecoverPassword: Kennwort: +EditRecoverStatusNone: Kein Problem, du kannst ein neues Kennwort erstellen. +EditRecoverStatusInvalid: Bitte eine gültige E-Mail angeben. +EditRecoverStatusPassword: Bitte ein neues Kennwort angeben. +EditRecoverStatusWeak: Bitte ein anderes Kennwort angeben. +EditRecoverStatusNext: Benutzerkonto wird wiederhergestellt, bitte überprüfe deine E-Mails. +EditSettingsTitle: Einstellungen +EditSettingsStatusInvalid: Bitte eine gültige E-Mail angeben. +EditSettingsStatusTaken: Bitte eine andere E-Mail angeben. +EditSettingsStatusWeak: Bitte ein anderes Kennwort angeben. +EditSettingsStatusNext: Benutzerkonto wird geändert, bitte überprüfe deine E-Mails. +EditConfirmSubject: Benutzerkonto bestätigen +EditConfirmMessage: Hallo @usershort, bitte bestätige dein Benutzerkonto. Klicke auf den folgenden Link. +EditConfirmStatusDone: Benutzerkonto wurde bestätigt und wartet auf Genehmigung. Vielen Dank! +EditConfirmStatusExpired: Benutzerkonto kann nicht bestätigt werden. Link ist abgelaufen! +EditApproveSubject: Benutzerkonto genehmigen +EditApproveMessage: Hallo @usershort, bitte genehmige ein neues Benutzerkonto für @useraccount. Klicke auf den folgenden Link. +EditApproveStatusDone: Benutzerkonto wurde genehmigt. Vielen Dank! +EditApproveStatusExpired: Benutzerkonto kann nicht genehmigt werden. Link ist abgelaufen! +EditRecoverSubject: Benutzerkonto wiederherstellen +EditRecoverMessage: Hallo @usershort, bitte bestätige, dass du dein Kennwort vergessen hast. Klicke auf den folgenden Link. +EditRecoverStatusDone: Benutzerkonto wurde wiederhergestellt. Vielen Dank! +EditRecoverStatusExpired: Benutzerkonto kann nicht wiederhergestellt werden. Link ist abgelaufen! +EditReconfirmSubject: Benutzerkonto ändern +EditReconfirmMessage: Hallo @usershort, bitte bestätige eine neue E-Mail für dein Benutzerkonto. Klicke auf den folgenden Link. +EditReconfirmStatusDone: Benutzerkonto wurde bestätigt. Vielen Dank! +EditReconfirmStatusExpired: Benutzerkonto kann nicht bestätigt werden. Link ist abgelaufen! +EditChangeSubject: Benutzerkonto ändern +EditChangeMessage: Hallo @usershort, bitte bestätige, dass du dein Benutzerkonto ändern möchtest. Klicke auf den folgenden Link. +EditChangeStatusDone: Benutzerkonto wurde geändert. Vielen Dank! +EditChangeStatusExpired: Benutzerkonto kann nicht geändert werden. Link ist abgelaufen! +EditWelcomeSubject: Willkommen +EditWelcomeMessage: Hallo @usershort, dein Benutzerkonto wurde erstellt. Viel Spass beim Bearbeiten der Webseite. +EditInformationSubject: Willkommen zurück +EditInformationMessage: Hallo @usershort, dein Benutzerkonto wurde geändert. Du kannst dich jetzt anmelden. +EditVersionTitle: Über diese Webseite +EditVersionStatusNone: Yellow ist für Menschen die Webseiten machen. +EditVersionStatusCheck: Nach Aktualisierung suchen… +EditVersionStatusDone: Deine Webseite ist auf dem neusten Stand. +EditVersionStatusUpdates: Aktualisierung verfügbar, bitte an den Webmaster wenden. +EditVersionUpdateNormal: Aktualisierung verfügbar, jetzt aktualisieren. +EditVersionUpdateForce: Aktualisierung erzwingen +EditVersionUpdateModified: @software wurde modifiziert +EditOkButton: Ok +EditCancelButton: Abbruch +EditCreateButton: Erzeugen +EditEditButton: Ändern +EditDeleteButton: Löschen +EditEdit: Seite ändern +EditCreate: + +EditDelete: - +EditCreateTitle: Seite erzeugen +EditDeleteTitle: Seite löschen +EditMarkdownHelp: Markdown +EditUserHelp: Hilfe +EditUserLogout: Abmelden PagePrevious: ← Zurück: @title PageNext: Weiter: @title → PaginationPrevious: ← Zurück @@ -34,84 +112,6 @@ SearchResultsNone: Bitte einen Suchbegriff eingeben. SearchResultsEmpty: Keine Treffer für diese Suchanfrage. SearchSpecialChanges: Letzte Änderungen SearchButton: Suchen -WebinterfaceInstallationTitle: Hallo -WebinterfaceInstallationFeature: Was willst du machen? -WebinterfaceInstallationHomePage: Deine Webseite funktioniert!\n\nDu kannst [edit - diese Seite bearbeiten] oder einen Texteditor benutzen. -WebinterfaceInstallationAboutPage: Diese Webseite ist erstellt mit [yellow]. [Weitere Informationen](https://developers.datenstrom.se/help/help-de). -WebinterfaceLoginTitle: Willkommen -WebinterfaceLoginEmail: E-Mail: -WebinterfaceLoginPassword: Kennwort: -WebinterfaceLoginRecover: Kennwort vergessen? -WebinterfaceLoginSignup: Benutzerkonto erstellen? -WebinterfaceLoginButton: Anmelden -WebinterfaceSignupTitle: Benutzerkonto erstellen -WebinterfaceSignupName: Name: -WebinterfaceSignupEmail: E-Mail: -WebinterfaceSignupPassword: Kennwort: -WebinterfaceSignupButton: Erstellen -WebinterfaceSignupStatusNone: Hier kannst du ein neues Benutzerkonto erstellen. -WebinterfaceSignupStatusIncomplete: Bitte alle Felder ausfüllen. -WebinterfaceSignupStatusInvalid: Bitte eine gültige E-Mail angeben. -WebinterfaceSignupStatusWeak: Bitte ein anderes Kennwort angeben. -WebinterfaceSignupStatusNext: Benutzerkonto wird erstellt, bitte überprüfe deine E-Mails. -WebinterfaceRecoverTitle: Kennwort vergessen -WebinterfaceRecoverEmail: E-Mail: -WebinterfaceRecoverPassword: Kennwort: -WebinterfaceRecoverStatusNone: Kein Problem, du kannst ein neues Kennwort erstellen. -WebinterfaceRecoverStatusInvalid: Bitte eine gültige E-Mail angeben. -WebinterfaceRecoverStatusPassword: Bitte ein neues Kennwort angeben. -WebinterfaceRecoverStatusWeak: Bitte ein anderes Kennwort angeben. -WebinterfaceRecoverStatusNext: Benutzerkonto wird wiederhergestellt, bitte überprüfe deine E-Mails. -WebinterfaceSettingsTitle: Einstellungen -WebinterfaceSettingsStatusInvalid: Bitte eine gültige E-Mail angeben. -WebinterfaceSettingsStatusTaken: Bitte eine andere E-Mail angeben. -WebinterfaceSettingsStatusWeak: Bitte ein anderes Kennwort angeben. -WebinterfaceSettingsStatusNext: Benutzerkonto wird geändert, bitte überprüfe deine E-Mails. -WebinterfaceConfirmSubject: Benutzerkonto bestätigen -WebinterfaceConfirmMessage: Hallo @usershort, bitte bestätige dein Benutzerkonto. Klicke auf den folgenden Link. -WebinterfaceConfirmStatusDone: Benutzerkonto wurde bestätigt und wartet auf Genehmigung. Vielen Dank! -WebinterfaceConfirmStatusExpired: Benutzerkonto kann nicht bestätigt werden. Link ist abgelaufen! -WebinterfaceApproveSubject: Benutzerkonto genehmigen -WebinterfaceApproveMessage: Hallo @usershort, bitte genehmige ein neues Benutzerkonto für @useraccount. Klicke auf den folgenden Link. -WebinterfaceApproveStatusDone: Benutzerkonto wurde genehmigt. Vielen Dank! -WebinterfaceApproveStatusExpired: Benutzerkonto kann nicht genehmigt werden. Link ist abgelaufen! -WebinterfaceRecoverSubject: Benutzerkonto wiederherstellen -WebinterfaceRecoverMessage: Hallo @usershort, bitte bestätige, dass du dein Kennwort vergessen hast. Klicke auf den folgenden Link. -WebinterfaceRecoverStatusDone: Benutzerkonto wurde wiederhergestellt. Vielen Dank! -WebinterfaceRecoverStatusExpired: Benutzerkonto kann nicht wiederhergestellt werden. Link ist abgelaufen! -WebinterfaceReconfirmSubject: Benutzerkonto ändern -WebinterfaceReconfirmMessage: Hallo @usershort, bitte bestätige eine neue E-Mail für dein Benutzerkonto. Klicke auf den folgenden Link. -WebinterfaceReconfirmStatusDone: Benutzerkonto wurde bestätigt. Vielen Dank! -WebinterfaceReconfirmStatusExpired: Benutzerkonto kann nicht bestätigt werden. Link ist abgelaufen! -WebinterfaceChangeSubject: Benutzerkonto ändern -WebinterfaceChangeMessage: Hallo @usershort, bitte bestätige, dass du dein Benutzerkonto ändern möchtest. Klicke auf den folgenden Link. -WebinterfaceChangeStatusDone: Benutzerkonto wurde geändert. Vielen Dank! -WebinterfaceChangeStatusExpired: Benutzerkonto kann nicht geändert werden. Link ist abgelaufen! -WebinterfaceWelcomeSubject: Willkommen -WebinterfaceWelcomeMessage: Hallo @usershort, dein Benutzerkonto wurde erstellt. Viel Spass beim Bearbeiten der Webseite. -WebinterfaceInformationSubject: Willkommen zurück -WebinterfaceInformationMessage: Hallo @usershort, dein Benutzerkonto wurde geändert. Du kannst dich jetzt anmelden. -WebinterfaceVersionTitle: Über diese Webseite -WebinterfaceVersionStatusNone: Yellow ist für Menschen die Webseiten machen. -WebinterfaceVersionStatusCheck: Nach Aktualisierung suchen… -WebinterfaceVersionStatusDone: Deine Webseite ist auf dem neusten Stand. -WebinterfaceVersionStatusUpdates: Aktualisierung verfügbar, bitte an den Webmaster wenden. -WebinterfaceVersionUpdateNormal: Aktualisierung verfügbar, jetzt aktualisieren. -WebinterfaceVersionUpdateForce: Aktualisierung erzwingen -WebinterfaceVersionUpdateModified: @software wurde modifiziert -WebinterfaceOkButton: Ok -WebinterfaceCancelButton: Abbruch -WebinterfaceCreateButton: Erzeugen -WebinterfaceEditButton: Ändern -WebinterfaceDeleteButton: Löschen -WebinterfaceEdit: Seite ändern -WebinterfaceCreate: + -WebinterfaceDelete: - -WebinterfaceCreateTitle: Seite erzeugen -WebinterfaceDeleteTitle: Seite löschen -WebinterfaceMarkdownHelp: Markdown -WebinterfaceUserHelp: Hilfe -WebinterfaceUserLogout: Abmelden WikiFilter: Wiki: WikiTag: Tags: WikiSpecialPages: Alle Seiten diff --git a/system/plugins/language-en.txt b/system/plugins/language-en.txt @@ -3,7 +3,7 @@ Language: en LanguageDescription: English LanguageTranslator: Mark Seuffert -LanguageVersion: 0.6.16 +LanguageVersion: 0.7.1 BlogBy: by BlogFilter: Blog: @@ -25,6 +25,84 @@ DateFormatShort: F Y DateFormatMedium: Y-m-d DateFormatLong: Y-m-d H:i DateFormatTime: H:i +EditInstallationTitle: Hello +EditInstallationFeature: What do you want to make? +EditInstallationHomePage: Your website works!\n\nYou can [edit this page] or use your text editor. +EditInstallationAboutPage: This website is made with [yellow]. [Learn more](https://developers.datenstrom.se/help/). +EditLoginTitle: Welcome +EditLoginEmail: Email: +EditLoginPassword: Password: +EditLoginRecover: Forgot your password? +EditLoginSignup: Create user account? +EditLoginButton: Log in +EditSignupTitle: Create user account +EditSignupName: Name: +EditSignupEmail: Email: +EditSignupPassword: Password: +EditSignupButton: Create +EditSignupStatusNone: Here you can create a new user account. +EditSignupStatusIncomplete: Please fill out all fields. +EditSignupStatusInvalid: Please enter a valid email. +EditSignupStatusWeak: Please enter a different password. +EditSignupStatusNext: User account will be created, please check your emails. +EditRecoverTitle: Forgot your password +EditRecoverEmail: Email: +EditRecoverPassword: Password: +EditRecoverStatusNone: No problem, you can create a new password. +EditRecoverStatusInvalid: Please enter a valid email. +EditRecoverStatusPassword: Please enter a new password. +EditRecoverStatusWeak: Please enter a different password. +EditRecoverStatusNext: User account will be recovered, please check your emails. +EditSettingsTitle: Settings +EditSettingsStatusInvalid: Please enter a valid email. +EditSettingsStatusTaken: Please enter a different email. +EditSettingsStatusWeak: Please enter a different password. +EditSettingsStatusNext: User account will be changed, please check your emails. +EditConfirmSubject: Confirm user account +EditConfirmMessage: Hi @usershort, please confirm your user account. Click the following link. +EditConfirmStatusDone: User account confirmed and waiting for approval. Thank you! +EditConfirmStatusExpired: User account can not be confirmed. Link has expired! +EditApproveSubject: Approve user account +EditApproveMessage: Hi @usershort, please approve a new user account for @useraccount. Click the following link. +EditApproveStatusDone: User account approved. Thank you! +EditApproveStatusExpired: User account can not be approved. Link has expired! +EditRecoverSubject: Recover user account +EditRecoverMessage: Hi @usershort, please confirm that you forgot your password. Click the following link. +EditRecoverStatusDone: User account recovered. Thank you! +EditRecoverStatusExpired: User account can not be recovered. Link has expired! +EditReconfirmSubject: Change user account +EditReconfirmMessage: Hi @usershort, please confirm a new email for your user account. Click the following link. +EditReconfirmStatusDone: User account confirmed. Thank you! +EditReconfirmStatusExpired: User account can not be confirmed. Link has expired! +EditChangeSubject: Change user account +EditChangeMessage: Hi @usershort, please confirm that you want to change your user account. Click the following link. +EditChangeStatusDone: User account changed. Thank you! +EditChangeStatusExpired: User account can not be changed. Link has expired! +EditWelcomeSubject: Welcome +EditWelcomeMessage: Hi @usershort, your user account has been created. Have fun editing the website. +EditInformationSubject: Welcome back +EditInformationMessage: Hi @usershort, your user account has been changed. You can now log in. +EditVersionTitle: About this website +EditVersionStatusNone: Yellow is for people who make websites. +EditVersionStatusCheck: Checking for updates… +EditVersionStatusDone: Your website is up to date. +EditVersionStatusUpdates: Updates available, please contact the webmaster. +EditVersionUpdateNormal: Updates available, update now. +EditVersionUpdateForce: Force update +EditVersionUpdateModified: @software has been modified +EditOkButton: Ok +EditCancelButton: Cancel +EditCreateButton: Create +EditEditButton: Save +EditDeleteButton: Delete +EditEdit: Edit page +EditCreate: + +EditDelete: - +EditCreateTitle: Create page +EditDeleteTitle: Delete page +EditMarkdownHelp: Markdown +EditUserHelp: Help +EditUserLogout: Logout PagePrevious: ← Previous: @title PageNext: Next: @title → PaginationPrevious: ← Previous @@ -34,84 +112,6 @@ SearchResultsNone: Enter a search term. SearchResultsEmpty: No results found. SearchSpecialChanges: Recent changes SearchButton: Search -WebinterfaceInstallationTitle: Hello -WebinterfaceInstallationFeature: What do you want to make? -WebinterfaceInstallationHomePage: Your website works!\n\nYou can [edit this page] or use your text editor. -WebinterfaceInstallationAboutPage: This website is made with [yellow]. [Learn more](https://developers.datenstrom.se/help/). -WebinterfaceLoginTitle: Welcome -WebinterfaceLoginEmail: Email: -WebinterfaceLoginPassword: Password: -WebinterfaceLoginRecover: Forgot your password? -WebinterfaceLoginSignup: Create user account? -WebinterfaceLoginButton: Log in -WebinterfaceSignupTitle: Create user account -WebinterfaceSignupName: Name: -WebinterfaceSignupEmail: Email: -WebinterfaceSignupPassword: Password: -WebinterfaceSignupButton: Create -WebinterfaceSignupStatusNone: Here you can create a new user account. -WebinterfaceSignupStatusIncomplete: Please fill out all fields. -WebinterfaceSignupStatusInvalid: Please enter a valid email. -WebinterfaceSignupStatusWeak: Please enter a different password. -WebinterfaceSignupStatusNext: User account will be created, please check your emails. -WebinterfaceRecoverTitle: Forgot your password -WebinterfaceRecoverEmail: Email: -WebinterfaceRecoverPassword: Password: -WebinterfaceRecoverStatusNone: No problem, you can create a new password. -WebinterfaceRecoverStatusInvalid: Please enter a valid email. -WebinterfaceRecoverStatusPassword: Please enter a new password. -WebinterfaceRecoverStatusWeak: Please enter a different password. -WebinterfaceRecoverStatusNext: User account will be recovered, please check your emails. -WebinterfaceSettingsTitle: Settings -WebinterfaceSettingsStatusInvalid: Please enter a valid email. -WebinterfaceSettingsStatusTaken: Please enter a different email. -WebinterfaceSettingsStatusWeak: Please enter a different password. -WebinterfaceSettingsStatusNext: User account will be changed, please check your emails. -WebinterfaceConfirmSubject: Confirm user account -WebinterfaceConfirmMessage: Hi @usershort, please confirm your user account. Click the following link. -WebinterfaceConfirmStatusDone: User account confirmed and waiting for approval. Thank you! -WebinterfaceConfirmStatusExpired: User account can not be confirmed. Link has expired! -WebinterfaceApproveSubject: Approve user account -WebinterfaceApproveMessage: Hi @usershort, please approve a new user account for @useraccount. Click the following link. -WebinterfaceApproveStatusDone: User account approved. Thank you! -WebinterfaceApproveStatusExpired: User account can not be approved. Link has expired! -WebinterfaceRecoverSubject: Recover user account -WebinterfaceRecoverMessage: Hi @usershort, please confirm that you forgot your password. Click the following link. -WebinterfaceRecoverStatusDone: User account recovered. Thank you! -WebinterfaceRecoverStatusExpired: User account can not be recovered. Link has expired! -WebinterfaceReconfirmSubject: Change user account -WebinterfaceReconfirmMessage: Hi @usershort, please confirm a new email for your user account. Click the following link. -WebinterfaceReconfirmStatusDone: User account confirmed. Thank you! -WebinterfaceReconfirmStatusExpired: User account can not be confirmed. Link has expired! -WebinterfaceChangeSubject: Change user account -WebinterfaceChangeMessage: Hi @usershort, please confirm that you want to change your user account. Click the following link. -WebinterfaceChangeStatusDone: User account changed. Thank you! -WebinterfaceChangeStatusExpired: User account can not be changed. Link has expired! -WebinterfaceWelcomeSubject: Welcome -WebinterfaceWelcomeMessage: Hi @usershort, your user account has been created. Have fun editing the website. -WebinterfaceInformationSubject: Welcome back -WebinterfaceInformationMessage: Hi @usershort, your user account has been changed. You can now log in. -WebinterfaceVersionTitle: About this website -WebinterfaceVersionStatusNone: Yellow is for people who make websites. -WebinterfaceVersionStatusCheck: Checking for updates… -WebinterfaceVersionStatusDone: Your website is up to date. -WebinterfaceVersionStatusUpdates: Updates available, please contact the webmaster. -WebinterfaceVersionUpdateNormal: Updates available, update now. -WebinterfaceVersionUpdateForce: Force update -WebinterfaceVersionUpdateModified: @software has been modified -WebinterfaceOkButton: Ok -WebinterfaceCancelButton: Cancel -WebinterfaceCreateButton: Create -WebinterfaceEditButton: Save -WebinterfaceDeleteButton: Delete -WebinterfaceEdit: Edit page -WebinterfaceCreate: + -WebinterfaceDelete: - -WebinterfaceCreateTitle: Create page -WebinterfaceDeleteTitle: Delete page -WebinterfaceMarkdownHelp: Markdown -WebinterfaceUserHelp: Help -WebinterfaceUserLogout: Logout WikiFilter: Wiki: WikiTag: Tags: WikiSpecialPages: All pages diff --git a/system/plugins/language-fr.txt b/system/plugins/language-fr.txt @@ -3,7 +3,7 @@ Language: fr LanguageDescription: Français LanguageTranslator: Juh Nibreh -LanguageVersion: 0.6.16 +LanguageVersion: 0.7.1 BlogBy: par BlogFilter: Blog: @@ -25,6 +25,84 @@ DateFormatShort: F Y DateFormatMedium: d/m/Y DateFormatLong: d/m/Y H:i DateFormatTime: H:i +EditInstallationTitle: Bonjour +EditInstallationFeature: Que voulez-vous faire? +EditInstallationHomePage: Votre site web fonctionne!\n\nVous pouvez [edit - modifier cette page] ou utiliser un éditeur de texte. +EditInstallationAboutPage: Ce site web est fait avec [yellow]. [Apprenez-en plus](https://developers.datenstrom.se/help/help-fr). +EditLoginTitle: Bienvenue +EditLoginEmail: Email: +EditLoginPassword: Mot de passe: +EditLoginRecover: Mot de passe oublié? +EditLoginSignup: Créer un compte utilisateur? +EditLoginButton: Se connecter +EditSignupTitle: Créer un compte utilisateur +EditSignupName: Nom: +EditSignupEmail: Email: +EditSignupPassword: Mot de passe: +EditSignupButton: Créer +EditSignupStatusNone: Ici, vous pouvez créer un nouveau compte utilisateur. +EditSignupStatusIncomplete: Veuillez remplir tous les champs. +EditSignupStatusInvalid: S'il vous plaît, veuillez entrer une adresse email valide. +EditSignupStatusWeak: S'il vous plaît, choisissez un mot de passe différent. +EditSignupStatusNext: Votre compte a été créé, vérifiez vos emails. +EditRecoverTitle: Mot de passe oublié +EditRecoverEmail: Email: +EditRecoverPassword: Mot de passe: +EditRecoverStatusNone: Pas de problème, vous pouvez créer un nouveau mot de passe. +EditRecoverStatusInvalid: S'il vous plaît, veuillez entrer une adresse email valide. +EditRecoverStatusPassword: S'il vous plaît, choisissez un nouveau mot de passe. +EditRecoverStatusWeak: S'il vous plaît, choisissez un mot de passe différent. +EditRecoverStatusNext: Votre compte est à nouveau disponible, vérifiez vos emails. +EditSettingsTitle: Paramètres +EditSettingsStatusInvalid: S'il vous plaît, veuillez entrer une adresse email valide. +EditSettingsStatusTaken: S'il vous plaît, veuillez entrer une adresse email différent. +EditSettingsStatusWeak: S'il vous plaît, choisissez un mot de passe différent. +EditSettingsStatusNext: Votre compte a été changé, vérifiez vos emails. +EditConfirmSubject: Confirmation d'un compte utilisateur +EditConfirmMessage: Bonjour @usershort, veuillez confirmer votre compte utilisateur. Cliquez sur le lien suivant. +EditConfirmStatusDone: Votre compte utilisateur est confirmé et en attente d'approbation. Merci! +EditConfirmStatusExpired: Le compte ne peut pas être créé. Le lien de confirmation a expiré! +EditApproveSubject: Approuver un nouvel utilisateur +EditApproveMessage: Bonjour @usershort, veuillez approuver la création d'un nouveau compte utilisateur pour @useraccount. Cliquez sur le lien suivant. +EditApproveStatusDone: Compte utilisateur approuvé. Merci! +EditApproveStatusExpired: Le compte ne peut pas être approuvé. Le lien de confirmation a expiré! +EditRecoverSubject: Restauration d'un compte utilisateur +EditRecoverMessage: Bonjour @usershort, veuillez confirmer que vous avez oublié votre mot de passe. Cliquez sur le lien suivant. +EditRecoverStatusDone: Compte utilisateur restauré. Merci! +EditRecoverStatusExpired: Le compte ne peut pas être restauré. Le lien de confirmation a expiré! +EditReconfirmSubject: Changement d'un compte utilisateur +EditReconfirmMessage: Bonjour @usershort, veuillez confirmer une nouvelle adresse email pour votre compte utilisateur. Cliquez sur le lien suivant. +EditReconfirmStatusDone: Votre compte utilisateur est confirmé. Merci! +EditReconfirmStatusExpired: Le compte ne peut pas être créé. Le lien de confirmation a expiré! +EditChangeSubject: Changement d'un compte utilisateur +EditChangeMessage: Bonjour @usershort, veuillez confirmer que vous souhaitez modifier votre compte utilisateur. Cliquez sur le lien suivant. +EditChangeStatusDone: Compte utilisateur changé. Merci! +EditChangeStatusExpired: Le compte ne peut pas être changé. Le lien de confirmation a expiré! +EditWelcomeSubject: Bienvenue +EditWelcomeMessage: Bonjour @usershort, votre compte utilisateur a bien été créé. Amusez-vous bien en éditant le site web. +EditInformationSubject: Bienvenue à nouveau +EditInformationMessage: Bonjour @usershort, votre compte utilisateur a bien été changé. Vous pouvez maintenant vous connecter. +EditVersionTitle: A propos de ce site +EditVersionStatusNone: Yellow est fait pour les gens qui font des sites web. +EditVersionStatusCheck: Vérification des mises à jour… +EditVersionStatusDone: Votre site est à jour. +EditVersionStatusUpdates: Mises à jour disponibles, s'il vous plaît contacter le webmestre. +EditVersionUpdateNormal: Mises à jour disponibles, mettre à jour maintenant. +EditVersionUpdateForce: Forcer mise à jour +EditVersionUpdateModified: @software a été modifié +EditOkButton: Ok +EditCancelButton: Annuler +EditCreateButton: Créer +EditEditButton: Sauvegarder +EditDeleteButton: Supprimer +EditEdit: Éditer page +EditCreate: + +EditDelete: - +EditCreateTitle: Créer page +EditDeleteTitle: Supprimer page +EditMarkdownHelp: Markdown +EditUserHelp: Aide +EditUserLogout: Déconnexion PagePrevious: ← Précédent: @title PageNext: Suivant: @title → PaginationPrevious: ← Précédent @@ -34,84 +112,6 @@ SearchResultsNone: Entrez un mot dans le champ de recherche. SearchResultsEmpty: Pas de résultats. SearchSpecialChanges: Changements récents SearchButton: Rechercher -WebinterfaceInstallationTitle: Bonjour -WebinterfaceInstallationFeature: Que voulez-vous faire? -WebinterfaceInstallationHomePage: Votre site web fonctionne!\n\nVous pouvez [edit - modifier cette page] ou utiliser un éditeur de texte. -WebinterfaceInstallationAboutPage: Ce site web est fait avec [yellow]. [Apprenez-en plus](https://developers.datenstrom.se/help/help-fr). -WebinterfaceLoginTitle: Bienvenue -WebinterfaceLoginEmail: Email: -WebinterfaceLoginPassword: Mot de passe: -WebinterfaceLoginRecover: Mot de passe oublié? -WebinterfaceLoginSignup: Créer un compte utilisateur? -WebinterfaceLoginButton: Se connecter -WebinterfaceSignupTitle: Créer un compte utilisateur -WebinterfaceSignupName: Nom: -WebinterfaceSignupEmail: Email: -WebinterfaceSignupPassword: Mot de passe: -WebinterfaceSignupButton: Créer -WebinterfaceSignupStatusNone: Ici, vous pouvez créer un nouveau compte utilisateur. -WebinterfaceSignupStatusIncomplete: Veuillez remplir tous les champs. -WebinterfaceSignupStatusInvalid: S'il vous plaît, veuillez entrer une adresse email valide. -WebinterfaceSignupStatusWeak: S'il vous plaît, choisissez un mot de passe différent. -WebinterfaceSignupStatusNext: Votre compte a été créé, vérifiez vos emails. -WebinterfaceRecoverTitle: Mot de passe oublié -WebinterfaceRecoverEmail: Email: -WebinterfaceRecoverPassword: Mot de passe: -WebinterfaceRecoverStatusNone: Pas de problème, vous pouvez créer un nouveau mot de passe. -WebinterfaceRecoverStatusInvalid: S'il vous plaît, veuillez entrer une adresse email valide. -WebinterfaceRecoverStatusPassword: S'il vous plaît, choisissez un nouveau mot de passe. -WebinterfaceRecoverStatusWeak: S'il vous plaît, choisissez un mot de passe différent. -WebinterfaceRecoverStatusNext: Votre compte est à nouveau disponible, vérifiez vos emails. -WebinterfaceSettingsTitle: Paramètres -WebinterfaceSettingsStatusInvalid: S'il vous plaît, veuillez entrer une adresse email valide. -WebinterfaceSettingsStatusTaken: S'il vous plaît, veuillez entrer une adresse email différent. -WebinterfaceSettingsStatusWeak: S'il vous plaît, choisissez un mot de passe différent. -WebinterfaceSettingsStatusNext: Votre compte a été changé, vérifiez vos emails. -WebinterfaceConfirmSubject: Confirmation d'un compte utilisateur -WebinterfaceConfirmMessage: Bonjour @usershort, veuillez confirmer votre compte utilisateur. Cliquez sur le lien suivant. -WebinterfaceConfirmStatusDone: Votre compte utilisateur est confirmé et en attente d'approbation. Merci! -WebinterfaceConfirmStatusExpired: Le compte ne peut pas être créé. Le lien de confirmation a expiré! -WebinterfaceApproveSubject: Approuver un nouvel utilisateur -WebinterfaceApproveMessage: Bonjour @usershort, veuillez approuver la création d'un nouveau compte utilisateur pour @useraccount. Cliquez sur le lien suivant. -WebinterfaceApproveStatusDone: Compte utilisateur approuvé. Merci! -WebinterfaceApproveStatusExpired: Le compte ne peut pas être approuvé. Le lien de confirmation a expiré! -WebinterfaceRecoverSubject: Restauration d'un compte utilisateur -WebinterfaceRecoverMessage: Bonjour @usershort, veuillez confirmer que vous avez oublié votre mot de passe. Cliquez sur le lien suivant. -WebinterfaceRecoverStatusDone: Compte utilisateur restauré. Merci! -WebinterfaceRecoverStatusExpired: Le compte ne peut pas être restauré. Le lien de confirmation a expiré! -WebinterfaceReconfirmSubject: Changement d'un compte utilisateur -WebinterfaceReconfirmMessage: Bonjour @usershort, veuillez confirmer une nouvelle adresse email pour votre compte utilisateur. Cliquez sur le lien suivant. -WebinterfaceReconfirmStatusDone: Votre compte utilisateur est confirmé. Merci! -WebinterfaceReconfirmStatusExpired: Le compte ne peut pas être créé. Le lien de confirmation a expiré! -WebinterfaceChangeSubject: Changement d'un compte utilisateur -WebinterfaceChangeMessage: Bonjour @usershort, veuillez confirmer que vous souhaitez modifier votre compte utilisateur. Cliquez sur le lien suivant. -WebinterfaceChangeStatusDone: Compte utilisateur changé. Merci! -WebinterfaceChangeStatusExpired: Le compte ne peut pas être changé. Le lien de confirmation a expiré! -WebinterfaceWelcomeSubject: Bienvenue -WebinterfaceWelcomeMessage: Bonjour @usershort, votre compte utilisateur a bien été créé. Amusez-vous bien en éditant le site web. -WebinterfaceInformationSubject: Bienvenue à nouveau -WebinterfaceInformationMessage: Bonjour @usershort, votre compte utilisateur a bien été changé. Vous pouvez maintenant vous connecter. -WebinterfaceVersionTitle: A propos de ce site -WebinterfaceVersionStatusNone: Yellow est fait pour les gens qui font des sites web. -WebinterfaceVersionStatusCheck: Vérification des mises à jour… -WebinterfaceVersionStatusDone: Votre site est à jour. -WebinterfaceVersionStatusUpdates: Mises à jour disponibles, s'il vous plaît contacter le webmestre. -WebinterfaceVersionUpdateNormal: Mises à jour disponibles, mettre à jour maintenant. -WebinterfaceVersionUpdateForce: Forcer mise à jour -WebinterfaceVersionUpdateModified: @software a été modifié -WebinterfaceOkButton: Ok -WebinterfaceCancelButton: Annuler -WebinterfaceCreateButton: Créer -WebinterfaceEditButton: Sauvegarder -WebinterfaceDeleteButton: Supprimer -WebinterfaceEdit: Éditer page -WebinterfaceCreate: + -WebinterfaceDelete: - -WebinterfaceCreateTitle: Créer page -WebinterfaceDeleteTitle: Supprimer page -WebinterfaceMarkdownHelp: Markdown -WebinterfaceUserHelp: Aide -WebinterfaceUserLogout: Déconnexion WikiFilter: Wiki: WikiTag: Tags: WikiSpecialPages: Toutes les pages diff --git a/system/plugins/language.php b/system/plugins/language.php @@ -5,7 +5,7 @@ class YellowLanguage { - const VERSION = "0.6.16"; + const VERSION = "0.7.1"; } $yellow->plugins->register("language", "YellowLanguage", YellowLanguage::VERSION); diff --git a/system/plugins/update.php b/system/plugins/update.php @@ -5,7 +5,7 @@ class YellowUpdate { - const VERSION = "0.6.15"; + const VERSION = "0.7.1"; var $yellow; //access to API var $updates; //number of updates @@ -23,35 +23,80 @@ class YellowUpdate // Handle startup function onStartup($update) { - if($this->yellow->config->isExisting("updateNotification")) //TODO: remove later, cleans old config + if($update) //TODO: remove later, converts old config file { - $update = true; $fileNameConfig = $this->yellow->config->get("configDir").$this->yellow->config->get("configFile"); $fileData = $this->yellow->toolbox->readFile($fileNameConfig); foreach($this->yellow->toolbox->getTextLines($fileData) as $line) { + $line = preg_replace("/^Webinterface/i", "Edit", $line); preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); - if(!empty($matches[1]) && is_null($this->yellow->config->configDefaults[$matches[1]]) && - $line[0]!='#' && substru($line, 0, 5)!="Login") + if(!empty($matches[1]) && is_null($this->yellow->config->configDefaults[$matches[1]])) { $fileDataNew .= "# $line"; } else { $fileDataNew .= $line; } } - $this->yellow->toolbox->createFile($fileNameConfig, $fileDataNew); + if($fileData!=$fileDataNew) $this->yellow->toolbox->createFile($fileNameConfig, $fileDataNew); } - if($update) //TODO: remove later, converts old Yellow version + if($update) //TODO: remove later, converts old error page { - $fileNameScript = "yellow.php"; - if(filesize($fileNameScript)==591) + $fileName = $this->yellow->config->get("configDir")."page-error-500.txt"; + if(is_file($fileName)) { - $fileData = $this->yellow->toolbox->readFile($fileNameScript); - $fileData = preg_replace("#yellow->plugins->load\(\)#", "yellow->load()", $fileData); - $this->yellow->toolbox->createFile($fileNameScript, $fileData); + $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, imports old file format + 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; + if(empty($this->yellow->toolbox->getMetaData($page->rawData, "template")) && $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); + } + if($update) //TODO: remove later, converts theme files { $path = $this->yellow->config->get("themeDir"); foreach($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.css$/", true, false) as $entry) @@ -460,8 +505,8 @@ class YellowUpdate if($language!="en") { $fileData = strreplaceu("\r\n", "\n", $this->yellow->toolbox->readFile($fileName)); - $rawDataOld = strreplaceu("\\n", "\n", $this->yellow->text->getText("webinterfaceInstallation{$name}Page", "en")); - $rawDataNew = strreplaceu("\\n", "\n", $this->yellow->text->getText("webinterfaceInstallation{$name}Page", $language)); + $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; @@ -522,10 +567,10 @@ class YellowUpdate if($status=="install") { $status = "ok"; - if(!empty($email) && !empty($password) && $this->yellow->plugins->isExisting("webinterface")) + if(!empty($email) && !empty($password) && $this->yellow->plugins->isExisting("edit")) { - $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("webinterfaceUserFile"); - $status = $this->yellow->plugins->get("webinterface")->users->update($fileNameUser, $email, $password, $name, $language) ? "ok" : "error"; + $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("editUserFile"); + $status = $this->yellow->plugins->get("edit")->users->update($fileNameUser, $email, $password, $name, $language) ? "ok" : "error"; if($status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); } } @@ -572,16 +617,16 @@ class YellowUpdate 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("webinterfaceNewFile")); + $fileName = strreplaceu("(.*)", "installation", $this->yellow->config->get("configDir").$this->yellow->config->get("editNewFile")); $rawData = $this->yellow->toolbox->readFile($fileName); if(empty($rawData)) { $this->yellow->text->setLanguage($language); - $rawData = "---\nTitle:".$this->yellow->text->get("webinterfaceInstallationTitle")."\nLanguage:$language\nNavigation:navigation\n---\n"; + $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("webinterfaceSignupName")."</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("webinterfaceSignupEmail")."</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("webinterfaceSignupPassword")."</label><br /><input class=\"form-control\" type=\"password\" maxlength=\"64\" name=\"password\" id=\"password\" value=\"\"></p>\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>"; @@ -594,7 +639,7 @@ class YellowUpdate } if(count($this->getInstallationFeatures())>1) { - $rawData .= "<p>".$this->yellow->text->get("webinterfaceInstallationFeature")."<p>"; + $rawData .= "<p>".$this->yellow->text->get("editInstallationFeature")."<p>"; foreach($this->getInstallationFeatures() as $feature) { $checked = $feature=="website" ? " checked=\"checked\"" : ""; @@ -602,7 +647,7 @@ class YellowUpdate } $rawData .= "</p>\n"; } - $rawData .= "<input class=\"btn\" type=\"submit\" value=\"".$this->yellow->text->get("webinterfaceOkButton")."\" />\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"; } diff --git a/system/plugins/webinterface.css b/system/plugins/webinterface.css @@ -1,129 +0,0 @@ -/* Webinterface plugin, https://github.com/datenstrom/yellow-plugins/tree/master/webinterface */ -/* Copyright (c) 2013-2017 Datenstrom, https://datenstrom.se */ -/* This file may be used and distributed under the terms of the public license. */ - -.yellow-bar { position:relative; overflow:hidden; 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-body-modal-open { overflow:hidden; } - -.yellow-pane { - position:absolute; display:none; z-index:100; - margin:10px 0; 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; } -.yellow-pane p { margin:0.5em; } -.yellow-pane ul { list-style:none; margin:0 0.5em; padding:0; } -.yellow-pane div { overflow:hidden; } -.yellow-close { display:block; float:right; padding:0 0.5em; font-size:1.1em; 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; - 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; -} -.yellow-arrow:before { - border-color:rgba(187, 187, 187, 0); - border-bottom-color:#bbb; - border-width:11px; - margin-left:-11px; -} - -.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; -} -.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; -} -.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); } -.yellow-btn-delete { - background-color:#c33c35; color:#ffffff; - background-image:linear-gradient(to bottom, #ee5f5b, #bd362f); - border-color:#b13121 #b13121 #802020; -} -.yellow-btn-delete:hover, .yellow-btn-delete:focus, .yellow-btn-delete:active { color:#ffffff; } - -#yellow-pane-login { text-align:center; white-space:nowrap; } -#yellow-pane-login h1 { margin:0 1em; font-size:2em; } -#yellow-pane-login .yellow-form-control { width:15em; box-sizing:border-box; } -#yellow-pane-login .yellow-btn { width:15em; margin:1em 1em 0.5em 0; } -#yellow-pane-login-fields { width:15em; text-align:left; margin:0 auto; } -#yellow-pane-login-buttons { margin:0.5em 0; } -#yellow-pane-login-buttons p { margin:0; } - -#yellow-pane-signup { text-align:center; white-space:nowrap; } -#yellow-pane-signup h1 { margin:0 1em; font-size:2em; } -#yellow-pane-signup .yellow-form-control { width:15em; box-sizing:border-box; } -#yellow-pane-signup .yellow-btn { width:15em; margin:1em 1em 0.5em 0; } -#yellow-pane-signup-status { margin:0.5em 0; display:inline-block; } -#yellow-pane-signup-fields { width:15em; text-align:left; margin:0 auto; } -#yellow-pane-signup-buttons { margin-top:-0.5em; } - -#yellow-pane-recover { text-align:center; white-space:nowrap; } -#yellow-pane-recover h1 { margin:0 1em; font-size:2em; } -#yellow-pane-recover .yellow-form-control { width:15em; box-sizing:border-box; } -#yellow-pane-recover .yellow-btn { width:15em; margin:1em 1em 0.5em 0; } -#yellow-pane-recover-status { margin:0.5em 0; display:inline-block; } -#yellow-pane-recover-fields-first, #yellow-pane-recover-fields-second { width:15em; text-align:left; margin:0 auto; } -#yellow-pane-recover-buttons { margin-top:-0.5em; } - -#yellow-pane-settings { text-align:center; white-space:nowrap; } -#yellow-pane-settings h1 { margin:0 1em; font-size:2em; } -#yellow-pane-settings .yellow-form-control { width:15em; box-sizing:border-box; } -#yellow-pane-settings .yellow-btn { width:15em; margin:1em 1em 0.5em 0; } -#yellow-pane-settings-status { margin:0.5em 0; display:inline-block; } -#yellow-pane-settings-fields { width:15em; text-align:left; margin:0 auto; } -#yellow-pane-settings-buttons { margin-top:-0.5em; } - -#yellow-pane-version { text-align:center; white-space:nowrap; } -#yellow-pane-version h1 { margin:0 1em; font-size:2em; } -#yellow-pane-version .yellow-btn { width:15em; margin:1em 1em 0.5em 0; } -#yellow-pane-version-status { margin:0.5em 0; display:inline-block; } -#yellow-pane-version-fields { text-align:center; margin:0.5em 0; } -#yellow-pane-version-buttons { margin-top:-0.5em; } - -#yellow-pane-edit { } -#yellow-pane-edit h1 { margin:0 0 10px 0; font-size:2em; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; } -#yellow-pane-edit-page { padding:5px; outline:none; resize:none; } -#yellow-pane-edit-buttons { margin-top:5px; } -#yellow-pane-edit-buttons input { margin-right:10px; } -#yellow-pane-edit-help { float:right; } - -#yellow-pane-user { cursor:pointer; } -#yellow-pane-user a { text-decoration:none; } -#yellow-pane-user a:hover { text-decoration:underline; } diff --git a/system/plugins/webinterface.js b/system/plugins/webinterface.js @@ -1,808 +0,0 @@ -// Webinterface plugin, https://github.com/datenstrom/yellow-plugins/tree/master/webinterface -// Copyright (c) 2013-2017 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.webinterface.action(action, status, args); }, - onLoad: function() { yellow.webinterface.loadInterface(); }, - onClick: function(e) { yellow.webinterface.hidePanesOnClick(yellow.toolbox.getEventElement(e)); }, - onKeydown: function(e) { yellow.webinterface.hidePanesOnKeydown(yellow.toolbox.getEventKeycode(e)); }, - onUpdate: function() { yellow.webinterface.updatePane(yellow.webinterface.paneId, yellow.webinterface.paneAction, yellow.webinterface.paneStatus); }, - onResize: function() { yellow.webinterface.resizePane(yellow.webinterface.paneId, yellow.webinterface.paneAction, yellow.webinterface.paneStatus); } -}; - -yellow.webinterface = -{ - paneId: 0, //visible pane ID - paneAction: 0, //current pane action - paneStatus: 0, //current pane status - intervalId: 0, //timer interval ID - - // 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 "recover": this.showPane("yellow-pane-recover", action, status); break; - case "settings": this.showPane("yellow-pane-settings", action, status); break; - case "reconfirm": this.showPane("yellow-pane-settings", action, status); break; - case "change": this.showPane("yellow-pane-settings", action, status); break; - case "version": this.showPane("yellow-pane-version", action, status); break; - case "update": this.sendPane("yellow-pane-update", action, status, args); break; - case "create": this.showPane("yellow-pane-edit", action, status, true); break; - case "edit": this.showPane("yellow-pane-edit", action, status, true); break; - case "delete": this.showPane("yellow-pane-edit", action, status, true); break; - 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 "help": this.hidePane(this.paneId); location.href = this.getText("UserHelpUrl", "yellow"); break; - } - }, - - // Initialise interface - loadInterface: 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); - } - }, - - // Create bar - createBar: function(barId) - { - if(yellow.config.debug) console.log("yellow.webinterface.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); - } - if(yellow.config.userName) - { - elementBar.innerHTML = - "<div class=\"yellow-bar-left\">"+ - "<a href=\"#\" onclick=\"yellow.action('edit'); return false;\" id=\"yellow-pane-edit-link\">"+this.getText("Edit")+"</a>"+ - "</div>"+ - "<div class=\"yellow-bar-right\">"+ - "<a href=\"#\" onclick=\"yellow.action('create'); return false;\" id=\"yellow-pane-create-link\">"+this.getText("Create")+"</a>"+ - "<a href=\"#\" onclick=\"yellow.action('delete'); return false;\" id=\"yellow-pane-delete-link\">"+this.getText("Delete")+"</a>"+ - "<a href=\"#\" onclick=\"yellow.action('user'); return false;\" id=\"yellow-pane-user-link\">"+yellow.toolbox.encodeHtml(yellow.config.userName)+"</a>"+ - "</div>"; - } - yellow.toolbox.insertBefore(elementBar, document.getElementsByTagName("body")[0].firstChild); - return elementBar; - }, - - // Create pane - createPane: function(paneId, paneAction, paneStatus) - { - if(yellow.config.debug) console.log("yellow.webinterface.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, "keyup", yellow.onUpdate); - yellow.toolbox.addEvent(elementPane, "change", yellow.onUpdate); - } - 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.setAttribute("id", paneId+"-content"); - switch(paneId) - { - case "yellow-pane-login": - elementDiv.innerHTML = - "<form method=\"post\">"+ - "<a href=\"#\" onclick=\"yellow.action('close'); return false;\" class=\"yellow-close\">x</a>"+ - "<h1>"+this.getText("LoginTitle")+"</h1>"+ - "<div 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.loginEmail)+"\" /></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.loginPassword)+"\" /></p>"+ - "<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("LoginButton")+"\" /></p>"+ - "</div>"+ - "<div id=\"yellow-pane-login-buttons\">"+ - "<p><a href=\"#\" onclick=\"yellow.action('recover'); return false;\">"+this.getText("LoginRecover")+"</a><p>"+ - "<p><a href=\"#\" onclick=\"yellow.action('signup'); return false;\">"+this.getText("LoginSignup")+"</a><p>"+ - "</div>"+ - "</form>"; - break; - case "yellow-pane-signup": - elementDiv.innerHTML = - "<form method=\"post\">"+ - "<a href=\"#\" onclick=\"yellow.action('close'); return false;\" class=\"yellow-close\">x</a>"+ - "<h1>"+this.getText("SignupTitle")+"</h1>"+ - "<div id=\"yellow-pane-signup-status\" class=\""+paneStatus+"\">"+this.getText(paneAction+"Status", "", paneStatus)+"</div>"+ - "<div 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 class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("SignupButton")+"\" /></p>"+ - "</div>"+ - "<div id=\"yellow-pane-signup-buttons\">"+ - "<p><input class=\"yellow-btn\" type=\"button\" onclick=\"yellow.action('close'); return false;\" value=\""+this.getText("OkButton")+"\" /></p>"+ - "</div>"+ - "</form>"; - break; - case "yellow-pane-recover": - elementDiv.innerHTML = - "<form method=\"post\">"+ - "<a href=\"#\" onclick=\"yellow.action('close'); return false;\" class=\"yellow-close\">x</a>"+ - "<h1>"+this.getText("RecoverTitle")+"</h1>"+ - "<div id=\"yellow-pane-recover-status\" class=\""+paneStatus+"\">"+this.getText(paneAction+"Status", "", paneStatus)+"</div>"+ - "<div id=\"yellow-pane-recover-fields-first\">"+ - "<input type=\"hidden\" name=\"action\" value=\"recover\" />"+ - "<p><label for=\"yellow-pane-recover-email\">"+this.getText("RecoverEmail")+"</label><br /><input class=\"yellow-form-control\" name=\"email\" id=\"yellow-pane-recover-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 id=\"yellow-pane-recover-fields-second\">"+ - "<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 id=\"yellow-pane-recover-buttons\">"+ - "<p><input class=\"yellow-btn\" type=\"button\" onclick=\"yellow.action('close'); return false;\" value=\""+this.getText("OkButton")+"\" /></p>"+ - "</div>"+ - "</form>"; - break; - case "yellow-pane-settings": - elementDiv.innerHTML = - "<form method=\"post\">"+ - "<a href=\"#\" onclick=\"yellow.action('close'); return false;\" class=\"yellow-close\">x</a>"+ - "<h1 id=\"yellow-pane-settings-title\">"+this.getText("SettingsTitle")+"</h1>"+ - "<div id=\"yellow-pane-settings-status\" class=\""+paneStatus+"\">"+this.getText(paneAction+"Status", "", paneStatus)+"</div>"+ - "<div id=\"yellow-pane-settings-fields\">"+ - "<input type=\"hidden\" name=\"action\" value=\"settings\" />"+ - "<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>"+this.getLanguages(paneId)+ - "<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("OkButton")+"\" /></p>"+ - "</div>"+ - "<div id=\"yellow-pane-settings-buttons\">"+ - "<p><input class=\"yellow-btn\" type=\"button\" onclick=\"yellow.action('close'); return false;\" value=\""+this.getText("OkButton")+"\" /></p>"+ - "</div>"+ - "</form>"; - break; - case "yellow-pane-version": - elementDiv.innerHTML = - "<form method=\"post\">"+ - "<a href=\"#\" onclick=\"yellow.action('close'); return false;\" class=\"yellow-close\">x</a>"+ - "<h1 id=\"yellow-pane-version-title\">"+yellow.toolbox.encodeHtml(yellow.config.serverVersion)+"</h1>"+ - "<div id=\"yellow-pane-version-status\" class=\""+paneStatus+"\">"+this.getText("VersionStatus", "", paneStatus)+"</div>"+ - "<div id=\"yellow-pane-version-fields\">"+yellow.page.rawDataOutput+"</div>"+ - "<div id=\"yellow-pane-version-buttons\">"+ - "<p><input class=\"yellow-btn\" type=\"button\" onclick=\"yellow.action('close'); return false;\" value=\""+this.getText("OkButton")+"\" /></p>"+ - "</div>"+ - "</form>"; - break; - case "yellow-pane-edit": - elementDiv.innerHTML = - "<form method=\"post\">"+ - "<a href=\"#\" onclick=\"yellow.action('close'); return false;\" class=\"yellow-close\">x</a>"+ - "<h1 id=\"yellow-pane-edit-title\">"+this.getText("Edit")+"</h1>"+ - "<textarea id=\"yellow-pane-edit-page\" class=\"yellow-form-control\" name=\"rawdataedit\"></textarea>"+ - "<div id=\"yellow-pane-edit-buttons\">"+ - "<input id=\"yellow-pane-edit-send\" class=\"yellow-btn\" type=\"button\" onclick=\"yellow.action('send'); return false;\" value=\""+this.getText("EditButton")+"\" />"+ - "<a href=\""+this.getText("MarkdownHelpUrl", "yellow")+"\" target=\"_blank\" id=\"yellow-pane-edit-help\">"+this.getText("MarkdownHelp")+"</a>" + - "</div>"+ - "</form>"; - break; - case "yellow-pane-user": - elementDiv.innerHTML = - "<p>"+yellow.toolbox.encodeHtml(yellow.config.userEmail)+"</p>"+ - "<p><a href=\"#\" onclick=\"yellow.action('settings'); return false;\">"+this.getText("SettingsTitle")+"</a></p>" + - "<p><a href=\"#\" onclick=\"yellow.action('help'); return false;\">"+this.getText("UserHelp")+"</a></p>" + - "<p><a href=\"#\" onclick=\"yellow.action('logout'); return false;\">"+this.getText("UserLogout")+"</a></p>"; - break; - } - elementPane.appendChild(elementDiv); - yellow.toolbox.insertAfter(elementPane, document.getElementsByTagName("body")[0].firstChild); - return elementPane; - }, - - // Update pane - updatePane: function(paneId, paneAction, paneStatus, init) - { - if(yellow.config.debug) console.log("yellow.webinterface.updatePane id:"+paneId); - var showFields = paneStatus!="next" && paneStatus!="done" && paneStatus!="expired"; - switch(paneId) - { - case "yellow-pane-login": - if(yellow.config.loginRestrictions) - { - yellow.toolbox.setVisible(document.getElementById("yellow-pane-login-buttons"), 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-recover": - yellow.toolbox.setVisible(document.getElementById("yellow-pane-recover-fields-first"), showFields); - yellow.toolbox.setVisible(document.getElementById("yellow-pane-recover-fields-second"), showFields); - yellow.toolbox.setVisible(document.getElementById("yellow-pane-recover-buttons"), !showFields); - if(showFields) - { - if(this.getRequest("id")) - { - yellow.toolbox.setVisible(document.getElementById("yellow-pane-recover-fields-first"), false); - } else { - yellow.toolbox.setVisible(document.getElementById("yellow-pane-recover-fields-second"), false); - } - } - 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=\"#\" onclick=\"yellow.action('version'); return false;\">"+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" && yellow.config.pluginUpdate) - { - document.getElementById("yellow-pane-version-status").innerHTML = this.getText("VersionStatusCheck"); - document.getElementById("yellow-pane-version-fields").innerHTML = ""; - setTimeout("yellow.action('send');", 500); - } - if(paneStatus=="updates" && yellow.config.userWebmaster) - { - document.getElementById("yellow-pane-version-status").innerHTML = "<a href=\"#\" onclick=\"yellow.action('update'); return false;\">"+this.getText("VersionUpdateNormal")+"</a>"; - } - break; - case "yellow-pane-edit": - if(init) - { - var title = yellow.page.title; - var string = yellow.page.rawDataEdit; - switch(paneAction) - { - case "create": title = this.getText("CreateTitle"); string = yellow.page.rawDataNew; break; - case "delete": title = this.getText("DeleteTitle"); break; - } - document.getElementById("yellow-pane-edit-title").innerHTML = yellow.toolbox.encodeHtml(title); - document.getElementById("yellow-pane-edit-page").value = string; - yellow.toolbox.setCursorPosition(document.getElementById("yellow-pane-edit-page"), 0); - } - paneAction = this.getPaneAction(paneId, paneAction); - var key, className, readOnly; - switch(paneAction) - { - case "create": key = "CreateButton"; className = "yellow-btn yellow-btn-create"; readOnly = false; break; - case "edit": key = "EditButton"; className = "yellow-btn yellow-btn-edit"; readOnly = false; break; - case "delete": key = "DeleteButton"; className = "yellow-btn yellow-btn-delete"; readOnly = false; break; - case "": key = "CancelButton"; className = "yellow-btn yellow-btn-cancel"; readOnly = true; break; - } - document.getElementById("yellow-pane-edit-send").value = this.getText(key); - document.getElementById("yellow-pane-edit-send").className = className; - document.getElementById("yellow-pane-edit-page").readOnly = readOnly; - break; - } - }, - - // Resize pane - resizePane: function(paneId, paneAction, paneStatus) - { - var elementBar = document.getElementById("yellow-bar"); - var paneLeft = yellow.toolbox.getOuterLeft(elementBar); - var paneTop = yellow.toolbox.getOuterTop(elementBar) + yellow.toolbox.getOuterHeight(elementBar); - var paneWidth = yellow.toolbox.getOuterWidth(elementBar); - var paneHeight = yellow.toolbox.getWindowHeight() - paneTop - yellow.toolbox.getOuterHeight(elementBar); - switch(paneId) - { - case "yellow-pane-login": - case "yellow-pane-signup": - case "yellow-pane-recover": - case "yellow-pane-settings": - case "yellow-pane-version": - 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); - yellow.toolbox.setOuterWidth(document.getElementById("yellow-pane-edit-page"), yellow.toolbox.getWidth(document.getElementById("yellow-pane-edit"))); - var height1 = yellow.toolbox.getHeight(document.getElementById("yellow-pane-edit")); - var height2 = yellow.toolbox.getOuterHeight(document.getElementById("yellow-pane-edit-content")); - var height3 = yellow.toolbox.getOuterHeight(document.getElementById("yellow-pane-edit-page")); - yellow.toolbox.setOuterHeight(document.getElementById("yellow-pane-edit-page"), height1 - height2 + height3); - 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); - yellow.toolbox.setOuterHeight(document.getElementById("yellow-pane-user"), paneHeight, true); - 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); - var element = document.getElementById(paneId); - if(!element) element = this.createPane(paneId, paneAction, paneStatus); - if(!yellow.toolbox.isVisible(element)) - { - if(yellow.config.debug) console.log("yellow.webinterface.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.resizePane(paneId, paneAction, paneStatus); - this.updatePane(paneId, paneAction, paneStatus, true); - } - } else { - this.hidePane(this.paneId); - } - }, - - // Hide pane - hidePane: function(paneId) - { - var element = document.getElementById(paneId); - if(yellow.toolbox.isVisible(element)) - { - if(yellow.config.debug) console.log("yellow.webinterface.hidePane id:"+paneId); - 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); - this.paneId = 0; - this.paneAction = 0; - this.paneStatus = 0; - } - }, - - // Hide all panes - hidePanes: function() - { - for(var element=document.getElementById("yellow-bar"); element; element=element.nextSibling) - { - if(element.className && element.className.indexOf("yellow-pane")>=0) - { - this.hidePane(element.getAttribute("id")); - } - } - }, - - // Hide all panes on mouse click outside - hidePanesOnClick: function(element) - { - for(;element; element=element.parentNode) - { - if(element.className) - { - if(element.className.indexOf("yellow-pane")>=0 || element.className.indexOf("yellow-bar-")>=0) return; - } - } - this.hidePanes(); - }, - - // Hide all panes on ESC key - hidePanesOnKeydown: function(keycode) - { - if(keycode==27) this.hidePanes(); - }, - - // Send pane - sendPane: function(paneId, paneAction, paneStatus, paneArgs) - { - if(yellow.config.debug) console.log("yellow.webinterface.sendPane id:"+paneId); - if(paneId=="yellow-pane-edit") - { - paneAction = this.getPaneAction(paneId, paneAction); - if(paneAction) - { - var args = {}; - args.action = paneAction; - args.rawdatasource = yellow.page.rawDataSource; - args.rawdataedit = document.getElementById("yellow-pane-edit-page").value; - yellow.toolbox.submitForm(args, true); - } else { - this.hidePane(paneId); - } - } else { - var args = {"action":paneAction}; - if(paneArgs) - { - var tokens = paneArgs.split('/'); - for(var i=0; i<tokens.length; i++) - { - var pair = tokens[i].split(/[:=]/); - if(!pair[0] || !pair[1]) continue; - args[pair[0]] = pair[1]; - } - } - yellow.toolbox.submitForm(args); - } - }, - - // Return pane action - getPaneAction: function(paneId, paneAction) - { - if(paneId=="yellow-pane-edit") - { - var string = document.getElementById("yellow-pane-edit-page").value; - var paneActionOld = paneAction; - switch(paneAction) - { - case "create": paneAction = "create"; break; - case "edit": paneAction = string ? "edit" : "delete"; break; - case "delete": paneAction = "delete"; break; - } - if(yellow.page.statusCode==424 && paneActionOld!="delete") paneAction = "create"; - if(yellow.config.userRestrictions) paneAction = ""; - } - return paneAction; - }, - - // Return language selection - getLanguages: function(paneId) - { - var languages = ""; - if(yellow.config.serverLanguages && yellow.toolbox.getLength(yellow.config.serverLanguages)>1) - { - languages += "<p>"; - for(var language in yellow.config.serverLanguages) - { - var checked = language==this.getRequest("language") ? " checked=\"checked\"" : ""; - languages += "<label for=\""+paneId+"-"+language+"\"><input type=\"radio\" name=\"language\" id=\""+paneId+"-"+language+"\" value=\""+language+"\""+checked+"> "+yellow.config.serverLanguages[language]+"</label><br />"; - } - languages += "</p>"; - } - return languages; - }, - - // Return request string - getRequest: function(key, prefix) - { - if(!prefix) prefix = "request"; - key = prefix + key.charAt(0).toUpperCase() + key.slice(1); - return (key in yellow.page) ? yellow.page[key] : ""; - }, - - // Return text string - getText: function(key, prefix, postfix) - { - if(!prefix) prefix = "webinterface"; - if(!postfix) postfix = ""; - key = prefix + key.charAt(0).toUpperCase() + key.slice(1) + postfix.charAt(0).toUpperCase() + postfix.slice(1); - return (key in yellow.text) ? yellow.text[key] : "["+key+"]"; - } -}; - -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); - }, - - // Add element class - addClass: function(element, name) - { - var string = element.className + " " + name; - element.className = string.replace(/^\s+|\s+$/, ""); - }, - - // Remove element class - removeClass: function(element, name) - { - var string = (" " + element.className + " ").replace(" " + name + " ", " "); - element.className = string.replace(/^\s+|\s+$/, ""); - }, - - // 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) - { - if(element.addEventListener) element.addEventListener(type, handler, false); - else element.attachEvent("on"+type, handler); - }, - - // Remove event handler - removeEvent: function(element, type, handler) - { - if(element.removeEventListener) element.removeEventListener(type, handler, false); - else element.detachEvent("on"+type, handler); - }, - - // Return element of event - getEventElement: function(e) - { - e = e ? e : window.event; - return e.target ? e.target : e.srcElement; - }, - - // Return keycode of event - getEventKeycode: function(e) - { - e = e ? e : window.event; - return e.keyCode; - }, - - // Return element length - getLength: function(element) - { - return Object.keys ? Object.keys(element).length : 0; - }, - - // 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, setMax) - { - width -= this.getBoxSize(element).width; - if(setMax) - { - element.style.maxWidth = Math.max(0, width) + "px"; - } else { - element.style.width = Math.max(0, width) + "px"; - } - }, - - // Set element height in pixel, including padding and border - setOuterHeight: function(element, height, setMax) - { - height -= this.getBoxSize(element).height; - if(setMax) - { - element.style.maxHeight = Math.max(0, height) + "px"; - } else { - element.style.height = Math.max(0, 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) - { - var left = element.getBoundingClientRect().left; - return left + (window.pageXOffset || document.documentElement.scrollLeft); - }, - - // Return element top position in pixel - getOuterTop: function(element) - { - var top = element.getBoundingClientRect().top; - return top + (window.pageYOffset || document.documentElement.scrollTop); - }, - - // Return window width in pixel - getWindowWidth: function() - { - return window.innerWidth || document.documentElement.clientWidth; - }, - - // Return window height in pixel - getWindowHeight: function() - { - return window.innerHeight || document.documentElement.clientHeight; - }, - - // Return element CSS property - getStyle: function(element, property) - { - var string = ""; - if(window.getComputedStyle) - { - string = window.getComputedStyle(element, null).getPropertyValue(property); - } else { - property = property.replace(/\-(\w)/g, function(match, m) { return m.toUpperCase(); }); - string = element.currentStyle[property]; - } - return string; - }, - - // 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 input cursor position - setCursorPosition: function(element, pos) - { - if(element.setSelectionRange) - { - element.focus(); - element.setSelectionRange(pos, pos); - } else if(element.createTextRange) { - var range = element.createTextRange(); - range.move("character", pos); - range.select(); - } - }, - - // Get input cursor position - getCursorPosition: function(element) - { - var pos = 0; - if(element.setSelectionRange) - { - pos = element.selectionStart; - } else if(document.selection) { - var range = document.selection.createRange(); - var rangeDuplicate = range.duplicate(); - rangeDuplicate.moveToElementText(element); - rangeDuplicate.setEndPoint("EndToEnd", range); - pos = rangeDuplicate.text.length - range.text.length; - } - return pos; - }, - - // Set element visibility - setVisible: function(element, show) - { - element.style.display = show ? "block" : "none"; - }, - - // Check if element exists and is visible - isVisible: function(element) - { - return element && element.style.display!="none"; - }, - - // Encode newline characters - encodeNewline: function(string) - { - return string - .replace(/[%]/g, "%25") - .replace(/[\r]/g, "%0d") - .replace(/[\n]/g, "%0a"); - }, - - // 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, encodeNewline) - { - var elementForm = document.createElement("form"); - elementForm.setAttribute("method", "post"); - for(var key in args) - { - if(!args.hasOwnProperty(key)) continue; - var value = encodeNewline ? this.encodeNewline(args[key]) : args[key]; - var elementInput = document.createElement("input"); - elementInput.setAttribute("type", "hidden"); - elementInput.setAttribute("name", key); - elementInput.setAttribute("value", value); - elementForm.appendChild(elementInput); - } - document.body.appendChild(elementForm); - elementForm.submit(); - } -}; - -yellow.webinterface.intervalId = setInterval("yellow.onLoad()", 1); diff --git a/system/plugins/webinterface.php b/system/plugins/webinterface.php @@ -1,1510 +0,0 @@ -<?php -// Webinterface plugin, https://github.com/datenstrom/yellow-plugins/tree/master/webinterface -// Copyright (c) 2013-2017 Datenstrom, https://datenstrom.se -// This file may be used and distributed under the terms of the public license. - -class YellowWebinterface -{ - const VERSION = "0.6.21"; - var $yellow; //access to API - var $response; //web interface response - var $users; //web interface users - var $merge; //web interface 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("webinterfaceLocation", "/edit/"); - $this->yellow->config->setDefault("webinterfaceNewFile", "page-new-(.*).txt"); - $this->yellow->config->setDefault("webinterfaceMetaFilePrefix", "published"); - $this->yellow->config->setDefault("webinterfaceUserFile", "user.ini"); - $this->yellow->config->setDefault("webinterfaceUserPasswordMinLength", "4"); - $this->yellow->config->setDefault("webinterfaceUserHashAlgorithm", "bcrypt"); - $this->yellow->config->setDefault("webinterfaceUserHashCost", "10"); - $this->yellow->config->setDefault("webinterfaceUserStatus", "active"); - $this->yellow->config->setDefault("webinterfaceUserHome", "/"); - $this->users->load($this->yellow->config->get("configDir").$this->yellow->config->get("webinterfaceUserFile")); - } - - // Handle startup - function onStartup($update) - { - if($update) $this->cleanCommand(array("clean", "all")); - } - - // 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("webinterfaceLocation"), '/'); - 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($page->statusCode==424) $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}webinterface.css\" />\n"; - $output .= "<script type=\"text/javascript\" src=\"{$pluginLocation}webinterface.js\"></script>\n"; - $output .= "<script type=\"text/javascript\">\n"; - $output .= "// <![CDATA[\n"; - $output .= "yellow.page = ".json_encode($this->response->getPageData()).";\n"; - $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 "clean": $statusCode = $this->cleanCommand($args); break; - case "user": $statusCode = $this->userCommand($args); break; - default: $statusCode = 0; - } - return $statusCode; - } - - // Handle command help - function onCommandHelp() - { - return "user [EMAIL PASSWORD NAME LANGUAGE]\n"; - } - - // Clean user accounts - function cleanCommand($args) - { - $statusCode = 0; - list($command, $path) = $args; - if($path=="all") - { - $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("webinterfaceUserFile"); - if(!$this->users->clean($fileNameUser)) $statusCode = 500; - if($statusCode==500) echo "ERROR cleaning configuration: Can't write file '$fileNameUser'!\n"; - } - return $statusCode; - } - - // Update user account - function userCommand($args) - { - $statusCode = 0; - list($command, $email, $password, $name, $language) = $args; - if(!empty($email) && !empty($password)) - { - $userExisting = $this->users->isExisting($email); - $status = $this->getUserAccount($email, $password, $command); - switch($status) - { - case "invalid": echo "ERROR updating configuration: Please enter a valid 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("webinterfaceUserFile"); - $status = $this->users->update($fileNameUser, $email, $password, $name, $language, "active") ? "ok" : "error"; - if($status=="error") echo "ERROR updating configuration: Can't write file '$fileNameUser'!\n"; - } - if($status=="ok") - { - $algorithm = $this->yellow->config->get("webinterfaceUserHashAlgorithm"); - $status = substru($this->users->getHash($email), 0, 5)!="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 " : ""); - echo ($userExisting ? "updated" : "created")."\n"; - } else { - $statusCode = 200; - foreach($this->users->getData() as $line) echo "$line\n"; - if(!$this->users->getNumber()) echo "Yellow $command: No user accounts\n"; - } - return $statusCode; - } - - // Process request - function processRequest($scheme, $address, $base, $location, $fileName) - { - $statusCode = 0; - if($this->checkUser($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 "signup": $statusCode = $this->processRequestSignup($scheme, $address, $base, $location, $fileName); break; - case "confirm": $statusCode = $this->processRequestConfirm($scheme, $address, $base, $location, $fileName); break; - case "approve": $statusCode = $this->processRequestApprove($scheme, $address, $base, $location, $fileName); break; - case "recover": $statusCode = $this->processRequestRecover($scheme, $address, $base, $location, $fileName); break; - case "settings": $statusCode = $this->processRequestSettings($scheme, $address, $base, $location, $fileName); break; - case "reconfirm": $statusCode = $this->processRequestReconfirm($scheme, $address, $base, $location, $fileName); break; - case "change": $statusCode = $this->processRequestChange($scheme, $address, $base, $location, $fileName); break; - case "version": $statusCode = $this->processRequestVersion($scheme, $address, $base, $location, $fileName); break; - case "update": $statusCode = $this->processRequestUpdate($scheme, $address, $base, $location, $fileName); break; - case "create": $statusCode = $this->processRequestCreate($scheme, $address, $base, $location, $fileName); break; - case "edit": $statusCode = $this->processRequestEdit($scheme, $address, $base, $location, $fileName); break; - case "delete": $statusCode = $this->processRequestDelete($scheme, $address, $base, $location, $fileName); break; - } - } else { - $this->yellow->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 "confirm": $statusCode = $this->processRequestConfirm($scheme, $address, $base, $location, $fileName); break; - case "approve": $statusCode = $this->processRequestApprove($scheme, $address, $base, $location, $fileName); break; - case "recover": $statusCode = $this->processRequestRecover($scheme, $address, $base, $location, $fileName); break; - case "reconfirm": $statusCode = $this->processRequestReconfirm($scheme, $address, $base, $location, $fileName); break; - case "change": $statusCode = $this->processRequestChange($scheme, $address, $base, $location, $fileName); break; - } - if($this->response->action=="fail") $this->yellow->page->error(500, "Login failed, [please log in](javascript:yellow.action('login');)!"); - } - 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)) - { - $statusCode = 301; - $location = $this->yellow->lookup->isFileLocation($location) ? "$location/" : "/".$this->yellow->getRequestLanguage()."/"; - $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); - $this->yellow->sendStatus($statusCode, $location); - } else { - $statusCode = $this->response->isUserRestrictions() ? 404 : 424; - $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); - $this->yellow->page->error($statusCode); - } - } - return $statusCode; - } - - // Process request for user login - function processRequestLogin($scheme, $address, $base, $location, $fileName) - { - $statusCode = 0; - $home = $this->users->getHome($this->response->userEmail); - if(substru($location, 0, strlenu($home))==$home) - { - $statusCode = 303; - $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); - $this->yellow->sendStatus($statusCode, $location); - } else { - $statusCode = 302; - $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $home); - $this->yellow->sendStatus($statusCode, $location); - } - return $statusCode; - } - - // Process request for user logout - function processRequestLogout($scheme, $address, $base, $location, $fileName) - { - $statusCode = 302; - $this->response->userEmail = ""; - $this->response->destroyCookie($scheme, $address, $base); - $location = $this->yellow->lookup->normaliseUrl( - $this->yellow->config->get("serverScheme"), - $this->yellow->config->get("serverAddress"), - $this->yellow->config->get("serverBase"), $location); - $this->yellow->sendStatus($statusCode, $location); - return $statusCode; - } - - // Process request for user signup - function processRequestSignup($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"]); - if(empty($name) || empty($email) || empty($password)) $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("webinterfaceUserFile"); - $this->response->status = $this->users->update($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") - { - $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->users->getResponseStatus($email, $_REQUEST["action"], $_REQUEST["expire"], $_REQUEST["id"]); - if($this->response->status=="ok") - { - $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("webinterfaceUserFile"); - $this->response->status = $this->users->update($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->users->getResponseStatus($email, $_REQUEST["action"], $_REQUEST["expire"], $_REQUEST["id"]); - if($this->response->status=="ok") - { - $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("webinterfaceUserFile"); - $this->response->status = $this->users->update($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 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"]); - if(empty($_REQUEST["id"])) - { - if(!filter_var($email, FILTER_VALIDATE_EMAIL)) $this->response->status = "invalid"; - if($this->response->status=="ok" && $this->response->isLoginRestrictions()) $this->response->status = "next"; - 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!"); - } - } else { - $this->response->status = $this->users->getResponseStatus($email, $_REQUEST["action"], $_REQUEST["expire"], $_REQUEST["id"]); - 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("webinterfaceUserFile"); - $this->response->status = $this->users->update($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->userEmail = ""; - $this->response->destroyCookie($scheme, $address, $base); - $this->response->status = $this->response->sendMail($scheme, $address, $base, $email, "information") ? "done" : "error"; - if($this->response->status=="error") $this->yellow->page->error(500, "Can't send email on this server!"); - } - } - } - $statusCode = $this->yellow->processRequest($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; - $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("webinterfaceUserFile"); - $this->response->status = $this->users->update($fileNameUser, $email, "no", $name, $language, "unconfirmed", "", $pending) ? "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("webinterfaceUserFile"); - $this->response->status = $this->users->update($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 ? "reconfirm" : "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("webinterfaceUserFile"); - $this->response->status = $this->users->update($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") - { - $statusCode = 303; - $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); - $this->yellow->sendStatus($statusCode, $location); - } else { - $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); - } - return $statusCode; - } - - // Process request to reconfirm email - function processRequestReconfirm($scheme, $address, $base, $location, $fileName) - { - $this->response->action = "reconfirm"; - $this->response->status = "ok"; - $email = $emailSource = $_REQUEST["email"]; - $this->response->status = $this->users->getResponseStatus($email, $_REQUEST["action"], $_REQUEST["expire"], $_REQUEST["id"]); - 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("webinterfaceUserFile"); - $this->response->status = $this->users->update($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 settings - function processRequestChange($scheme, $address, $base, $location, $fileName) - { - $this->response->action = "change"; - $this->response->status = "ok"; - $email = $emailSource = trim($_REQUEST["email"]); - $this->response->status = $this->users->getResponseStatus($email, $_REQUEST["action"], $_REQUEST["expire"], $_REQUEST["id"]); - 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" && $email!=$emailSource) - { - $fileNameUser = $this->yellow->config->get("configDir").$this->yellow->config->get("webinterfaceUserFile"); - $this->users->users[$emailSource]["pending"] = "none"; - $this->response->status = $this->users->update($fileNameUser, $emailSource, "", "", "", "inactive") ? "ok" : "error"; - if($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); - } - 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("webinterfaceUserFile"); - $this->response->status = $this->users->update($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->userEmail = ""; - $this->response->destroyCookie($scheme, $address, $base); - $this->response->status = $this->response->sendMail($scheme, $address, $base, $email, "information") ? "done" : "error"; - if($this->response->status=="error") $this->yellow->page->error(500, "Can't send email on this server!"); - } - $statusCode = $this->yellow->processRequest($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; - if(!empty($this->response->rawDataOutput)) $this->response->rawDataOutput .= "<br />\n"; - $this->response->rawDataOutput .= htmlspecialchars("$key $dataLatest[$key]"); - } - } - if($updates==0) - { - foreach($dataCurrent as $key=>$value) - { - if(!is_null($dataModified[$key]) && !is_null($dataLatest[$key])) - { - $rawData = $this->yellow->text->getTextHtml("webinterfaceVersionUpdateModified", $this->response->language)." - <a href=\"#\" onclick=\"yellow.action('update','update','".$this->yellow->toolbox->normaliseArgs("option:force/feature:$key")."'); return false;\">".$this->yellow->text->getTextHtml("webinterfaceVersionUpdateForce", $this->response->language)."</a>"; - $rawData = preg_replace("/@software/i", htmlspecialchars("$key $dataLatest[$key]"), $rawData); - if(!empty($this->response->rawDataOutput)) $this->response->rawDataOutput .= "<br />\n"; - $this->response->rawDataOutput .= $rawData; - } - } - } - } else { - foreach($dataCurrent as $key=>$value) - { - if(strnatcasecmp($dataCurrent[$key], $dataLatest[$key])<0) ++$updates; - } - } - $this->response->status = $updates ? "updates" : "done"; - if($statusCode!=200) $this->response->status = "error"; - } - $statusCode = $this->yellow->processRequest($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) - { - $statusCode = 303; - $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); - $this->yellow->sendStatus($statusCode, $location); - } - } - return $statusCode; - } - - // Process request to create page - function processRequestCreate($scheme, $address, $base, $location, $fileName) - { - $statusCode = 0; - if(!$this->response->isUserRestrictions() && !empty($_POST["rawdataedit"])) - { - $this->response->rawDataSource = $this->response->rawDataEdit = rawurldecode($_POST["rawdatasource"]); - $rawData = $this->response->normaliseText(rawurldecode($_POST["rawdataedit"])); - $page = $this->response->getPageNew($scheme, $address, $base, $location, $fileName, $rawData); - if(!$page->isError()) - { - if($this->yellow->toolbox->createFile($page->fileName, $page->rawData, true)) - { - $statusCode = 303; - $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $page->location); - $this->yellow->sendStatus($statusCode, $location); - } else { - $statusCode = 500; - $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); - $this->yellow->page->error($statusCode, "Can't write file '$page->fileName'!"); - } - } else { - $statusCode = 500; - $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); - $this->yellow->page->error($statusCode, $page->get("pageError")); - } - } - return $statusCode; - } - - // Process request to edit page - function processRequestEdit($scheme, $address, $base, $location, $fileName) - { - $statusCode = 0; - if(!$this->response->isUserRestrictions() && !empty($_POST["rawdataedit"])) - { - $this->response->rawDataSource = rawurldecode($_POST["rawdatasource"]); - $this->response->rawDataEdit = $this->response->normaliseText(rawurldecode($_POST["rawdataedit"])); - $page = $this->response->getPageUpdate($scheme, $address, $base, $location, $fileName, - $this->response->rawDataSource, $this->response->rawDataEdit, $this->yellow->toolbox->readFile($fileName)); - if(!$page->isError()) - { - if($this->yellow->toolbox->renameFile($fileName, $page->fileName) && - $this->yellow->toolbox->createFile($page->fileName, $page->rawData)) - { - $statusCode = 303; - $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $page->location); - $this->yellow->sendStatus($statusCode, $location); - } else { - $statusCode = 500; - $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); - $this->yellow->page->error($statusCode, "Can't write file '$page->fileName'!"); - } - } else { - $statusCode = 500; - $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); - $this->yellow->page->error($statusCode, $page->get("pageError")); - } - } - 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 = $this->response->rawDataEdit = rawurldecode($_POST["rawdatasource"]); - if($this->yellow->lookup->isFileLocation($location)) - { - if($this->yellow->toolbox->deleteFile($fileName, $this->yellow->config->get("trashDir"))) - { - $statusCode = 303; - $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); - $this->yellow->sendStatus($statusCode, $location); - } else { - $statusCode = 500; - $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); - $this->yellow->page->error($statusCode, "Can't delete file '$fileName'!"); - } - } else { - if($this->yellow->toolbox->deleteDirectory(dirname($fileName), $this->yellow->config->get("trashDir"))) - { - $statusCode = 303; - $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); - $this->yellow->sendStatus($statusCode, $location); - } else { - $statusCode = 500; - $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); - $this->yellow->page->error($statusCode, "Can't delete file '$fileName'!"); - } - } - } - return $statusCode; - } - - // Check web interface request - function checkRequest($location) - { - $locationLength = strlenu($this->yellow->config->get("webinterfaceLocation")); - $this->response->active = substru($location, 0, $locationLength)==$this->yellow->config->get("webinterfaceLocation"); - return $this->response->isActive(); - } - - // Check web interface user - function checkUser($scheme, $address, $base, $location, $fileName) - { - if($_POST["action"]=="login") - { - $email = $_POST["email"]; - $password = $_POST["password"]; - if($this->users->checkUser($email, $password)) - { - $this->response->createCookie($scheme, $address, $base, $email); - $this->response->userEmail = $email; - $this->response->userRestrictions = $this->getUserRestrictions($email, $location, $fileName); - $this->response->language = $this->response->getLanguage($email); - } else { - $this->response->action = "fail"; - } - } else if(isset($_COOKIE["login"])) { - list($email, $session) = explode(',', $_COOKIE["login"], 2); - if($this->users->checkCookie($email, $session)) - { - $this->response->userEmail = $email; - $this->response->userRestrictions = $this->getUserRestrictions($email, $location, $fileName); - $this->response->language = $this->response->getLanguage($email); - } else { - $this->response->action = "fail"; - } - } - return $this->response->isUser(); - } - - // Return user account changes - function getUserAccount($email, $password, $action) - { - $status = null; - foreach($this->yellow->plugins->plugins as $key=>$value) - { - if(method_exists($value["obj"], "onUserAccount")) - { - $status = $value["obj"]->onUserAccount($email, $password, $action, $status, $this->users); - if(!is_null($status)) break; - } - } - if(is_null($status)) - { - $status = "ok"; - if(!empty($password) && strlenu($password)<$this->yellow->config->get("webinterfaceUserPasswordMinLength")) $status = "weak"; - if(!empty($email) && !filter_var($email, FILTER_VALIDATE_EMAIL)) $status = "invalid"; - } - return $status; - } - - // Return user restrictions to change page - function getUserRestrictions($email, $location, $fileName) - { - $userRestrictions = null; - foreach($this->yellow->plugins->plugins as $key=>$value) - { - if(method_exists($value["obj"], "onUserRestrictions")) - { - $userRestrictions = $value["obj"]->onUserRestrictions($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; - } -} - -class YellowResponse -{ - var $yellow; //access to API - var $webinterface; //access to web interface - var $userEmail; //user email - var $userRestrictions; //user can change page? (boolean) - var $active; //web interface is active? (boolean) - var $rawDataSource; //raw data of page for comparison - var $rawDataEdit; //raw data of page for editing - var $rawDataOutput; //raw data of dynamic output - var $language; //response language - var $action; //response action - var $status; //response status - - function __construct($yellow) - { - $this->yellow = $yellow; - $this->webinterface = $yellow->plugins->get("webinterface"); - } - - // Return new page - function getPageNew($scheme, $address, $base, $location, $fileName, $rawData) - { - $page = new YellowPage($this->yellow); - $page->setRequestInformation($scheme, $address, $base, $location, $fileName); - $page->parseData($rawData, false, 0); - if($this->yellow->lookup->isFileLocation($location) || is_file($fileName)) - { - $page->fileName = $this->yellow->lookup->findFileFromTitle( - $page->get($this->yellow->config->get("webinterfaceMetaFilePrefix")), $page->get("title"), $fileName, - $this->yellow->config->get("contentDefaultFile"), $this->yellow->config->get("contentExtension")); - $page->location = $this->yellow->lookup->findLocationFromFile($page->fileName); - if($this->yellow->pages->find($page->location)) - { - preg_match("/^(.*?)(\d*)$/", $page->get("title"), $matches); - $titleText = $matches[1]; - $titleNumber = $matches[2]; - if(strempty($titleNumber)) { $titleNumber = 2; $titleText = $titleText.' '; } - for(; $titleNumber<=999; ++$titleNumber) - { - $page->rawData = $this->updateTextTitle($rawData, $titleText.$titleNumber); - $page->fileName = $this->yellow->lookup->findFileFromTitle( - $page->get($this->yellow->config->get("webinterfaceMetaFilePrefix")), $titleText.$titleNumber, $fileName, - $this->yellow->config->get("contentDefaultFile"), $this->yellow->config->get("contentExtension")); - $page->location = $this->yellow->lookup->findLocationFromFile($page->fileName); - if(!$this->yellow->pages->find($page->location)) { $ok = true; break; } - } - if(!$ok) $page->error(500, "Page '".$page->get("title")."' can not be created!"); - } - } - if(!is_dir(dirname($page->fileName))) - { - preg_match("/^([\d\-\_\.]*)(.*)$/", $page->get("title"), $matches); - if(preg_match("/\d$/", $matches[1])) $matches[1] .= '-'; - $page->fileName = $this->yellow->lookup->findFilePageNew($fileName, $matches[1]); - $page->location = $this->yellow->lookup->findLocationFromFile($page->fileName); - } - if($this->webinterface->getUserRestrictions($this->userEmail, $page->location, $page->fileName)) - { - $page->error(500, "Page '".$page->get("title")."' is not allowed!"); - } - return $page; - } - - // Return modified page - function getPageUpdate($scheme, $address, $base, $location, $fileName, $rawDataSource, $rawDataEdit, $rawDataFile) - { - $page = new YellowPage($this->yellow); - $page->setRequestInformation($scheme, $address, $base, $location, $fileName); - $page->parseData($this->webinterface->merge->merge($rawDataSource, $rawDataEdit, $rawDataFile), false, 0); - if(empty($page->rawData)) $page->error(500, "Page has been modified by someone else!"); - if($this->yellow->lookup->isFileLocation($location) && !$page->isError()) - { - $pageSource = new YellowPage($this->yellow); - $pageSource->setRequestInformation($scheme, $address, $base, $location, $fileName); - $pageSource->parseData($rawDataSource, false, 0); - $prefix = $this->yellow->config->get("webinterfaceMetaFilePrefix"); - if($pageSource->get($prefix)!=$page->get($prefix) || $pageSource->get("title")!=$page->get("title")) - { - $page->fileName = $this->yellow->lookup->findFileFromTitle( - $page->get($prefix), $page->get("title"), $fileName, - $this->yellow->config->get("contentDefaultFile"), $this->yellow->config->get("contentExtension")); - $page->location = $this->yellow->lookup->findLocationFromFile($page->fileName); - if($pageSource->location!=$page->location) - { - if(!$this->yellow->lookup->isFileLocation($page->location)) - { - $page->error(500, "Page '".$page->get("title")."' is not allowed!"); - } else if($this->yellow->pages->find($page->location)) { - $page->error(500, "Page '".$page->get("title")."' already exists!"); - } - } - } - } - if($this->webinterface->getUserRestrictions($this->userEmail, $page->location, $page->fileName)) - { - $page->error(500, "Page '".$page->get("title")."' is not allowed!"); - } - return $page; - } - - // Return page data including login information - function getPageData() - { - $data = array(); - if($this->isUser()) - { - $data["title"] = $this->getPageTitle($this->rawDataEdit); - $data["rawDataSource"] = $this->rawDataSource; - $data["rawDataEdit"] = $this->rawDataEdit; - $data["rawDataNew"] = $this->getRawDataNew(); - $data["rawDataOutput"] = strval($this->rawDataOutput); - $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->webinterface->users->getName($this->userEmail); - $data["userLanguage"] = $this->webinterface->users->getLanguage($this->userEmail); - $data["userStatus"] = $this->webinterface->users->getStatus($this->userEmail); - $data["userHome"] = $this->webinterface->users->getHome($this->userEmail); - $data["userRestrictions"] = intval($this->isUserRestrictions()); - $data["userWebmaster"] = intval($this->isUserWebmaster()); - $data["pluginUpdate"] = intval($this->yellow->plugins->isExisting("update")); - $data["serverLanguages"] = array(); - foreach($this->yellow->text->getLanguages() as $language) - { - $data["serverLanguages"][$language] = $this->yellow->text->getTextHtml("languageDescription", $language); - } - $data["serverScheme"] = $this->yellow->config->get("serverScheme"); - $data["serverAddress"] = $this->yellow->config->get("serverAddress"); - $data["serverBase"] = $this->yellow->config->get("serverBase"); - $data["serverVersion"] = "Yellow ".YellowCore::VERSION; - } else { - $data["loginEmail"] = $this->yellow->config->get("loginEmail"); - $data["loginPassword"] = $this->yellow->config->get("loginPassword"); - $data["loginRestrictions"] = 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=="login" || $key=="password") continue; - $data["request".ucfirst($key)] = trim($value); - } - return $data; - } - - // Return text strings - function getTextData() - { - $textLanguage = $this->yellow->text->getData("language", $this->language); - $textWebinterface = $this->yellow->text->getData("webinterface", $this->language); - $textYellow = $this->yellow->text->getData("yellow", $this->language); - return array_merge($textLanguage, $textWebinterface, $textYellow); - } - - // Return raw data for new page - function getRawDataNew($location = "") - { - $fileName = $this->yellow->lookup->findFileFromLocation($this->yellow->page->location); - $fileName = $this->yellow->lookup->findFileFromConfig($fileName, - $this->yellow->config->get("webinterfaceNewFile"), $this->yellow->config->get("template")); - $rawData = $this->yellow->toolbox->readFile($fileName); - $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->webinterface->users->getName($this->userEmail), " "), $rawData); - $rawData = preg_replace("/@username/i", $this->webinterface->users->getName($this->userEmail), $rawData); - $rawData = preg_replace("/@userlanguage/i", $this->webinterface->users->getLanguage($this->userEmail), $rawData); - if(!empty($location)) - { - $title = $this->yellow->toolbox->createTextTitle($location); - $rawData = $this->updateTextTitle($rawData, $title); - } - return $rawData; - } - - // Return page title - function getPageTitle($rawData) - { - $title = $this->yellow->page->get("title"); - if(preg_match("/^(\xEF\xBB\xBF)?\-\-\-[\r\n]+(.+?)\-\-\-[\r\n]+/s", $rawData, $parts)) - { - foreach($this->yellow->toolbox->getTextLines($parts[2]) as $line) - { - preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); - if(lcfirst($matches[1])=="title" && !strempty($matches[2])) { $title = $matches[2]; break; } - } - } - return $title; - } - - // Return language for user - function getLanguage($email) - { - $language = $this->webinterface->users->getLanguage($email); - if(!$this->yellow->text->isLanguage($language)) $language = $this->yellow->config->get("language"); - return $language; - } - - // Update text title - function updateTextTitle($rawData, $title) - { - foreach($this->yellow->toolbox->getTextLines($rawData) as $line) - { - preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); - if(lcfirst($matches[1])=="title") $line = "Title: $title\n"; - $rawDataNew .= $line; - } - return $rawDataNew; - } - - // Normlise text with special characters - function normaliseText($text) - { - if($this->yellow->plugins->isExisting("emojiawesome")) - { - $text = $this->yellow->plugins->get("emojiawesome")->normaliseText($text, true, false); - } - return $text; - } - - // Create browser cookie - function createCookie($scheme, $address, $base, $email) - { - $session = $this->webinterface->users->createSession($email); - setcookie("login", "$email,$session", time()+60*60*24*365, "$base/", "", $scheme=="https"); - } - - // Destroy browser cookie - function destroyCookie($scheme, $address, $base) - { - setcookie("login", "", time()-60*60, "$base/", "", $scheme=="https"); - } - - // Send mail to user - function sendMail($scheme, $address, $base, $email, $action) - { - if($action=="welcome" || $action=="information") - { - $url = "$scheme://$address$base/"; - } else { - $expire = time()+60*60*24; - $id = $this->webinterface->users->createRequestId($email, $action, $expire); - $url = "$scheme://$address$base"."/action:$action/email:$email/expire:$expire/id:$id/"; - } - if($action=="approve") - { - $account = $email; - $name = $this->yellow->config->get("author"); - $email = $this->yellow->config->get("email"); - } else { - $account = $email; - $name = $this->webinterface->users->getName($email); - } - $language = $this->webinterface->users->getLanguage($email); - if(!$this->yellow->text->isLanguage($language)) $language = $this->yellow->config->get("language"); - $sitename = $this->yellow->config->get("sitename"); - $prefix = "webinterface".ucfirst($action); - $message = $this->yellow->text->getText("{$prefix}Message", $language); - $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 .= mb_encode_mimeheader("X-Remote-Addr: $_SERVER[REMOTE_ADDR]")."\r\n"; - $mailHeaders .= "Mime-Version: 1.0\r\n"; - $mailHeaders .= "Content-Type: text/plain; charset=utf-8\r\n"; - $mailMessage = "$message\r\n\r\n$url\r\n-- \r\n$sitename"; - return mail($mailTo, $mailSubject, $mailMessage, $mailHeaders); - } - - // Check if web interface active - function isActive() - { - return $this->active; - } - - // Check if web interface has login restrictions - function isLoginRestrictions() - { - return substru($this->yellow->config->get("email"), 0, 7)=="noreply"; - } - - // 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"); - } -} - -class YellowUsers -{ - var $yellow; //access to API - var $users; //registered users - - 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, $modified, $pending, $home) = explode(',', $matches[2]); - $this->set($matches[1], $hash, $name, $language, $status, $modified, $pending, $home); - if(defined("DEBUG") && DEBUG>=3) echo "YellowUsers::load email:$matches[1]<br/>\n"; - } - } - } - - // Clean users in file - function clean($fileName) - { - $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])) - { - list($hash, $name, $language, $status, $modified, $pending, $home) = explode(',', $matches[2]); - if($status=="active" || $status=="inactive") - { - $pending = "none"; - $fileDataNew .= "$matches[1]: $hash,$name,$language,$status,$modified,$pending,$home\n"; - } - } else { - $fileDataNew .= $line; - } - } - return $this->yellow->toolbox->createFile($fileName, $fileDataNew); - } - - // Update users in file - function update($fileName, $email, $password = "", $name = "", $language = "", $status = "", $modified = "", $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); - $modified = strreplaceu(',', '-', empty($modified) ? time() : $modified); - $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) ? $this->yellow->config->get("webinterfaceUserStatus") : $status); - $modified = strreplaceu(',', '-', empty($modified) ? time() : $modified); - $pending = strreplaceu(',', '-', empty($pending) ? "none" : $pending); - $home = strreplaceu(',', '-', empty($home) ? $this->yellow->config->get("webinterfaceUserHome") : $home); - } - $this->set($email, $hash, $name, $language, $status, $modified, $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,$modified,$pending,$home\n"; - $found = true; - } else { - $fileDataNew .= $line; - } - } - if(!$found) $fileDataNew .= "$email: $hash,$name,$language,$status,$modified,$pending,$home\n"; - return $this->yellow->toolbox->createFile($fileName, $fileDataNew); - } - - // Set user data - function set($email, $hash, $name, $language, $status, $modified, $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]["modified"] = $modified; - $this->users[$email]["pending"] = $pending; - $this->users[$email]["home"] = $home; - } - - // Check user login from email and password - function checkUser($email, $password) - { - $algorithm = $this->yellow->config->get("webinterfaceUserHashAlgorithm"); - return $this->isExisting($email) && $this->users[$email]["status"]=="active" && - $this->yellow->toolbox->verifyHash($password, $algorithm, $this->users[$email]["hash"]); - } - - // Check user login from email and session - function checkCookie($email, $session) - { - return $this->isExisting($email) && $this->users[$email]["status"]=="active" && - $this->yellow->toolbox->verifyHash($this->users[$email]["hash"], "sha256", $session); - } - - // Create session - function createSession($email) - { - if($this->isExisting($email)) - { - $session = $this->yellow->toolbox->createHash($this->users[$email]["hash"], "sha256"); - if(empty($session)) $session = "error-hash-algorithm-sha256"; - } - return $session; - } - - // Create password hash - function createHash($password) - { - $algorithm = $this->yellow->config->get("webinterfaceUserHashAlgorithm"); - $cost = $this->yellow->config->get("webinterfaceUserHashCost"); - $hash = $this->yellow->toolbox->createHash($password, $algorithm, $cost); - if(empty($hash)) $hash = "error-hash-algorithm-$algorithm"; - return $hash; - } - - // Create request ID for action - function createRequestId($email, $action, $expire) - { - return $this->yellow->toolbox->createHash($this->users[$email]["hash"].$action.$expire, "sha256"); - } - - // Return response status for action - function getResponseStatus($email, $action, $expire, $id) - { - $status = "done"; - switch($action) - { - case "confirm": $statusExpected = "unconfirmed"; break; - case "reconfirm": $statusExpected = "unconfirmed"; break; - case "approve": $statusExpected = "unapproved"; break; - default: $statusExpected = "active"; break; - } - if($this->isExisting($email) && $this->users[$email]["status"]==$statusExpected && - $this->yellow->toolbox->verifyHash($this->users[$email]["hash"].$action.$expire, "sha256", $id)) - { - $status = "ok"; - } - if($expire<=time()) $status = "expired"; - return $status; - } - - // 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"] : ""; - } - - // 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 modified - function getModified($email = "") - { - return $this->isExisting($email) ? $this->users[$email]["modified"] : ""; - } - - // 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 data - function getData() - { - $data = array(); - foreach($this->users as $key=>$value) - { - $name = $value["name"]; if(preg_match("/\s/", $name)) $name = "\"$name\""; - $language = $value["language"]; if(preg_match("/\s/", $language)) $language = "\"$language\""; - $status = $value["status"]; if(preg_match("/\s/", $status)) $status = "\"$status\""; - $data[$key] = "$value[email] - $name $language $status"; - if($value["home"]!="/") $data[$key] .= " restrictions"; - } - usort($data, strnatcasecmp); - 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]); - } -} - -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; - } -} - -$yellow->plugins->register("webinterface", "YellowWebinterface", YellowWebinterface::VERSION); -?> diff --git a/system/plugins/yellow-blog.zip.installation b/system/plugins/yellow-blog.zip.installation Binary files differ. diff --git a/system/plugins/yellow-wiki.zip.installation b/system/plugins/yellow-wiki.zip.installation Binary files differ.