Bladeren bron

Hello command line (static website update)

markseu 12 jaren geleden
bovenliggende
commit
1a2d981929
5 gewijzigde bestanden met toevoegingen van 289 en 101 verwijderingen
  1. 3 0
      .htaccess
  2. 1 0
      system/config/config.ini
  3. 167 82
      system/core/core.php
  4. 107 8
      system/core/core_commandline.php
  5. 11 11
      system/core/core_webinterface.php

+ 3 - 0
.htaccess

@@ -15,4 +15,7 @@ RewriteRule ^$ yellow.php [L]
 RewriteCond %{REQUEST_FILENAME} !-f
 RewriteCond %{REQUEST_FILENAME} !-f
 RewriteCond %{REQUEST_FILENAME} !-d
 RewriteCond %{REQUEST_FILENAME} !-d
 RewriteRule ^ yellow.php [L]
 RewriteRule ^ yellow.php [L]
+
+RewriteCond %{REQUEST_FILENAME} -f
+RewriteRule ^[^\.]+$ - [T=text/html,L]
 </IfModule>
 </IfModule>

+ 1 - 0
system/config/config.ini

@@ -16,6 +16,7 @@ configDir = system/config/
 pluginDir = system/plugins/
 pluginDir = system/plugins/
 snippetDir = system/snippets/
 snippetDir = system/snippets/
 templateDir = system/templates/
 templateDir = system/templates/
+mediaDir = media/
 styleDir = media/styles/
 styleDir = media/styles/
 imageDir = media/images/
 imageDir = media/images/
 contentDir = content/
 contentDir = content/

+ 167 - 82
system/core/core.php

@@ -5,7 +5,7 @@
 // Yellow main class
 // Yellow main class
 class Yellow
 class Yellow
 {
 {
-	const Version = "0.1.7";
+	const Version = "0.1.8";
 	var $page;				//current page data
 	var $page;				//current page data
 	var $pages;				//current page tree from file system
 	var $pages;				//current page tree from file system
 	var $toolbox;			//toolbox with helpers
 	var $toolbox;			//toolbox with helpers
@@ -37,6 +37,7 @@ class Yellow
 		$this->config->setDefault("pluginDir", "system/plugins/");
 		$this->config->setDefault("pluginDir", "system/plugins/");
 		$this->config->setDefault("snippetDir", "system/snippets/");
 		$this->config->setDefault("snippetDir", "system/snippets/");
 		$this->config->setDefault("templateDir", "system/templates/");
 		$this->config->setDefault("templateDir", "system/templates/");
+		$this->config->setDefault("mediaDir", "media/");
 		$this->config->setDefault("styleDir", "media/styles/");
 		$this->config->setDefault("styleDir", "media/styles/");
 		$this->config->setDefault("imageDir", "media/images/");
 		$this->config->setDefault("imageDir", "media/images/");
 		$this->config->setDefault("contentDir", "content/");
 		$this->config->setDefault("contentDir", "content/");
@@ -51,23 +52,16 @@ class Yellow
 		$this->text->load($this->config->get("configDir").$this->config->get("textStringFile"), $this->toolbox);
 		$this->text->load($this->config->get("configDir").$this->config->get("textStringFile"), $this->toolbox);
 	}
 	}
 	
 	
-	// Start and handle request
+	// Handle request
 	function request()
 	function request()
-	{
-		$this->toolbox->timerStart($time);
-		$this->processRequest();
-		$this->toolbox->timerStop($time);
-		if(defined("DEBUG") && DEBUG>=1) echo "Yellow::request time:$time ms<br>\n";
-	}
-
-	// Process request
-	function processRequest()
 	{
 	{
 		ob_start();
 		ob_start();
+		$this->toolbox->timerStart($time);
 		$baseLocation = $this->config->get("baseLocation");
 		$baseLocation = $this->config->get("baseLocation");
 		$location = $this->getRelativeLocation($baseLocation);
 		$location = $this->getRelativeLocation($baseLocation);
 		$fileName = $this->getContentFileName($location);
 		$fileName = $this->getContentFileName($location);
 		$statusCode = 0;
 		$statusCode = 0;
+		$this->page = new Yellow_Page($this, $location);
 		foreach($this->plugins->plugins as $key=>$value)
 		foreach($this->plugins->plugins as $key=>$value)
 		{
 		{
 			if(method_exists($value["obj"], "onRequest"))
 			if(method_exists($value["obj"], "onRequest"))
@@ -76,26 +70,28 @@ class Yellow
 				if($statusCode) break;
 				if($statusCode) break;
 			}
 			}
 		}
 		}
