diff --git a/.htaccess b/.htaccess index a9269a6..87e7af4 100644 --- a/.htaccess +++ b/.htaccess @@ -3,25 +3,11 @@ RewriteEngine on # Yellow dynamic pages -RewriteCond %{ENV:REDIRECT_STATUS} ^$ -RewriteRule ^(content|system)/ error404 [L] -RewriteCond %{REQUEST_FILENAME} !-f -RewriteRule ^robots.txt$ system/config/robots.txt [L] -RewriteCond %{REQUEST_FILENAME} !-f -RewriteCond %{REQUEST_URI} \.(css|js|jpg|png|woff)$ -RewriteRule ^media/plugins/(core-.+) system/core/$1 [L] -RewriteCond %{REQUEST_FILENAME} !-f -RewriteCond %{REQUEST_URI} \.(css|js|jpg|png|woff)$ -RewriteRule ^media/plugins/(.+) system/plugins/$1 [L] -RewriteCond %{REQUEST_FILENAME} !-f -RewriteCond %{REQUEST_URI} \.(css|js|jpg|png|woff)$ -RewriteRule ^media/themes/(.+) system/themes/$1 [L] - -RewriteCond yellow.php -F RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^ yellow.php [L] DirectoryIndex index.html yellow.php +RewriteRule ^(cache|content|system)/ error404.html [L] # Yellow static pages diff --git a/README.md b/README.md index a8c3f89..c83f153 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Yellow 0.4.19 +Yellow 0.4.20 ============= [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/markseu/yellowcms) diff --git a/system/config/config.ini b/system/config/config.ini index 4e425d6..604b5ae 100644 --- a/system/config/config.ini +++ b/system/config/config.ini @@ -15,12 +15,16 @@ pluginLocation = /media/plugins/ themeLocation = /media/themes/ systemDir = system/ configDir = system/config/ +coreDir = system/core/ pluginDir = system/plugins/ snippetDir = system/snippets/ templateDir = system/templates/ themeDir = system/themes/ mediaDir = media/ imageDir = media/images/ +staticDir = cache/ +staticDefaultFile = index.html +staticErrorFile = error404.html contentDir = content/ contentRootDir = default/ contentHomeDir = home/ @@ -31,6 +35,7 @@ configExtension = .ini errorPageFile = error(.*).txt newPageFile = new(.*).txt textStringFile = language(.*).ini +robotsTextFile = robots.txt parser = markdownextra parserSafeMode = 0 multiLanguageMode = 0 @@ -40,6 +45,5 @@ webinterfaceUserHashAlgorithm = bcrypt webinterfaceUserHashCost = 10 webinterfaceUserFile = user.ini webinterfaceFilePrefix = published -commandlineDefaultFile = index.html -commandlineErrorFile = error404.html -commandlineSystemFile = .htaccess, system/config/robots.txt +commandlineIgnoreLocation = +commandlineSystemFile = .htaccess diff --git a/system/core/core-commandline.php b/system/core/core-commandline.php index 346fefb..3e2515f 100755 --- a/system/core/core-commandline.php +++ b/system/core/core-commandline.php @@ -5,7 +5,7 @@ // Command line core plugin class YellowCommandline { - const Version = "0.4.3"; + const Version = "0.4.4"; var $yellow; //access to API var $content; //number of content pages var $media; //number of media files @@ -13,14 +13,12 @@ class YellowCommandline var $error; //number of build errors var $locationsArguments; //locations with arguments detected var $locationsPagination; //locations with pagination detected - var $fileNamesPlugin; //plugin files detected // Handle plugin initialisation function onLoad($yellow) { $this->yellow = $yellow; - $this->yellow->config->setDefault("commandlineDefaultFile", "index.html"); - $this->yellow->config->setDefault("commandlineErrorFile", "error404.html"); + $this->yellow->config->setDefault("commandlineIgnoreLocation", ""); $this->yellow->config->setDefault("commandlineSystemFile", ".htaccess"); } @@ -28,8 +26,8 @@ class YellowCommandline function onCommandHelp() { $help .= "version\n"; - $help .= "build DIRECTORY [LOCATION]\n"; - $help .= "clean DIRECTORY [LOCATION]\n"; + $help .= "build [DIRECTORY LOCATION]\n"; + $help .= "clean [DIRECTORY LOCATION]\n"; return $help; } @@ -74,11 +72,11 @@ class YellowCommandline { $statusCode = 0; list($dummy, $command, $path, $location) = $args; - if(!empty($path) && $path!="/" && (empty($location) || $location[0]=='/')) + if(empty($location) || $location[0]=='/') { if($this->checkStaticConfig()) { - $statusCode = $this->buildStatic($location, $path); + $statusCode = $this->buildStatic($path, $location); } else { $statusCode = 500; list($this->content, $this->media, $this->system, $this->error) = array(0, 0, 0, 1); @@ -95,60 +93,52 @@ class YellowCommandline return $statusCode; } - // Build static locations and files - function buildStatic($location, $path) + // Build static directories and files + function buildStatic($path, $location) { $this->yellow->toolbox->timerStart($time); - $pluginDir = $this->yellow->config->get("pluginDir"); - $pathPlugin = rtrim($path.$this->yellow->config->get("pluginLocation"), '/'); + $path = rtrim(empty($path) ? $this->yellow->config->get("staticDir") : $path, '/'); $this->content = $this->media = $this->system = $this->error = $statusCode = 0; - $this->locationsArguments = $this->locationsPagination = $this->fileNamesPlugin = array(); + $this->locationsArguments = $this->locationsPagination = array(); if(empty($location)) { - $statusCode = $this->cleanStatic($location, $path); + $statusCode = $this->cleanStatic($path, $location); foreach($this->getStaticLocations() as $location) { - $statusCode = max($statusCode, $this->buildStaticLocation($location, $path, true)); + $statusCode = max($statusCode, $this->buildStaticRequest($path, $location, true)); } foreach($this->locationsArguments as $location) { - $statusCode = max($statusCode, $this->buildStaticLocation($location, $path, true)); + $statusCode = max($statusCode, $this->buildStaticRequest($path, $location, true)); } foreach($this->locationsPagination as $location) { for($pageNumber=2; $pageNumber<=999; ++$pageNumber) { - $statusCodeLocation = $this->buildStaticLocation($location.$pageNumber, $path, false, true); + $statusCodeLocation = $this->buildStaticRequest($path, $location.$pageNumber, false, true); $statusCode = max($statusCode, $statusCodeLocation); if($statusCodeLocation == 0) break; } } - $fileNamesMedia = $this->yellow->toolbox->getDirectoryEntriesRecursive( - $this->yellow->config->get("mediaDir"), "/.*/", false, false); - $fileNamesSystem = preg_split("/,\s*/", $this->yellow->config->get("commandlineSystemFile")); - array_push($fileNamesSystem, $this->yellow->config->get("commandlineErrorFile")); - foreach($fileNamesMedia as $fileName) + $statusCode = max($statusCode, $this->buildStaticError($path, 404)); + foreach($this->getStaticFilesMedia($path) as $fileNameSource=>$fileNameDest) { - $statusCode = max($statusCode, $this->buildStaticFile($fileName, "$path/$fileName")); + $statusCode = max($statusCode, $this->buildStaticFile($fileNameSource, $fileNameDest, true)); } - foreach($this->fileNamesPlugin as $fileName) + foreach($this->getStaticFilesSystem($path) as $fileNameSource=>$fileNameDest) { - $statusCode = max($statusCode, $this->buildStaticFile("$pluginDir$fileName", "$pathPlugin/$fileName")); - } - foreach($fileNamesSystem as $fileName) - { - $statusCode = max($statusCode, $this->buildStaticFile($fileName, "$path/".basename($fileName), false)); + $statusCode = max($statusCode, $this->buildStaticFile($fileNameSource, $fileNameDest, false)); } } else { - $statusCode = $this->buildStaticLocation($location, $path); + $statusCode = $this->buildStaticRequest($path, $location); } $this->yellow->toolbox->timerStop($time); if(defined("DEBUG") && DEBUG>=1) echo "YellowCommandline::buildStatic time:$time ms\n"; return $statusCode; } - // Build static location - function buildStaticLocation($location, $path, $analyse = false, $probe = false) + // Build static request + function buildStaticRequest($path, $location, $analyse = false, $probe = false) { ob_start(); $_SERVER["SERVER_PROTOCOL"] = "HTTP/1.1"; @@ -157,11 +147,12 @@ class YellowCommandline $_SERVER["SCRIPT_NAME"] = $this->yellow->config->get("serverBase")."yellow.php"; $_REQUEST = array(); $statusCode = $this->yellow->request(); - if($statusCode != 404) + if($statusCode < 400) { - $modified = strtotime($this->yellow->page->getHeader("Last-Modified")); - $fileName = $this->getStaticFileName($location, $path); + $fileName = $this->yellow->toolbox->findStaticFileFromLocation($location, $path, + $this->yellow->config->get("staticDefaultFile")); $fileData = ob_get_contents(); + $modified = strtotime($this->yellow->page->getHeader("Last-Modified")); if(!$this->yellow->toolbox->createFile($fileName, $fileData, true) || !$this->yellow->toolbox->modifyFile($fileName, $modified)) { @@ -170,11 +161,7 @@ class YellowCommandline } } ob_end_clean(); - if($statusCode==200 && $analyse) - { - $this->analyseStaticContent($fileData); - $this->analyseStaticMedia($fileData); - } + if($statusCode==200 && $analyse) $this->analyseStaticContent($fileData); if($statusCode==404 && $probe) $statusCode = 0; if($statusCode != 0) ++$this->content; if($statusCode >= 400) @@ -183,50 +170,57 @@ class YellowCommandline echo "ERROR building content location '$location', ".$this->yellow->page->getStatusCode(true)."\n"; } if(defined("DEBUG") && DEBUG>=3) echo $fileData; - if(defined("DEBUG") && DEBUG>=1) echo "YellowCommandline::buildStaticLocation status:$statusCode location:$location\n"; + if(defined("DEBUG") && DEBUG>=1) echo "YellowCommandline::buildStaticRequest status:$statusCode location:$location\n"; + return $statusCode; + } + + // Build static error + function buildStaticError($path, $statusCodeRequest) + { + ob_start(); + $_SERVER["SERVER_PROTOCOL"] = "HTTP/1.1"; + $_SERVER["SERVER_NAME"] = $this->yellow->config->get("serverName"); + $_SERVER["REQUEST_URI"] = $this->yellow->config->get("serverBase")."/"; + $_SERVER["SCRIPT_NAME"] = $this->yellow->config->get("serverBase")."yellow.php"; + $_REQUEST = array(); + $statusCode = $this->yellow->request($statusCodeRequest); + $fileName = "$path/".$this->yellow->config->get("staticErrorFile"); + if($statusCode == $statusCodeRequest) + { + $statusCode = 200; + $fileData = ob_get_contents(); + $modified = strtotime($this->yellow->page->getHeader("Last-Modified")); + if(!$this->yellow->toolbox->createFile($fileName, $fileData, true) || + !$this->yellow->toolbox->modifyFile($fileName, $modified)) + { + $statusCode = 500; + $this->yellow->page->error($statusCode, "Can't write file '$fileName'!"); + } + } + ob_end_clean(); + ++$this->system; + if($statusCode >= 400) + { + ++$this->error; + echo "ERROR building error file '$fileName', ".$this->yellow->page->getStatusCode(true)."\n"; + } + if(defined("DEBUG") && DEBUG>=1) echo "YellowCommandline::buildStaticError status:$statusCode file:$fileName\n"; return $statusCode; } // Build static file - function buildStaticFile($fileNameSource, $fileNameDest, $fileTypeMedia = true) + function buildStaticFile($fileNameSource, $fileNameDest, $fileTypeMedia) { - if($fileNameSource != $this->yellow->config->get("commandlineErrorFile")) - { - $statusCode = $this->yellow->toolbox->copyFile($fileNameSource, $fileNameDest, true) && - $this->yellow->toolbox->modifyFile($fileNameDest, filemtime($fileNameSource)) ? 200 : 500; - } else { - ob_start(); - $_SERVER["SERVER_PROTOCOL"] = "HTTP/1.1"; - $_SERVER["SERVER_NAME"] = $this->yellow->config->get("serverName"); - $_SERVER["REQUEST_URI"] = $this->yellow->config->get("serverBase")."/"; - $_SERVER["SCRIPT_NAME"] = $this->yellow->config->get("serverBase")."yellow.php"; - $_REQUEST = array(); - $statusCodeRequest = 404; - $statusCode = $this->yellow->request($statusCodeRequest); - if($statusCode == $statusCodeRequest) - { - $statusCode = 200; - $modified = strtotime($this->yellow->page->getHeader("Last-Modified")); - if(!$this->yellow->toolbox->createFile($fileNameDest, ob_get_contents(), true) || - !$this->yellow->toolbox->modifyFile($fileNameDest, $modified)) - { - $statusCode = 500; - $this->yellow->page->error($statusCode, "Can't write file '$fileNameDest'!"); - } - } else { - $statusCode = 500; - $this->yellow->page->error($statusCode, "Error $statusCodeRequest does not exist!"); - } - ob_end_clean(); - } + $statusCode = $this->yellow->toolbox->copyFile($fileNameSource, $fileNameDest, true) && + $this->yellow->toolbox->modifyFile($fileNameDest, filemtime($fileNameSource)) ? 200 : 500; if($fileTypeMedia) { ++$this->media; } else { ++$this->system; } if($statusCode >= 400) { ++$this->error; $fileType = $fileTypeMedia ? "media file" : "system file"; - echo "ERROR building $fileType '$fileNameSource', ".$this->yellow->toolbox->getHttpStatusFormatted($statusCode)."\n"; + echo "ERROR building $fileType '$fileNameDest', ".$this->yellow->toolbox->getHttpStatusFormatted($statusCode)."\n"; } - if(defined("DEBUG") && DEBUG>=1) echo "YellowCommandline::buildStaticFile status:$statusCode file:$fileNameSource\n"; + if(defined("DEBUG") && DEBUG>=1) echo "YellowCommandline::buildStaticFile status:$statusCode file:$fileNameDest\n"; return $statusCode; } @@ -266,38 +260,14 @@ class YellowCommandline } } - // Analyse static media, detect plugin files - function analyseStaticMedia($text) - { - $serverName = $this->yellow->config->get("serverName"); - $serverBase = $this->yellow->config->get("serverBase"); - $pluginLocation = $this->yellow->config->get("pluginLocation"); - preg_match_all("#<(.*?)\"([^\"]*{$pluginLocation}[^\"]+)\"(.*?)>#", $text, $matches); - foreach($matches[2] as $match) - { - if(preg_match("/^\w+:\/+(.*?)(\/.*)$/", $match, $tokens)) - { - if($tokens[1] != $serverName) continue; - $match = $tokens[2]; - } - if(substru($match, 0, strlenu($serverBase.$pluginLocation)) != $serverBase.$pluginLocation) continue; - $fileName = rawurldecode(substru($match, strlenu($serverBase.$pluginLocation))); - if(is_null($this->fileNamesPlugin[$fileName])) - { - $this->fileNamesPlugin[$fileName] = $fileName; - if(defined("DEBUG") && DEBUG>=2) echo "YellowCommandline::analyseStaticMedia type:plugin file:$fileName\n"; - } - } - } - // Clean static pages function cleanCommand($args) { $statusCode = 0; list($dummy, $command, $path, $location) = $args; - if(!empty($path) && $path!="/" && (empty($location) || $location[0]=='/')) + if(empty($location) || $location[0]=='/') { - $statusCode = $this->cleanStatic($location, $path); + $statusCode = $this->cleanStatic($path, $location); echo "Yellow $command: Static page".(empty($location) ? "s" : "")." ".($statusCode!=200 ? "not " : "")."cleaned\n"; } else { $statusCode = 400; @@ -306,16 +276,17 @@ class YellowCommandline return $statusCode; } - // Clean static directory and files - function cleanStatic($location, $path) + // Clean static directories and files + function cleanStatic($path, $location) { $statusCode = 200; + $path = rtrim(empty($path) ? $this->yellow->config->get("staticDir") : $path, '/'); if(empty($location)) { $statusCode = max($statusCode, $this->pluginCommand(array("all", "clean"))); $statusCode = max($statusCode, $this->cleanStaticDirectory($path)); } else { - $statusCode = $this->cleanStaticFile($location, $path); + $statusCode = $this->cleanStaticFile($path, $location); } return $statusCode; } @@ -326,7 +297,7 @@ class YellowCommandline $statusCode = 200; if($this->isYellowDirectory($path)) { - if(is_file("$path/yellow.php") || !$this->yellow->toolbox->deleteDirectory($path, true)) + if(is_file("$path/yellow.php") || $path=="/" || !$this->yellow->toolbox->deleteDirectory($path, true)) { $statusCode = 500; echo "ERROR cleaning pages: Can't delete directory '$path'!\n"; @@ -336,10 +307,11 @@ class YellowCommandline } // Clean static file - function cleanStaticFile($location, $path) + function cleanStaticFile($path, $location) { $statusCode = 200; - $fileName = $this->getStaticFileName($location, $path); + $fileName = $this->yellow->toolbox->findStaticFileFromLocation($location, $path, + $this->yellow->config->get("staticDefaultFile")); if($this->isYellowDirectory($path) && is_file($fileName)) { $entry = basename($fileName); @@ -377,27 +349,62 @@ class YellowCommandline $serverScheme = $this->yellow->config->get("serverScheme"); $serverName = $this->yellow->config->get("serverName"); $serverBase = $this->yellow->config->get("serverBase"); - return !empty($serverScheme) && !empty($serverName) && $this->yellow->toolbox->isValidLocation($serverBase) && $serverBase!="/"; + return !empty($serverScheme) && !empty($serverName) && + $this->yellow->toolbox->isValidLocation($serverBase) && $serverBase!="/"; } - // Return all static locations from file system + // Return static locations from file system function getStaticLocations() { $locations = array(); - foreach($this->yellow->pages->index(true, true) as $page) array_push($locations, $page->location); + $serverScheme = $this->yellow->config->get("serverScheme"); + $serverName = $this->yellow->config->get("serverName"); + $serverBase = $this->yellow->config->get("serverBase"); + $this->yellow->page->setRequestInformation($serverScheme, $serverName, $serverBase, "", ""); + if($this->yellow->config->isExisting("commandlineIgnoreLocation")) + { + $regex = "#^".$this->yellow->config->get("commandlineIgnoreLocation")."#"; + } + foreach($this->yellow->pages->index(true, true) as $page) + { + if(!empty($regex) && preg_match($regex, $page->location)) continue; + array_push($locations, $page->location); + } if(!$this->yellow->pages->find("/") && $this->yellow->config->get("multiLanguageMode")) array_unshift($locations, "/"); return $locations; } - // Return static file name from location - function getStaticFileName($location, $path) + // Return static media files + function getStaticFilesMedia($path) { - $fileName = $path.$location; - if(!$this->yellow->toolbox->isFileLocation($location)) + $files = array(); + $fileNames = $this->yellow->toolbox->getDirectoryEntriesRecursive( + $this->yellow->config->get("mediaDir"), "/.*/", false, false); + foreach($fileNames as $fileName) $files[$fileName] = "$path/$fileName"; + return $files; + } + + // Return static system files + function getStaticFilesSystem($path) + { + $files = array(); + $fileNames = $this->yellow->toolbox->getDirectoryEntries( + $this->yellow->config->get("pluginDir"), "/\.(css|js|jpg|png|txt|woff)/", false, false); + foreach($fileNames as $fileName) { - $fileName .= $this->yellow->config->get("commandlineDefaultFile"); + $files[$fileName] = $path.$this->yellow->config->get("pluginLocation").basename($fileName); } - return $fileName; + $fileNames = $this->yellow->toolbox->getDirectoryEntries( + $this->yellow->config->get("themeDir"), "/\.(css|js|jpg|png|txt|woff)/", false, false); + foreach($fileNames as $fileName) + { + $files[$fileName] = $path.$this->yellow->config->get("themeLocation").basename($fileName); + } + $fileNames = array(); + array_push($fileNames, $this->yellow->config->get("commandlineSystemFile")); + array_push($fileNames, $this->yellow->config->get("configDir").$this->yellow->config->get("robotsTextFile")); + foreach($fileNames as $fileName) $files[$fileName] = "$path/".basename($fileName); + return $files; } // Return command help @@ -431,8 +438,7 @@ class YellowCommandline // Check if directory contains Yellow files function isYellowDirectory($path) { - $fileNamesSystem = preg_split("/,\s*/", $this->yellow->config->get("commandlineSystemFile")); - return is_file("$path/yellow.php") || is_file("$path/$fileNamesSystem[0]"); + return is_file("$path/yellow.php") || is_file("$path/".$this->yellow->config->get("commandlineSystemFile")); } } diff --git a/system/core/core-webinterface.php b/system/core/core-webinterface.php index 5b92fec..b556fec 100755 --- a/system/core/core-webinterface.php +++ b/system/core/core-webinterface.php @@ -5,7 +5,7 @@ // Web interface core plugin class YellowWebinterface { - const Version = "0.4.7"; + const Version = "0.4.8"; var $yellow; //access to API var $active; //web interface is active? (boolean) var $userLoginFailed; //web interface login failed? (boolean) @@ -47,7 +47,7 @@ class YellowWebinterface $locationHeader = $this->yellow->toolbox->getLocationHeader( $this->yellow->config->get("webinterfaceServerScheme"), $this->yellow->config->get("webinterfaceServerName"), $base, $activeLocation); - $this->yellow->sendStatus($statusCode, false, $locationHeader); + $this->yellow->sendStatus($statusCode, $locationHeader, false); } } return $statusCode; @@ -184,8 +184,8 @@ class YellowWebinterface } if($statusCode == 0) { - $statusCode = $this->userLoginFailed ? 401 : 0; - $statusCode = $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, $statusCode, false); + $statusCode = $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, false); + if($this->userLoginFailed) $this->yellow->page->error(401); } return $statusCode; } @@ -196,16 +196,17 @@ class YellowWebinterface $statusCode = 0; if(is_readable($fileName)) { - $statusCode = $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, 0, false); + $statusCode = $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, false); } else { if($this->yellow->toolbox->isFileLocation($location) && $this->yellow->isContentDirectory("$location/")) { $statusCode = 301; $locationHeader = $this->yellow->toolbox->getLocationHeader($serverScheme, $serverName, $base, "$location/"); - $this->yellow->sendStatus($statusCode, false, $locationHeader); + $this->yellow->sendStatus($statusCode, $locationHeader, false); } else { $statusCode = $this->userPermission ? 424 : 404; - $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, $statusCode, false); + $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, false); + $this->yellow->page->error($statusCode); } } return $statusCode; @@ -225,15 +226,15 @@ class YellowWebinterface { $statusCode = 303; $locationHeader = $this->yellow->toolbox->getLocationHeader($serverScheme, $serverName, $base, $page->location); - $this->yellow->sendStatus($statusCode, false, $locationHeader); + $this->yellow->sendStatus($statusCode, $locationHeader, false); } else { $statusCode = 500; - $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, $statusCode, false); + $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, false); $this->yellow->page->error($statusCode, "Can't write file '$page->fileName'!"); } } else { $statusCode = 500; - $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, $statusCode, false); + $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, $false); $this->yellow->page->error($statusCode, $page->get("pageError")); } } @@ -257,15 +258,15 @@ class YellowWebinterface { $statusCode = 303; $locationHeader = $this->yellow->toolbox->getLocationHeader($serverScheme, $serverName, $base, $page->location); - $this->yellow->sendStatus($statusCode, false, $locationHeader); + $this->yellow->sendStatus($statusCode, $locationHeader, false); } else { $statusCode = 500; - $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, $statusCode, false); + $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, false); $this->yellow->page->error($statusCode, "Can't write file '$page->fileName'!"); } } else { $statusCode = 500; - $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, $statusCode, false); + $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, false); $this->yellow->page->error($statusCode, $page->get("pageError")); } } @@ -283,10 +284,10 @@ class YellowWebinterface { $statusCode = 303; $locationHeader = $this->yellow->toolbox->getLocationHeader($serverScheme, $serverName, $base, $location); - $this->yellow->sendStatus($statusCode, false, $locationHeader); + $this->yellow->sendStatus($statusCode, $locationHeader, false); } else { $statusCode = 500; - $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, $statusCode, false); + $this->yellow->processRequest($serverScheme, $serverName, $base, $location, $fileName, false); $this->yellow->page->error($statusCode, "Can't delete file '$fileName'!"); } } @@ -302,11 +303,11 @@ class YellowWebinterface { $statusCode = 303; $locationHeader = $this->yellow->toolbox->getLocationHeader($serverScheme, $serverName, $base, $location); - $this->yellow->sendStatus($statusCode, false, $locationHeader); + $this->yellow->sendStatus($statusCode, $locationHeader, false); } else { $statusCode = 302; $locationHeader = $this->yellow->toolbox->getLocationHeader($serverScheme, $serverName, $base, $home); - $this->yellow->sendStatus($statusCode, false, $locationHeader); + $this->yellow->sendStatus($statusCode, $locationHeader, false); } return $statusCode; } @@ -321,7 +322,7 @@ class YellowWebinterface $this->yellow->config->get("serverScheme"), $this->yellow->config->get("serverName"), $this->yellow->config->get("serverBase"), $location); - $this->yellow->sendStatus($statusCode, false, $locationHeader); + $this->yellow->sendStatus($statusCode, $locationHeader, false); return $statusCode; } @@ -405,8 +406,9 @@ class YellowWebinterface // Return new page function getPageNew($serverScheme, $serverName, $base, $location, $fileName, $rawData) { - $page = new YellowPage($this->yellow, $serverScheme, $serverName, $base, $location, $fileName); - $page->parseData($rawData, 0, false); + $page = new YellowPage($this->yellow); + $page->setRequestInformation($serverScheme, $serverName, $base, $location, $fileName); + $page->parseData($rawData, false); $page->fileName = $this->yellow->toolbox->findFileFromTitle( $page->get($this->yellow->config->get("webinterfaceFilePrefix")), $page->get("title"), $fileName, $this->yellow->config->get("contentDefaultFile"), $this->yellow->config->get("contentExtension")); @@ -441,13 +443,15 @@ class YellowWebinterface // Return modified page function getPageUpdate($serverScheme, $serverName, $base, $location, $fileName, $rawDataSource, $rawDataEdit, $rawDataFile) { - $page = new YellowPage($this->yellow, $serverScheme, $serverName, $base, $location, $fileName); - $page->parseData($this->merge->merge($rawDataSource, $rawDataEdit, $rawDataFile), 0, false); + $page = new YellowPage($this->yellow); + $page->setRequestInformation($serverScheme, $serverName, $base, $location, $fileName); + $page->parseData($this->merge->merge($rawDataSource, $rawDataEdit, $rawDataFile), false); if(empty($page->rawData)) $page->error(500, "Page has been modified by someone else!"); if($this->yellow->toolbox->isFileLocation($location) && !$page->isError()) { - $pageSource = new YellowPage($this->yellow, $serverScheme, $serverName, $base, $location, $fileName); - $pageSource->parseData($rawDataSource, 0, false); + $pageSource = new YellowPage($this->yellow); + $pageSource->setRequestInformation($serverScheme, $serverName, $base, $location, $fileName); + $pageSource->parseData($rawDataSource, false); $prefix = $this->yellow->config->get("webinterfaceFilePrefix"); if($pageSource->get($prefix)!=$page->get($prefix) || $pageSource->get("title")!=$page->get("title")) { diff --git a/system/core/core.php b/system/core/core.php index 4d7b1e8..c7e51c9 100755 --- a/system/core/core.php +++ b/system/core/core.php @@ -5,7 +5,7 @@ // Yellow main class class Yellow { - const Version = "0.4.19"; + const Version = "0.4.20"; var $page; //current page var $pages; //pages from file system var $config; //configuration @@ -15,6 +15,7 @@ class Yellow function __construct() { + $this->page = new YellowPage($this); $this->pages = new YellowPages($this); $this->config = new YellowConfig($this); $this->text = new YellowText($this); @@ -33,6 +34,10 @@ class Yellow $this->config->setDefault("themeLocation", "/media/themes/"); $this->config->setDefault("systemDir", "system/"); $this->config->setDefault("configDir", "system/config/"); + $this->config->setDefault("staticDir", "cache/"); + $this->config->setDefault("staticDefaultFile", "index.html"); + $this->config->setDefault("staticErrorFile", "error404.html"); + $this->config->setDefault("coreDir", "system/core/"); $this->config->setDefault("pluginDir", "system/plugins/"); $this->config->setDefault("snippetDir", "system/snippets/"); $this->config->setDefault("templateDir", "system/templates/"); @@ -50,6 +55,7 @@ class Yellow $this->config->setDefault("errorPageFile", "error(.*).txt"); $this->config->setDefault("newPageFile", "new(.*).txt"); $this->config->setDefault("textStringFile", "text(.*).ini"); + $this->config->setDefault("robotsTextFile", "robots.txt"); $this->config->setDefault("parser", "markdownextra"); $this->config->setDefault("parserSafeMode", "0"); $this->config->setDefault("multiLanguageMode", "0"); @@ -65,7 +71,7 @@ class Yellow ob_start(); $statusCode = 0; list($serverScheme, $serverName, $base, $location, $fileName) = $this->getRequestInformation(); - $this->page = new YellowPage($this, $serverScheme, $serverName, $base, $location, $fileName); + $this->page->setRequestInformation($serverScheme, $serverName, $base, $location, $fileName); foreach($this->plugins->plugins as $key=>$value) { if(method_exists($value["obj"], "onRequest")) @@ -78,9 +84,10 @@ class Yellow if($statusCode == 0) { $this->pages->requestHandler = "core"; - $statusCode = $this->processRequest($serverScheme, $serverName, $base, $location, $fileName, $statusCode, true); + $statusCode = $this->processRequest($serverScheme, $serverName, $base, $location, $fileName, true); } - if($this->page->isError() || $statusCodeRequest>=400) $statusCode = $this->processRequestError($statusCodeRequest); + if($statusCodeRequest >= 400) $this->page->error($statusCodeRequest, "Request error"); + if($this->page->isError()) $statusCode = $this->processRequestError(); ob_end_flush(); $this->toolbox->timerStop($time); if(defined("DEBUG") && DEBUG>=1) echo "Yellow::request status:$statusCode location:$location
\n"; @@ -89,59 +96,69 @@ class Yellow } // Process request - function processRequest($serverScheme, $serverName, $base, $location, $fileName, $statusCode, $cacheable) + function processRequest($serverScheme, $serverName, $base, $location, $fileName, $cacheable) { - $handler = $this->getRequestHandler(); - if($statusCode == 0) + if(is_readable($fileName)) { - if(is_readable($fileName)) + if($cacheable) $fileName = $this->getStaticFile($location, $fileName); + if($this->toolbox->isRequestCleanUrl($location)) { - if($this->toolbox->isRequestCleanUrl($location)) - { - $statusCode = 303; - $locationArgs = $this->toolbox->getLocationArgsCleanUrl($this->config->get("contentPagination")); - $locationHeader = $this->toolbox->getLocationHeader($serverScheme, $serverName, $base, $location.$locationArgs); - $this->sendStatus($statusCode, false, $locationHeader); - } else { - $statusCode = 200; - $fileName = $this->readPage($serverScheme, $serverName, $base, $location, $fileName, $statusCode, $cacheable); - } + $statusCode = 303; + $locationArgs = $this->toolbox->getLocationArgsCleanUrl($this->config->get("contentPagination")); + $locationHeader = $this->toolbox->getLocationHeader($serverScheme, $serverName, $base, $location.$locationArgs); + $this->sendStatus($statusCode, $locationHeader, false); + } else if($this->isStaticFile($fileName)) { + $statusCode = 200; + $statusCode = $this->sendFile($statusCode, $fileName, $cacheable); } else { - if(($this->toolbox->isFileLocation($location) && $this->isContentDirectory("$location/")) || - ($location=="/" && $this->config->get("multiLanguageMode"))) - { - $statusCode = 301; - $location = $this->toolbox->isFileLocation($location) ? "$location/" : "/".$this->getRequestLanguage()."/"; - $locationHeader = $this->toolbox->getLocationHeader($serverScheme, $serverName, $base, $location); - $this->sendStatus($statusCode, false, $locationHeader); - } else { - $statusCode = 404; - $fileName = $this->readPage($serverScheme, $serverName, $base, $location, $fileName, $statusCode, $cacheable); - } + $statusCode = 200; + $fileName = $this->readPage($serverScheme, $serverName, $base, $location, $fileName, $cacheable, $statusCode); + $statusCode = $this->sendPage(); + } + } else { + if($cacheable) $fileName = $this->getStaticFile("", ""); + if(($this->toolbox->isFileLocation($location) && $this->isContentDirectory("$location/")) || + ($location=="/" && $this->config->get("multiLanguageMode"))) + { + $statusCode = 301; + $location = $this->toolbox->isFileLocation($location) ? "$location/" : "/".$this->getRequestLanguage()."/"; + $locationHeader = $this->toolbox->getLocationHeader($serverScheme, $serverName, $base, $location); + $this->sendStatus($statusCode, $locationHeader, false); + } else if($this->isStaticFile($fileName)) { + $statusCode = 404; + $statusCode = $this->sendFile($statusCode, $fileName, $cacheable); + } else { + $statusCode = 404; + $fileName = $this->readPage($serverScheme, $serverName, $base, $location, $fileName, $cacheable, $statusCode); + $statusCode = $this->sendPage(); } - } else if($statusCode >= 400) { - $fileName = $this->readPage($serverScheme, $serverName, $base, $location, $fileName, $statusCode, $cacheable); } - if($this->page->statusCode != 0) $statusCode = $this->sendPage(); - if(defined("DEBUG") && DEBUG>=1) echo "Yellow::processRequest handler:$handler file:$fileName
\n"; + if(defined("DEBUG") && DEBUG>=1) + { + $handler = $this->getRequestHandler(); + echo "Yellow::processRequest handler:$handler file:$fileName
\n"; + } return $statusCode; } // Process request with error - function processRequestError($statusCodeRequest) + function processRequestError() { ob_clean(); - $handler = $this->getRequestHandler(); - if($statusCodeRequest >= 400) $this->page->error($statusCodeRequest, "Request error"); - $fileName = $this->readPage($this->page->serverScheme, $this->page->serverName, $this->page->base, $this->page->location, - $this->page->fileName, $this->page->statusCode, $this->page->cacheable, $this->page->get("pageError")); + $fileName = $this->readPage($this->page->serverScheme, $this->page->serverName, $this->page->base, + $this->page->location, $this->page->fileName, $this->page->cacheable, $this->page->statusCode, + $this->page->get("pageError")); $statusCode = $this->sendPage(); - if(defined("DEBUG") && DEBUG>=1) echo "Yellow::processRequestError handler:$handler file:$fileName
\n"; - return $statusCode; + if(defined("DEBUG") && DEBUG>=1) + { + $handler = $this->getRequestHandler(); + echo "Yellow::processRequestError handler:$handler file:$fileName
\n"; + } + return $statusCode; } // Read page from file - function readPage($serverScheme, $serverName, $base, $location, $fileName, $statusCode, $cacheable, $pageError = "") + function readPage($serverScheme, $serverName, $base, $location, $fileName, $cacheable, $statusCode, $pageError = "") { if($statusCode >= 400) { @@ -149,8 +166,9 @@ class Yellow $fileName = strreplaceu("(.*)", $statusCode, $fileName); $cacheable = false; } - $this->page = new YellowPage($this, $serverScheme, $serverName, $base, $location, $fileName); - $this->page->parseData($this->toolbox->getFileData($fileName), $statusCode, $cacheable, $pageError); + $this->page = new YellowPage($this); + $this->page->setRequestInformation($serverScheme, $serverName, $base, $location, $fileName); + $this->page->parseData($this->toolbox->getFileData($fileName), $cacheable, $statusCode, $pageError); $this->page->setHeader("Content-Type", "text/html; charset=UTF-8"); $this->page->setHeader("Last-Modified", $this->page->getModified(true)); if(!$this->page->isCacheable()) $this->page->setHeader("Cache-Control", "no-cache, must-revalidate"); @@ -216,9 +234,26 @@ class Yellow } return $statusCode; } + + // Send file response + function sendFile($statusCode, $fileName, $cacheable) + { + $lastModified = $this->toolbox->getHttpTimeFormatted(filemtime($fileName)); + if($statusCode==200 && $cacheable && $this->toolbox->isFileNotModified($lastModified)) + { + $statusCode = 304; + } else { + echo $this->toolbox->getFileData($fileName); + } + @header($this->toolbox->getHttpStatusFormatted($statusCode)); + @header("Content-Type: ".$this->toolbox->getMimeContentType($fileName)); + @header("Last-Modified: ".$lastModified); + if(!$cacheable) @header("Cache-Control: no-cache, must-revalidate"); + return $statusCode; + } // Send status response - function sendStatus($statusCode, $cacheable, $responseHeader = "") + function sendStatus($statusCode, $responseHeader, $cacheable) { if(PHP_SAPI != "cli") { @@ -252,9 +287,27 @@ class Yellow $base = empty($base) ? $this->config->get("serverBase") : $base; $location = $this->toolbox->getLocationClean(); $location = substru($location, strlenu($base)); - $fileName = $this->toolbox->findFileFromLocation($location, $this->config->get("contentDir"), - $this->config->get("contentRootDir"), $this->config->get("contentHomeDir"), - $this->config->get("contentDefaultFile"), $this->config->get("contentExtension")); + if(preg_match("/\.(css|js|jpg|png|txt|woff)$/", $location)) + { + $pluginLocationLength = strlenu($this->config->get("pluginLocation")); + $themeLocationLength = strlenu($this->config->get("themeLocation")); + if(substru($location, 0, $pluginLocationLength+5) == $this->config->get("pluginLocation")."core-") + { + $fileName = $this->config->get("coreDir").substru($location, $pluginLocationLength); + } else if(substru($location, 0, $pluginLocationLength) == $this->config->get("pluginLocation")) { + $fileName = $this->config->get("pluginDir").substru($location, $pluginLocationLength); + } else if(substru($location, 0, $themeLocationLength) == $this->config->get("themeLocation")) { + $fileName = $this->config->get("themeDir").substru($location, $themeLocationLength); + } else if($location == "/".$this->config->get("robotsTextFile")) { + $fileName = $this->config->get("configDir").$this->config->get("robotsTextFile"); + } + } + if(empty($fileName)) + { + $fileName = $this->toolbox->findFileFromLocation($location, $this->config->get("contentDir"), + $this->config->get("contentRootDir"), $this->config->get("contentHomeDir"), + $this->config->get("contentDefaultFile"), $this->config->get("contentExtension")); + } return array($serverScheme, $serverName, $base, $location, $fileName); } @@ -271,7 +324,33 @@ class Yellow return $this->pages->requestHandler; } - // Check if content directory exists + // Return static file + function getStaticFile($location, $fileName) + { + if(PHP_SAPI != "cli") + { + if(!empty($location)) + { + $fileNameStatic = $this->toolbox->findStaticFileFromLocation($location, + $this->config->get("staticDir"), $this->config->get("staticDefaultFile")); + } else { + $fileNameStatic = $this->config->get("staticDir").$this->config->get("staticErrorFile"); + } + if(is_readable($fileNameStatic)) $fileName = $fileNameStatic; + } + return $fileName; + } + + // Check if static file + function isStaticFile($fileName) + { + $staticDirLength = strlenu($this->config->get("staticDir")); + $systemDirLength = strlenu($this->config->get("systemDir")); + return substru($fileName, 0, $staticDirLength) == $this->config->get("staticDir") || + substru($fileName, 0, $systemDirLength) == $this->config->get("systemDir"); + } + + // Check if content directory function isContentDirectory($location) { $path = $this->toolbox->findFileFromLocation($location, $this->config->get("contentDir"), @@ -279,7 +358,7 @@ class Yellow return is_dir($path); } - // Update content configuration + // Update configuration function updateConfig() { list($pathRoot, $pathHome) = $this->toolbox->findRootConfig($this->config->get("contentDir"), @@ -359,23 +438,29 @@ class YellowPage var $cacheable; //page is cacheable? (boolean) var $statusCode; //status code - function __construct($yellow, $serverScheme, $serverName, $base, $location, $fileName) + function __construct($yellow) { $this->yellow = $yellow; + $this->metaData = array(); + $this->headerData = array(); + } + + // Set request information + function setRequestInformation($serverScheme, $serverName, $base, $location, $fileName) + { $this->serverScheme = $serverScheme; $this->serverName = $serverName; $this->base = $base; $this->location = $location; $this->fileName = $fileName; - $this->metaData = array(); - $this->headerData = array(); - $this->statusCode = 0; } // Parse page data - function parseData($rawData, $statusCode, $cacheable, $pageError = "") + function parseData($rawData, $cacheable, $statusCode = 0, $pageError = "") { $this->rawData = $rawData; + $this->parserData = ""; + $this->parser = NULL; $this->parserSafeMode = $this->yellow->config->get("parserSafeMode"); $this->active = $this->yellow->toolbox->isActiveLocation($this->location, $this->yellow->page->location, $this->yellow->pages->getHomeLocation($this->yellow->page->location)); @@ -383,8 +468,7 @@ class YellowPage $this->yellow->config->get("contentDir")); $this->cacheable = $cacheable; $this->statusCode = $statusCode; - if(!empty($pageError)) $this->error($statusCode, $pageError); - $this->parseMeta(); + $this->parseMeta($pageError); } // Parse page data update @@ -397,7 +481,6 @@ class YellowPage { $this->statusCode = 200; $this->rawData = fread($fileHandle, filesize($this->fileName)); - $this->metaData = array(); fclose($fileHandle); $this->parseMeta(); } @@ -405,10 +488,10 @@ class YellowPage } // Parse page meta data - function parseMeta() + function parseMeta($pageError = "") { - $fileDate = date("c", is_readable($this->fileName) ? filemtime($this->fileName) : 0); - $this->set("modified", $fileDate); + $this->metaData = array(); + $this->set("modified", date("c", is_readable($this->fileName) ? filemtime($this->fileName) : 0)); $this->set("title", $this->yellow->toolbox->createTextTitle($this->location)); $this->set("sitename", $this->yellow->config->get("sitename")); $this->set("author", $this->yellow->config->get("author")); @@ -445,6 +528,7 @@ class YellowPage $this->set("pageEdit", $this->yellow->toolbox->getUrl( $this->yellow->config->get("webinterfaceServerScheme"), $this->yellow->config->get("webinterfaceServerName"), $this->yellow->config->get("serverBase"), rtrim($this->yellow->config->get("webinterfaceLocation"), '/').$this->location)); + if(!empty($pageError)) $this->set("pageError", $pageError); foreach($this->yellow->plugins->plugins as $key=>$value) { if(method_exists($value["obj"], "onParseMeta")) $value["obj"]->onParseMeta($this, $this->rawData); @@ -997,16 +1081,18 @@ class YellowPages { if(defined("DEBUG") && DEBUG>=2) echo "YellowPages::scanChildren location:$location
\n"; $this->pages[$location] = array(); + $serverScheme = $this->yellow->page->serverScheme; + $serverName = $this->yellow->page->serverName; + $base = $this->yellow->page->base; if(empty($location)) { $rootLocations = $this->yellow->toolbox->findRootLocations($this->yellow->config->get("contentDir"), $this->yellow->config->get("contentRootDir")); foreach($rootLocations as $rootLocation) { - $page = new YellowPage($this->yellow, - $this->yellow->page->serverScheme, $this->yellow->page->serverName, $this->yellow->page->base, - $rootLocation, ""); - $page->parseData("", 0, false); + $page = new YellowPage($this->yellow); + $page->setRequestInformation($serverScheme, $serverName, $base, $rootLocation, ""); + $page->parseData("", false); array_push($this->pages[$location], $page); } } else { @@ -1025,13 +1111,13 @@ class YellowPages $fileData = ""; $statusCode = 0; } - $page = new YellowPage($this->yellow, - $this->yellow->page->serverScheme, $this->yellow->page->serverName, $this->yellow->page->base, + $page = new YellowPage($this->yellow); + $page->setRequestInformation($serverScheme, $serverName, $base, $this->yellow->toolbox->findLocationFromFile($fileName, $this->yellow->config->get("contentDir"), $this->yellow->config->get("contentRootDir"), $this->yellow->config->get("contentHomeDir"), $this->yellow->config->get("contentDefaultFile"), $this->yellow->config->get("contentExtension")), $fileName); - $page->parseData($fileData, $statusCode, false); + $page->parseData($fileData, false, $statusCode); array_push($this->pages[$location], $page); } } @@ -1318,7 +1404,7 @@ class YellowPlugins function load() { global $yellow; - $path = dirname(__FILE__); + $path = $yellow->config->get("coreDir"); foreach($yellow->toolbox->getDirectoryEntries($path, "/^core-.*\.php$/", true, false) as $entry) require_once($entry); $path = $yellow->config->get("pluginDir"); foreach($yellow->toolbox->getDirectoryEntries($path, "/^.*\.php$/", true, false) as $entry) require_once($entry); @@ -1745,6 +1831,14 @@ class YellowToolbox } return $invalid ? "" : $path; } + + // Return static file path from location + function findStaticFileFromLocation($location, $path, $fileDefault) + { + $path = rtrim($path, '/').$location; + if(!$this->isFileLocation($location)) $path .= $fileDefault; + return $path; + } // Return file or directory that matches token function findFileDirectory($path, $token, $tokenFailback, $directory, &$found, &$invalid) @@ -1927,6 +2021,22 @@ class YellowToolbox { return gmdate("D, d M Y H:i:s", $timestamp)." GMT"; } + + // Return MIME content type + function getMimeContentType($fileName) + { + $mimeTypes = array( + "css" => "text/css", + "js" => "application/javascript", + "jpg" => "image/jpeg", + "png" => "image/png", + "txt" => "text/plain", + "woff" => "application/font-woff"); + $contentType = "text/html; charset=UTF-8"; + $extension = ($pos = strrposu($fileName, '.')) ? substru($fileName, $pos+1) : ""; + if(array_key_exists(strtoloweru($extension), $mimeTypes)) $contentType = $mimeTypes[$extension]; + return $contentType; + } // Return URL function getUrl($serverScheme, $serverName, $base, $location) @@ -2009,17 +2119,10 @@ class YellowToolbox return @rmdir($path); } - // Return file data + // Return file data, empty string if not found function getFileData($fileName) { - $fileData = ""; - $fileHandle = @fopen($fileName, "r"); - if($fileHandle) - { - $fileData = fread($fileHandle, filesize($fileName)); - fclose($fileHandle); - } - return $fileData; + return is_readable($fileName) ? file_get_contents($fileName) : ""; } // Create file