mikuli.cz

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

commit d51be4b9fed5777eb8b47572c55402791ec1eec9
parent 5c7b56c2aedac45b1c378acf353006be57ea2d38
Author: markseu <mark2011@mayberg.se>
Date:   Sat, 16 Mar 2019 08:59:37 +0100

Updated system, new API

Diffstat:
Msystem/extensions/bundle.php | 8++------
Msystem/extensions/command.php | 11+++++------
Msystem/extensions/core.php | 89+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Msystem/extensions/edit.js | 10+++++-----
Msystem/extensions/edit.php | 114++++++++++++++++++++++++++++++++++++++++----------------------------------------
Msystem/extensions/install.php | 62++++++++++++++++++++++++++++++++++++++++++++++----------------
Msystem/extensions/update.php | 290+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msystem/settings/system.ini | 5+++--
8 files changed, 347 insertions(+), 242 deletions(-)

diff --git a/system/extensions/bundle.php b/system/extensions/bundle.php @@ -4,7 +4,7 @@ // This file may be used and distributed under the terms of the public license. class YellowBundle { - const VERSION = "0.8.2"; + const VERSION = "0.8.3"; const TYPE = "feature"; public $yellow; //access to API @@ -96,7 +96,6 @@ class YellowBundle { } } if (!empty($fileNames)) { - $this->yellow->toolbox->timerStart($time); $id = substru(md5(implode($fileNames).$base), 0, 10); $fileNameBundle = $this->yellow->system->get("resourceDir")."bundle-$id.min.$type";; $locationBundle = $base.$this->yellow->system->get("resourceLocation")."bundle-$id.min.$type"; @@ -124,10 +123,7 @@ class YellowBundle { $this->yellow->page->error(500, "Can't write file '$fileNameBundle'!"); } } - $this->yellow->toolbox->timerStop($time); - if (defined("DEBUG") && DEBUG>=2) { - $data["debug"] = "YellowBundle::processBundle file:$fileNameBundle time:$time ms<br/>\n"; - } + if (defined("DEBUG") && DEBUG>=2) $data["debug"] = "YellowBundle::processBundle file:$fileNameBundle<br/>\n"; } return $data; } diff --git a/system/extensions/command.php b/system/extensions/command.php @@ -4,8 +4,9 @@ // This file may be used and distributed under the terms of the public license. class YellowCommand { - const VERSION = "0.8.2"; + const VERSION = "0.8.3"; const TYPE = "feature"; + const PRIORITY = "3"; public $yellow; //access to API public $files; //number of files public $links; //number of links @@ -440,10 +441,8 @@ class YellowCommand { public function broadcastCommand($args) { $statusCode = 0; foreach ($this->yellow->extensions->extensions as $key=>$value) { - if ($key=="command") continue; - if (method_exists($value["obj"], "onCommand")) { - $statusCode = $value["obj"]->onCommand(func_get_args()); - if ($statusCode!=0) break; + if (method_exists($value["obj"], "onCommand") && $key!="command") { + $statusCode = max($statusCode, $value["obj"]->onCommand(func_get_args())); } } return $statusCode; @@ -456,7 +455,7 @@ class YellowCommand { list($scheme, $address, $base) = $this->yellow->lookup->getUrlInformation($url); if ($scheme=="http" && !empty($address)) { if (!preg_match("/\:\d+$/", $address)) $address .= ":8000"; - echo "Starting built-in web server on $scheme://$address\n"; + echo "Starting built-in web server on $scheme://$address/\n"; echo "Press Ctrl-C to quit...\n"; system("php -S $address yellow.php", $returnStatus); $statusCode = $returnStatus!=0 ? 500 : 200; diff --git a/system/extensions/core.php b/system/extensions/core.php @@ -4,7 +4,7 @@ // This file may be used and distributed under the terms of the public license. class YellowCore { - const VERSION = "0.8.2"; + const VERSION = "0.8.3"; const TYPE = "feature"; public $page; //current page public $content; //content files from file system @@ -75,9 +75,9 @@ class YellowCore { $this->system->setDefault("downloadExtension", ".download"); $this->system->setDefault("systemFile", "system.ini"); $this->system->setDefault("textFile", "text.ini"); + $this->system->setDefault("logFile", "yellow.log"); $this->system->setDefault("safeMode", "0"); $this->system->setDefault("multiLanguageMode", "0"); - $this->system->setDefault("startupUpdate", "none"); $this->system->setDefault("serverUrl", ""); } @@ -87,9 +87,9 @@ class YellowCore { // Handle initialisation public function load() { - if (defined("DEBUG") && DEBUG>=2) { + if (defined("DEBUG") && DEBUG>=3) { $serverVersion = $this->toolbox->getServerVersion(); - echo "Datenstrom Yellow ".YellowCore::VERSION.", PHP ".PHP_VERSION.", $serverVersion<br/>\n"; + echo "YellowCore::load Datenstrom Yellow ".YellowCore::VERSION.", PHP ".PHP_VERSION.", $serverVersion<br/>\n"; } $this->toolbox->timerStart($time); $this->system->load($this->system->get("settingDir").$this->system->get("systemFile")); @@ -295,13 +295,11 @@ class YellowCore { // Handle startup public function startup() { $this->updateFileSystem(); //TODO: remove later, for backwards compatibility - $tokens = explode(",", $this->system->get("startupUpdate")); foreach ($this->extensions->extensions as $key=>$value) { - if (method_exists($value["obj"], "onStartup")) $value["obj"]->onStartup(in_array($key, $tokens)); + if (method_exists($value["obj"], "onStartup")) $value["obj"]->onStartup(); } - if ($this->system->get("startupUpdate")!="none") { - $fileName = $this->system->get("settingDir").$this->system->get("systemFile"); - $this->system->save($fileName, array("startupUpdate" => "none")); + foreach ($this->extensions->extensions as $key=>$value) { + if (method_exists($value["obj"], "onUpdate")) $value["obj"]->onUpdate("startup"); } } @@ -317,7 +315,8 @@ class YellowCore { $fileData = $fileDataNew = $this->toolbox->readFile("yellow.php"); $fileDataNew = str_replace("system/plugins/core.php", "system/extensions/core.php", $fileData); if (is_dir("system/config/") || is_dir("system/themes/") || is_dir("system/plugins/") || $fileData!=$fileDataNew) { - $fileNameError = "system/settings/system-error.log"; + $statusCode = 200; + $this->log("info", "Update file system"); if (is_dir("system/config/")) { foreach ($this->toolbox->getDirectoryEntriesRecursive("system/config/", "/.*/", true, false) as $entry) { $entryNew = str_replace("system/config/", "system/settings/", $entry); @@ -325,7 +324,8 @@ class YellowCore { if (!is_file($entryNew)) $this->toolbox->copyFile($entry, $entryNew, true); } if (!$this->toolbox->deleteDirectory("system/config/", "system/trash/")) { - $fileDataError .= "ERROR deleting folder 'system/config/'!\n"; + $statusCode = 500; + $this->log("error", "Can't delete folder 'system/config/'!"); } } if (is_dir("system/themes/")) { @@ -350,7 +350,8 @@ class YellowCore { if (!is_file($entryNew)) $this->toolbox->copyFile($entry, $entryNew, true); } if (!$this->toolbox->deleteDirectory("system/themes/", "system/trash/")) { - $fileDataError .= "ERROR deleting folder 'system/themes/'!\n"; + $statusCode = 500; + $this->log("error", "Can't delete folder 'system/themes/'!"); } } if (is_dir("system/plugins/")) { @@ -359,12 +360,14 @@ class YellowCore { if (!is_file($entryNew)) $this->toolbox->copyFile($entry, $entryNew, true); } if (!$this->toolbox->deleteDirectory("system/plugins/", "system/trash/")) { - $fileDataError .= "ERROR deleting folder 'system/plugins/'!\n"; + $statusCode = 500; + $this->log("error", "Can't delete folder 'system/plugins/'!"); } } if (function_exists("opcache_reset")) opcache_reset(); if ($fileData!=$fileDataNew && !$this->toolbox->createFile("yellow.php", $fileDataNew)) { - $fileDataError .= "ERROR writing file 'yellow.php'!\n"; + $statusCode = 500; + $this->log("error", "Can't write file 'yellow.php'!"); } foreach ($this->toolbox->getDirectoryEntries("system/extensions/", "/^.*\.php$/", true, false) as $entry) { $fileData = $fileDataNew = $this->toolbox->readFile($entry); @@ -372,18 +375,32 @@ class YellowCore { if (preg_match("/^core\.php$/", basename($entry))) continue; if ($fileData!=$fileDataNew) $this->toolbox->createFile($entry, $fileDataNew); } - $this->system->save("system/settings/system.ini", array("startupUpdate" => "update")); - if (!empty($fileDataError)) $this->toolbox->createFile($fileNameError, $fileDataError); - @header($this->toolbox->getHttpStatusFormatted(empty($fileDataError) ? 200 : 500)); - die(empty($fileDataError) ? "System has been updated. Please update your website one more time.\n" : - "System has not been updated. Please check errors in file '$fileNameError'!\n"); + $this->system->save("system/settings/system.ini", array("updateNotification" => "update/update")); + @header($this->toolbox->getHttpStatusFormatted($statusCode)); + die($statusCode==200 ? "System has been updated. Please update your website one more time.\n" : + "System has not been updated. Please check errors in file 'system/extensions/yellow.log'!\n"); + } + } + + // Handle logging + public function log($action, $message) { + $statusCode = 0; + foreach ($this->extensions->extensions as $key=>$value) { + if (method_exists($value["obj"], "onLog")) { + $statusCode = $value["obj"]->onLog($action, $message); + if ($statusCode!=0) break; + } + } + if ($statusCode==0) { + $line = date("Y-m-d H:i:s")." ".trim($action)." ".trim($message)."\n"; + $this->toolbox->appendFile($this->system->get("extensionDir").$this->system->get("logFile"), $line); } } // Include layout public function layout($name, $args = null) { $this->lookup->layoutArgs = func_get_args(); - $this->page->includePageLayout($name); + $this->page->includeLayout($name); } public function snippet($name, $args = null) { //TODO: remove later, for backwards compatibility @@ -565,9 +582,9 @@ class YellowPage { public function parseContent($sizeMax = 0) { if (!is_object($this->parser)) { if ($this->yellow->extensions->isExisting($this->get("parser"))) { - $extension = $this->yellow->extensions->extensions[$this->get("parser")]; - if (method_exists($extension["obj"], "onParseContentRaw")) { - $this->parser = $extension["obj"]; + $value = $this->yellow->extensions->extensions[$this->get("parser")]; + if (method_exists($value["obj"], "onParseContentRaw")) { + $this->parser = $value["obj"]; $this->parserData = $this->getContent(true, $sizeMax); $this->parserData = preg_replace("/@pageRead/i", $this->get("pageRead"), $this->parserData); $this->parserData = preg_replace("/@pageEdit/i", $this->get("pageEdit"), $this->parserData); @@ -681,22 +698,24 @@ class YellowPage { } if (is_null($this->outputData)) { ob_start(); - $this->includePageLayout($name); + $this->includeLayout($name); $this->outputData = ob_get_contents(); ob_end_clean(); } } // Include page layout - public function includePageLayout($name) { + public function includeLayout($name) { $fileNameLayoutBasic = $this->yellow->system->get("layoutDir").$this->yellow->lookup->normaliseName($name).".html"; $fileNameLayoutTheme = $this->yellow->system->get("layoutDir"). $this->yellow->lookup->normaliseName($name)."-".$this->yellow->lookup->normaliseName($this->get("theme")).".html"; if (is_file($fileNameLayoutTheme)) { + if (defined("DEBUG") && DEBUG>=2) echo "YellowPage::includeLayout file:$fileNameLayoutTheme<br>\n"; $this->setLastModified(filemtime($fileNameLayoutTheme)); global $yellow; //TODO: remove later, for backwards compatibility require($fileNameLayoutTheme); } elseif (is_file($fileNameLayoutBasic)) { + if (defined("DEBUG") && DEBUG>=2) echo "YellowPage::includeLayout file:$fileNameLayoutBasic<br>\n"; $this->setLastModified(filemtime($fileNameLayoutBasic)); global $yellow; //TODO: remove later, for backwards compatibility require($fileNameLayoutBasic); @@ -2605,6 +2624,26 @@ class YellowToolbox { return $ok; } + // Append file + public function appendFile($fileName, $fileData, $mkdir = false) { + $ok = false; + if ($mkdir) { + $path = dirname($fileName); + if (!empty($path) && !is_dir($path)) @mkdir($path, 0777, true); + } + $fileHandle = @fopen($fileName, "ab"); + if ($fileHandle) { + clearstatcache(true, $fileName); + if (flock($fileHandle, LOCK_EX)) { + fwrite($fileHandle, $fileData); + flock($fileHandle, LOCK_UN); + } + fclose($fileHandle); + $ok = true; + } + return $ok; + } + // Copy file public function copyFile($fileNameSource, $fileNameDestination, $mkdir = false) { clearstatcache(); diff --git a/system/extensions/edit.js b/system/extensions/edit.js @@ -331,7 +331,7 @@ yellow.edit = { var showFields = paneStatus!="next" && paneStatus!="done"; switch (paneId) { case "yellow-pane-login": - if (yellow.system.editLoginRestrictions) { + if (yellow.system.editLoginRestriction) { yellow.toolbox.setVisible(document.getElementById("yellow-pane-login-signup"), false); } break; @@ -389,12 +389,12 @@ yellow.edit = { yellow.toolbox.setVisible(document.getElementById("yellow-pane-edit-toolbar-title"), false); this.updateToolbar(0, "yellow-toolbar-checked"); } - if (yellow.system.userRestrictions) { + if (yellow.system.userRestriction) { yellow.toolbox.setVisible(document.getElementById("yellow-pane-edit-send"), false); document.getElementById("yellow-pane-edit-text").readOnly = true; } } - if (!yellow.system.userRestrictions) { + if (!yellow.system.userRestriction) { var key, className; switch (this.getAction(paneId, paneAction)) { case "create": key = "CreateButton"; className = "yellow-toolbar-btn yellow-toolbar-btn-create"; break; @@ -556,7 +556,7 @@ yellow.edit = { if (yellow.system.debug) console.log("yellow.edit.processToolbar status:"+status); var elementText = document.getElementById("yellow-pane-edit-text"); var elementPreview = document.getElementById("yellow-pane-edit-preview"); - if (!yellow.system.userRestrictions && this.paneAction!="delete" && !yellow.toolbox.isVisible(elementPreview)) { + if (!yellow.system.userRestriction && this.paneAction!="delete" && !yellow.toolbox.isVisible(elementPreview)) { switch (status) { case "h1": yellow.editor.setMarkdown(elementText, "# ", "insert-multiline-block", true); break; case "h2": yellow.editor.setMarkdown(elementText, "## ", "insert-multiline-block", true); break; @@ -581,7 +581,7 @@ yellow.edit = { } } if (status=="preview") this.showPreview(elementText, elementPreview); - if (status=="save" && !yellow.system.userRestrictions && this.paneAction!="delete") this.action("send"); + if (status=="save" && !yellow.system.userRestriction && this.paneAction!="delete") this.action("send"); if (status=="help") window.open(this.getText("HelpUrl", "yellow"), "_blank"); if (status=="markdown") window.open(this.getText("MarkdownUrl", "yellow"), "_blank"); if (status=="format" || status=="heading" || status=="list" || status=="emojiawesome" || status=="fontawesome") { diff --git a/system/extensions/edit.php b/system/extensions/edit.php @@ -4,7 +4,7 @@ // This file may be used and distributed under the terms of the public license. class YellowEdit { - const VERSION = "0.8.2"; + const VERSION = "0.8.3"; const TYPE = "feature"; public $yellow; //access to API public $response; //web response @@ -29,35 +29,11 @@ class YellowEdit { $this->yellow->system->setDefault("editUserHashCost", "10"); $this->yellow->system->setDefault("editUserHome", "/"); $this->yellow->system->setDefault("editNewFile", "page-new-(.*).md"); - $this->yellow->system->setDefault("editLoginRestrictions", "0"); + $this->yellow->system->setDefault("editLoginRestriction", "0"); $this->yellow->system->setDefault("editLoginSessionTimeout", "2592000"); $this->yellow->system->setDefault("editBruteForceProtection", "25"); $this->users->load($this->yellow->system->get("settingDir").$this->yellow->system->get("editUserFile")); } - - // Handle startup - public function onStartup($update) { - if ($update) { - $fileNameUser = $this->yellow->system->get("settingDir").$this->yellow->system->get("editUserFile"); - $fileData = $this->yellow->toolbox->readFile($fileNameUser); - foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) { - preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); - if (!empty($matches[1]) && !empty($matches[2]) && $matches[1][0]!="#") { - list($hash, $name, $language, $status, $stamp, $modified, $errors, $pending, $home) = explode(",", $matches[2]); - if ($status!="active" && $status!="inactive") { - unset($this->users->users[$matches[1]]); - continue; - } - $pending = "none"; - $this->users->set($matches[1], $hash, $name, $language, $status, $stamp, $modified, $errors, $pending, $home); - $fileDataNew .= "$matches[1]: $hash,$name,$language,$status,$stamp,$modified,$errors,$pending,$home\n"; - } else { - $fileDataNew .= $line; - } - } - if ($fileData!=$fileDataNew) $this->yellow->toolbox->createFile($fileNameUser, $fileDataNew); - } - } // Handle request public function onRequest($scheme, $address, $base, $location, $fileName) { @@ -133,6 +109,30 @@ class YellowEdit { return "user [option email password name]\n"; } + // Handle update + public function onUpdate($action) { + if ($action=="update") { + $fileNameUser = $this->yellow->system->get("settingDir").$this->yellow->system->get("editUserFile"); + $fileData = $this->yellow->toolbox->readFile($fileNameUser); + foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) { + preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); + if (!empty($matches[1]) && !empty($matches[2]) && $matches[1][0]!="#") { + list($hash, $name, $language, $status, $stamp, $modified, $errors, $pending, $home) = explode(",", $matches[2]); + if ($status!="active" && $status!="inactive") { + unset($this->users->users[$matches[1]]); + continue; + } + $pending = "none"; + $this->users->set($matches[1], $hash, $name, $language, $status, $stamp, $modified, $errors, $pending, $home); + $fileDataNew .= "$matches[1]: $hash,$name,$language,$status,$stamp,$modified,$errors,$pending,$home\n"; + } else { + $fileDataNew .= $line; + } + } + if ($fileData!=$fileDataNew) $this->yellow->toolbox->createFile($fileNameUser, $fileDataNew); + } + } + // Process command to update user account public function processCommandUser($args) { list($command, $option) = $args; @@ -276,7 +276,7 @@ class YellowEdit { $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); $statusCode = $this->yellow->sendStatus(301, $location); } else { - $this->yellow->page->error($this->response->isUserRestrictions() ? 404 : 434); + $this->yellow->page->error($this->response->isUserRestriction() ? 404 : 434); $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); } } @@ -325,7 +325,7 @@ class YellowEdit { $consent = trim($_REQUEST["consent"]); if (empty($name) || empty($email) || empty($password) || empty($consent)) $this->response->status = "incomplete"; if ($this->response->status=="ok") $this->response->status = $this->getUserAccount($email, $password, $this->response->action); - if ($this->response->status=="ok" && $this->response->isLoginRestrictions()) $this->response->status = "next"; + if ($this->response->status=="ok" && $this->response->isLoginRestriction()) $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->system->get("settingDir").$this->yellow->system->get("editUserFile"); @@ -639,7 +639,7 @@ class YellowEdit { // Process request to create page public function processRequestCreate($scheme, $address, $base, $location, $fileName) { $statusCode = 0; - if (!$this->response->isUserRestrictions() && !empty($_REQUEST["rawdataedit"])) { + if (!$this->response->isUserRestriction() && !empty($_REQUEST["rawdataedit"])) { $this->response->rawDataSource = $_REQUEST["rawdatasource"]; $this->response->rawDataEdit = $_REQUEST["rawdatasource"]; $this->response->rawDataEndOfLine = $_REQUEST["rawdataendofline"]; @@ -665,7 +665,7 @@ class YellowEdit { // Process request to edit page public function processRequestEdit($scheme, $address, $base, $location, $fileName) { $statusCode = 0; - if (!$this->response->isUserRestrictions() && !empty($_REQUEST["rawdataedit"])) { + if (!$this->response->isUserRestriction() && !empty($_REQUEST["rawdataedit"])) { $this->response->rawDataSource = $_REQUEST["rawdatasource"]; $this->response->rawDataEdit = $_REQUEST["rawdataedit"]; $this->response->rawDataEndOfLine = $_REQUEST["rawdataendofline"]; @@ -703,7 +703,7 @@ class YellowEdit { // Process request to delete page public function processRequestDelete($scheme, $address, $base, $location, $fileName) { $statusCode = 0; - if (!$this->response->isUserRestrictions() && is_file($fileName)) { + if (!$this->response->isUserRestriction() && is_file($fileName)) { $this->response->rawDataSource = $_REQUEST["rawdatasource"]; $this->response->rawDataEdit = $_REQUEST["rawdatasource"]; $this->response->rawDataEndOfLine = $_REQUEST["rawdataendofline"]; @@ -756,7 +756,7 @@ class YellowEdit { $fileSizeMax = $this->yellow->toolbox->getNumberBytes(ini_get("upload_max_filesize")); $extension = strtoloweru(($pos = strrposu($fileNameShort, ".")) ? substru($fileNameShort, $pos) : ""); $extensions = preg_split("/\s*,\s*/", $this->yellow->system->get("editUploadExtensions")); - if (!$this->response->isUserRestrictions() && is_uploaded_file($fileNameTemp) && + if (!$this->response->isUserRestriction() && is_uploaded_file($fileNameTemp) && filesize($fileNameTemp)<=$fileSizeMax && in_array($extension, $extensions)) { $file = $this->response->getFileUpload($scheme, $address, $base, $location, $fileNameTemp, $fileNameShort); if (!$file->isError() && $this->yellow->toolbox->copyFile($fileNameTemp, $file->fileName, true)) { @@ -787,7 +787,7 @@ class YellowEdit { if ($this->users->checkAuthLogin($email, $password)) { $this->response->createCookies($scheme, $address, $base, $email); $this->response->userEmail = $email; - $this->response->userRestrictions = $this->getUserRestrictions($email, $location, $fileName); + $this->response->userRestriction = $this->getUserRestriction($email, $location, $fileName); $this->response->language = $this->getUserLanguage($email); } else { $this->response->userFailedError = "login"; @@ -797,7 +797,7 @@ class YellowEdit { } elseif (isset($_COOKIE["authtoken"]) && isset($_COOKIE["csrftoken"])) { if ($this->users->checkAuthToken($_COOKIE["authtoken"], $_COOKIE["csrftoken"], $_POST["csrftoken"], $_REQUEST["action"]=="")) { $this->response->userEmail = $email = $this->users->getAuthEmail($_COOKIE["authtoken"]); - $this->response->userRestrictions = $this->getUserRestrictions($email, $location, $fileName); + $this->response->userRestriction = $this->getUserRestriction($email, $location, $fileName); $this->response->language = $this->getUserLanguage($email); } else { $this->response->userFailedError = "auth"; @@ -892,20 +892,20 @@ class YellowEdit { return $status; } - // Return user restrictions - public function getUserRestrictions($email, $location, $fileName) { - $userRestrictions = null; + // Return user restriction + public function getUserRestriction($email, $location, $fileName) { + $userRestriction = null; foreach ($this->yellow->extensions->extensions as $key=>$value) { - if (method_exists($value["obj"], "onEditUserRestrictions")) { - $userRestrictions = $value["obj"]->onEditUserRestrictions($email, $location, $fileName, $this->users); - if (!is_null($userRestrictions)) break; + if (method_exists($value["obj"], "onEditUserRestriction")) { + $userRestriction = $value["obj"]->onEditUserRestriction($email, $location, $fileName, $this->users); + if (!is_null($userRestriction)) 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; + if (is_null($userRestriction)) { + $userRestriction = substru($location, 0, strlenu($this->users->getHome($email)))!=$this->users->getHome($email); + $userRestriction |= empty($fileName) || strlenu(dirname($fileName))>128 || strlenu(basename($fileName))>128; } - return $userRestrictions; + return $userRestriction; } // Return user language @@ -928,7 +928,7 @@ class YellowResponse { public $extension; //access to extension public $active; //location is active? (boolean) public $userEmail; //user email - public $userRestrictions; //user can change page? (boolean) + public $userRestriction; //user with restriction? (boolean) public $userFailedError; //error of failed authentication public $userFailedEmail; //email of failed authentication public $userFailedExpire; //expiration time of failed authentication @@ -967,7 +967,7 @@ class YellowResponse { } else { $page->fileName = $this->getPageNewFile($page->location); } - if ($this->extension->getUserRestrictions($this->userEmail, $page->location, $page->fileName)) { + if ($this->extension->getUserRestriction($this->userEmail, $page->location, $page->fileName)) { $page->error(500, "Page '".$page->get("title")."' is restricted!"); } return $page; @@ -994,8 +994,8 @@ class YellowResponse { } } if (empty($page->rawData)) $page->error(500, "Page has been modified by someone else!"); - if ($this->extension->getUserRestrictions($this->userEmail, $page->location, $page->fileName) || - $this->extension->getUserRestrictions($this->userEmail, $pageSource->location, $pageSource->fileName)) { + if ($this->extension->getUserRestriction($this->userEmail, $page->location, $page->fileName) || + $this->extension->getUserRestriction($this->userEmail, $pageSource->location, $pageSource->fileName)) { $page->error(500, "Page '".$page->get("title")."' is restricted!"); } return $page; @@ -1007,7 +1007,7 @@ class YellowResponse { $page->setRequestInformation($scheme, $address, $base, $location, $fileName); $page->parseData($this->normaliseLines($rawData, $endOfLine), false, 0); $this->editContentFile($page, "delete"); - if ($this->extension->getUserRestrictions($this->userEmail, $page->location, $page->fileName)) { + if ($this->extension->getUserRestriction($this->userEmail, $page->location, $page->fileName)) { $page->error(500, "Page '".$page->get("title")."' is restricted!"); } return $page; @@ -1080,7 +1080,7 @@ class YellowResponse { $data["userLanguage"] = $this->extension->users->getLanguage($this->userEmail); $data["userStatus"] = $this->extension->users->getStatus($this->userEmail); $data["userHome"] = $this->extension->users->getHome($this->userEmail); - $data["userRestrictions"] = intval($this->isUserRestrictions()); + $data["userRestriction"] = intval($this->isUserRestriction()); $data["userWebmaster"] = intval($this->isUserWebmaster()); $data["serverScheme"] = $this->yellow->system->get("serverScheme"); $data["serverAddress"] = $this->yellow->system->get("serverAddress"); @@ -1103,7 +1103,7 @@ class YellowResponse { } else { $data["editLoginEmail"] = $this->yellow->page->get("editLoginEmail"); $data["editLoginPassword"] = $this->yellow->page->get("editLoginPassword"); - $data["editLoginRestrictions"] = intval($this->isLoginRestrictions()); + $data["editLoginRestriction"] = intval($this->isLoginRestriction()); } if (defined("DEBUG") && DEBUG>=1) $data["debug"] = DEBUG; return $data; @@ -1437,14 +1437,14 @@ class YellowResponse { return !empty($this->userEmail) && $this->userEmail==$this->yellow->system->get("email"); } - // Check if user has restrictions - public function isUserRestrictions() { - return empty($this->userEmail) || $this->userRestrictions; + // Check if user with restriction + public function isUserRestriction() { + return empty($this->userEmail) || $this->userRestriction; } - // Check if login has restrictions - public function isLoginRestrictions() { - return $this->yellow->system->get("editLoginRestrictions"); + // Check if login with restriction + public function isLoginRestriction() { + return $this->yellow->system->get("editLoginRestriction"); } } diff --git a/system/extensions/install.php b/system/extensions/install.php @@ -4,7 +4,7 @@ // This file may be used and distributed under the terms of the public license. class YellowInstall { - const VERSION = "0.8.2"; + const VERSION = "0.8.3"; const TYPE = "feature"; const PRIORITY = "1"; public $yellow; //access to API @@ -33,7 +33,8 @@ class YellowInstall { // Process command to install website public function processCommandInstall() { - $statusCode = $this->updateLanguages(); + $statusCode = $this->updateLog(); + if ($statusCode==200) $statusCode = $this->updateLanguage(); if ($statusCode==200) $statusCode = $this->updateSettings($this->getSystemData()); if ($statusCode==200) $statusCode = $this->removeFiles(); if ($statusCode==200) { @@ -47,16 +48,16 @@ class YellowInstall { // Process request to install website public function processRequestInstall($scheme, $address, $base, $location, $fileName) { - $statusCode = 0; $name = trim(preg_replace("/[^\pL\d\-\. ]/u", "-", $_REQUEST["name"])); $email = trim($_REQUEST["email"]); $password = trim($_REQUEST["password"]); $language = trim($_REQUEST["language"]); $extension = trim($_REQUEST["extension"]); $status = trim($_REQUEST["status"]); + $statusCode = $this->updateLog(); + $statusCode = max($statusCode, $this->updateLanguage()); $this->yellow->content->pages["root/"] = array(); $this->yellow->page = new YellowPage($this->yellow); - $statusCode = $this->updateLanguages(); $this->yellow->page->setRequestInformation($scheme, $address, $base, $location, $fileName); $this->yellow->page->parseData($this->getRawDataInstall(), false, $statusCode, $this->yellow->page->get("pageError")); $this->yellow->page->safeMode = false; @@ -65,8 +66,8 @@ class YellowInstall { if ($status=="ok") $status = $this->updateContent($language, "Home", "/")==200 ? "ok" : "error"; if ($status=="ok") $status = $this->updateContent($language, "About", "/about/")==200 ? "ok" : "error"; if ($status=="ok") $status = $this->updateContent($language, "Footer", "/shared/footer")==200 ? "ok" : "error"; - if ($status=="ok") $status = $this->updateSettings($this->getSystemData()) ? "ok" : "error"; - if ($status=="ok") $status = $this->removeFiles() ? "done" : "error"; + if ($status=="ok") $status = $this->updateSettings($this->getSystemData())==200 ? "ok" : "error"; + if ($status=="ok") $status = $this->removeFiles()==200 ? "done" : "error"; if ($status=="done") { $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); $statusCode = $this->yellow->sendStatus(303, $location); @@ -76,24 +77,44 @@ class YellowInstall { return $statusCode; } - // Update languages - public function updateLanguages() { + // Update log + public function updateLog() { + $statusCode = 200; + $fileName = $this->yellow->system->get("extensionDir").$this->yellow->system->get("logFile"); + if (!is_file($fileName)) { + $serverVersion = $this->yellow->toolbox->getServerVersion(); + $this->yellow->log("info", "Datenstrom Yellow ".YellowCore::VERSION.", PHP ".PHP_VERSION.", $serverVersion"); + if (!$this->yellow->isCommandLine()) { + $server = $this->yellow->toolbox->getServerVersion(true); + $this->yellow->log("info", "Checked $server server configuration"); + } + if (!is_file($fileName)) { + $statusCode = 500; + $this->yellow->page->error(500, "Can't write file '$fileName'!"); + } + } + return $statusCode; + } + + // Update language + public function updateLanguage() { $statusCode = 200; $path = $this->yellow->system->get("extensionDir")."install-languages.zip"; if (is_file($path) && $this->yellow->extensions->isExisting("update")) { $zip = new ZipArchive(); if ($zip->open($path)===true) { - if (defined("DEBUG") && DEBUG>=2) echo "YellowInstall::updateLanguages file:$path<br/>\n"; $languages = $this->detectBrowserLanguages("en, de, fr"); $languagesFound = array(); foreach ($languages as $language) $languagesFound[$language] = ""; if (preg_match("#^(.*\/).*?$#", $zip->getNameIndex(0), $matches)) $pathBase = $matches[1]; $fileData = $zip->getFromName($pathBase.$this->yellow->system->get("updateExtensionFile")); - if (empty($fileData)) $fileData = $zip->getFromName($pathBase.$this->yellow->system->get("updateInformationFile")); //TODO: remove later, for backwards compatibility foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) { preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); if (!empty($matches[1]) && !empty($matches[2]) && strposu($matches[1], "/")) { - list($dummy, $entry) = explode("/", $matches[1], 2); + list($dummy, $entry) = explode(",", $matches[2], 3); + if (ctype_upper($matches[1][0])) { //TODO: remove later, converts old format + list($dummy, $entry) = explode("/", $matches[1], 2); + } $flags = explode(",", $matches[2]); $language = array_pop($flags); if (preg_match("/^(.*)\.php$/", basename($entry), $tokens) && in_array($language, $languages)) { @@ -107,8 +128,12 @@ class YellowInstall { if (lcfirst($matches[1])=="extension") $extension = lcfirst($matches[2]); if (lcfirst($matches[1])=="published") $modified = strtotime($matches[2]); if (!empty($matches[1]) && !empty($matches[2]) && strposu($matches[1], "/")) { - list($dummy, $entry) = explode("/", $matches[1], 2); - list($fileName) = explode(",", $matches[2], 2); + $fileName = $matches[1]; + list($dummy, $entry) = explode(",", $matches[2], 3); + if (ctype_upper($matches[1][0])) { //TODO: remove later, converts old format + list($dummy, $entry) = explode("/", $matches[1], 2); + list($fileName) = explode(",", $matches[2], 2); + } $fileData = $zip->getFromName($pathBase.basename($entry)); if (preg_match("/^(.*).php$/", basename($entry), $tokens) && in_array($tokens[1], $languagesFound)) { $statusCode = $this->yellow->extensions->get("update")->updateExtensionFile($fileName, $fileData, @@ -117,7 +142,9 @@ class YellowInstall { if (preg_match("/^(.*)-language\.txt$/", basename($entry), $tokens) && in_array($tokens[1], $languagesFound)) { $statusCode = $this->yellow->extensions->get("update")->updateExtensionFile($fileName, $fileData, $modified, 0, 0, "create,update", false, $extension); + $this->yellow->log($statusCode==200 ? "info" : "error", "Install language '".ucfirst($tokens[1])."'"); } + if ($statusCode!=200) break; } } $zip->close(); @@ -137,8 +164,11 @@ class YellowInstall { $statusCode = 200; if (!empty($email) && !empty($password) && $this->yellow->extensions->isExisting("edit")) { $fileNameUser = $this->yellow->system->get("settingDir").$this->yellow->system->get("editUserFile"); - $status = $this->yellow->extensions->get("edit")->users->save($fileNameUser, $email, $password, $name, $language) ? "ok" : "error"; - if ($status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); + if (!$this->yellow->extensions->get("edit")->users->save($fileNameUser, $email, $password, $name, $language)) { + $statusCode = 500; + $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); + } + $this->yellow->log($statusCode==200 ? "info" : "error", "Install webmaster '".strtok($name, " ")."'"); } return $statusCode; } @@ -151,7 +181,7 @@ class YellowInstall { foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.zip$/", true, false) as $entry) { if (preg_match("/^install-(.*?)\./", basename($entry), $matches)) { if (strtoloweru($matches[1])==strtoloweru($extension)) { - $statusCode = $this->yellow->extensions->get("update")->updateExtensionArchive($entry); + $statusCode = $this->yellow->extensions->get("update")->updateExtensionArchive($entry, "install"); break; } } diff --git a/system/extensions/update.php b/system/extensions/update.php @@ -4,7 +4,7 @@ // This file may be used and distributed under the terms of the public license. class YellowUpdate { - const VERSION = "0.8.3"; + const VERSION = "0.8.4"; const TYPE = "feature"; const PRIORITY = "2"; public $yellow; //access to API @@ -18,12 +18,46 @@ class YellowUpdate { $this->yellow->system->setDefault("updateExtensionFile", "extension.ini"); $this->yellow->system->setDefault("updateVersionFile", "version.ini"); $this->yellow->system->setDefault("updateWaffleFile", "waffle.ini"); + $this->yellow->system->setDefault("updateNotification", "none"); } - // Handle startup - public function onStartup($update) { - if ($update) { //TODO: remove later, converts old API in layouts - $fileNameError = $this->yellow->system->get("settingDir")."system-error.log"; + // Handle request + public function onRequest($scheme, $address, $base, $location, $fileName) { + $statusCode = 0; + if ($this->yellow->lookup->isContentFile($fileName) && $this->isExtensionPending()) { + $statusCode = $this->processRequestPending($scheme, $address, $base, $location, $fileName); + } + return $statusCode; + } + + // Handle command + public function onCommand($args) { + $statusCode = 0; + if ($this->isExtensionPending()) $statusCode = $this->processCommandPending(); + if ($statusCode==0) { + list($command) = $args; + switch ($command) { + case "clean": $statusCode = $this->processCommandClean($args); break; + case "install": $statusCode = $this->processCommandInstall($args); break; + case "uninstall": $statusCode = $this->processCommandUninstall($args); break; + case "update": $statusCode = $this->processCommandUpdate($args); break; + default: $statusCode = 0; break; + } + } + return $statusCode; + } + + // Handle command help + public function onCommandHelp() { + $help .= "install [extension]\n"; + $help .= "uninstall [extension]\n"; + $help .= "update [extension]\n"; + return $help; + } + + // Handle update + public function onUpdate($action) { + if ($action=="update") { //TODO: remove later, converts old API in layouts if ($this->yellow->system->isExisting("templateDir")) { $path = $this->yellow->system->get("layoutDir"); foreach ($this->yellow->toolbox->getDirectoryEntriesRecursive($path, "/^.*\.html$/", true, false) as $entry) { @@ -39,26 +73,22 @@ class YellowUpdate { $fileDataNew = str_replace("\"template\"", "\"layout\"", $fileDataNew); $fileDataNew = str_replace("\"page template-\"", "\"page layout-\"", $fileDataNew); if ($fileData!=$fileDataNew && !$this->yellow->toolbox->createFile($entry, $fileDataNew)) { - $fileDataError .= "ERROR writing file '$entry'!\n"; + $this->yellow->log("error", "Can't write file '$entry'!"); } } - if (!empty($fileDataError)) $this->yellow->toolbox->createFile($fileNameError, $fileDataError); } } - if ($update) { //TODO: remove later, converts old website icon - $fileNameError = $this->yellow->system->get("settingDir")."system-error.log"; + if ($action=="update") { //TODO: remove later, converts old website icon if ($this->yellow->system->isExisting("siteicon")) { $theme = $this->yellow->system->get("theme"); $fileName = $this->yellow->system->get("resourceDir")."icon.png"; $fileNameNew = $this->yellow->system->get("resourceDir").$this->yellow->lookup->normaliseName($theme)."-icon.png"; if (is_file($fileName) && !$this->yellow->toolbox->renameFile($fileName, $fileNameNew)) { - $fileDataError .= "ERROR renaming file '$fileName'!\n"; + $this->yellow->log("error", "Can't rename file '$fileName'!"); } - if (!empty($fileDataError)) $this->yellow->toolbox->createFile($fileNameError, $fileDataError); } } - if ($update) { //TODO: remove later, converts old language files - $fileNameError = $this->yellow->system->get("settingDir")."system-error.log"; + if ($action=="update") { //TODO: remove later, converts old language files if ($this->yellow->system->isExisting("languageFile")) { $path = $this->yellow->system->get("extensionDir"); foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.txt$/", true, false) as $entry) { @@ -67,48 +97,44 @@ class YellowUpdate { if (!empty($languageName)) { $entryNew = $path.$languageName."-language.txt"; if (!$this->yellow->toolbox->renameFile($entry, $entryNew)) { - $fileDataError .= "ERROR renaming file '$entry'!\n"; + $this->yellow->log("error", "Can't rename file '$entry'!"); } $fileNameNew = $path.$languageName.".php"; $fileDataNew = "<?php\n\nclass Yellow".ucfirst($languageName)." {\nconst VERSION = \"0.8.2\";\nconst TYPE = \"language\";\n}\n"; if (!$this->yellow->toolbox->createFile($fileNameNew, $fileDataNew)) { - $fileDataError .= "ERROR writing file '$fileNameNew'!\n"; + $this->yellow->log("error", "Can't write file '$fileNameNew'!"); } } } $fileName = $this->yellow->system->get("extensionDir")."language.php"; if (is_file($fileName) && !$this->yellow->toolbox->deleteFile($fileName, $this->yellow->system->get("trashDir"))) { - $fileDataError .= "ERROR deleting file '$fileName'!\n"; + $this->yellow->log("error", "Can't delete file '$fileName'!"); } - if (!empty($fileDataError)) $this->yellow->toolbox->createFile($fileNameError, $fileDataError); } } - if ($update) { //TODO: remove later, converts old Markdown files - $fileNameError = $this->yellow->system->get("settingDir")."system-error.log"; + if ($action=="update") { //TODO: remove later, converts old Markdown files if ($this->yellow->system->get("contentDefaultFile")=="page.txt") { $settings = array("contentDefaultFile" => "page.md", "contentExtension" => ".md", "editNewFile" => "page-new-(.*).md"); $fileName = $this->yellow->system->get("settingDir").$this->yellow->system->get("systemFile"); if (!$this->yellow->system->save($fileName, $settings)) { - $fileDataError .= "ERROR writing file '$fileName'!\n"; + $this->yellow->log("error", "Can't write file '$fileName'!"); } $path = $this->yellow->system->get("contentDir"); foreach ($this->yellow->toolbox->getDirectoryEntriesRecursive($path, "/^.*\.txt$/", true, false) as $entry) { if (!$this->yellow->toolbox->renameFile($entry, str_replace(".txt", ".md", $entry))) { - $fileDataError .= "ERROR renaming file '$entry'!\n"; + $this->yellow->log("error", "Can't rename file '$entry'!"); } } $path = $this->yellow->system->get("settingDir"); foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.txt$/", true, false) as $entry) { if (!$this->yellow->toolbox->renameFile($entry, str_replace(".txt", ".md", $entry))) { - $fileDataError .= "ERROR renaming file '$entry!'\n"; + $this->yellow->log("error", "Can't rename file '$entry'!"); } } - if (!empty($fileDataError)) $this->yellow->toolbox->createFile($fileNameError, $fileDataError); $_GET["clean-url"] = "system-updated"; } } - if ($update) { //TODO: remove later, converts old template setting - $fileNameError = $this->yellow->system->get("settingDir")."system-error.log"; + if ($action=="update") { //TODO: remove later, converts old template setting if ($this->yellow->system->isExisting("template")) { $path = $this->yellow->system->get("contentDir"); foreach ($this->yellow->toolbox->getDirectoryEntriesRecursive($path, "/^.*\.md$/", true, false) as $entry) { @@ -116,35 +142,33 @@ class YellowUpdate { $fileDataNew = preg_replace("/Template:/i", "Layout:", $fileDataNew); $fileDataNew = preg_replace("/TemplateNew:/i", "LayoutNew:", $fileDataNew); if ($fileData!=$fileDataNew && !$this->yellow->toolbox->createFile($entry, $fileDataNew)) { - $fileDataError .= "ERROR writing file '$entry'!\n"; + $this->yellow->log("error", "Can't write file '$entry'!"); } } - if (!empty($fileDataError)) $this->yellow->toolbox->createFile($fileNameError, $fileDataError); $_GET["clean-url"] = "system-updated"; } } - if ($update) { //TODO: remove later, updates shared pages - $fileNameError = $this->yellow->system->get("settingDir")."system-error.log"; + if ($action=="update") { //TODO: remove later, updates shared pages $pathSetting = $this->yellow->system->get("settingDir"); $pathShared = $this->yellow->system->get("contentDir").$this->yellow->system->get("contentSharedDir"); if (count($this->yellow->toolbox->getDirectoryEntries($pathSetting, "/.*/", false, false))>3) { $regex = "/^page-error-(.*)\.md$/"; foreach ($this->yellow->toolbox->getDirectoryEntries($pathSetting, $regex, true, false) as $entry) { if (!$this->yellow->toolbox->deleteFile($entry, $this->yellow->system->get("trashDir"))) { - $fileDataError .= "ERROR deleting file '$entry'!\n"; + $this->yellow->log("error", "Can't delete file '$entry'!"); } } $regex = "/^page-new-(.*)\.md$/"; foreach ($this->yellow->toolbox->getDirectoryEntries($pathSetting, $regex, true, false) as $entry) { if (!$this->yellow->toolbox->renameFile($entry, str_replace($pathSetting, $pathShared, $entry), true)) { - $fileDataError .= "ERROR moving file '$entry'!\n"; + $this->yellow->log("error", "Can't move file '$entry'!"); } } $fileNameHeader = $pathShared."header.md"; if (!is_file($fileNameHeader) && $this->yellow->system->isExisting("tagline")) { $fileDataHeader = "---\nTitle: Header\nStatus: hidden\n---\n".$this->yellow->system->get("tagline"); if (!$this->yellow->toolbox->createFile($fileNameHeader, $fileDataHeader, true)) { - $fileDataError .= "ERROR writing file '$fileNameHeader'!\n"; + $this->yellow->log("error", "Can't write file '$fileNameHeader'!"); } } $fileNameFooter = $pathShared."footer.md"; @@ -152,75 +176,63 @@ class YellowUpdate { $fileDataFooter = "---\nTitle: Footer\nStatus: hidden\n---\n"; $fileDataFooter .= $this->yellow->text->getText("InstallFooterText", $this->yellow->system->get("language")); if (!$this->yellow->toolbox->createFile($fileNameFooter, $fileDataFooter, true)) { - $fileDataError .= "ERROR writing file '$fileNameFooter'!\n"; + $this->yellow->log("error", "Can't write file '$fileNameFooter'!"); } } $this->updateContentMultiLanguage("shared-pages"); - if (!empty($fileDataError)) $this->yellow->toolbox->createFile($fileNameError, $fileDataError); } } - if ($update) { - $fileName = $this->yellow->system->get("settingDir").$this->yellow->system->get("systemFile"); - $fileData = $this->yellow->toolbox->readFile($fileName); - $fileDataHeader = $fileDataSettings = $fileDataFooter = ""; - $settings = new YellowDataCollection(); - $settings->exchangeArray($this->yellow->system->settingsDefaults->getArrayCopy()); - foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) { - preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); - if (empty($fileDataHeader) && preg_match("/^\#/", $line)) { - $fileDataHeader = $line; - } elseif (!empty($matches[1]) && !is_null($settings[$matches[1]])) { - $settings[$matches[1]] = $matches[2]; - } elseif (!empty($matches[1]) && $matches[1][0]!="#") { - $fileDataFooter .= "# $line"; - } elseif (!empty($matches[1])) { - $fileDataFooter .= $line; - } - } - unset($settings["systemFile"]); - foreach ($settings as $key=>$value) { - $fileDataSettings .= ucfirst($key).(strempty($value) ? ":\n" : ": $value\n"); - if ($key=="updateWaffleFile") $fileDataSettings .= "\n"; + if ($action=="update") { //TODO: remove later, converts old editor setting + if ($this->yellow->system->isExisting("editLoginRestrictions")) { + $editLoginRestriction = $this->yellow->system->get("editLoginRestrictions"); + $fileName = $this->yellow->system->get("settingDir").$this->yellow->system->get("systemFile"); + $this->yellow->system->save($fileName, array("editLoginRestriction" => $editLoginRestriction)); } - if (!empty($fileDataHeader)) $fileDataHeader .= "\n"; - if (!empty($fileDataFooter)) $fileDataSettings .= "\n"; - $fileDataNew = $fileDataHeader.$fileDataSettings.$fileDataFooter; - if ($fileData!=$fileDataNew) $this->yellow->toolbox->createFile($fileName, $fileDataNew); } - } - - // Handle request - public function onRequest($scheme, $address, $base, $location, $fileName) { - $statusCode = 0; - if ($this->yellow->lookup->isContentFile($fileName) && $this->isExtensionPending()) { - $statusCode = $this->processRequestPending($scheme, $address, $base, $location, $fileName); + if ($action=="startup") { //TODO: remove later, converts old startup setting + if ($this->yellow->system->isExisting("startupUpdate")) { + $fileName = $this->yellow->system->get("settingDir").$this->yellow->system->get("systemFile"); + $this->yellow->system->save($fileName, array("startupUpdate"=>"none", "updateNotification" => "update/update")); + } } - return $statusCode; - } - - // Handle command - public function onCommand($args) { - $statusCode = 0; - if ($this->isExtensionPending()) $statusCode = $this->processCommandPending(); - if ($statusCode==0) { - list($command) = $args; - switch ($command) { - case "clean": $statusCode = $this->processCommandClean($args); break; - case "install": $statusCode = $this->processCommandInstall($args); break; - case "uninstall": $statusCode = $this->processCommandUninstall($args); break; - case "update": $statusCode = $this->processCommandUpdate($args); break; - default: $statusCode = 0; break; + if ($action=="startup") { + if ($this->yellow->system->get("updateNotification")!="none") { + foreach (explode(",", $this->yellow->system->get("updateNotification")) as $token) { + list($extension, $action) = explode("/", $token, 2); + if ($this->yellow->extensions->isExisting($extension) && ($action!="startup" && $action!="uninstall")) { + $value = $this->yellow->extensions->extensions[$extension]; + if (method_exists($value["obj"], "onUpdate")) $value["obj"]->onUpdate($action); + } + } + $fileName = $this->yellow->system->get("settingDir").$this->yellow->system->get("systemFile"); + $this->yellow->system->save($fileName, array("updateNotification" => "none")); + $fileData = $this->yellow->toolbox->readFile($fileName); + $fileDataHeader = $fileDataSettings = $fileDataFooter = ""; + $settings = new YellowDataCollection(); + $settings->exchangeArray($this->yellow->system->settingsDefaults->getArrayCopy()); + foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) { + preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); + if (empty($fileDataHeader) && preg_match("/^\#/", $line)) { + $fileDataHeader = $line; + } elseif (!empty($matches[1]) && !is_null($settings[$matches[1]])) { + $settings[$matches[1]] = $matches[2]; + } elseif (!empty($matches[1]) && $matches[1][0]!="#") { + $fileDataFooter .= "# $line"; + } elseif (!empty($matches[1])) { + $fileDataFooter .= $line; + } + } + unset($settings["systemFile"]); + foreach ($settings as $key=>$value) { + $fileDataSettings .= ucfirst($key).(strempty($value) ? ":\n" : ": $value\n"); + if ($key=="updateNotification") $fileDataSettings .= "\n"; + } + if (!empty($fileDataHeader)) $fileDataHeader .= "\n"; + if (!empty($fileDataFooter)) $fileDataSettings .= "\n"; + $fileDataNew = $fileDataHeader.$fileDataSettings.$fileDataFooter; + if ($fileData!=$fileDataNew) $this->yellow->toolbox->createFile($fileName, $fileDataNew); } } - return $statusCode; - } - - // Handle command help - public function onCommandHelp() { - $help .= "install [extension]\n"; - $help .= "uninstall [extension]\n"; - $help .= "update [extension]\n"; - return $help; } // Process command to clean downloads @@ -245,7 +257,7 @@ class YellowUpdate { $this->updates = 0; list($statusCode, $data) = $this->getInstallInformation($extensions); if ($statusCode==200) $statusCode = $this->downloadExtensions($data); - if ($statusCode==200) $statusCode = $this->updateExtensions(); + if ($statusCode==200) $statusCode = $this->updateExtensions("install"); if ($statusCode>=400) echo "ERROR installing files: ".$this->yellow->page->get("pageError")."\n"; echo "Yellow $command: Website ".($statusCode!=200 ? "not " : "")."updated"; echo ", $this->updates extension".($this->updates!=1 ? "s" : "")." installed\n"; @@ -278,7 +290,7 @@ class YellowUpdate { if ($statusCode!=200 || !empty($data)) { $this->updates = 0; if ($statusCode==200) $statusCode = $this->downloadExtensions($data); - if ($statusCode==200) $statusCode = $this->updateExtensions($force); + if ($statusCode==200) $statusCode = $this->updateExtensions("update", $force); if ($statusCode>=400) echo "ERROR updating files: ".$this->yellow->page->get("pageError")."\n"; echo "Yellow $command: Website ".($statusCode!=200 ? "not " : "")."updated"; echo ", $this->updates update".($this->updates!=1 ? "s" : "")." installed\n"; @@ -288,17 +300,17 @@ class YellowUpdate { return $statusCode; } - // Process command to update website with pending extension + // Process command to install pending extension public function processCommandPending() { - $statusCode = $this->updateExtensions(); + $statusCode = $this->updateExtensions("install"); if ($statusCode!=200) echo "ERROR updating files: ".$this->yellow->page->get("pageError")."\n"; echo "Your website has ".($statusCode!=200 ? "not " : "")."been updated: Please run command again\n"; return $statusCode; } - // Process request to update website with pending extension + // Process request to install pending extension public function processRequestPending($scheme, $address, $base, $location, $fileName) { - $statusCode = $this->updateExtensions(); + $statusCode = $this->updateExtensions("install"); if ($statusCode==200) { $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); $statusCode = $this->yellow->sendStatus(303, $location); @@ -306,6 +318,25 @@ class YellowUpdate { return $statusCode; } + // Process update notification + public function processUpdateNotification($extension, $action) { + $statusCode = 200; + if ($this->yellow->extensions->isExisting($extension) && $action=="uninstall") { + $value = $this->yellow->extensions->extensions[$extension]; + if (method_exists($value["obj"], "onUpdate")) $value["obj"]->onUpdate($action); + } + $updateNotification = $this->yellow->system->get("updateNotification"); + if ($updateNotification=="none") $updateNotification = ""; + if (!empty($updateNotification)) $updateNotification .= ","; + $updateNotification .= "$extension/$action"; + $fileName = $this->yellow->system->get("settingDir").$this->yellow->system->get("systemFile"); + if (!$this->yellow->system->save($fileName, array("updateNotification" => $updateNotification))) { + $statusCode = 500; + $this->yellow->page->error(500, "Can't write file '$fileName'!"); + } + return $statusCode; + } + // Return extension information public function getExtensionInformation($args) { $command = array_shift($args); @@ -453,12 +484,12 @@ class YellowUpdate { } // Update extensions - public function updateExtensions($force = false) { + public function updateExtensions($action, $force = false) { $statusCode = 200; if (function_exists("opcache_reset")) opcache_reset(); $path = $this->yellow->system->get("extensionDir"); foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.zip$/", true, false) as $entry) { - $statusCode = max($statusCode, $this->updateExtensionArchive($entry, $force)); + $statusCode = max($statusCode, $this->updateExtensionArchive($entry, $action, $force)); if (!$this->yellow->toolbox->deleteFile($entry)) { $statusCode = 500; $this->yellow->page->error($statusCode, "Can't delete file '$entry'!"); @@ -468,7 +499,7 @@ class YellowUpdate { } // Update extension from archive - public function updateExtensionArchive($path, $force = false) { + public function updateExtensionArchive($path, $action, $force = false) { $statusCode = 200; $zip = new ZipArchive(); if ($zip->open($path)===true) { @@ -478,12 +509,19 @@ class YellowUpdate { if (empty($fileData)) $fileData = $zip->getFromName($pathBase.$this->yellow->system->get("updateInformationFile")); //TODO: remove later, for backwards compatibility foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) { preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); - if (!empty($matches[1]) && !empty($matches[2])) { - list($fileName) = explode(",", $matches[2], 2); + if (!empty($matches[1]) && !empty($matches[2]) && strposu($matches[1], "/")) { + $fileName = $matches[1]; if (is_file($fileName)) { $lastPublished = filemtime($fileName); break; } + if (ctype_upper($matches[1][0])) { //TODO: remove later, converts old format + list($fileName) = explode(",", $matches[2], 2); + if (is_file($fileName)) { + $lastPublished = filemtime($fileName); + break; + } + } } } foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) { @@ -491,10 +529,15 @@ class YellowUpdate { if (lcfirst($matches[1])=="extension") $extension = lcfirst($matches[2]); if (lcfirst($matches[1])=="plugin") $extension = lcfirst(substru($matches[2], 6)); //TODO: remove later, for backwards compatibility if (lcfirst($matches[1])=="theme") $extension = lcfirst(substru($matches[2], 11)); //TODO: remove later, for backwards compatibility + if (lcfirst($matches[1])=="version") $version = lcfirst($matches[2]); if (lcfirst($matches[1])=="published") $modified = strtotime($matches[2]); if (!empty($matches[1]) && !empty($matches[2]) && strposu($matches[1], "/")) { - list($dummy, $entry) = explode("/", $matches[1], 2); - list($fileName, $flags) = explode(",", $matches[2], 2); + $fileName = $matches[1]; + list($dummy, $entry, $flags) = explode(",", $matches[2], 3); + if (ctype_upper($matches[1][0])) { //TODO: remove later, converts old format + list($dummy, $entry) = explode("/", $matches[1], 2); + list($fileName, $flags) = explode(",", $matches[2], 2); + } $fileData = $zip->getFromName($pathBase.basename($entry)); $lastModified = $this->yellow->toolbox->getFileModified($fileName); $statusCode = $this->updateExtensionFile($fileName, $fileData, $modified, $lastModified, $lastPublished, $flags, $force, $extension); @@ -502,8 +545,9 @@ class YellowUpdate { } } $zip->close(); - if ($statusCode==200) $statusCode = $this->updateContentMultiLanguage($extension); - if ($statusCode==200) $statusCode = $this->updateStartupNotification($extension); + $statusCode = max($statusCode, $this->updateContentMultiLanguage($extension)); + $statusCode = max($statusCode, $this->processUpdateNotification($extension, $action)); + $this->yellow->log($statusCode==200 ? "info" : "error", ucfirst($action)." extension '".ucfirst($extension)." $version'"); ++$this->updates; } else { $statusCode = 500; @@ -595,20 +639,6 @@ class YellowUpdate { return $statusCode; } - // Update notification for next startup - public function updateStartupNotification($extension) { - $statusCode = 200; - $startupUpdate = $this->yellow->system->get("startupUpdate"); - if ($startupUpdate=="none") $startupUpdate = "update"; - if ($extension!="update") $startupUpdate .= ",$extension"; - $fileName = $this->yellow->system->get("settingDir").$this->yellow->system->get("systemFile"); - if (!$this->yellow->system->save($fileName, array("startupUpdate" => $startupUpdate))) { - $statusCode = 500; - $this->yellow->page->error(500, "Can't write file '$fileName'!"); - } - return $statusCode; - } - // Remove extensions public function removeExtensions($data) { $statusCode = 200; @@ -617,9 +647,11 @@ class YellowUpdate { foreach (preg_split("/\s*,\s*/", $value) as $fileName) { $statusCode = max($statusCode, $this->removeExtensionsFile($fileName, $key)); } + $statusCode = max($statusCode, $this->processUpdateNotification($key, "uninstall")); + $version = $this->yellow->extensions->isExisting($key) ? $this->yellow->extensions->extensions[$key]["version"] : ""; + $this->yellow->log($statusCode==200 ? "info" : "error", "Uninstall extension '".ucfirst($key)." $version'"); ++$this->updates; } - if ($statusCode==200) $statusCode = $this->updateStartupNotification("update"); return $statusCode; } @@ -671,8 +703,12 @@ class YellowUpdate { foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) { preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); if (!empty($matches[1]) && !empty($matches[2])) { - list($extension) = explode("/", lcfirst($matches[1])); - list($fileName) = explode(",", $matches[2], 2); + $fileName = $matches[1]; + list($extension) = explode(",", lcfirst($matches[2]), 3); + if (ctype_upper($matches[1][0])) { //TODO: remove later, converts old format + list($extension) = explode("/", lcfirst($matches[1])); + list($fileName) = explode(",", $matches[2], 2); + } if (!is_null($data[$extension])) $data[$extension] .= ","; $data[$extension] .= $fileName; } @@ -691,8 +727,12 @@ class YellowUpdate { foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) { preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches); if (!empty($matches[1]) && !empty($matches[2])) { - list($extensionNew) = explode("/", lcfirst($matches[1])); - list($fileName, $flags) = explode(",", $matches[2], 2); + $fileName = $matches[1]; + list($extensionNew, $dummy, $flags) = explode(",", lcfirst($matches[2]), 3); + if (ctype_upper($matches[1][0])) { //TODO: remove later, converts old format + list($extensionNew) = explode("/", lcfirst($matches[1])); + list($fileName, $flags) = explode(",", $matches[2], 2); + } if ($extension!=$extensionNew) { $extension = $extensionNew; $lastPublished = $this->yellow->toolbox->getFileModified($fileName); diff --git a/system/settings/system.ini b/system/settings/system.ini @@ -40,15 +40,16 @@ ContentDefaultFile: page.md ContentExtension: .md DownloadExtension: .download TextFile: text.ini +LogFile: yellow.log SafeMode: 0 MultiLanguageMode: 0 -StartupUpdate: none ServerUrl: UpdateExtensionUrl: https://github.com/datenstrom/yellow-extensions UpdateInformationFile: update.ini UpdateExtensionFile: extension.ini UpdateVersionFile: version.ini UpdateWaffleFile: waffle.ini +UpdateNotification: none BundleAndMinify: 1 EditLocation: /edit/ @@ -63,7 +64,7 @@ EditUserHashAlgorithm: bcrypt EditUserHashCost: 10 EditUserHome: / EditNewFile: page-new-(.*).md -EditLoginRestrictions: 0 +EditLoginRestriction: 0 EditLoginSessionTimeout: 2592000 EditBruteForceProtection: 25 ImageAlt: Image