-		if($statusCode == 0) $statusCode = $this->processRequestFile($baseLocation, $location, $fileName, $statusCode, true);
-		if(is_object($this->page) && $this->page->isError())
+		if($statusCode == 0) $statusCode = $this->processRequest($baseLocation, $location, $fileName, true, $statusCode);
+		if($this->page->isExisting("pageError"))
 		{
 		{
 			ob_clean();
 			ob_clean();
-			$statusCode = $this->processRequestError($baseLocation, $location, $fileName);
+			$statusCode = $this->processRequestError();
 		}
 		}
+		$this->toolbox->timerStop($time);
 		ob_end_flush();
 		ob_end_flush();
-		if(defined("DEBUG") && DEBUG>=1) echo "Yellow::processRequest status:$statusCode location:$location<br>\n";
+		if(defined("DEBUG") && DEBUG>=1) echo "Yellow::request status:$statusCode location:$location<br>\n";
+		if(defined("DEBUG") && DEBUG>=1) echo "Yellow::request time:$time ms<br>\n";
 		return $statusCode;
 		return $statusCode;
 	}
 	}
 	
 	
-	// Process request for a file
-	function processRequestFile($baseLocation, $location, $fileName, $statusCode, $cacheable)
+	// Process request
+	function processRequest($baseLocation, $location, $fileName, $cacheable, $statusCode)
 	{
 	{
 		if($statusCode == 0)
 		if($statusCode == 0)
 		{
 		{
 			if(is_readable($fileName))
 			if(is_readable($fileName))
 			{
 			{
 				$statusCode = 200;
 				$statusCode = 200;
-				$fileName = $this->readPage($baseLocation, $location, $fileName, $statusCode, $cacheable);
+				$fileName = $this->readPage($baseLocation, $location, $fileName, $cacheable, $statusCode);
 			} else {
 			} else {
 				if($this->toolbox->isFileLocation($location) && is_dir($this->getContentDirectory("$location/")))
 				if($this->toolbox->isFileLocation($location) && is_dir($this->getContentDirectory("$location/")))
 				{
 				{
@@ -104,28 +100,30 @@ class Yellow
 					$this->sendStatus($statusCode, "Location: http://$serverName$baseLocation$location/");
 					$this->sendStatus($statusCode, "Location: http://$serverName$baseLocation$location/");
 				} else {
 				} else {
 					$statusCode = 404;
 					$statusCode = 404;
-					$fileName = $this->readPage($baseLocation, $location, $fileName, $statusCode, $cacheable);
+					$fileName = $this->readPage($baseLocation, $location, $fileName, $cacheable, $statusCode);
 				}
 				}
 			}
 			}
 		} else if($statusCode >= 400) {
 		} else if($statusCode >= 400) {
-			$fileName = $this->readPage($baseLocation, $location, $fileName, $statusCode, $cacheable);
+			$fileName = $this->readPage($baseLocation, $location, $fileName, $cacheable, $statusCode);
 		}
 		}
-		if(is_object($this->page)) $statusCode = $this->sendPage();
-		if(defined("DEBUG") && DEBUG>=1) echo "Yellow::processRequestFile base:$baseLocation file:$fileName<br>\n";
+		if($this->page->statusCode != 0) $statusCode = $this->sendPage();
+		if(defined("DEBUG") && DEBUG>=1) echo "Yellow::processRequest base:$baseLocation file:$fileName<br>\n";
 		return $statusCode;
 		return $statusCode;
 	}
 	}
 	
 	
 	// Process request with error
 	// Process request with error
-	function processRequestError($baseLocation, $location, $fileName)
+	function processRequestError()
 	{
 	{
-		$fileName = $this->readPage($baseLocation, $location, $fileName, $this->page->statusCode, false);
+		$baseLocation = $this->pages->baseLocation;
+		$fileName = $this->readPage($baseLocation, $this->page->location, $this->page->fileName, $this->page->cacheable,
+			$this->page->statusCode, $this->page->get("pageError"));
 		$statusCode = $this->sendPage();
 		$statusCode = $this->sendPage();
 		if(defined("DEBUG") && DEBUG>=1) echo "Yellow::processRequestError base:$baseLocation file:$fileName<br>\n";
 		if(defined("DEBUG") && DEBUG>=1) echo "Yellow::processRequestError base:$baseLocation file:$fileName<br>\n";
 		return $statusCode;		
 		return $statusCode;		
 	}
 	}
 	
 	
-	// Read page for response
-	function readPage($baseLocation, $location, $fileName, $statusCode, $cacheable)
+	// Read page from file
+	function readPage($baseLocation, $location, $fileName, $cacheable, $statusCode, $pageError = "")
 	{
 	{
 		if($statusCode >= 400)
 		if($statusCode >= 400)
 		{
 		{
@@ -140,14 +138,8 @@ class Yellow
 			fclose($fileHandle);
 			fclose($fileHandle);
 		}
 		}
 		$this->pages->baseLocation = $baseLocation;
 		$this->pages->baseLocation = $baseLocation;
-		if(is_object($this->page) && $this->page->isError())
-		{
-			$text = $this->page->get("pageError");
-			$this->page = new Yellow_Page($this, $location, $fileName, $fileData, $statusCode, $cacheable);
-			$this->page->error($statusCode, $text);
-		} else {
-			$this->page = new Yellow_Page($this, $location, $fileName, $fileData, $statusCode, $cacheable);
-		}
+		$this->page = new Yellow_Page($this, $location);
+		$this->page->parseData($fileName, $fileData, $cacheable, $statusCode, $pageError);
 		$this->page->parseContent();
 		$this->page->parseContent();
 		$this->text->setLanguage($this->page->get("language"));
 		$this->text->setLanguage($this->page->get("language"));
 		return $fileName;
 		return $fileName;
@@ -167,40 +159,32 @@ class Yellow
 		{
 		{
 			$this->page->error(500, "Parser '".$this->page->get("parser")."' does not exist!");
 			$this->page->error(500, "Parser '".$this->page->get("parser")."' does not exist!");
 		}
 		}
-		
-		$pageHeader["content-type"] = "text/html; charset=UTF-8";
-		$pageHeader["last-modified"] = $this->page->getModified(true);
-		foreach(headers_list() as $header)
-		{
-			$tokens = explode(':', $header, 2);
-			$key = strtoloweru($tokens[0]);
-			if(!is_null($pageHeader[$key])) $pageHeader[$key] = trim($tokens[1]);
-		}
+
 		$statusCode = $this->page->statusCode;
 		$statusCode = $this->page->statusCode;
-		if($statusCode==200 && $this->page->isCacheable() && $this->toolbox->isFileNotModified($pageHeader["last-modified"]))
+		if($statusCode==200 && $this->page->isCacheable() &&
+		   $this->toolbox->isFileNotModified($this->page->getHeader("Last-Modified")))
 		{
 		{
+			ob_clean();
 			$statusCode = 304;
 			$statusCode = 304;
-			header_remove();
-			header($this->toolbox->getHttpStatusFormatted($statusCode));
-		} else {
-			header($this->toolbox->getHttpStatusFormatted($statusCode));
-			header("Content-Type: ".$pageHeader["content-type"]);
-			header("Last-Modified: ".$pageHeader["last-modified"]);
-			if(!$this->page->isCacheable()) header("Cache-Control: no-cache, must-revalidate");
+		}
+		if(PHP_SAPI != "cli")
+		{
+			@header($this->toolbox->getHttpStatusFormatted($statusCode));
+			if($statusCode != 304) foreach($this->page->headerData as $key=>$value) @header("$key: $value");
 		}
 		}
 		if(defined("DEBUG") && DEBUG>=1)
 		if(defined("DEBUG") && DEBUG>=1)
 		{
 		{
-			$cacheable = intval($this->page->isCacheable());
-			echo "Yellow::sendPage template:$fileNameTemplate style:$fileNameStyle cacheable:$cacheable<br>\n";			
+			foreach($this->page->headerData as $key=>$value) echo "Yellow::sendPage $key: $value<br>\n";
+			echo "Yellow::sendPage template:$fileNameTemplate style:$fileNameStyle<br>\n";
 		}
 		}
 		return $statusCode;
 		return $statusCode;
 	}
 	}
-	
+
 	// Send status response
 	// Send status response
 	function sendStatus($statusCode, $text = "")
 	function sendStatus($statusCode, $text = "")
 	{
 	{
-		header($this->toolbox->getHttpStatusFormatted($statusCode));
-		if(!empty($text)) header($text);
+		@header($this->toolbox->getHttpStatusFormatted($statusCode));
+		if(!empty($text)) @header($text);
 	}
 	}
 	
 	
 	// Execute a template
 	// Execute a template
@@ -279,7 +263,8 @@ class Yellow
 			$plugin = $this->plugins->plugins[$name];
 			$plugin = $this->plugins->plugins[$name];
 			if(method_exists($plugin["obj"], "onCommand")) $statusCode = $plugin["obj"]->onCommand(func_get_args());
 			if(method_exists($plugin["obj"], "onCommand")) $statusCode = $plugin["obj"]->onCommand(func_get_args());
 		} else {
 		} else {
-			$this->page->error(500, "Plugin '$name' does not exist!");
+			$statusCode = 500;
+			$this->page->error($statusCode, "Plugin '$name' does not exist!");
 		}
 		}
 		return $statusCode;
 		return $statusCode;
 	}
 	}
@@ -289,6 +274,13 @@ class Yellow
 	{
 	{
 		$this->plugins->register($name, $class, $version);
 		$this->plugins->register($name, $class, $version);
 	}
 	}
+	
+	// Set a response header
+	function header($text)
+	{
+		$tokens = explode(':', $text, 2);
+		$this->page->setHeader(trim($tokens[0]), trim($tokens[1]));
+	}
 }
 }
 	
 	
 // Yellow page data
 // Yellow page data
@@ -298,32 +290,41 @@ class Yellow_Page
 	var $location;				//page location
 	var $location;				//page location
 	var $fileName;				//content file name
 	var $fileName;				//content file name
 	var $rawData;				//raw data of page
 	var $rawData;				//raw data of page
-	var $metaData;				//meta data of page
 	var $metaDataOffsetBytes;	//meta data offset
 	var $metaDataOffsetBytes;	//meta data offset
+	var $metaData;				//meta data of page
+	var $headerData;			//response header of page
 	var $parser;				//parser for page content
 	var $parser;				//parser for page content
-	var $statusCode;			//status code of page
-	var $error;					//page error happened? (boolean)
 	var $active;				//page is active location? (boolean)
 	var $active;				//page is active location? (boolean)
 	var $visible;				//page is visible location? (boolean)
 	var $visible;				//page is visible location? (boolean)
 	var $cacheable;				//page is cacheable? (boolean)
 	var $cacheable;				//page is cacheable? (boolean)
-	
-	function __construct($yellow, $location, $fileName, $rawData, $statusCode, $cacheable)
+	var $statusCode;			//status code of page
+
+	function __construct($yellow, $location)
 	{
 	{
 		$this->yellow = $yellow;
 		$this->yellow = $yellow;
 		$this->location = $location;
 		$this->location = $location;
+		$this->metaData = array();
+		$this->headerData = array();
+		$this->statusCode = 0;
+	}
+	
+	// Parse page data
+	function parseData($fileName, $rawData, $cacheable, $statusCode, $pageError = "")
+	{
 		$this->fileName = $fileName;
 		$this->fileName = $fileName;
 		$this->rawData = $rawData;
 		$this->rawData = $rawData;
-		$this->parseMeta();
-		$this->statusCode = $statusCode;
-		$this->active = $yellow->toolbox->isActiveLocation($yellow->pages->baseLocation, $location);
-		$this->visible = $yellow->toolbox->isVisibleLocation($fyellow->pages->baseLocation, $location, $fileName, $yellow->config->get("contentDir"));
+		$this->active = $this->yellow->toolbox->isActiveLocation($this->yellow->pages->baseLocation, $this->location);
+		$this->visible = $this->yellow->toolbox->isVisibleLocation($this->yellow->pages->baseLocation, $this->location,
+							$fileName, $this->yellow->config->get("contentDir"));
 		$this->cacheable = $cacheable;
 		$this->cacheable = $cacheable;
+		$this->statusCode = $statusCode;
+		if(!empty($pageError)) $this->error($statusCode, $pageError);
+		$this->parseMeta();
 	}
 	}
 	
 	
 	// Parse page meta data
 	// Parse page meta data
 	function parseMeta()
 	function parseMeta()
 	{
 	{
-		$this->metaData = array();
 		$this->set("title", $this->yellow->toolbox->createTextTitle($this->location));
 		$this->set("title", $this->yellow->toolbox->createTextTitle($this->location));
 		$this->set("author", $this->yellow->config->get("author"));
 		$this->set("author", $this->yellow->config->get("author"));
 		$this->set("language", $this->yellow->config->get("language"));
 		$this->set("language", $this->yellow->config->get("language"));
@@ -342,6 +343,13 @@ class Yellow_Page
 			$this->metaDataOffsetBytes = strlenb($parsed[0]);
 			$this->metaDataOffsetBytes = strlenb($parsed[0]);
 			$this->set("title", $parsed[1]);
 			$this->set("title", $parsed[1]);
 		}
 		}
+
+		if($this == $this->yellow->page)
+		{
+			$this->setHeader("Content-Type", "text/html; charset=UTF-8");
+			$this->setHeader("Last-Modified", $this->getModified(true));
+			if(!$this->isCacheable()) $this->setHeader("Cache-Control", "no-cache, must-revalidate");
+		}
 	}
 	}
 	
 	
 	// Parse page content
 	// Parse page content
@@ -373,16 +381,27 @@ class Yellow_Page
 	}
 	}
 	
 	
 	// Respond with error page
 	// Respond with error page
-	function error($statusCode, $text = "")
+	function error($statusCode, $pageError = "")
 	{
 	{
-		if(!$this->isError())
+		if(!$this->isExisting("pageError"))
 		{
 		{
-			$this->error = true;
 			$this->statusCode = $statusCode;
 			$this->statusCode = $statusCode;
-			if(!empty($text)) $this->set("pageError", $text);
+			$this->set("pageError", empty($pageError) ? "Template/snippet error" : $pageError);
 		}
 		}
 	}
 	}
 	
 	
+	// Set page response header
+	function setHeader($key, $value)
+	{
+		$this->headerData[$key] = $value;
+	}
+	
+	// Return page response header
+	function getHeader($key)
+	{
+		return $this->isHeader($key) ? $this->headerData[$key] : "";
+	}
+	
 	// Set page meta data
 	// Set page meta data
 	function set($key, $value)
 	function set($key, $value)
 	{
 	{
@@ -392,7 +411,7 @@ class Yellow_Page
 	// Return page meta data
 	// Return page meta data
 	function get($key)
 	function get($key)
 	{
 	{
-		return $this->IsExisting($key) ? $this->metaData[$key] : "";
+		return $this->isExisting($key) ? $this->metaData[$key] : "";
 	}
 	}
 
 
 	// Return page meta data, HTML encoded
 	// Return page meta data, HTML encoded
@@ -430,10 +449,22 @@ class Yellow_Page
 	function getModified($httpFormat = false)
 	function getModified($httpFormat = false)
 	{
 	{
 		$modified = is_readable($this->fileName) ? filemtime($this->fileName) : "";
 		$modified = is_readable($this->fileName) ? filemtime($this->fileName) : "";
-		if($this->IsExisting("modified")) $modified = strtotime($this->get("modified"));
+		if($this->isExisting("modified")) $modified = strtotime($this->get("modified"));
 		return $httpFormat ? $this->yellow->toolbox->getHttpTimeFormatted($modified) : $modified;
 		return $httpFormat ? $this->yellow->toolbox->getHttpTimeFormatted($modified) : $modified;
 	}
 	}
 	
 	
+	// Return page status code
+	function getStatusCode($httpFormat = false)
+	{
+		$statusCode = $this->statusCode;
+		if($httpFormat)
+		{
+			$statusCode = $this->yellow->toolbox->getHttpStatusFormatted($statusCode);
+			if($this->isExisting("pageError")) $statusCode .= ": ".$this->get("pageError");
+		}
+		return $statusCode;
+	}
+	
 	// Return child pages relative to current page
 	// Return child pages relative to current page
 	function getChildren($showHidden = false)
 	function getChildren($showHidden = false)
 	{
 	{
@@ -453,18 +484,18 @@ class Yellow_Page
 		$parentLocation = $this->yellow->pages->getParentLocation($this->location);
 		$parentLocation = $this->yellow->pages->getParentLocation($this->location);
 		return $this->yellow->pages->find($parentLocation, false);
 		return $this->yellow->pages->find($parentLocation, false);
 	}
 	}
+	
+	// Check if response header exists
+	function isHeader($key)
+	{
+		return !is_null($this->headerData[$key]);
+	}
 
 
 	// Check if meta data exists
 	// Check if meta data exists
 	function isExisting($key)
 	function isExisting($key)
 	{
 	{
 		return !is_null($this->metaData[$key]);
 		return !is_null($this->metaData[$key]);
 	}
 	}
-
-	// Check if page error happened
-	function isError()
-	{
-		return $this->error;
-	}
 	
 	
 	// Check if page is within current HTTP request
 	// Check if page is within current HTTP request
 	function isActive()
 	function isActive()
@@ -650,7 +681,7 @@ class Yellow_Pages
 	}
 	}
 	
 	
 	// Find child pages recursively
 	// Find child pages recursively
-	function findChildrenRecursive($location, $showHidden, $levelMax)
+	function findChildrenRecursive($location, $showHidden = false, $levelMax = 0)
 	{
 	{
 		--$levelMax;
 		--$levelMax;
 		$pages = new Yellow_PageCollection($this->yellow, $location);
 		$pages = new Yellow_PageCollection($this->yellow, $location);
@@ -705,7 +736,8 @@ class Yellow_Pages
 				} else {
 				} else {
 					$fileData = "";
 					$fileData = "";
 				}
 				}
-				$page = new Yellow_Page($this->yellow, $childLocation, $fileName, $fileData, 0, false);
+				$page = new Yellow_Page($this->yellow, $childLocation);
+				$page->parseData($fileName, $fileData, false, 0);
 				array_push($this->pages[$location], $page);
 				array_push($this->pages[$location], $page);
 			}
 			}
 		}
 		}
@@ -732,7 +764,7 @@ class Yellow_Toolbox
 	// Return server base from current HTTP request
 	// Return server base from current HTTP request
 	static function getServerBase()
 	static function getServerBase()
 	{
 	{
-		$serverBase = "/";
+		$serverBase = "";
 		if(preg_match("/^(.*)\//", $_SERVER["SCRIPT_NAME"], $matches)) $serverBase = $matches[1];
 		if(preg_match("/^(.*)\//", $_SERVER["SCRIPT_NAME"], $matches)) $serverBase = $matches[1];
 		return $serverBase;
 		return $serverBase;
 	}
 	}
@@ -904,6 +936,7 @@ class Yellow_Toolbox
 	{
 	{
 		switch($statusCode)
 		switch($statusCode)
 		{
 		{
+			case 0:   $text = "$_SERVER[SERVER_PROTOCOL] $statusCode No data"; break;
 			case 200: $text = "$_SERVER[SERVER_PROTOCOL] $statusCode OK"; break;
 			case 200: $text = "$_SERVER[SERVER_PROTOCOL] $statusCode OK"; break;
 			case 301: $text = "$_SERVER[SERVER_PROTOCOL] $statusCode Moved permanently"; break;
 			case 301: $text = "$_SERVER[SERVER_PROTOCOL] $statusCode Moved permanently"; break;
 			case 302: $text = "$_SERVER[SERVER_PROTOCOL] $statusCode Moved temporarily"; break;
 			case 302: $text = "$_SERVER[SERVER_PROTOCOL] $statusCode Moved temporarily"; break;
@@ -949,7 +982,59 @@ class Yellow_Toolbox
 		}
 		}
 		return $entries;
 		return $entries;
 	}
 	}
+	
+	// Return files and directories recursively
+	static function getDirectoryEntriesRecursive($path, $regex = "/.*/", $sort = false, $directories = true, $levelMax = 0)
+	{
+		$entries = array();
+		foreach(self::getDirectoryEntries($path, $regex, $sort, $directories) as $entry) array_push($entries, "$path/$entry");
+		--$levelMax;
+		if($levelMax != 0)
+		{
+			foreach(self::getDirectoryEntries($path, "/.*/", $sort, true) as $entry)
+			{
+				$entries = array_merge($entries, self::getDirectoryEntriesRecursive("$path/$entry", $regex, $sort, $directories, $levelMax));
+			}
+		}
+		return $entries;
+	}
+
+	// Create new file
+	function makeFile($fileName, $fileData, $mkdir = false)
+	{
+		$ok = false;
+		if($mkdir)
+		{
+			$path = dirname($fileName);
+			if(!empty($path) && !is_dir($path)) mkdir($path, 0777, true);
+		}
+		$fileHandle = @fopen($fileName, "w");
+		if($fileHandle)
+		{
+			fwrite($fileHandle, $fileData);
+			fclose($fileHandle);
+			$ok = true;
+		}
+		return $ok;
+	}
+	
+	// Copy file
+	function copyFile($fileNameSource, $fileNameDest, $mkdir = false)
+	{
+		if($mkdir)
+		{
+			$path = dirname($fileNameDest);
+			if(!empty($path) && !is_dir($path)) mkdir($path, 0777, true);
+		}
+		return @copy($fileNameSource, $fileNameDest);
+	}
 
 
+	// Set file modification time, Unix time
+	function modifyFile($fileName, $modified)
+	{
+		return @touch($fileName, $modified);
+	}
+	
 	// Create description from text string
 	// Create description from text string
 	static function createTextDescription($text, $lengthMax, $removeHtml = true)
 	static function createTextDescription($text, $lengthMax, $removeHtml = true)
 	{
 	{

+ 107 - 8
system/core/core_commandline.php

@@ -5,22 +5,26 @@
 // Command line core plugin
 // Command line core plugin
 class Yellow_Commandline
 class Yellow_Commandline
 {
 {
-	const Version = "0.0.0"; //Hello command line!
+	const Version = "0.1.1";
 	var $yellow;			//access to API
 	var $yellow;			//access to API
 
 
 	// Initialise plugin
 	// Initialise plugin
 	function initPlugin($yellow)
 	function initPlugin($yellow)
 	{
 	{
-		$this->yellow = $yellow;		
+		$this->yellow = $yellow;
+		$this->yellow->config->setDefault("commandBuildDefaultFile", "index.html");
 	}
 	}
 	
 	
 	// Handle command
 	// Handle command
 	function onCommand($args)
 	function onCommand($args)
 	{
 	{
-		$statusCode = 0;
 		list($name, $command) = $args;
 		list($name, $command) = $args;
-		if($command == "version") $statusCode = $this->version($args);
-		else $this->help();
+		switch($command)
+		{
+			case "build":	$statusCode = $this->build($args); break;
+			case "version":	$statusCode = $this->version(); break;
+			default:		$statusCode = $this->help();
+		}
 		return $statusCode;
 		return $statusCode;
 	}
 	}
 	
 	
@@ -28,15 +32,110 @@ class Yellow_Commandline
 	function help()
 	function help()
 	{
 	{
 		echo "Yellow command line ".Yellow_Commandline::Version."\n";
 		echo "Yellow command line ".Yellow_Commandline::Version."\n";
-		echo "Syntax: yellow version\n";
+		echo "Syntax: yellow.php build DIRECTORY [LOCATION]\n";
+		echo "        yellow.php version\n";
+		return 0;
 	}
 	}
-
+	
 	// Show software version
 	// Show software version
-	function version($args)
+	function version()
 	{
 	{
 		echo "Yellow ".Yellow::Version."\n";
 		echo "Yellow ".Yellow::Version."\n";
 		foreach($this->yellow->plugins->plugins as $key=>$value) echo "$value[class] $value[version]\n";
 		foreach($this->yellow->plugins->plugins as $key=>$value) echo "$value[class] $value[version]\n";
 		return 0;
 		return 0;
+	}	
+	
+	// Build static website
+	function build($args)
+	{		
+		$statusCodeMax = $errorCount = 0;
+		list($name, $command, $path, $location) = $args;
+		if(!empty($path) && $path!="/")
+		{
+			$this->yellow->toolbox->timerStart($time);
+			if(empty($location))
+			{
+				$pages = $this->yellow->pages->index(true);
+				$fileNames = $this->yellow->toolbox->getDirectoryEntriesrecursive($this->yellow->config->get("mediaDir"), "/.*/", false, false);
+			} else {
+				$pages = new Yellow_PageCollection($this->yellow, $location);
+				$pages->append(new Yellow_Page($this->yellow, $location));
+				$fileNames = array();
+			}
+			foreach($pages as $page)
+			{
+				$statusCode = $this->buildContentFile($path, $page->location);
+				$statusCodeMax = max($statusCodeMax, $statusCode);
+				if($statusCode >= 400)
+				{
+					++$errorCount;
+					echo "ERROR building location '".$page->location."', ".$this->yellow->page->getStatusCode(true)."\n";
+				}
+				if(defined("DEBUG") && DEBUG>=1) echo "Yellow_Commandline::build status:$statusCode location:".$page->location."\n";
+			}
+			foreach($fileNames as $fileName)
+			{
+				$statusCode = $this->buildMediaFile($fileName, "$path/$fileName");
+				$statusCodeMax = max($statusCodeMax, $statusCode);
+				if($statusCode >= 400)
+				{
+					++$errorCount;
+					echo "ERROR building file '$path/$fileName', ".$this->yellow->toolbox->getHttpStatusFormatted($statusCode)."\n";
+				}
+				if(defined("DEBUG") && DEBUG>=1) echo "Yellow_Commandline::build status:$statusCode file:$fileName\n";
+			}
+			$this->yellow->toolbox->timerStop($time);
+			if(defined("DEBUG") && DEBUG>=1) echo "Yellow_Commandline::build time:$time ms\n";
+			echo "Yellow build: ".count($pages)." content, ".count($fileNames)." media";
+			echo ", $errorCount error".($errorCount!=1 ? 's' : '');
+			echo ", status $statusCodeMax\n";
+		} else {
+			echo "Yellow build: Invalid arguments\n";
+		}
+		return $statusCodeMax;
+	}
+	
+	// Build content file
+	function buildContentFile($path, $location)
+	{		
+		ob_start();
+		$_SERVER["REQUEST_URI"] = $this->yellow->config->get("baseLocation").$location;
+		$_SERVER["SCRIPT_NAME"] = $this->yellow->config->get("baseLocation")."yellow.php";
+		$_SERVER["SERVER_NAME"] = $this->yellow->config->get("serverName");
+		$_SERVER["SERVER_PROTOCOL"] = "HTTP/1.1";
+		$fileName = $path.$location;
+		if(!$this->yellow->toolbox->isFileLocation($location)) $fileName .= $this->yellow->config->get("commandBuildDefaultFile");
+		$statusCode = $this->yellow->request();
+		if($statusCode != 404)
+		{
+			$modified = strtotime($this->yellow->page->getHeader("Last-Modified"));
+			if(!$this->yellow->toolbox->makeFile($fileName, ob_get_contents(), true) ||
+			   !$this->yellow->toolbox->modifyFile($fileName, $modified))
+			{
+				$statusCode = 500;
+				$this->yellow->page->error($statusCode, "Can't write file '$fileName'!");
+			}
+			list($contentType) = explode(';', $this->yellow->page->getHeader("Content-Type"));
+			if($contentType != "text/html")
+			{
+				$statusCode = 500;
+				$this->yellow->page->error($statusCode, "Unsupported type '$contentType'!");				
+			}
+		}
+		ob_end_clean();
+		return $statusCode;
+	}
+	
+	// Build media file
+	function buildMediaFile($fileNameSource, $fileNameDest)
+	{
+		$statusCode = 200;
+		if(!$this->yellow->toolbox->copyFile($fileNameSource, $fileNameDest, true) ||
+		   !$this->yellow->toolbox->modifyFile($fileNameDest, filemtime($fileNameSource)))
+		{
+			$statusCode = 500;
+		}
+		return $statusCode;
 	}
 	}
 }
 }
 	
 	

+ 11 - 11
system/core/core_webinterface.php

@@ -5,7 +5,7 @@
 // Web interface core plugin
 // Web interface core plugin
 class Yellow_Webinterface
 class Yellow_Webinterface
 {
 {
-	const Version = "0.1.4";
+	const Version = "0.1.5";
 	var $yellow;				//access to API
 	var $yellow;				//access to API
 	var $users;					//web interface users
 	var $users;					//web interface users
 	var $activeLocation;		//web interface location? (boolean)
 	var $activeLocation;		//web interface location? (boolean)
@@ -33,8 +33,8 @@ class Yellow_Webinterface
 			$location = $this->yellow->getRelativeLocation($baseLocation);
 			$location = $this->yellow->getRelativeLocation($baseLocation);
 			$fileName = $this->yellow->getContentFileName($location);
 			$fileName = $this->yellow->getContentFileName($location);
 			if($this->checkUser()) $statusCode = $this->processRequestAction($baseLocation, $location, $fileName);
 			if($this->checkUser()) $statusCode = $this->processRequestAction($baseLocation, $location, $fileName);
-			if($statusCode == 0) $statusCode = $this->yellow->processRequestFile($baseLocation, $location, $fileName,
-													$this->activeUserFail ? 401 : 0, false);
+			if($statusCode == 0) $statusCode = $this->yellow->processRequest($baseLocation, $location, $fileName,
+													false, $this->activeUserFail ? 401 : 0);
 		} else {
 		} else {
 			if($this->yellow->config->get("webinterfaceLocation") == "$location/")
 			if($this->yellow->config->get("webinterfaceLocation") == "$location/")
 			{
 			{
@@ -106,16 +106,16 @@ class Yellow_Webinterface
 		{
 		{
 			if(!empty($_POST["rawdata"]))
 			if(!empty($_POST["rawdata"]))
 			{
 			{
-				$fileHandle = @fopen($fileName, "w");
-				if($fileHandle)
+				$this->rawDataOriginal = $_POST["rawdata"];
+				if($this->yellow->toolbox->makeFile($fileName, $_POST["rawdata"]))
 				{
 				{
-					fwrite($fileHandle, $_POST["rawdata"]);
-					fclose($fileHandle);
+					$statusCode = 303;
+					$this->yellow->sendStatus($statusCode, "Location: http://$serverName$baseLocation$location");
 				} else {
 				} else {
-					die("Server error: Can't save page '$fileName'!");
+					$statusCode = 500;
+					$this->yellow->processRequest($baseLocation, $location, $fileName, false, $statusCode);
+					$this->yellow->page->error($statusCode, "Can't write file '$fileName'!");
 				}
 				}
-				$statusCode = 303;
-				$this->yellow->sendStatus($statusCode, "Location: http://$serverName$baseLocation$location");
 			}
 			}
 		} else if($_POST["action"]== "login") {
 		} else if($_POST["action"]== "login") {
 			$statusCode = 303;
 			$statusCode = 303;
@@ -135,7 +135,7 @@ class Yellow_Webinterface
 					$this->yellow->sendStatus($statusCode, "Location: http://$serverName$baseLocation$location/");
 					$this->yellow->sendStatus($statusCode, "Location: http://$serverName$baseLocation$location/");
 				} else {
 				} else {
 					$statusCode = $this->checkUserPermissions($location, $fileName) ? 424 : 404;
 					$statusCode = $this->checkUserPermissions($location, $fileName) ? 424 : 404;
-					$this->yellow->processRequestFile($baseLocation, $location, $fileName, $statusCode, false);
+					$this->yellow->processRequest($baseLocation, $location, $fileName, false, $statusCode);
 				}
 				}
 			}
 			}
 		}
 		}