commit a964a7cfb912813afa8a2a60ac9648b0167629eb
parent e7ab8529eac20216f8b15474b33d73d414efb123
Author: markseu <mark2011@mayberg.se>
Date: Thu, 10 Nov 2022 15:43:31 +0100
Updated API, moved normalisation to lookup
Diffstat:
8 files changed, 2673 insertions(+), 2666 deletions(-)
diff --git a/system/extensions/core.php b/system/extensions/core.php
@@ -2,20 +2,19 @@
// Core extension, https://github.com/annaesvensson/yellow-core
class YellowCore {
- const VERSION = "0.8.100";
+ const VERSION = "0.8.101";
const RELEASE = "0.8.21";
- public $page; // current page
public $content; // content files
public $media; // media files
public $system; // system settings
public $language; // language settings
public $user; // user settings
public $extension; // extensions
- public $lookup; // location and file lookup
+ public $lookup; // lookup and normalisation
public $toolbox; // toolbox with helper functions
+ public $page; // current page
public function __construct() {
- $this->page = new YellowPage($this);
$this->content = new YellowContent($this);
$this->media = new YellowMedia($this);
$this->system = new YellowSystem($this);
@@ -23,7 +22,8 @@ class YellowCore {
$this->user = new YellowUser($this);
$this->extension = new YellowExtension($this);
$this->lookup = new YellowLookup($this);
- $this->toolbox = new YellowToolbox();
+ $this->toolbox = new YellowToolbox($this);
+ $this->page = new YellowPage($this);
$this->checkRequirements();
$this->system->setDefault("sitename", "Localhost");
$this->system->setDefault("author", "Datenstrom");
@@ -93,7 +93,7 @@ class YellowCore {
$this->startup();
}
- // Handle request
+ // Handle request from web server
public function request() {
$statusCode = 0;
$this->toolbox->timerStart($time);
@@ -249,7 +249,7 @@ class YellowCore {
return $statusCode;
}
- // Handle command
+ // Handle command from command line
public function command($line = "") {
$statusCode = 0;
$this->toolbox->timerStart($time);
@@ -299,7 +299,7 @@ class YellowCore {
}
}
- // Handle logging
+ // Write information to log file
public function log($action, $message) {
$statusCode = 0;
foreach ($this->extension->data as $key=>$value) {
@@ -399,3229 +399,3236 @@ class YellowCore {
return isset($this->extension->data);
}
}
-
-class YellowPage {
- public $yellow; // access to API
- public $scheme; // server scheme
- public $address; // server address
- public $base; // base location
- public $location; // page location
- public $fileName; // content file name
- public $rawData; // raw data of page
- public $metaDataOffsetBytes; // meta data offset
- public $metaData; // meta data
- public $pageCollections; // additional pages
- public $sharedPages; // shared pages
- public $headerData; // response header
- public $outputData; // response output
- public $parser; // content parser
- public $parserData; // content data of page
- public $statusCode; // status code
- public $errorMessage; // error message
- public $lastModified; // last modification date
- public $available; // page is available? (boolean)
- public $visible; // page is visible location? (boolean)
- public $active; // page is active location? (boolean)
- public $cacheable; // page is cacheable? (boolean)
+class YellowContent {
+ public $yellow; // access to API
+ public $pages; // scanned pages
+
public function __construct($yellow) {
$this->yellow = $yellow;
- $this->scheme = "";
- $this->address = "";
- $this->base = "";
- $this->location = "";
- $this->fileName = "";
- $this->metaData = new YellowArray();
- $this->pageCollections = array();
- $this->sharedPages = array();
- $this->headerData = array();
- }
-
- // Set request information
- public function setRequestInformation($scheme, $address, $base, $location, $fileName, $cacheable) {
- $this->scheme = $scheme;
- $this->address = $address;
- $this->base = $base;
- $this->location = $location;
- $this->fileName = $fileName;
- $this->cacheable = $cacheable;
+ $this->pages = array();
}
- // Parse page meta
- public function parseMeta($rawData, $statusCode = 0, $errorMessage = "") {
- $this->rawData = $rawData;
- $this->parser = null;
- $this->parserData = "";
- $this->statusCode = $statusCode;
- $this->errorMessage = $errorMessage;
- $this->lastModified = 0;
- $this->available = $this->yellow->lookup->isAvailableLocation($this->location, $this->fileName);
- $this->visible = true;
- $this->active = $this->yellow->lookup->isActiveLocation($this->location, $this->yellow->page->location);
- $this->parseMetaData();
+ // Scan file system on demand
+ public function scanLocation($location) {
+ if (!isset($this->pages[$location])) {
+ $this->pages[$location] = array();
+ $scheme = $this->yellow->page->scheme;
+ $address = $this->yellow->page->address;
+ $base = $this->yellow->page->base;
+ if (is_string_empty($location)) {
+ $rootLocations = $this->yellow->lookup->findContentRootLocations();
+ foreach ($rootLocations as $rootLocation=>$rootFileName) {
+ $page = new YellowPage($this->yellow);
+ $page->setRequestInformation($scheme, $address, $base, $rootLocation, $rootFileName, false);
+ $page->parseMeta("");
+ array_push($this->pages[$location], $page);
+ }
+ } else {
+ if ($this->yellow->system->get("coreDebugMode")>=2) echo "YellowContent::scanLocation location:$location<br/>\n";
+ $fileNames = $this->yellow->lookup->findChildrenFromContentLocation($location);
+ foreach ($fileNames as $fileName) {
+ $page = new YellowPage($this->yellow);
+ $page->setRequestInformation($scheme, $address, $base,
+ $this->yellow->lookup->findContentLocationFromFile($fileName), $fileName, false);
+ $page->parseMeta($this->yellow->toolbox->readFile($fileName, 4096));
+ if (strlenb($page->rawData)<4096) $page->statusCode = 200;
+ array_push($this->pages[$location], $page);
+ }
+ }
+ }
+ return $this->pages[$location];
}
-
- // Parse page meta update
- public function parseMetaUpdate() {
- if ($this->statusCode==0) {
- $this->rawData = $this->yellow->toolbox->readFile($this->fileName);
- $this->statusCode = 200;
- $this->parseMetaData();
+
+ // Return page from, null if not found
+ public function find($location, $absoluteLocation = false) {
+ $found = false;
+ if ($absoluteLocation) $location = substru($location, strlenu($this->yellow->page->base));
+ foreach ($this->scanLocation($this->getParentLocation($location)) as $page) {
+ if ($page->location==$location) {
+ if (!$this->yellow->lookup->isRootLocation($page->location)) {
+ $found = true;
+ break;
+ }
+ }
}
+ return $found ? $page : null;
}
- // Parse page meta data
- public function parseMetaData() {
- $this->metaData = new YellowArray();
- $this->metaDataOffsetBytes = 0;
- if (!is_null($this->rawData)) {
- $this->set("title", $this->yellow->toolbox->createTextTitle($this->location));
- $this->set("language", $this->yellow->lookup->findContentLanguage($this->fileName, $this->yellow->system->get("language")));
- $this->set("modified", date("Y-m-d H:i:s", $this->yellow->toolbox->getFileModified($this->fileName)));
- $this->parseMetaDataRaw(array("sitename", "author", "layout", "theme", "parser", "status"));
- $titleHeader = ($this->location==$this->yellow->content->getHomeLocation($this->location)) ?
- $this->get("sitename") : $this->get("title")." - ".$this->get("sitename");
- if (!$this->isExisting("titleContent")) $this->set("titleContent", $this->get("title"));
- if (!$this->isExisting("titleNavigation")) $this->set("titleNavigation", $this->get("title"));
- if (!$this->isExisting("titleHeader")) $this->set("titleHeader", $titleHeader);
- if ($this->get("status")=="unlisted") $this->visible = false;
- if ($this->get("status")=="shared") $this->available = false;
- $this->parseMetaDataShared();
- } else {
- $this->set("size", filesize($this->fileName));
- $this->set("type", $this->yellow->toolbox->getFileType($this->fileName));
- $this->set("group", $this->yellow->toolbox->getFileGroup($this->fileName, $this->yellow->system->get("coreMediaDirectory")));
- $this->set("modified", date("Y-m-d H:i:s", $this->yellow->toolbox->getFileModified($this->fileName)));
- }
- foreach ($this->yellow->extension->data as $key=>$value) {
- if (method_exists($value["object"], "onParseMetaData")) $value["object"]->onParseMetaData($this);
- }
+ // Return page collection with all pages
+ public function index($showInvisible = false, $multiLanguage = false, $levelMax = 0) {
+ $rootLocation = $multiLanguage ? "" : $this->getRootLocation($this->yellow->page->location);
+ return $this->getChildrenRecursive($rootLocation, $showInvisible, $levelMax);
}
- // Parse page meta data from raw data
- public function parseMetaDataRaw($defaultKeys) {
- foreach ($defaultKeys as $key) {
- $value = $this->yellow->system->get($key);
- if (!is_string_empty($key) && !is_string_empty($value)) $this->set($key, $value);
- }
- if (preg_match("/^(\xEF\xBB\xBF)?\-\-\-[\r\n]+(.+?)\-\-\-[\r\n]+/s", $this->rawData, $parts)) {
- $this->metaDataOffsetBytes = strlenb($parts[0]);
- foreach (preg_split("/[\r\n]+/", $parts[2]) as $line) {
- if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) {
- if (!is_string_empty($matches[1]) && !is_string_empty($matches[2])) $this->set($matches[1], $matches[2]);
+ // Return page collection with top-level navigation
+ public function top($showInvisible = false, $showOnePager = true) {
+ $rootLocation = $this->getRootLocation($this->yellow->page->location);
+ $pages = $this->getChildren($rootLocation, $showInvisible);
+ if (count($pages)==1 && $showOnePager) {
+ $scheme = $this->yellow->page->scheme;
+ $address = $this->yellow->page->address;
+ $base = $this->yellow->page->base;
+ $one = ($pages->offsetGet(0)->location!=$this->yellow->page->location) ? $pages->offsetGet(0) : $this->yellow->page;
+ preg_match_all("/<h(\d) id=\"([^\"]+)\">(.*?)<\/h\d>/i", $one->getContent(), $matches, PREG_SET_ORDER);
+ foreach ($matches as $match) {
+ if ($match[1]==2) {
+ $page = new YellowPage($this->yellow);
+ $page->setRequestInformation($scheme, $address, $base, $one->location."#".$match[2], $one->fileName, false);
+ $page->parseMeta("---\nTitle: $match[3]\n---\n");
+ $pages->append($page);
}
}
- } elseif (preg_match("/^(\xEF\xBB\xBF)?([^\r\n]+)[\r\n]+=+[\r\n]+/", $this->rawData, $parts)) {
- $this->metaDataOffsetBytes = strlenb($parts[0]);
- $this->set("title", $parts[2]);
}
+ return $pages;
}
- // Parse page meta data for shared pages
- public function parseMetaDataShared() {
- $this->sharedPages["main"] = $this;
- if ($this->available && $this->statusCode!=0) {
- foreach ($this->yellow->content->getShared($this->location) as $page) {
- if ($page->get("status")=="shared") {
- $this->sharedPages[basename($page->location)] = $page;
- $page->sharedPages["main"] = $this;
- }
+ // Return page collection with path ancestry
+ public function path($location, $absoluteLocation = false) {
+ $pages = new YellowPageCollection($this->yellow);
+ if ($absoluteLocation) $location = substru($location, strlenu($this->yellow->page->base));
+ if ($page = $this->find($location)) {
+ $pages->prepend($page);
+ for (; $parent = $page->getParent(); $page=$parent) {
+ $pages->prepend($parent);
}
+ $home = $this->find($this->getHomeLocation($page->location));
+ if ($home && $home->location!=$page->location) $pages->prepend($home);
}
+ return $pages;
}
- // Parse page content on demand
- public function parseContent() {
- if (!is_null($this->rawData) && !is_object($this->parser)) {
- if ($this->yellow->extension->isExisting($this->get("parser"))) {
- $value = $this->yellow->extension->data[$this->get("parser")];
- if (method_exists($value["object"], "onParseContentRaw")) {
- $this->parser = $value["object"];
- $this->parserData = $this->getContent(true);
- $this->parserData = $this->parser->onParseContentRaw($this, $this->parserData);
- foreach ($this->yellow->extension->data as $key=>$value) {
- if (method_exists($value["object"], "onParseContentHtml")) {
- $output = $value["object"]->onParseContentHtml($this, $this->parserData);
- if (!is_null($output)) $this->parserData = $output;
- }
- }
+ // Return page collection with multiple languages
+ public function multi($location, $absoluteLocation = false, $showInvisible = false) {
+ $pages = new YellowPageCollection($this->yellow);
+ if ($absoluteLocation) $location = substru($location, strlenu($this->yellow->page->base));
+ $locationEnd = substru($location, strlenu($this->getRootLocation($location)) - 4);
+ foreach ($this->scanLocation("") as $page) {
+ if ($content = $this->find(substru($page->location, 4).$locationEnd)) {
+ if ($content->isAvailable() && ($content->isVisible() || $showInvisible)) {
+ if (!$this->yellow->lookup->isRootLocation($content->location)) $pages->append($content);
}
- } else {
- $this->parserData = $this->getContent(true);
- $this->parserData = preg_replace("/\[yellow error\]/i", $this->errorMessage, $this->parserData);
- }
- if (!$this->isExisting("description")) {
- $description = $this->yellow->toolbox->createTextDescription($this->parserData, 150);
- $this->set("description", !is_string_empty($description) ? $description : $this->get("title"));
}
- if ($this->yellow->system->get("coreDebugMode")>=3) {
- echo "YellowPage::parseContent location:".$this->location."<br/>\n";
+ }
+ return $pages;
+ }
+
+ // Return page collection that's empty
+ public function clean() {
+ return new YellowPageCollection($this->yellow);
+ }
+
+ // Return languages in multi language mode
+ public function getLanguages($showInvisible = false) {
+ $languages = array();
+ if ($this->yellow->system->get("coreMultiLanguageMode")) {
+ foreach ($this->scanLocation("") as $page) {
+ if ($page->isAvailable() && ($page->isVisible() || $showInvisible)) array_push($languages, $page->get("language"));
}
}
+ return $languages;
}
- // Parse page content shortcut
- public function parseContentShortcut($name, $text, $type) {
- $output = null;
- foreach ($this->yellow->extension->data as $key=>$value) {
- if (method_exists($value["object"], "onParseContentShortcut")) {
- $output = $value["object"]->onParseContentShortcut($this, $name, $text, $type);
- if (!is_null($output)) break;
+ // Return child pages
+ public function getChildren($location, $showInvisible = false) {
+ $pages = new YellowPageCollection($this->yellow);
+ foreach ($this->scanLocation($location) as $page) {
+ if ($page->isAvailable() && ($page->isVisible() || $showInvisible)) {
+ if (!$this->yellow->lookup->isRootLocation($page->location) && is_readable($page->fileName)) $pages->append($page);
}
}
- if (is_null($output)) {
- if ($name=="yellow" && $type=="inline" && $text=="error") {
- $output = $this->errorMessage;
- }
- }
- if ($this->yellow->system->get("coreDebugMode")>=3 && !is_string_empty($name)) {
- echo "YellowPage::parseContentShortcut name:$name type:$type<br/>\n";
- }
- return $output;
+ return $pages;
}
- // Parse page
- public function parsePage() {
- $this->parsePageLayout($this->get("layout"));
- if (!$this->isCacheable()) $this->setHeader("Cache-Control", "no-cache, no-store");
- if (!$this->isHeader("Content-Type")) $this->setHeader("Content-Type", "text/html; charset=utf-8");
- if (!$this->isHeader("Content-Modified")) $this->setHeader("Content-Modified", $this->getModified(true));
- if (!$this->isHeader("Last-Modified")) $this->setHeader("Last-Modified", $this->getLastModified(true));
- $fileNameTheme = $this->yellow->system->get("coreThemeDirectory").$this->yellow->lookup->normaliseName($this->get("theme")).".css";
- if (!is_file($fileNameTheme)) {
- $this->error(500, "Theme '".$this->get("theme")."' does not exist!");
- }
- if (!$this->yellow->language->isExisting($this->get("language"))) {
- $this->error(500, "Language '".$this->get("language")."' does not exist!");
- }
- if (!is_object($this->parser)) {
- $this->error(500, "Parser '".$this->get("parser")."' does not exist!");
- }
- if ($this->yellow->lookup->isNestedLocation($this->location, $this->fileName, true)) {
- $this->error(500, "Folder '".dirname($this->fileName)."' may not contain subfolders!");
- }
- if ($this->yellow->getRequestHandler()=="core" && $this->isExisting("redirect") && $this->statusCode==200) {
- $location = $this->yellow->lookup->normaliseLocation($this->get("redirect"), $this->location);
- $location = $this->yellow->lookup->normaliseUrl($this->scheme, $this->address, "", $location);
- $this->status(301, $location);
- }
- if ($this->yellow->getRequestHandler()=="core" && !$this->isAvailable() && $this->statusCode==200) {
- $this->error(404);
- }
- if ($this->isExisting("pageClean")) $this->outputData = null;
- foreach ($this->yellow->extension->data as $key=>$value) {
- if (method_exists($value["object"], "onParsePageOutput")) {
- $output = $value["object"]->onParsePageOutput($this, $this->outputData);
- if (!is_null($output)) $this->outputData = $output;
+ // Return child pages recursively
+ public function getChildrenRecursive($location, $showInvisible = false, $levelMax = 0) {
+ --$levelMax;
+ $pages = new YellowPageCollection($this->yellow);
+ foreach ($this->scanLocation($location) as $page) {
+ if ($page->isAvailable() && ($page->isVisible() || $showInvisible)) {
+ if (!$this->yellow->lookup->isRootLocation($page->location) && is_readable($page->fileName)) $pages->append($page);
+ }
+ if (!$this->yellow->lookup->isFileLocation($page->location) && $levelMax!=0) {
+ $pages->merge($this->getChildrenRecursive($page->location, $showInvisible, $levelMax));
}
}
+ return $pages;
}
- // Parse page layout
- public function parsePageLayout($name) {
- $this->outputData = null;
- foreach ($this->yellow->extension->data as $key=>$value) {
- if (method_exists($value["object"], "onParsePageLayout")) {
- $value["object"]->onParsePageLayout($this, $name);
- }
- }
- if (is_null($this->outputData)) {
- ob_start();
- $this->includeLayout($name);
- $this->outputData = ob_get_contents();
- ob_end_clean();
- }
+ // Return shared pages
+ public function getShared($location) {
+ $pages = new YellowPageCollection($this->yellow);
+ $sharedLocation = $this->getHomeLocation($location)."shared/";
+ return $pages->merge($this->scanLocation($sharedLocation));
}
- // Include page layout
- public function includeLayout($name) {
- $fileNameLayoutNormal = $this->yellow->system->get("coreLayoutDirectory").$this->yellow->lookup->normaliseName($name).".html";
- $fileNameLayoutTheme = $this->yellow->system->get("coreLayoutDirectory").
- $this->yellow->lookup->normaliseName($this->get("theme"))."-".$this->yellow->lookup->normaliseName($name).".html";
- if (is_file($fileNameLayoutTheme)) {
- if ($this->yellow->system->get("coreDebugMode")>=2) {
- echo "YellowPage::includeLayout file:$fileNameLayoutTheme<br/>\n";
- }
- $this->setLastModified(filemtime($fileNameLayoutTheme));
- require($fileNameLayoutTheme);
- } elseif (is_file($fileNameLayoutNormal)) {
- if ($this->yellow->system->get("coreDebugMode")>=2) {
- echo "YellowPage::includeLayout file:$fileNameLayoutNormal<br/>\n";
+ // Return root location
+ public function getRootLocation($location) {
+ $rootLocation = "root/";
+ if ($this->yellow->system->get("coreMultiLanguageMode")) {
+ foreach ($this->scanLocation("") as $page) {
+ $token = substru($page->location, 4);
+ if ($token!="/" && substru($location, 0, strlenu($token))==$token) {
+ $rootLocation = "root$token";
+ break;
+ }
}
- $this->setLastModified(filemtime($fileNameLayoutNormal));
- require($fileNameLayoutNormal);
- } else {
- $this->error(500, "Layout '$name' does not exist!");
- echo "Layout error<br/>\n";
}
- }
-
- // Set page setting
- public function set($key, $value) {
- $this->metaData[$key] = $value;
- }
-
- // Return page setting
- public function get($key) {
- return $this->isExisting($key) ? $this->metaData[$key] : "";
+ return $rootLocation;
}
- // Return page setting, HTML encoded
- public function getHtml($key) {
- return htmlspecialchars($this->get($key));
+ // Return home location
+ public function getHomeLocation($location) {
+ return substru($this->getRootLocation($location), 4);
}
- // Return page setting as language specific date
- public function getDate($key, $format = "") {
- if (!is_string_empty($format)) {
- $format = $this->yellow->language->getText($format);
- } else {
- $format = $this->yellow->language->getText("coreDateFormatMedium");
+ // Return parent location
+ public function getParentLocation($location) {
+ $parentLocation = "";
+ $token = rtrim(substru($this->getRootLocation($location), 4), "/");
+ if (preg_match("#^($token.*\/).+?$#", $location, $matches)) {
+ if ($matches[1]!="$token/" || $this->yellow->lookup->isFileLocation($location)) $parentLocation = $matches[1];
}
- return $this->yellow->language->getDateFormatted(strtotime($this->get($key)), $format);
+ if (is_string_empty($parentLocation)) $parentLocation = "root$token/";
+ return $parentLocation;
}
-
- // Return page setting as language specific date, HTML encoded
- public function getDateHtml($key, $format = "") {
- return htmlspecialchars($this->getDate($key, $format));
+
+ // Return top-level location
+ public function getParentTopLocation($location) {
+ $parentTopLocation = "";
+ $token = rtrim(substru($this->getRootLocation($location), 4), "/");
+ if (preg_match("#^($token.+?\/)#", $location, $matches)) $parentTopLocation = $matches[1];
+ if (is_string_empty($parentTopLocation)) $parentTopLocation = "$token/";
+ return $parentTopLocation;
+ }
+}
+
+class YellowMedia {
+ public $yellow; // access to API
+ public $files; // scanned files
+
+ public function __construct($yellow) {
+ $this->yellow = $yellow;
+ $this->files = array();
}
- // Return page setting as language specific date, relative to today
- public function getDateRelative($key, $format = "", $daysLimit = 30) {
- if (!is_string_empty($format)) {
- $format = $this->yellow->language->getText($format);
- } else {
- $format = $this->yellow->language->getText("coreDateFormatMedium");
+ // Scan file system on demand
+ public function scanLocation($location) {
+ if (!isset($this->files[$location])) {
+ $this->files[$location] = array();
+ $scheme = $this->yellow->page->scheme;
+ $address = $this->yellow->page->address;
+ $base = $this->yellow->system->get("coreServerBase");
+ if (is_string_empty($location)) {
+ $fileNames = array($this->yellow->system->get("coreMediaDirectory"));
+ } else {
+ if ($this->yellow->system->get("coreDebugMode")>=2) echo "YellowMedia::scanLocation location:$location<br/>\n";
+ $fileNames = $this->yellow->lookup->findChildrenFromMediaLocation($location);
+ }
+ foreach ($fileNames as $fileName) {
+ $file = new YellowPage($this->yellow);
+ $file->setRequestInformation($scheme, $address, $base,
+ $this->yellow->lookup->findMediaLocationFromFile($fileName), $fileName, false);
+ $file->parseMeta(null);
+ array_push($this->files[$location], $file);
+ }
}
- return $this->yellow->language->getDateRelative(strtotime($this->get($key)), $format, $daysLimit);
+ return $this->files[$location];
}
- // Return page setting as language specific date, relative to today, HTML encoded
- public function getDateRelativeHtml($key, $format = "", $daysLimit = 30) {
- return htmlspecialchars($this->getDateRelative($key, $format, $daysLimit));
+ // Return page with media file information, null if not found
+ public function find($location, $absoluteLocation = false) {
+ $found = false;
+ if ($absoluteLocation) $location = substru($location, strlenu($this->yellow->system->get("coreServerBase")));
+ foreach ($this->scanLocation($this->getParentLocation($location)) as $file) {
+ if ($file->location==$location) {
+ if ($this->yellow->lookup->isFileLocation($file->location)) {
+ $found = true;
+ break;
+ }
+ }
+ }
+ return $found ? $file : null;
}
- // Return page setting as date
- public function getDateFormatted($key, $format) {
- return $this->yellow->language->getDateFormatted(strtotime($this->get($key)), $format);
+ // Return page collection with all media files
+ public function index($showInvisible = false, $multiPass = false, $levelMax = 0) {
+ return $this->getChildrenRecursive("", $showInvisible, $levelMax);
}
- // Return page setting as date, HTML encoded
- public function getDateFormattedHtml($key, $format) {
- return htmlspecialchars($this->getDateFormatted($key, $format));
+ // Return page collection that's empty
+ public function clean() {
+ return new YellowPageCollection($this->yellow);
}
- // Return page content, HTML encoded or raw format
- public function getContent($rawFormat = false) {
- if ($rawFormat) {
- $this->parseMetaUpdate();
- $text = substrb($this->rawData, $this->metaDataOffsetBytes);
- } else {
- $this->parseContent();
- $text = $this->parserData;
+ // Return child files
+ public function getChildren($location, $showInvisible = false) {
+ $files = new YellowPageCollection($this->yellow);
+ foreach ($this->scanLocation($location) as $file) {
+ if ($file->isAvailable() && ($file->isVisible() || $showInvisible)) {
+ if ($this->yellow->lookup->isFileLocation($file->location)) $files->append($file);
+ }
}
- return $text;
- }
-
- // Return parent page, null if none
- public function getParent() {
- $parentLocation = $this->yellow->content->getParentLocation($this->location);
- return $this->yellow->content->find($parentLocation);
+ return $files;
}
- // Return top-level parent page, null if none
- public function getParentTop($homeFallback = false) {
- $parentTopLocation = $this->yellow->content->getParentTopLocation($this->location);
- if (!$this->yellow->content->find($parentTopLocation) && $homeFallback) {
- $parentTopLocation = $this->yellow->content->getHomeLocation($this->location);
+ // Return child files recursively
+ public function getChildrenRecursive($location, $showInvisible = false, $levelMax = 0) {
+ --$levelMax;
+ $files = new YellowPageCollection($this->yellow);
+ foreach ($this->scanLocation($location) as $file) {
+ if ($file->isAvailable() && ($file->isVisible() || $showInvisible)) {
+ if ($this->yellow->lookup->isFileLocation($file->location)) $files->append($file);
+ }
+ if (!$this->yellow->lookup->isFileLocation($file->location) && $levelMax!=0) {
+ $files->merge($this->getChildrenRecursive($file->location, $showInvisible, $levelMax));
+ }
}
- return $this->yellow->content->find($parentTopLocation);
+ return $files;
}
- // Return page collection with pages on the same level
- public function getSiblings($showInvisible = false) {
- $parentLocation = $this->yellow->content->getParentLocation($this->location);
- return $this->yellow->content->getChildren($parentLocation, $showInvisible);
- }
-
- // Return page collection with child pages
- public function getChildren($showInvisible = false) {
- return $this->yellow->content->getChildren($this->location, $showInvisible);
+ // Return home location
+ public function getHomeLocation($location) {
+ return $this->yellow->system->get("coreMediaLocation");
}
- // Return page collection with child pages recursively
- public function getChildrenRecursive($showInvisible = false, $levelMax = 0) {
- return $this->yellow->content->getChildrenRecursive($this->location, $showInvisible, $levelMax);
+ // Return parent location
+ public function getParentLocation($location) {
+ $token = rtrim($this->yellow->system->get("coreMediaLocation"), "/");
+ if (preg_match("#^($token.*\/).+?$#", $location, $matches)) {
+ if ($matches[1]!="$token/" || $this->yellow->lookup->isFileLocation($location)) $parentLocation = $matches[1];
+ }
+ if (is_string_empty($parentLocation)) $parentLocation = "";
+ return $parentLocation;
}
- // Set page collection with additional pages
- public function setPages($key, $pages) {
- $this->pageCollections[$key] = $pages;
+ // Return top-level location
+ public function getParentTopLocation($location) {
+ $token = rtrim($this->yellow->system->get("coreMediaLocation"), "/");
+ if (preg_match("#^($token.+?\/)#", $location, $matches)) $parentTopLocation = $matches[1];
+ if (is_string_empty($parentTopLocation)) $parentTopLocation = "$token/";
+ return $parentTopLocation;
}
+}
- // Return page collection with additional pages
- public function getPages($key) {
- return isset($this->pageCollections[$key]) ? $this->pageCollections[$key] : new YellowPageCollection($this->yellow);
- }
-
- // Set shared page
- public function setPage($key, $page) {
- $this->sharedPages[$key] = $page;
- }
+class YellowSystem {
+ public $yellow; // access to API
+ public $modified; // system modification date
+ public $settings; // system settings
+ public $settingsDefaults; // system settings defaults
- // Return shared page
- public function getPage($key) {
- return isset($this->sharedPages[$key]) ? $this->sharedPages[$key] : new YellowPage($this->yellow);
+ public function __construct($yellow) {
+ $this->yellow = $yellow;
+ $this->modified = 0;
+ $this->settings = new YellowArray();
+ $this->settingsDefaults = new YellowArray();
}
- // Return page URL
- public function getUrl() {
- return $this->yellow->lookup->normaliseUrl($this->scheme, $this->address, $this->base, $this->location);
+ // Load system settings from file
+ public function load($fileName) {
+ $this->modified = $this->yellow->toolbox->getFileModified($fileName);
+ $fileData = $this->yellow->toolbox->readFile($fileName);
+ $this->settings = $this->yellow->toolbox->getTextSettings($fileData, "");
+ if ($this->yellow->system->get("coreDebugMode")>=2) echo "YellowSystem::load file:$fileName<br/>\n";
+ if ($this->yellow->system->get("coreDebugMode")>=3) {
+ foreach ($this->settings as $key=>$value) {
+ echo "YellowSystem::load ".ucfirst($key).":$value<br/>\n";
+ }
+ }
}
- // Return page base
- public function getBase($multiLanguage = false) {
- return $multiLanguage ? rtrim($this->base.$this->yellow->content->getHomeLocation($this->location), "/") : $this->base;
+ // Save system settings to file
+ public function save($fileName, $settings) {
+ $this->modified = time();
+ $settingsNew = new YellowArray();
+ foreach ($settings as $key=>$value) {
+ if (!is_string_empty($key) && !is_string_empty($value)) {
+ $this->set($key, $value);
+ $settingsNew[$key] = $value;
+ }
+ }
+ $fileData = $this->yellow->toolbox->readFile($fileName);
+ $fileData = $this->yellow->toolbox->setTextSettings($fileData, "", "", $settingsNew);
+ return $this->yellow->toolbox->createFile($fileName, $fileData);
}
- // Return page location
- public function getLocation($absoluteLocation = false) {
- return $absoluteLocation ? $this->base.$this->location : $this->location;
+ // Set default system setting
+ public function setDefault($key, $value) {
+ $this->settingsDefaults[$key] = $value;
}
-
- // Set page request argument
- public function setRequest($key, $value) {
- $_REQUEST[$key] = $value;
+
+ // Set default system settings
+ public function setDefaults($lines) {
+ foreach ($lines as $line) {
+ if (preg_match("/^\#/", $line)) continue;
+ if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) {
+ if (!is_string_empty($matches[1]) && !is_string_empty($matches[2])) {
+ $this->settingsDefaults[$matches[1]] = $matches[2];
+ }
+ }
+ }
}
- // Return page request argument
- public function getRequest($key) {
- return isset($_REQUEST[$key]) ? $_REQUEST[$key] : "";
+ // Set system setting
+ public function set($key, $value) {
+ $this->settings[$key] = $value;
}
- // Return page request argument, HTML encoded
- public function getRequestHtml($key) {
- return htmlspecialchars($this->getRequest($key));
+ // Return system setting
+ public function get($key) {
+ if (isset($this->settings[$key])) {
+ $value = $this->settings[$key];
+ } else {
+ $value = isset($this->settingsDefaults[$key]) ? $this->settingsDefaults[$key] : "";
+ }
+ return $value;
}
- // Set page response header
- public function setHeader($key, $value) {
- $this->headerData[$key] = $value;
+ // Return system setting, HTML encoded
+ public function getHtml($key) {
+ return htmlspecialchars($this->get($key));
}
- // Return page response header
- public function getHeader($key) {
- return $this->isHeader($key) ? $this->headerData[$key] : "";
+ // Return different value for system setting
+ public function getDifferent($key) {
+ return reset(array_diff($this->getAvailable($key), array($this->get($key))));
}
-
- // Return page extra data
- public function getExtra($name) {
- $output = "";
- foreach ($this->yellow->extension->data as $key=>$value) {
- if (method_exists($value["object"], "onParsePageExtra")) {
- $outputExtension = $value["object"]->onParsePageExtra($this, $name);
- if (!is_null($outputExtension)) $output .= $outputExtension;
+
+ // Return available values for system setting
+ public function getAvailable($key) {
+ $values = array();
+ $valueDefault = isset($this->settingsDefaults[$key]) ? $this->settingsDefaults[$key] : "";
+ if ($key=="email") {
+ foreach ($this->yellow->user->settings as $userKey=>$userValue) {
+ array_push($values, $userKey);
}
- }
- if ($name=="header") {
- $fileNameTheme = $this->yellow->system->get("coreThemeDirectory").$this->yellow->lookup->normaliseName($this->get("theme")).".css";
- if (is_file($fileNameTheme)) {
- $locationTheme = $this->yellow->system->get("coreServerBase").
- $this->yellow->system->get("coreThemeLocation").$this->yellow->lookup->normaliseName($this->get("theme")).".css";
- $output .= "<link rel=\"stylesheet\" type=\"text/css\" media=\"all\" href=\"$locationTheme\" />\n";
+ } elseif ($key=="language") {
+ foreach ($this->yellow->language->settings as $languageKey=>$languageValue) {
+ array_push($values, $languageKey);
}
- $fileNameScript = $this->yellow->system->get("coreThemeDirectory").$this->yellow->lookup->normaliseName($this->get("theme")).".js";
- if (is_file($fileNameScript)) {
- $locationScript = $this->yellow->system->get("coreServerBase").
- $this->yellow->system->get("coreThemeLocation").$this->yellow->lookup->normaliseName($this->get("theme")).".js";
- $output .= "<script type=\"text/javascript\" src=\"$locationScript\"></script>\n";
+ } elseif ($key=="layout") {
+ $path = $this->yellow->system->get("coreLayoutDirectory");
+ foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.html$/", true, false, false) as $entry) {
+ array_push($values, lcfirst(substru($entry, 0, -5)));
}
- $fileNameFavicon = $this->yellow->system->get("coreThemeDirectory").$this->yellow->lookup->normaliseName($this->get("theme")).".png";
- if (is_file($fileNameFavicon)) {
- $locationFavicon = $this->yellow->system->get("coreServerBase").
- $this->yellow->system->get("coreThemeLocation").$this->yellow->lookup->normaliseName($this->get("theme")).".png";
- $output .= "<link rel=\"icon\" type=\"image/png\" href=\"$locationFavicon\" />\n";
+ } elseif ($key=="theme") {
+ $path = $this->yellow->system->get("coreThemeDirectory");
+ foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.css$/", true, false, false) as $entry) {
+ array_push($values, lcfirst(substru($entry, 0, -4)));
}
}
- return $output;
+ return !is_array_empty($values) ? $values : array($valueDefault);
}
+ public function getValues($key) { return $this->getAvailable($key); } //TODO: remove later, for backwards compatibility
- // Set page response output
- public function setOutput($output) {
- $this->outputData = $output;
+ // Return system settings
+ public function getSettings($filterStart = "", $filterEnd = "") {
+ $settings = array();
+ if (is_string_empty($filterStart) && is_string_empty($filterEnd)) {
+ $settings = array_merge($this->settingsDefaults->getArrayCopy(), $this->settings->getArrayCopy());
+ } else {
+ foreach (array_merge($this->settingsDefaults->getArrayCopy(), $this->settings->getArrayCopy()) as $key=>$value) {
+ if (!is_string_empty($filterStart) && substru($key, 0, strlenu($filterStart))==$filterStart) $settings[$key] = $value;
+ if (!is_string_empty($filterEnd) && substru($key, -strlenu($filterEnd))==$filterEnd) $settings[$key] = $value;
+ }
+ }
+ return $settings;
}
- // Return page modification date, Unix time or HTTP format
+ // Return system settings modification date, Unix time or HTTP format
public function getModified($httpFormat = false) {
- $modified = strtotime($this->get("modified"));
- return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($modified) : $modified;
+ return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($this->modified) : $this->modified;
}
- // Set last modification date, Unix time
- public function setLastModified($modified) {
- $this->lastModified = max($this->lastModified, $modified);
+ // Check if system setting exists
+ public function isExisting($key) {
+ return isset($this->settings[$key]);
}
+}
+
+class YellowLanguage {
+ public $yellow; // access to API
+ public $modified; // language modification date
+ public $settings; // language settings
+ public $settingsDefaults; // language settings defaults
+ public $language; // current language
- // Return last modification date, Unix time or HTTP format
- public function getLastModified($httpFormat = false) {
- $lastModified = max($this->lastModified, $this->getModified(), $this->yellow->system->getModified(),
- $this->yellow->language->getModified(), $this->yellow->extension->getModified());
- foreach ($this->pageCollections as $pages) $lastModified = max($lastModified, $pages->getModified());
- foreach ($this->sharedPages as $page) $lastModified = max($lastModified, $page->getModified());
- return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($lastModified) : $lastModified;
+ public function __construct($yellow) {
+ $this->yellow = $yellow;
+ $this->modified = 0;
+ $this->settings = new YellowArray();
+ $this->settingsDefaults = new YellowArray();
+ $this->language = "";
}
- // Return raw data for error page
- public function getRawDataError() {
- $statusCode = $this->statusCode;
- $sharedLocation = $this->yellow->content->getHomeLocation($this->location)."shared/";
- $fileNameError = $this->yellow->lookup->findFileFromContentLocation($sharedLocation, true).$this->yellow->system->get("coreContentErrorFile");
- $fileNameError = str_replace("(.*)", $statusCode, $fileNameError);
- $languageError = $this->yellow->lookup->findContentLanguage($this->fileName, $this->yellow->system->get("language"));
- if (is_file($fileNameError)) {
- $rawData = $this->yellow->toolbox->readFile($fileNameError);
- } elseif ($this->yellow->language->isText("coreError{$statusCode}Title", $languageError)) {
- $rawData = "---\nTitle: ".$this->yellow->language->getText("coreError{$statusCode}Title", $languageError)."\n";
- $rawData .= "Layout: error\n---\n".$this->yellow->language->getText("coreError{$statusCode}Text", $languageError);
- } else {
- $rawData = "---\nTitle:".$this->yellow->toolbox->getHttpStatusFormatted($statusCode, true)."\n";
- $rawData .= "Layout:error\n---\n".$this->errorMessage;
+ // Load language settings from file
+ public function load($fileName) {
+ $this->modified = $this->yellow->toolbox->getFileModified($fileName);
+ $fileData = $this->yellow->toolbox->readFile($fileName);
+ $settings = $this->yellow->toolbox->getTextSettings($fileData, "language");
+ foreach ($settings as $language=>$block) {
+ if (!isset($this->settings[$language])) $this->settings[$language] = new YellowArray();
+ foreach ($block as $key=>$value) {
+ $this->settings[$language][$key] = $value;
+ }
}
- return $rawData;
- }
-
- // Return page status code, number or HTTP format
- public function getStatusCode($httpFormat = false) {
- $statusCode = $this->statusCode;
- if ($httpFormat) {
- $statusCode = $this->yellow->toolbox->getHttpStatusFormatted($statusCode);
- if (!is_string_empty($this->errorMessage)) $statusCode .= ": ".$this->errorMessage;
+ if ($this->yellow->system->get("coreDebugMode")>=2) echo "YellowLanguage::load file:$fileName<br/>\n";
+ foreach ($this->settings->getArrayCopy() as $key=>$value) {
+ if (!isset($this->settings[$key]["languageDescription"])) {
+ unset($this->settings[$key]);
+ }
}
- return $statusCode;
+ $callback = function ($a, $b) {
+ return strnatcmp($a["languageDescription"], $b["languageDescription"]);
+ };
+ $this->settings->uasort($callback);
}
- // Respond with status code, no page content
- public function status($statusCode, $location = "") {
- if ($statusCode>0 && !$this->isExisting("pageClean")) {
- $this->statusCode = $statusCode;
- $this->lastModified = 0;
- $this->headerData = array();
- if (!is_string_empty($location)) {
- $this->setHeader("Location", $location);
- $this->setHeader("Cache-Control", "no-cache, no-store");
- }
- $this->set("pageClean", (string)$statusCode);
- }
+ // Set current language
+ public function set($language) {
+ $this->language = $language;
}
- // Respond with error page
- public function error($statusCode, $errorMessage = "") {
- if ($statusCode>=400 && is_string_empty($this->errorMessage)) {
- $this->statusCode = $statusCode;
- $this->errorMessage = is_string_empty($errorMessage) ? "Page error!" : $errorMessage;
+ // Set default language setting
+ public function setDefault($key, $value, $language) {
+ if (!isset($this->settings[$language])) $this->settings[$language] = new YellowArray();
+ $this->settings[$language][$key] = $value;
+ $this->settingsDefaults[$key] = $value;
+ }
+
+ // Set default language settings
+ public function setDefaults($lines) {
+ $language = "";
+ foreach ($lines as $line) {
+ if (preg_match("/^\#/", $line)) continue;
+ if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) {
+ if (lcfirst($matches[1])=="language" && !is_string_empty($matches[2])) {
+ $language = $matches[2];
+ if (!isset($this->settings[$language])) $this->settings[$language] = new YellowArray();
+ }
+ if (!is_string_empty($language) && !is_string_empty($matches[1]) && !is_string_empty($matches[2])) {
+ $this->settings[$language][$matches[1]] = $matches[2];
+ $this->settingsDefaults[$matches[1]] = true;
+ }
+ }
}
}
- // Check if page is available
- public function isAvailable() {
- return $this->available;
+ // Set language setting
+ public function setText($key, $value, $language) {
+ if (!isset($this->settings[$language])) $this->settings[$language] = new YellowArray();
+ $this->settings[$language][$key] = $value;
}
- // Check if page is visible
- public function isVisible() {
- return $this->visible;
+ // Return language setting
+ public function getText($key, $language = "") {
+ if (is_string_empty($language)) $language = $this->language;
+ return $this->isText($key, $language) ? $this->settings[$language][$key] : "[$key]";
}
-
- // Check if page is within current HTTP request
- public function isActive() {
- return $this->active;
+
+ // Return language setting, HTML encoded
+ public function getTextHtml($key, $language = "") {
+ return htmlspecialchars($this->getText($key, $language));
}
- // Check if page is cacheable
- public function isCacheable() {
- return $this->cacheable;
+ // Return human readable date
+ public function getDateFormatted($timestamp, $format, $language = "") {
+ $dateMonthsNominative = preg_split("/\s*,\s*/", $this->getText("coreDateMonthsNominative", $language));
+ $dateMonthsGenitive = preg_split("/\s*,\s*/", $this->getText("coreDateMonthsGenitive", $language));
+ $dateWeekdays = preg_split("/\s*,\s*/", $this->getText("coreDateWeekdays", $language));
+ $monthNominative = $dateMonthsNominative[date("n", $timestamp) - 1];
+ $monthGenitive = $dateMonthsGenitive[date("n", $timestamp) - 1];
+ $weekday = $dateWeekdays[date("N", $timestamp) - 1];
+ $timeZone = $this->yellow->system->get("coreTimezone");
+ $timeZoneHelper = new DateTime("now", new DateTimeZone($timeZone));
+ $timeZoneOffset = $timeZoneHelper->getOffset();
+ $timeZoneAbbreviation = "GMT".($timeZoneOffset<0 ? "-" : "+").abs(intval($timeZoneOffset/3600));
+ $format = preg_replace("/(?<!\\\)F/", addcslashes($monthNominative, "A..Za..z"), $format);
+ $format = preg_replace("/(?<!\\\)V/", addcslashes($monthGenitive, "A..Za..z"), $format);
+ $format = preg_replace("/(?<!\\\)M/", addcslashes(substru($monthNominative, 0, 3), "A..Za..z"), $format);
+ $format = preg_replace("/(?<!\\\)D/", addcslashes(substru($weekday, 0, 3), "A..Za..z"), $format);
+ $format = preg_replace("/(?<!\\\)l/", addcslashes($weekday, "A..Za..z"), $format);
+ $format = preg_replace("/(?<!\\\)T/", addcslashes($timeZoneAbbreviation, "A..Za..z"), $format);
+ return date($format, $timestamp);
}
-
- // Check if page with error
- public function isError() {
- return $this->statusCode>=400;
+
+ // Return human readable date, relative to today
+ public function getDateRelative($timestamp, $format, $daysLimit, $language = "") {
+ $timeDifference = time() - $timestamp;
+ $days = abs(intval($timeDifference/86400));
+ $key = $timeDifference>=0 ? "coreDatePast" : "coreDateFuture";
+ $tokens = preg_split("/\s*,\s*/", $this->getText($key, $language));
+ if (count($tokens)>=8) {
+ if ($days<=$daysLimit || $daysLimit==0) {
+ if ($days==0) {
+ $output = $tokens[0];
+ } elseif ($days==1) {
+ $output = $tokens[1];
+ } elseif ($days>=2 && $days<=29) {
+ $output = preg_replace("/@x/i", $days, $tokens[2]);
+ } elseif ($days>=30 && $days<=59) {
+ $output = $tokens[3];
+ } elseif ($days>=60 && $days<=364) {
+ $output = preg_replace("/@x/i", intval($days/30), $tokens[4]);
+ } elseif ($days>=365 && $days<=729) {
+ $output = $tokens[5];
+ } else {
+ $output = preg_replace("/@x/i", intval($days/365.25), $tokens[6]);
+ }
+ } else {
+ $output = preg_replace("/@x/i", $this->getDateFormatted($timestamp, $format, $language), $tokens[7]);
+ }
+ } else {
+ $output = "[$key]";
+ }
+ return $output;
}
- // Check if page setting exists
- public function isExisting($key) {
- return isset($this->metaData[$key]);
+ // Return language settings
+ public function getSettings($filterStart = "", $filterEnd = "", $language = "") {
+ $settings = array();
+ if (is_string_empty($language)) $language = $this->language;
+ if (isset($this->settings[$language])) {
+ if (is_string_empty($filterStart) && is_string_empty($filterEnd)) {
+ $settings = $this->settings[$language]->getArrayCopy();
+ } else {
+ foreach ($this->settings[$language] as $key=>$value) {
+ if (!is_string_empty($filterStart) && substru($key, 0, strlenu($filterStart))==$filterStart) $settings[$key] = $value;
+ if (!is_string_empty($filterEnd) && substru($key, -strlenu($filterEnd))==$filterEnd) $settings[$key] = $value;
+ }
+ }
+ }
+ return $settings;
}
- // Check if request argument exists
- public function isRequest($key) {
- return isset($_REQUEST[$key]);
+ // Return language settings modification date, Unix time or HTTP format
+ public function getModified($httpFormat = false) {
+ return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($this->modified) : $this->modified;
}
- // Check if response header exists
- public function isHeader($key) {
- return isset($this->headerData[$key]);
+ // Normalise date into known format
+ public function normaliseDate($text, $language = "") {
+ if (preg_match("/^\d+\-\d+$/", $text)) {
+ $output = $this->getDateFormatted(strtotime($text), $this->getText("coreDateFormatShort", $language), $language);
+ } elseif (preg_match("/^\d+\-\d+\-\d+$/", $text)) {
+ $output = $this->getDateFormatted(strtotime($text), $this->getText("coreDateFormatMedium", $language), $language);
+ } elseif (preg_match("/^\d+\-\d+\-\d+ \d+\:\d+$/", $text)) {
+ $output = $this->getDateFormatted(strtotime($text), $this->getText("coreDateFormatLong", $language), $language);
+ } else {
+ $output = $text;
+ }
+ return $output;
}
- // Check if shared page exists
- public function isPage($key) {
- return isset($this->sharedPages[$key]);
+ // Check if language setting exists
+ public function isText($key, $language = "") {
+ if (is_string_empty($language)) $language = $this->language;
+ return isset($this->settings[$language]) && isset($this->settings[$language][$key]);
+ }
+
+ // Check if language exists
+ public function isExisting($language) {
+ return isset($this->settings[$language]);
}
}
-class YellowPageCollection extends ArrayObject {
- public $yellow; // access to API
- public $filterValue; // current page filter value
- public $paginationNumber; // current page number in pagination
- public $paginationCount; // highest page number in pagination
+class YellowUser {
+ public $yellow; // access to API
+ public $modified; // user modification date
+ public $settings; // user settings
+ public $email; // current email
public function __construct($yellow) {
- parent::__construct(array());
$this->yellow = $yellow;
+ $this->modified = 0;
+ $this->settings = new YellowArray();
+ $this->email = "";
}
-
- // Append to end of page collection
- #[\ReturnTypeWillChange]
- public function append($page) {
- parent::append($page);
- }
-
- // Prepend to start of page collection
- #[\ReturnTypeWillChange]
- public function prepend($page) {
- $array = $this->getArrayCopy();
- array_unshift($array, $page);
- $this->exchangeArray($array);
+
+ // Load user settings from file
+ public function load($fileName) {
+ $this->modified = $this->yellow->toolbox->getFileModified($fileName);
+ $fileData = $this->yellow->toolbox->readFile($fileName);
+ $this->settings = $this->yellow->toolbox->getTextSettings($fileData, "email");
+ if ($this->yellow->system->get("coreDebugMode")>=2) echo "YellowUser::load file:$fileName<br/>\n";
}
-
- // Filter page collection by page setting
- public function filter($key, $value, $exactMatch = true): YellowPageCollection {
- $array = array();
- $value = str_replace(" ", "-", strtoloweru($value));
- $valueLength = strlenu($value);
- $this->filterValue = "";
- foreach ($this->getArrayCopy() as $page) {
- if ($page->isExisting($key)) {
- foreach (preg_split("/\s*,\s*/", $page->get($key)) as $pageValue) {
- $pageValueLength = $exactMatch ? strlenu($pageValue) : $valueLength;
- if ($value==substru(str_replace(" ", "-", strtoloweru($pageValue)), 0, $pageValueLength)) {
- if (is_string_empty($this->filterValue)) $this->filterValue = substru($pageValue, 0, $pageValueLength);
- array_push($array, $page);
- break;
- }
- }
+
+ // Save user settings to file
+ public function save($fileName, $email, $settings) {
+ $this->modified = time();
+ $settingsNew = new YellowArray();
+ $settingsNew["email"] = $email;
+ foreach ($settings as $key=>$value) {
+ if (!is_string_empty($key) && !is_string_empty($value)) {
+ $this->setUser($key, $value, $email);
+ $settingsNew[$key] = $value;
}
}
- $this->exchangeArray($array);
- return $this;
+ $fileData = $this->yellow->toolbox->readFile($fileName);
+ $fileData = $this->yellow->toolbox->setTextSettings($fileData, "email", $email, $settingsNew);
+ return $this->yellow->toolbox->createFile($fileName, $fileData);
}
- // Filter page collection by location or file
- public function match($regex = "/.*/", $filterByLocation = true): YellowPageCollection {
- $array = array();
- $this->filterValue = $regex;
- foreach ($this->getArrayCopy() as $page) {
- if (preg_match($regex, $filterByLocation ? $page->location : $page->fileName)) array_push($array, $page);
- }
- $this->exchangeArray($array);
- return $this;
+ // Remove user settings from file
+ public function remove($fileName, $email) {
+ $this->modified = time();
+ if (isset($this->settings[$email])) unset($this->settings[$email]);
+ $fileData = $this->yellow->toolbox->readFile($fileName);
+ $fileData = $this->yellow->toolbox->unsetTextSettings($fileData, "email", $email);
+ return $this->yellow->toolbox->createFile($fileName, $fileData);
}
- // Sort page collection by page setting
- public function sort($key, $ascendingOrder = true): YellowPageCollection {
- $array = $this->getArrayCopy();
- $sortIndex = 0;
- foreach ($array as $page) {
- $page->set("sortIndex", ++$sortIndex);
- }
- $callback = function ($a, $b) use ($key, $ascendingOrder) {
- $result = $ascendingOrder ?
- strnatcasecmp($a->get($key), $b->get($key)) :
- strnatcasecmp($b->get($key), $a->get($key));
- return $result==0 ? $a->get("sortIndex") - $b->get("sortIndex") : $result;
- };
- usort($array, $callback);
- $this->exchangeArray($array);
- return $this;
+ // Set current email
+ public function set($email) {
+ $this->email = $email;
}
- // Sort page collection by settings similarity
- public function similar($page, $ascendingOrder = false): YellowPageCollection {
- $location = $page->location;
- $keywords = strtoloweru($page->get("title").",".$page->get("tag").",".$page->get("author"));
- $tokens = array_unique(array_filter(preg_split("/[,\s\(\)\+\-]/", $keywords), "strlen"));
- if (!is_array_empty($tokens)) {
- $array = array();
- foreach ($this->getArrayCopy() as $page) {
- $sortScore = 0;
- foreach ($tokens as $token) {
- if (stristr($page->get("title"), $token)) $sortScore += 50;
- if (stristr($page->get("tag"), $token)) $sortScore += 5;
- if (stristr($page->get("author"), $token)) $sortScore += 2;
- }
- if ($page->location!=$location) {
- $page->set("sortScore", $sortScore);
- array_push($array, $page);
- }
- }
- $this->exchangeArray($array);
- $this->sort("modified", $ascendingOrder)->sort("sortScore", $ascendingOrder);
- }
- return $this;
- }
-
- // Calculate union, merge page collection
- public function merge($input): YellowPageCollection {
- $this->exchangeArray(array_merge($this->getArrayCopy(), (array)$input));
- return $this;
+ // Set user setting
+ public function setUser($key, $value, $email) {
+ if (!isset($this->settings[$email])) $this->settings[$email] = new YellowArray();
+ $this->settings[$email][$key] = $value;
}
- // Calculate intersection, remove pages that are not present in another page collection
- public function intersect($input): YellowPageCollection {
- $callback = function ($a, $b) {
- return strcmp(spl_object_hash($a), spl_object_hash($b));
- };
- $this->exchangeArray(array_uintersect($this->getArrayCopy(), (array)$input, $callback));
- return $this;
+ // Return user setting
+ public function getUser($key, $email = "") {
+ if (is_string_empty($email)) $email = $this->email;
+ return isset($this->settings[$email]) && isset($this->settings[$email][$key]) ? $this->settings[$email][$key] : "";
}
- // Calculate difference, remove pages that are present in another page collection
- public function diff($input): YellowPageCollection {
- $callback = function ($a, $b) {
- return strcmp(spl_object_hash($a), spl_object_hash($b));
- };
- $this->exchangeArray(array_udiff($this->getArrayCopy(), (array)$input, $callback));
- return $this;
- }
-
- // Limit the number of pages in page collection
- public function limit($pagesMax): YellowPageCollection {
- $this->exchangeArray(array_slice($this->getArrayCopy(), 0, $pagesMax));
- return $this;
- }
-
- // Reverse page collection
- public function reverse(): YellowPageCollection {
- $this->exchangeArray(array_reverse($this->getArrayCopy()));
- return $this;
- }
-
- // Randomize page collection
- public function shuffle(): YellowPageCollection {
- $array = $this->getArrayCopy();
- shuffle($array);
- $this->exchangeArray($array);
- return $this;
+ // Return user setting, HTML encoded
+ public function getUserHtml($key, $email = "") {
+ return htmlspecialchars($this->getUser($key, $email));
}
- // Paginate page collection
- public function paginate($limit): YellowPageCollection {
- if (!$this->isPagination() && $limit!=0) {
- $this->paginationNumber = 1;
- $this->paginationCount = ceil($this->count() / $limit);
- if ($this->yellow->page->isRequest("page")) {
- $this->paginationNumber = intval($this->yellow->page->getRequest("page"));
- }
- if ($this->paginationNumber<0 || $this->paginationNumber>$this->paginationCount) $this->paginationNumber = 0;
- if ($this->paginationNumber) {
- $this->exchangeArray(array_slice($this->getArrayCopy(), ($this->paginationNumber - 1) * $limit, $limit));
- } else {
- $this->yellow->page->error(404);
- }
- }
- return $this;
- }
-
- // Return current page number in pagination
- public function getPaginationNumber() {
- return $this->paginationNumber;
+ // Return user settings
+ public function getSettings($email = "") {
+ $settings = array();
+ if (is_string_empty($email)) $email = $this->email;
+ if (isset($this->settings[$email])) $settings = $this->settings[$email]->getArrayCopy();
+ return $settings;
}
- // Return highest page number in pagination
- public function getPaginationCount() {
- return $this->paginationCount;
+ // Return user settings modification date, Unix time or HTTP format
+ public function getModified($httpFormat = false) {
+ return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($this->modified) : $this->modified;
}
- // Return location for a page in pagination
- public function getPaginationLocation($absoluteLocation = true, $pageNumber = 1) {
- $location = $locationArguments = "";
- if ($pageNumber>=1 && $pageNumber<=$this->paginationCount) {
- $location = $this->yellow->page->getLocation($absoluteLocation);
- $locationArguments = $this->yellow->toolbox->getLocationArgumentsNew("page", $pageNumber>1 ? "$pageNumber" : "");
- }
- return $location.$locationArguments;
+ // Check if user setting exists
+ public function isUser($key, $email = "") {
+ if (is_string_empty($email)) $email = $this->email;
+ return isset($this->settings[$email]) && isset($this->settings[$email][$key]);
}
- // Return location for previous page in pagination
- public function getPaginationPrevious($absoluteLocation = true) {
- $pageNumber = $this->paginationNumber-1;
- return $this->getPaginationLocation($absoluteLocation, $pageNumber);
+ // Check if user exists
+ public function isExisting($email) {
+ return isset($this->settings[$email]);
}
+}
- // Return location for next page in pagination
- public function getPaginationNext($absoluteLocation = true) {
- $pageNumber = $this->paginationNumber+1;
- return $this->getPaginationLocation($absoluteLocation, $pageNumber);
+class YellowExtension {
+ public $yellow; // access to API
+ public $modified; // extension modification date
+ public $data; // extension data
+
+ public function __construct($yellow) {
+ $this->yellow = $yellow;
+ $this->modified = 0;
+ $this->data = array();
}
- // Return current page number in collection
- public function getPageNumber($page) {
- $pageNumber = 0;
- foreach ($this->getIterator() as $key=>$value) {
- if ($page->getLocation()==$value->getLocation()) {
- $pageNumber = $key+1;
- break;
- }
+ // Load extensions
+ public function load($path) {
+ foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.php$/", true, false) as $entry) {
+ $this->modified = max($this->modified, filemtime($entry));
+ require_once($entry);
+ $name = $this->yellow->lookup->normaliseName(basename($entry), true, true);
+ $this->register(lcfirst($name), "Yellow".ucfirst($name));
+ if ($this->yellow->system->get("coreDebugMode")>=3) echo "YellowExtension::load file:$entry<br/>\n";
+ }
+ $callback = function ($a, $b) {
+ return $a["priority"] - $b["priority"];
+ };
+ uasort($this->data, $callback);
+ foreach ($this->data as $key=>$value) {
+ if (method_exists($this->data[$key]["object"], "onLoad")) $this->data[$key]["object"]->onLoad($this->yellow);
}
- return $pageNumber;
- }
-
- // Return page in collection, null if none
- public function getPage($pageNumber = 1) {
- return ($pageNumber>=1 && $pageNumber<=$this->count()) ? $this->offsetGet($pageNumber-1) : null;
- }
-
- // Return previous page in collection, null if none
- public function getPagePrevious($page) {
- $pageNumber = $this->getPageNumber($page)-1;
- return $this->getPage($pageNumber);
}
- // Return next page in collection, null if none
- public function getPageNext($page) {
- $pageNumber = $this->getPageNumber($page)+1;
- return $this->getPage($pageNumber);
+ // Register extension
+ public function register($key, $class) {
+ if (!$this->isExisting($key) && class_exists($class)) {
+ $this->data[$key] = array();
+ $this->data[$key]["object"] = $class=="YellowCore" ? new stdClass : new $class;
+ $this->data[$key]["class"] = $class;
+ $this->data[$key]["version"] = defined("$class::VERSION") ? $class::VERSION : 0;
+ $this->data[$key]["priority"] = defined("$class::PRIORITY") ? $class::PRIORITY : count($this->data) + 10;
+ }
}
- // Return current page filter
- public function getFilter() {
- return $this->filterValue;
+ // Return extension
+ public function get($key) {
+ return $this->data[$key]["object"];
}
- // Return page collection modification date, Unix time or HTTP format
+ // Return extensions modification date, Unix time or HTTP format
public function getModified($httpFormat = false) {
- $modified = 0;
- foreach ($this->getIterator() as $page) {
- $modified = max($modified, $page->getModified());
- }
- return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($modified) : $modified;
- }
-
- // Check if there is a pagination
- public function isPagination() {
- return $this->paginationCount>1;
+ return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($this->modified) : $this->modified;
}
- // Check if page collection is empty
- public function isEmpty() {
- return empty($this->getArrayCopy());
+ // Check if extension exists
+ public function isExisting($key) {
+ return isset($this->data[$key]);
}
}
-class YellowContent {
- public $yellow; // access to API
- public $pages; // scanned pages
+class YellowLookup {
+ public $yellow; // access to API
+ public $requestHandler; // request handler name
+ public $commandHandler; // command handler name
+ public $layoutArguments; // layout arguments
public function __construct($yellow) {
$this->yellow = $yellow;
- $this->pages = array();
}
- // Scan file system on demand
- public function scanLocation($location) {
- if (!isset($this->pages[$location])) {
- $this->pages[$location] = array();
- $scheme = $this->yellow->page->scheme;
- $address = $this->yellow->page->address;
- $base = $this->yellow->page->base;
- if (is_string_empty($location)) {
- $rootLocations = $this->yellow->lookup->findContentRootLocations();
- foreach ($rootLocations as $rootLocation=>$rootFileName) {
- $page = new YellowPage($this->yellow);
- $page->setRequestInformation($scheme, $address, $base, $rootLocation, $rootFileName, false);
- $page->parseMeta("");
- array_push($this->pages[$location], $page);
- }
- } else {
- if ($this->yellow->system->get("coreDebugMode")>=2) echo "YellowContent::scanLocation location:$location<br/>\n";
- $fileNames = $this->yellow->lookup->findChildrenFromContentLocation($location);
- foreach ($fileNames as $fileName) {
- $page = new YellowPage($this->yellow);
- $page->setRequestInformation($scheme, $address, $base,
- $this->yellow->lookup->findContentLocationFromFile($fileName), $fileName, false);
- $page->parseMeta($this->yellow->toolbox->readFile($fileName, 4096));
- if (strlenb($page->rawData)<4096) $page->statusCode = 200;
- array_push($this->pages[$location], $page);
+ // Return file system information
+ public function findFileSystemInformation() {
+ $pathInstall = substru(__DIR__, 0, 1-strlenu($this->yellow->system->get("coreExtensionDirectory")));
+ $pathBase = $this->yellow->system->get("coreContentDirectory");
+ $pathRoot = $this->yellow->system->get("coreMultiLanguageMode") ? "default/" : "";
+ $pathHome = "home/";
+ if (!is_string_empty($pathRoot)) {
+ $firstRoot = "";
+ $token = $root = rtrim($pathRoot, "/");
+ foreach ($this->yellow->toolbox->getDirectoryEntries($pathBase, "/.*/", true, true, false) as $entry) {
+ if (is_string_empty($firstRoot)) $firstRoot = $token = $entry;
+ if ($this->normaliseToken($entry)==$root) {
+ $token = $entry;
+ break;
}
}
+ $pathRoot = $this->normaliseToken($token)."/";
+ $pathBase .= "$firstRoot/";
}
- return $this->pages[$location];
- }
-
- // Return page from, null if not found
- public function find($location, $absoluteLocation = false) {
- $found = false;
- if ($absoluteLocation) $location = substru($location, strlenu($this->yellow->page->base));
- foreach ($this->scanLocation($this->getParentLocation($location)) as $page) {
- if ($page->location==$location) {
- if (!$this->yellow->lookup->isRootLocation($page->location)) {
- $found = true;
+ if (!is_string_empty($pathHome)) {
+ $firstHome = "";
+ $token = $home = rtrim($pathHome, "/");
+ foreach ($this->yellow->toolbox->getDirectoryEntries($pathBase, "/.*/", true, true, false) as $entry) {
+ if (is_string_empty($firstHome)) $firstHome = $token = $entry;
+ if ($this->normaliseToken($entry)==$home) {
+ $token = $entry;
break;
}
}
+ $pathHome = $this->normaliseToken($token)."/";
}
- return $found ? $page : null;
- }
-
- // Return page collection with all pages
- public function index($showInvisible = false, $multiLanguage = false, $levelMax = 0) {
- $rootLocation = $multiLanguage ? "" : $this->getRootLocation($this->yellow->page->location);
- return $this->getChildrenRecursive($rootLocation, $showInvisible, $levelMax);
+ return array($pathInstall, $pathRoot, $pathHome);
}
- // Return page collection with top-level navigation
- public function top($showInvisible = false, $showOnePager = true) {
- $rootLocation = $this->getRootLocation($this->yellow->page->location);
- $pages = $this->getChildren($rootLocation, $showInvisible);
- if (count($pages)==1 && $showOnePager) {
- $scheme = $this->yellow->page->scheme;
- $address = $this->yellow->page->address;
- $base = $this->yellow->page->base;
- $one = ($pages->offsetGet(0)->location!=$this->yellow->page->location) ? $pages->offsetGet(0) : $this->yellow->page;
- preg_match_all("/<h(\d) id=\"([^\"]+)\">(.*?)<\/h\d>/i", $one->getContent(), $matches, PREG_SET_ORDER);
- foreach ($matches as $match) {
- if ($match[1]==2) {
- $page = new YellowPage($this->yellow);
- $page->setRequestInformation($scheme, $address, $base, $one->location."#".$match[2], $one->fileName, false);
- $page->parseMeta("---\nTitle: $match[3]\n---\n");
- $pages->append($page);
- }
+ // Return content language
+ public function findContentLanguage($fileName, $languageDefault) {
+ $language = $languageDefault;
+ $pathBase = $this->yellow->system->get("coreContentDirectory");
+ $pathRoot = $this->yellow->system->get("coreServerRootDirectory");
+ if (!is_string_empty($pathRoot)) {
+ $fileName = substru($fileName, strlenu($pathBase));
+ if (preg_match("/^(.+?)\//", $fileName, $matches)) {
+ $name = $this->normaliseToken($matches[1]);
+ if (strlenu($name)==2) $language = $name;
}
}
- return $pages;
+ return $language;
}
-
- // Return page collection with path ancestry
- public function path($location, $absoluteLocation = false) {
- $pages = new YellowPageCollection($this->yellow);
- if ($absoluteLocation) $location = substru($location, strlenu($this->yellow->page->base));
- if ($page = $this->find($location)) {
- $pages->prepend($page);
- for (; $parent = $page->getParent(); $page=$parent) {
- $pages->prepend($parent);
+
+ // Return content root locations
+ public function findContentRootLocations() {
+ $rootLocations = array();
+ $pathBase = $this->yellow->system->get("coreContentDirectory");
+ $pathRoot = $this->yellow->system->get("coreServerRootDirectory");
+ if (!is_string_empty($pathRoot)) {
+ foreach ($this->yellow->toolbox->getDirectoryEntries($pathBase, "/.*/", true, true, false) as $entry) {
+ $token = $this->normaliseToken($entry)."/";
+ if ($token==$pathRoot) $token = "";
+ $rootLocations["root/$token"] = "$pathBase$entry/";
}
- $home = $this->find($this->getHomeLocation($page->location));
- if ($home && $home->location!=$page->location) $pages->prepend($home);
+ } else {
+ $rootLocations["root/"] = "$pathBase";
}
- return $pages;
- }
-
- // Return page collection with multiple languages
- public function multi($location, $absoluteLocation = false, $showInvisible = false) {
- $pages = new YellowPageCollection($this->yellow);
- if ($absoluteLocation) $location = substru($location, strlenu($this->yellow->page->base));
- $locationEnd = substru($location, strlenu($this->getRootLocation($location)) - 4);
- foreach ($this->scanLocation("") as $page) {
- if ($content = $this->find(substru($page->location, 4).$locationEnd)) {
- if ($content->isAvailable() && ($content->isVisible() || $showInvisible)) {
- if (!$this->yellow->lookup->isRootLocation($content->location)) $pages->append($content);
- }
+ if ($this->yellow->system->get("coreDebugMode")>=3) {
+ foreach ($rootLocations as $key=>$key) {
+ echo "YellowLookup::findContentRootLocations $key -> $value<br/>\n";
}
}
- return $pages;
- }
-
- // Return page collection that's empty
- public function clean() {
- return new YellowPageCollection($this->yellow);
+ return $rootLocations;
}
- // Return languages in multi language mode
- public function getLanguages($showInvisible = false) {
- $languages = array();
- if ($this->yellow->system->get("coreMultiLanguageMode")) {
- foreach ($this->scanLocation("") as $page) {
- if ($page->isAvailable() && ($page->isVisible() || $showInvisible)) array_push($languages, $page->get("language"));
+ // Return content location from file path
+ public function findContentLocationFromFile($fileName) {
+ $invalid = false;
+ $location = "/";
+ $pathBase = $this->yellow->system->get("coreContentDirectory");
+ $pathRoot = $this->yellow->system->get("coreServerRootDirectory");
+ $pathHome = $this->yellow->system->get("coreServerHomeDirectory");
+ $fileDefault = $this->yellow->system->get("coreContentDefaultFile");
+ $fileExtension = $this->yellow->system->get("coreContentExtension");
+ if (substru($fileName, 0, strlenu($pathBase))==$pathBase && mb_check_encoding($fileName, "UTF-8")) {
+ $fileName = substru($fileName, strlenu($pathBase));
+ $tokens = explode("/", $fileName);
+ if (!is_string_empty($pathRoot)) {
+ $token = $this->normaliseToken($tokens[0])."/";
+ if ($token!=$pathRoot) $location .= $token;
+ array_shift($tokens);
}
- }
- return $languages;
- }
-
- // Return child pages
- public function getChildren($location, $showInvisible = false) {
- $pages = new YellowPageCollection($this->yellow);
- foreach ($this->scanLocation($location) as $page) {
- if ($page->isAvailable() && ($page->isVisible() || $showInvisible)) {
- if (!$this->yellow->lookup->isRootLocation($page->location) && is_readable($page->fileName)) $pages->append($page);
+ for ($i=0; $i<count($tokens)-1; ++$i) {
+ $token = $this->normaliseToken($tokens[$i])."/";
+ if ($i || $token!=$pathHome) $location .= $token;
}
+ $token = $this->normaliseToken($tokens[$i], $fileExtension);
+ if ($token!=$fileDefault) {
+ $location .= $this->normaliseToken($tokens[$i], $fileExtension, true);
+ }
+ $extension = ($pos = strrposu($fileName, ".")) ? substru($fileName, $pos) : "";
+ if ($extension!=$fileExtension) $invalid = true;
+ } else {
+ $invalid = true;
}
- return $pages;
+ if ($this->yellow->system->get("coreDebugMode")>=2) {
+ $debug = ($invalid ? "INVALID" : $location)." <- $pathBase$fileName";
+ echo "YellowLookup::findContentLocationFromFile $debug<br/>\n";
+ }
+ return $invalid ? "" : $location;
}
- // Return child pages recursively
- public function getChildrenRecursive($location, $showInvisible = false, $levelMax = 0) {
- --$levelMax;
- $pages = new YellowPageCollection($this->yellow);
- foreach ($this->scanLocation($location) as $page) {
- if ($page->isAvailable() && ($page->isVisible() || $showInvisible)) {
- if (!$this->yellow->lookup->isRootLocation($page->location) && is_readable($page->fileName)) $pages->append($page);
+ // Return file path from content location
+ public function findFileFromContentLocation($location, $directory = false) {
+ $found = $invalid = false;
+ $path = $this->yellow->system->get("coreContentDirectory");
+ $pathRoot = $this->yellow->system->get("coreServerRootDirectory");
+ $pathHome = $this->yellow->system->get("coreServerHomeDirectory");
+ $fileDefault = $this->yellow->system->get("coreContentDefaultFile");
+ $fileExtension = $this->yellow->system->get("coreContentExtension");
+ $tokens = explode("/", $location);
+ if ($this->isRootLocation($location)) {
+ if (!is_string_empty($pathRoot)) {
+ $token = (count($tokens)>2) ? $tokens[1] : rtrim($pathRoot, "/");
+ $path .= $this->findFileDirectory($path, $token, "", true, true, $found, $invalid);
}
- if (!$this->yellow->lookup->isFileLocation($page->location) && $levelMax!=0) {
- $pages->merge($this->getChildrenRecursive($page->location, $showInvisible, $levelMax));
+ } else {
+ if (!is_string_empty($pathRoot)) {
+ if (count($tokens)>2) {
+ if ($this->normaliseToken($tokens[1])==$this->normaliseToken(rtrim($pathRoot, "/"))) $invalid = true;
+ $path .= $this->findFileDirectory($path, $tokens[1], "", true, false, $found, $invalid);
+ if ($found) array_shift($tokens);
+ }
+ if (!$found) {
+ $path .= $this->findFileDirectory($path, rtrim($pathRoot, "/"), "", true, true, $found, $invalid);
+ }
+ }
+ if (count($tokens)>2) {
+ if ($this->normaliseToken($tokens[1])==$this->normaliseToken(rtrim($pathHome, "/"))) $invalid = true;
+ for ($i=1; $i<count($tokens)-1; ++$i) {
+ $path .= $this->findFileDirectory($path, $tokens[$i], "", true, true, $found, $invalid);
+ }
+ } else {
+ $i = 1;
+ $tokens[0] = rtrim($pathHome, "/");
+ $path .= $this->findFileDirectory($path, $tokens[0], "", true, true, $found, $invalid);
+ }
+ if (!$directory) {
+ if (!is_string_empty($tokens[$i])) {
+ $token = $tokens[$i].$fileExtension;
+ if ($token==$fileDefault) $invalid = true;
+ $path .= $this->findFileDirectory($path, $token, $fileExtension, false, true, $found, $invalid);
+ } else {
+ $path .= $this->findFileDefault($path, $fileDefault, $fileExtension, false);
+ }
+ if ($this->yellow->system->get("coreDebugMode")>=2) {
+ $debug = "$location -> ".($invalid ? "INVALID" : $path);
+ echo "YellowLookup::findFileFromContentLocation $debug<br/>\n";
+ }
}
}
- return $pages;
- }
-
- // Return shared pages
- public function getShared($location) {
- $pages = new YellowPageCollection($this->yellow);
- $sharedLocation = $this->getHomeLocation($location)."shared/";
- return $pages->merge($this->scanLocation($sharedLocation));
+ return $invalid ? "" : $path;
}
- // Return root location
- public function getRootLocation($location) {
- $rootLocation = "root/";
- if ($this->yellow->system->get("coreMultiLanguageMode")) {
- foreach ($this->scanLocation("") as $page) {
- $token = substru($page->location, 4);
- if ($token!="/" && substru($location, 0, strlenu($token))==$token) {
- $rootLocation = "root$token";
- break;
+ // Return children from content location
+ public function findChildrenFromContentLocation($location) {
+ $fileNames = array();
+ if (!$this->isFileLocation($location)) {
+ $path = $this->findFileFromContentLocation($location, true);
+ $fileDefault = $this->yellow->system->get("coreContentDefaultFile");
+ $fileExtension = $this->yellow->system->get("coreContentExtension");
+ foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/.*/", true, true, false) as $entry) {
+ $token = $this->findFileDefault($path.$entry, $fileDefault, $fileExtension, false);
+ array_push($fileNames, $path.$entry."/".$token);
+ }
+ if (!$this->isRootLocation($location)) {
+ $regex = "/^.*\\".$fileExtension."$/";
+ foreach ($this->yellow->toolbox->getDirectoryEntries($path, $regex, true, false, false) as $entry) {
+ if ($this->normaliseToken($entry, $fileExtension)==$fileDefault) continue;
+ array_push($fileNames, $path.$entry);
}
}
}
- return $rootLocation;
- }
-
- // Return home location
- public function getHomeLocation($location) {
- return substru($this->getRootLocation($location), 4);
+ return $fileNames;
}
- // Return parent location
- public function getParentLocation($location) {
- $parentLocation = "";
- $token = rtrim(substru($this->getRootLocation($location), 4), "/");
- if (preg_match("#^($token.*\/).+?$#", $location, $matches)) {
- if ($matches[1]!="$token/" || $this->yellow->lookup->isFileLocation($location)) $parentLocation = $matches[1];
+ // Return media location from file path
+ public function findMediaLocationFromFile($fileName) {
+ $location = "";
+ $extensionDirectoryLength = strlenu($this->yellow->system->get("coreExtensionDirectory"));
+ $themeDirectoryLength = strlenu($this->yellow->system->get("coreThemeDirectory"));
+ $mediaDirectoryLength = strlenu($this->yellow->system->get("coreMediaDirectory"));
+ if (substru($fileName, 0, $extensionDirectoryLength)==$this->yellow->system->get("coreExtensionDirectory")) {
+ if ($this->isSafeFile($fileName)) {
+ $location = $this->yellow->system->get("coreExtensionLocation").substru($fileName, $extensionDirectoryLength);
+ }
+ } elseif (substru($fileName, 0, $themeDirectoryLength)==$this->yellow->system->get("coreThemeDirectory")) {
+ if ($this->isSafeFile($fileName)) {
+ $location = $this->yellow->system->get("coreThemeLocation").substru($fileName, $themeDirectoryLength);
+ }
+ } elseif (substru($fileName, 0, $mediaDirectoryLength)==$this->yellow->system->get("coreMediaDirectory")) {
+ $location = "/".$fileName;
}
- if (is_string_empty($parentLocation)) $parentLocation = "root$token/";
- return $parentLocation;
- }
-
- // Return top-level location
- public function getParentTopLocation($location) {
- $parentTopLocation = "";
- $token = rtrim(substru($this->getRootLocation($location), 4), "/");
- if (preg_match("#^($token.+?\/)#", $location, $matches)) $parentTopLocation = $matches[1];
- if (is_string_empty($parentTopLocation)) $parentTopLocation = "$token/";
- return $parentTopLocation;
- }
-}
-
-class YellowMedia {
- public $yellow; // access to API
- public $files; // scanned files
-
- public function __construct($yellow) {
- $this->yellow = $yellow;
- $this->files = array();
+ return $location;
}
- // Scan file system on demand
- public function scanLocation($location) {
- if (!isset($this->files[$location])) {
- $this->files[$location] = array();
- $scheme = $this->yellow->page->scheme;
- $address = $this->yellow->page->address;
- $base = $this->yellow->system->get("coreServerBase");
- if (is_string_empty($location)) {
- $fileNames = array($this->yellow->system->get("coreMediaDirectory"));
- } else {
- if ($this->yellow->system->get("coreDebugMode")>=2) echo "YellowMedia::scanLocation location:$location<br/>\n";
- $fileNames = $this->yellow->lookup->findChildrenFromMediaLocation($location);
+ // Return file path from media location
+ public function findFileFromMediaLocation($location) {
+ $fileName = "";
+ $extensionLocationLength = strlenu($this->yellow->system->get("coreExtensionLocation"));
+ $themeLocationLength = strlenu($this->yellow->system->get("coreThemeLocation"));
+ $mediaLocationLength = strlenu($this->yellow->system->get("coreMediaLocation"));
+ if (substru($location, 0, $extensionLocationLength)==$this->yellow->system->get("coreExtensionLocation")) {
+ if ($this->isSafeFile($location)) {
+ $fileName = $this->yellow->system->get("coreExtensionDirectory").substru($location, $extensionLocationLength);
}
- foreach ($fileNames as $fileName) {
- $file = new YellowPage($this->yellow);
- $file->setRequestInformation($scheme, $address, $base,
- $this->yellow->lookup->findMediaLocationFromFile($fileName), $fileName, false);
- $file->parseMeta(null);
- array_push($this->files[$location], $file);
+ } elseif (substru($location, 0, $themeLocationLength)==$this->yellow->system->get("coreThemeLocation")) {
+ if ($this->isSafeFile($location)) {
+ $fileName = $this->yellow->system->get("coreThemeDirectory").substru($location, $themeLocationLength);
}
+ } elseif (substru($location, 0, $mediaLocationLength)==$this->yellow->system->get("coreMediaLocation")) {
+ $fileName = substru($location, 1);
}
- return $this->files[$location];
+ return $fileName;
}
- // Return page with media file information, null if not found
- public function find($location, $absoluteLocation = false) {
- $found = false;
- if ($absoluteLocation) $location = substru($location, strlenu($this->yellow->system->get("coreServerBase")));
- foreach ($this->scanLocation($this->getParentLocation($location)) as $file) {
- if ($file->location==$location) {
- if ($this->yellow->lookup->isFileLocation($file->location)) {
- $found = true;
- break;
- }
+ // Return children from media location
+ public function findChildrenFromMediaLocation($location) {
+ $fileNames = array();
+ if (!$this->isFileLocation($location)) {
+ $path = $this->findFileFromMediaLocation($location);
+ foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/.*/", true, true, true) as $entry) {
+ array_push($fileNames, $entry."/");
+ }
+ foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/.*/", true, false, true) as $entry) {
+ array_push($fileNames, $entry);
}
}
- return $found ? $file : null;
- }
-
- // Return page collection with all media files
- public function index($showInvisible = false, $multiPass = false, $levelMax = 0) {
- return $this->getChildrenRecursive("", $showInvisible, $levelMax);
+ return $fileNames;
}
- // Return page collection that's empty
- public function clean() {
- return new YellowPageCollection($this->yellow);
+ // Return media directory from a system setting
+ public function findMediaDirectory($key) {
+ return substru($key, -8, 8)=="Location" ? $this->findFileFromMediaLocation($this->yellow->system->get($key)) : "";
}
- // Return child files
- public function getChildren($location, $showInvisible = false) {
- $files = new YellowPageCollection($this->yellow);
- foreach ($this->scanLocation($location) as $file) {
- if ($file->isAvailable() && ($file->isVisible() || $showInvisible)) {
- if ($this->yellow->lookup->isFileLocation($file->location)) $files->append($file);
+ // Return file or directory that matches token
+ public function findFileDirectory($path, $token, $fileExtension, $directory, $default, &$found, &$invalid) {
+ if ($this->normaliseToken($token, $fileExtension)!=$token) $invalid = true;
+ if (!$invalid) {
+ $regex = "/^[\d\-\_\.]*".str_replace("-", ".", $token)."$/";
+ foreach ($this->yellow->toolbox->getDirectoryEntries($path, $regex, false, $directory, false) as $entry) {
+ if ($this->normaliseToken($entry, $fileExtension)==$token) {
+ $token = $entry;
+ $found = true;
+ break;
+ }
}
}
- return $files;
+ if ($directory) $token .= "/";
+ return ($default || $found) ? $token : "";
}
- // Return child files recursively
- public function getChildrenRecursive($location, $showInvisible = false, $levelMax = 0) {
- --$levelMax;
- $files = new YellowPageCollection($this->yellow);
- foreach ($this->scanLocation($location) as $file) {
- if ($file->isAvailable() && ($file->isVisible() || $showInvisible)) {
- if ($this->yellow->lookup->isFileLocation($file->location)) $files->append($file);
- }
- if (!$this->yellow->lookup->isFileLocation($file->location) && $levelMax!=0) {
- $files->merge($this->getChildrenRecursive($file->location, $showInvisible, $levelMax));
+ // Return default file in directory
+ public function findFileDefault($path, $fileDefault, $fileExtension, $includePath = true) {
+ $token = $fileDefault;
+ if (!is_file($path."/".$fileDefault)) {
+ $regex = "/^[\d\-\_\.]*($fileDefault)$/";
+ foreach ($this->yellow->toolbox->getDirectoryEntries($path, $regex, true, false, false) as $entry) {
+ if ($this->normaliseToken($entry, $fileExtension)==$fileDefault) {
+ $token = $entry;
+ break;
+ }
}
}
- return $files;
+ return $includePath ? "$path/$token" : $token;
}
- // Return home location
- public function getHomeLocation($location) {
- return $this->yellow->system->get("coreMediaLocation");
- }
-
- // Return parent location
- public function getParentLocation($location) {
- $token = rtrim($this->yellow->system->get("coreMediaLocation"), "/");
- if (preg_match("#^($token.*\/).+?$#", $location, $matches)) {
- if ($matches[1]!="$token/" || $this->yellow->lookup->isFileLocation($location)) $parentLocation = $matches[1];
- }
- if (is_string_empty($parentLocation)) $parentLocation = "";
- return $parentLocation;
+ // Normalise file/directory token
+ public function normaliseToken($text, $fileExtension = "", $removeExtension = false) {
+ if (!is_string_empty($fileExtension)) $text = ($pos = strrposu($text, ".")) ? substru($text, 0, $pos) : $text;
+ if (preg_match("/^[\d\-\_\.]+(.*)$/", $text, $matches) && !is_string_empty($matches[1])) $text = $matches[1];
+ return preg_replace("/[^\pL\d\-\_]/u", "-", $text).($removeExtension ? "" : $fileExtension);
}
- // Return top-level location
- public function getParentTopLocation($location) {
- $token = rtrim($this->yellow->system->get("coreMediaLocation"), "/");
- if (preg_match("#^($token.+?\/)#", $location, $matches)) $parentTopLocation = $matches[1];
- if (is_string_empty($parentTopLocation)) $parentTopLocation = "$token/";
- return $parentTopLocation;
+ // Normalise name
+ public function normaliseName($text, $removePrefix = false, $removeExtension = false, $filterStrict = false) {
+ if ($removeExtension) $text = ($pos = strrposu($text, ".")) ? substru($text, 0, $pos) : $text;
+ if ($removePrefix && preg_match("/^[\d\-\_\.]+(.*)$/", $text, $matches) && !is_string_empty($matches[1])) $text = $matches[1];
+ if ($filterStrict) $text = strtoloweru($text);
+ return preg_replace("/[^\pL\d\-\_]/u", "-", $text);
}
-}
-
-class YellowSystem {
- public $yellow; // access to API
- public $modified; // system modification date
- public $settings; // system settings
- public $settingsDefaults; // system settings defaults
- public function __construct($yellow) {
- $this->yellow = $yellow;
- $this->modified = 0;
- $this->settings = new YellowArray();
- $this->settingsDefaults = new YellowArray();
+ // Normalise prefix
+ public function normalisePrefix($text) {
+ $prefix = "";
+ if (preg_match("/^([\d\-\_\.]*)(.*)$/", $text, $matches)) $prefix = $matches[1];
+ if (!is_string_empty($prefix) && !preg_match("/[\-\_\.]$/", $prefix)) $prefix .= "-";
+ return $prefix;
}
- // Load system settings from file
- public function load($fileName) {
- $this->modified = $this->yellow->toolbox->getFileModified($fileName);
- $fileData = $this->yellow->toolbox->readFile($fileName);
- $this->settings = $this->yellow->toolbox->getTextSettings($fileData, "");
- if ($this->yellow->system->get("coreDebugMode")>=2) echo "YellowSystem::load file:$fileName<br/>\n";
- if ($this->yellow->system->get("coreDebugMode")>=3) {
- foreach ($this->settings as $key=>$value) {
- echo "YellowSystem::load ".ucfirst($key).":$value<br/>\n";
- }
+ // Normalise elements and attributes in HTML/SVG data
+ public function normaliseData($text, $type = "html", $filterStrict = true) {
+ $output = "";
+ $elementsHtml = array(
+ "a", "abbr", "acronym", "address", "area", "article", "aside", "audio", "b", "bdi", "bdo", "big", "blink", "blockquote", "body", "br", "button", "canvas", "caption", "center", "cite", "code", "col", "colgroup", "content", "data", "datalist", "dd", "decorator", "del", "details", "dfn", "dir", "div", "dl", "dt", "element", "em", "fieldset", "figcaption", "figure", "font", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hgroup", "hr", "html", "i", "iframe", "image", "img", "input", "ins", "kbd", "label", "legend", "li", "main", "map", "mark", "marquee", "menu", "menuitem", "meta", "meter", "nav", "nobr", "ol", "optgroup", "option", "output", "p", "picture", "pre", "progress", "q", "rp", "rt", "ruby", "s", "samp", "section", "select", "shadow", "small", "source", "spacer", "span", "strike", "strong", "style", "sub", "summary", "sup", "table", "tbody", "td", "template", "textarea", "tfoot", "th", "thead", "time", "title", "tr", "track", "tt", "u", "ul", "var", "video", "wbr");
+ $elementsSvg = array(
+ "svg", "altglyph", "altglyphdef", "altglyphitem", "animatecolor", "animatemotion", "animatetransform", "circle", "clippath", "defs", "desc", "ellipse", "feblend", "fecolormatrix", "fecomponenttransfer", "fecomposite", "feconvolvematrix", "fediffuselighting", "fedisplacementmap", "fedistantlight", "feflood", "fefunca", "fefuncb", "fefuncg", "fefuncr", "fegaussianblur", "femerge", "femergenode", "femorphology", "feoffset", "fepointlight", "fespecularlighting", "fespotlight", "fetile", "feturbulence", "filter", "font", "g", "glyph", "glyphref", "hkern", "image", "line", "lineargradient", "marker", "mask", "metadata", "mpath", "path", "pattern", "polygon", "polyline", "radialgradient", "rect", "stop", "switch", "symbol", "text", "textpath", "title", "tref", "tspan", "use", "view", "vkern");
+ $attributesHtml = array(
+ "accept", "action", "align", "allow", "allowfullscreen", "alt", "autocomplete", "autoplay", "background", "bgcolor", "border", "cellpadding", "cellspacing", "charset", "checked", "cite", "class", "clear", "color", "cols", "colspan", "content", "contenteditable", "controls", "coords", "crossorigin", "datetime", "default", "dir", "disabled", "download", "enctype", "face", "for", "frameborder", "headers", "height", "hidden", "high", "href", "hreflang", "id", "integrity", "ismap", "label", "lang", "list", "loading", "loop", "low", "max", "maxlength", "media", "method", "min", "multiple", "muted", "name", "noshade", "novalidate", "nowrap", "open", "optimum", "pattern", "placeholder", "poster", "prefix", "preload", "property", "pubdate", "radiogroup", "readonly", "rel", "required", "rev", "reversed", "role", "rows", "rowspan", "spellcheck", "scope", "selected", "shape", "size", "sizes", "span", "srclang", "start", "src", "srcset", "step", "style", "summary", "tabindex", "target", "title", "type", "usemap", "valign", "value", "width", "xmlns");
+ $attributesSvg = array(
+ "accent-height", "accumulate", "additivive", "alignment-baseline", "ascent", "attributename", "attributetype", "azimuth", "basefrequency", "baseline-shift", "begin", "bias", "by", "class", "clip", "clip-path", "clip-rule", "color", "color-interpolation", "color-interpolation-filters", "color-profile", "color-rendering", "cx", "cy", "d", "datenstrom", "dx", "dy", "diffuseconstant", "direction", "display", "divisor", "dur", "edgemode", "elevation", "end", "fill", "fill-opacity", "fill-rule", "filter", "flood-color", "flood-opacity", "font-family", "font-size", "font-size-adjust", "font-stretch", "font-style", "font-variant", "font-weight", "fx", "fy", "g1", "g2", "glyph-name", "glyphref", "gradientunits", "gradienttransform", "height", "href", "id", "image-rendering", "in", "in2", "k", "k1", "k2", "k3", "k4", "kerning", "keypoints", "keysplines", "keytimes", "lang", "lengthadjust", "letter-spacing", "kernelmatrix", "kernelunitlength", "lighting-color", "local", "marker-end", "marker-mid", "marker-start", "markerheight", "markerunits", "markerwidth", "maskcontentunits", "maskunits", "max", "mask", "media", "method", "mode", "min", "name", "numoctaves", "offset", "operator", "opacity", "order", "orient", "orientation", "origin", "overflow", "paint-order", "path", "pathlength", "patterncontentunits", "patterntransform", "patternunits", "points", "preservealpha", "preserveaspectratio", "r", "rx", "ry", "radius", "refx", "refy", "repeatcount", "repeatdur", "restart", "result", "rotate", "scale", "seed", "shape-rendering", "specularconstant", "specularexponent", "spreadmethod", "stddeviation", "stitchtiles", "stop-color", "stop-opacity", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke", "stroke-width", "style", "surfacescale", "tabindex", "targetx", "targety", "transform", "text-anchor", "text-decoration", "text-rendering", "textlength", "type", "u1", "u2", "unicode", "values", "viewbox", "visibility", "vert-adv-y", "vert-origin-x", "vert-origin-y", "width", "word-spacing", "wrap", "writing-mode", "xchannelselector", "ychannelselector", "x", "x1", "x2", "xlink:href", "xml:id", "xml:space", "xmlns", "y", "y1", "y2", "z", "zoomandpan");
+ $attributesAllowEmptyString = array("alt", "value");
+ $elementsSafe = $elementsHtml;
+ $attributesSafe = $attributesHtml;
+ if ($type=="svg") {
+ $elementsSafe = array_merge($elementsHtml, $elementsSvg);
+ $attributesSafe = array_merge($attributesHtml, $attributesSvg);
}
- }
-
- // Save system settings to file
- public function save($fileName, $settings) {
- $this->modified = time();
- $settingsNew = new YellowArray();
- foreach ($settings as $key=>$value) {
- if (!is_string_empty($key) && !is_string_empty($value)) {
- $this->set($key, $value);
- $settingsNew[$key] = $value;
+ $offsetBytes = 0;
+ while (true) {
+ $elementFound = preg_match("/<(\/?)([\!\?\w]+)(.*?)(\/?)>/s", $text, $matches, PREG_OFFSET_CAPTURE, $offsetBytes);
+ $elementBefore = $elementFound ? substrb($text, $offsetBytes, $matches[0][1] - $offsetBytes) : substrb($text, $offsetBytes);
+ $elementStart = $elementFound ? $matches[1][0] : "";
+ $elementName = $elementFound ? $matches[2][0]: "";
+ $elementMiddle = $elementFound ? $matches[3][0]: "";
+ $elementEnd = $elementFound ? $matches[4][0]: "";
+ $output .= $elementBefore;
+ if (substrb($elementName, 0, 1)=="!") {
+ $output .= "<$elementName$elementMiddle>";
+ } elseif (in_array(strtolower($elementName), $elementsSafe)) {
+ $elementAttributes = $this->getTextAttributes($elementMiddle, $attributesAllowEmptyString);
+ foreach ($elementAttributes as $key=>$value) {
+ if (!in_array(strtolower($key), $attributesSafe) && !preg_match("/^(aria|data)-/i", $key)) {
+ unset($elementAttributes[$key]);
+ }
+ }
+ if ($filterStrict) {
+ $href = isset($elementAttributes["href"]) ? $elementAttributes["href"] : "";
+ if (preg_match("/^\w+:/", $href) && !$this->isSafeUrl($href)) {
+ $elementAttributes["href"] = "error-xss-filter";
+ }
+ $href = isset($elementAttributes["xlink:href"]) ? $elementAttributes["xlink:href"] : "";
+ if (preg_match("/^\w+:/", $href) && !$this->isSafeUrl($href)) {
+ $elementAttributes["xlink:href"] = "error-xss-filter";
+ }
+ }
+ $output .= "<$elementStart$elementName";
+ foreach ($elementAttributes as $key=>$value) $output .= " $key=\"$value\"";
+ if (!is_string_empty($elementEnd)) $output .= " ";
+ $output .= "$elementEnd>";
}
+ if (!$elementFound) break;
+ $offsetBytes = $matches[0][1] + strlenb($matches[0][0]);
}
- $fileData = $this->yellow->toolbox->readFile($fileName);
- $fileData = $this->yellow->toolbox->setTextSettings($fileData, "", "", $settingsNew);
- return $this->yellow->toolbox->createFile($fileName, $fileData);
+ return $output;
}
- // Set default system setting
- public function setDefault($key, $value) {
- $this->settingsDefaults[$key] = $value;
+ // Normalise array, make keys with same upper/lower case
+ public function normaliseArray($input) {
+ $array = array();
+ foreach ($input as $key=>$value) {
+ if (is_string_empty($key) || is_string_empty($value)) continue;
+ $keySearch = strtoloweru($key);
+ foreach ($array as $keyNew=>$valueNew) {
+ if (strtoloweru($keyNew)==$keySearch) {
+ $key = $keyNew;
+ break;
+ }
+ }
+ if (!isset($array[$key])) $array[$key] = 0;
+ $array[$key] += $value;
+ }
+ return $array;
}
- // Set default system settings
- public function setDefaults($lines) {
- foreach ($lines as $line) {
- if (preg_match("/^\#/", $line)) continue;
- if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) {
- if (!is_string_empty($matches[1]) && !is_string_empty($matches[2])) {
- $this->settingsDefaults[$matches[1]] = $matches[2];
- }
+ // Normalise relative path tokens
+ public function normalisePath($text) {
+ $textFiltered = "";
+ $textLength = strlenb($text);
+ for ($pos=0; $pos<$textLength; ++$pos) {
+ if ($text[$pos]=="." && ($pos==0 || $text[$pos-1]=="/")) {
+ while ($text[$pos]==".") ++$pos;
+ if ($text[$pos]=="/") ++$pos;
+ --$pos;
+ continue;
}
+ $textFiltered .= $text[$pos];
}
+ return $textFiltered;
}
- // Set system setting
- public function set($key, $value) {
- $this->settings[$key] = $value;
+ // Normalise text lines, convert line endings
+ public function normaliseLines($text, $endOfLine = "lf") {
+ if ($endOfLine=="lf") {
+ $text = preg_replace("/\R/u", "\n", $text);
+ } else {
+ $text = preg_replace("/\R/u", "\r\n", $text);
+ }
+ return $text;
}
- // Return system setting
- public function get($key) {
- if (isset($this->settings[$key])) {
- $value = $this->settings[$key];
+ // Normalise text into UTF-8 NFC
+ public function normaliseUnicode($text) {
+ if (PHP_OS=="Darwin" && !mb_check_encoding($text, "ASCII")) {
+ $utf8nfc = preg_match("//u", $text) && !preg_match("/[^\\x00-\\x{2FF}]/u", $text);
+ if (!$utf8nfc) $text = iconv("UTF-8-MAC", "UTF-8", $text);
+ }
+ return $text;
+ }
+
+ // Normalise location, make absolute location
+ public function normaliseLocation($location, $pageLocation, $filterStrict = true) {
+ if (!preg_match("/^\w+:/", trim(html_entity_decode($location, ENT_QUOTES, "UTF-8")))) {
+ $pageBase = $this->yellow->page->base;
+ $mediaBase = $this->yellow->system->get("coreServerBase").$this->yellow->system->get("coreMediaLocation");
+ if (!preg_match("/^\#/", $location)) {
+ if (!preg_match("/^\//", $location)) {
+ $location = $this->getDirectoryLocation($pageBase.$pageLocation).$location;
+ } elseif (!preg_match("#^($pageBase|$mediaBase)#", $location)) {
+ $location = $pageBase.$location;
+ }
+ }
+ $location = str_replace("/./", "/", $location);
+ $location = str_replace(":", $this->yellow->toolbox->getLocationArgumentsSeparator(), $location);
} else {
- $value = isset($this->settingsDefaults[$key]) ? $this->settingsDefaults[$key] : "";
+ if ($filterStrict && !$this->isSafeUrl($location)) $location = "error-xss-filter";
}
- return $value;
+ return $location;
}
- // Return system setting, HTML encoded
- public function getHtml($key) {
- return htmlspecialchars($this->get($key));
+ // Normalise location arguments
+ public function normaliseArguments($text, $appendSlash = true, $filterStrict = true) {
+ if ($appendSlash) $text .= "/";
+ if ($filterStrict) $text = str_replace(" ", "-", strtoloweru($text));
+ $text = str_replace(":", $this->yellow->toolbox->getLocationArgumentsSeparator(), $text);
+ return str_replace(array("%2F","%3A","%3D"), array("/",":","="), rawurlencode($text));
}
- // Return different value for system setting
- public function getDifferent($key) {
- return reset(array_diff($this->getAvailable($key), array($this->get($key))));
+ // Normalise URL, make absolute URL
+ public function normaliseUrl($scheme, $address, $base, $location, $filterStrict = true) {
+ if (!preg_match("/^\w+:/", $location)) {
+ $url = "$scheme://$address$base$location";
+ } else {
+ if ($filterStrict && !$this->isSafeUrl($location)) $location = "error-xss-filter";
+ $url = $location;
+ }
+ return $url;
}
-
- // Return available values for system setting
- public function getAvailable($key) {
- $values = array();
- $valueDefault = isset($this->settingsDefaults[$key]) ? $this->settingsDefaults[$key] : "";
- if ($key=="email") {
- foreach ($this->yellow->user->settings as $userKey=>$userValue) {
- array_push($values, $userKey);
+
+ // Return URL information
+ public function getUrlInformation($url) {
+ $scheme = $address = $base = "";
+ if (preg_match("#^(\w+)://([^/]+)(.*)$#", rtrim($url, "/"), $matches)) {
+ $scheme = $matches[1];
+ $address = $matches[2];
+ $base = $matches[3];
+ }
+ return array($scheme, $address, $base);
+ }
+
+ // Return attributes from text
+ public function getTextAttributes($text, $attributesAllowEmptyString) {
+ $tokens = array();
+ $posStart = $posQuote = 0;
+ $textLength = strlenb($text);
+ for ($pos=0; $pos<$textLength; ++$pos) {
+ if ($text[$pos]==" " && !$posQuote) {
+ if ($pos>$posStart) array_push($tokens, substrb($text, $posStart, $pos-$posStart));
+ $posStart = $pos+1;
}
- } elseif ($key=="language") {
- foreach ($this->yellow->language->settings as $languageKey=>$languageValue) {
- array_push($values, $languageKey);
+ if ($text[$pos]=="=" && !$posQuote) {
+ if ($pos>$posStart) array_push($tokens, substrb($text, $posStart, $pos-$posStart));
+ array_push($tokens, "=");
+ $posStart = $pos+1;
}
- } elseif ($key=="layout") {
- $path = $this->yellow->system->get("coreLayoutDirectory");
- foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.html$/", true, false, false) as $entry) {
- array_push($values, lcfirst(substru($entry, 0, -5)));
+ if ($text[$pos]=="\"") {
+ if ($posQuote) {
+ if ($pos>$posQuote) array_push($tokens, substrb($text, $posQuote+1, $pos-$posQuote-1));
+ $posQuote = 0;
+ $posStart = $pos+1;
+ } else {
+ if ($pos==$posStart) $posQuote = $pos;
+ }
}
- } elseif ($key=="theme") {
- $path = $this->yellow->system->get("coreThemeDirectory");
- foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.css$/", true, false, false) as $entry) {
- array_push($values, lcfirst(substru($entry, 0, -4)));
+ }
+ if ($pos>$posStart && !$posQuote) {
+ array_push($tokens, substrb($text, $posStart, $pos-$posStart));
+ }
+ $attributes = array();
+ for ($i=0; $i<count($tokens); ++$i) {
+ if ($i+2<count($tokens) && $tokens[$i+1]=="=") {
+ $key = $tokens[$i];
+ $value = $tokens[$i+2];
+ $i += 2;
+ } else {
+ $key = $value = $tokens[$i];
+ }
+ if (!is_string_empty($key) && (!is_string_empty($value) || in_array(strtolower($key), $attributesAllowEmptyString))) {
+ $attributes[$key] = $value;
}
}
- return !is_array_empty($values) ? $values : array($valueDefault);
+ return $attributes;
}
- public function getValues($key) { return $this->getAvailable($key); } //TODO: remove later, for backwards compatibility
- // Return system settings
- public function getSettings($filterStart = "", $filterEnd = "") {
- $settings = array();
- if (is_string_empty($filterStart) && is_string_empty($filterEnd)) {
- $settings = array_merge($this->settingsDefaults->getArrayCopy(), $this->settings->getArrayCopy());
+ // Return directory location
+ public function getDirectoryLocation($location) {
+ return ($pos = strrposu($location, "/")) ? substru($location, 0, $pos+1) : "/";
+ }
+
+ // Return redirect location
+ public function getRedirectLocation($location) {
+ if ($this->isFileLocation($location)) {
+ $location = "$location/";
} else {
- foreach (array_merge($this->settingsDefaults->getArrayCopy(), $this->settings->getArrayCopy()) as $key=>$value) {
- if (!is_string_empty($filterStart) && substru($key, 0, strlenu($filterStart))==$filterStart) $settings[$key] = $value;
- if (!is_string_empty($filterEnd) && substru($key, -strlenu($filterEnd))==$filterEnd) $settings[$key] = $value;
- }
+ $languageDefault = $this->yellow->system->get("language");
+ $language = $this->yellow->toolbox->detectBrowserLanguage($this->yellow->content->getLanguages(), $languageDefault);
+ $location = "/$language/";
}
- return $settings;
+ return $location;
}
- // Return system settings modification date, Unix time or HTTP format
- public function getModified($httpFormat = false) {
- return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($this->modified) : $this->modified;
+ // Check if clean URL is requested
+ public function isRequestCleanUrl($location) {
+ return isset($_REQUEST["clean-url"]) && substru($location, -1, 1)=="/";
}
- // Check if system setting exists
- public function isExisting($key) {
- return isset($this->settings[$key]);
+ // Check if location is specifying root
+ public function isRootLocation($location) {
+ return substru($location, 0, 1)!="/";
}
-}
-
-class YellowLanguage {
- public $yellow; // access to API
- public $modified; // language modification date
- public $settings; // language settings
- public $settingsDefaults; // language settings defaults
- public $language; // current language
- public function __construct($yellow) {
- $this->yellow = $yellow;
- $this->modified = 0;
- $this->settings = new YellowArray();
- $this->settingsDefaults = new YellowArray();
- $this->language = "";
+ // Check if location is specifying file or directory
+ public function isFileLocation($location) {
+ return substru($location, -1, 1)!="/";
}
- // Load language settings from file
- public function load($fileName) {
- $this->modified = $this->yellow->toolbox->getFileModified($fileName);
- $fileData = $this->yellow->toolbox->readFile($fileName);
- $settings = $this->yellow->toolbox->getTextSettings($fileData, "language");
- foreach ($settings as $language=>$block) {
- if (!isset($this->settings[$language])) $this->settings[$language] = new YellowArray();
- foreach ($block as $key=>$value) {
- $this->settings[$language][$key] = $value;
- }
- }
- if ($this->yellow->system->get("coreDebugMode")>=2) echo "YellowLanguage::load file:$fileName<br/>\n";
- foreach ($this->settings->getArrayCopy() as $key=>$value) {
- if (!isset($this->settings[$key]["languageDescription"])) {
- unset($this->settings[$key]);
- }
+ // Check if location can be redirected into directory
+ public function isRedirectLocation($location) {
+ $redirect = false;
+ if ($this->isFileLocation($location)) {
+ $redirect = is_dir($this->findFileFromContentLocation("$location/", true));
+ } elseif ($location=="/") {
+ $redirect = $this->yellow->system->get("coreMultiLanguageMode");
}
- $callback = function ($a, $b) {
- return strnatcmp($a["languageDescription"], $b["languageDescription"]);
- };
- $this->settings->uasort($callback);
+ return $redirect;
}
- // Set current language
- public function set($language) {
- $this->language = $language;
+ // Check if location contains nested directories
+ public function isNestedLocation($location, $fileName, $checkHomeLocation = false) {
+ $nested = false;
+ if (!$checkHomeLocation || $location==$this->yellow->content->getHomeLocation($location)) {
+ $path = dirname($fileName);
+ if (!is_array_empty($this->yellow->toolbox->getDirectoryEntries($path, "/.*/", true, true, false))) $nested = true;
+ }
+ return $nested;
}
- // Set default language setting
- public function setDefault($key, $value, $language) {
- if (!isset($this->settings[$language])) $this->settings[$language] = new YellowArray();
- $this->settings[$language][$key] = $value;
- $this->settingsDefaults[$key] = $value;
+ // Check if location is available
+ public function isAvailableLocation($location, $fileName) {
+ $available = true;
+ $pathBase = $this->yellow->system->get("coreContentDirectory");
+ if (substru($fileName, 0, strlenu($pathBase))==$pathBase) {
+ $sharedLocation = $this->yellow->content->getHomeLocation($location)."shared/";
+ if (substru($location, 0, strlenu($sharedLocation))==$sharedLocation) $available = false;
+ }
+ return $available;
}
- // Set default language settings
- public function setDefaults($lines) {
- $language = "";
- foreach ($lines as $line) {
- if (preg_match("/^\#/", $line)) continue;
- if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) {
- if (lcfirst($matches[1])=="language" && !is_string_empty($matches[2])) {
- $language = $matches[2];
- if (!isset($this->settings[$language])) $this->settings[$language] = new YellowArray();
- }
- if (!is_string_empty($language) && !is_string_empty($matches[1]) && !is_string_empty($matches[2])) {
- $this->settings[$language][$matches[1]] = $matches[2];
- $this->settingsDefaults[$matches[1]] = true;
- }
+ // Check if location is within current HTTP request
+ public function isActiveLocation($location, $currentLocation) {
+ if ($this->isFileLocation($location)) {
+ $active = $currentLocation==$location;
+ } else {
+ if ($location==$this->yellow->content->getHomeLocation($location)) {
+ $active = $this->getDirectoryLocation($currentLocation)==$location;
+ } else {
+ $active = substru($currentLocation, 0, strlenu($location))==$location;
}
}
+ return $active;
}
- // Set language setting
- public function setText($key, $value, $language) {
- if (!isset($this->settings[$language])) $this->settings[$language] = new YellowArray();
- $this->settings[$language][$key] = $value;
- }
-
- // Return language setting
- public function getText($key, $language = "") {
- if (is_string_empty($language)) $language = $this->language;
- return $this->isText($key, $language) ? $this->settings[$language][$key] : "[$key]";
- }
-
- // Return language setting, HTML encoded
- public function getTextHtml($key, $language = "") {
- return htmlspecialchars($this->getText($key, $language));
- }
-
- // Return human readable date
- public function getDateFormatted($timestamp, $format, $language = "") {
- $dateMonthsNominative = preg_split("/\s*,\s*/", $this->getText("coreDateMonthsNominative", $language));
- $dateMonthsGenitive = preg_split("/\s*,\s*/", $this->getText("coreDateMonthsGenitive", $language));
- $dateWeekdays = preg_split("/\s*,\s*/", $this->getText("coreDateWeekdays", $language));
- $monthNominative = $dateMonthsNominative[date("n", $timestamp) - 1];
- $monthGenitive = $dateMonthsGenitive[date("n", $timestamp) - 1];
- $weekday = $dateWeekdays[date("N", $timestamp) - 1];
- $timeZone = $this->yellow->system->get("coreTimezone");
- $timeZoneHelper = new DateTime("now", new DateTimeZone($timeZone));
- $timeZoneOffset = $timeZoneHelper->getOffset();
- $timeZoneAbbreviation = "GMT".($timeZoneOffset<0 ? "-" : "+").abs(intval($timeZoneOffset/3600));
- $format = preg_replace("/(?<!\\\)F/", addcslashes($monthNominative, "A..Za..z"), $format);
- $format = preg_replace("/(?<!\\\)V/", addcslashes($monthGenitive, "A..Za..z"), $format);
- $format = preg_replace("/(?<!\\\)M/", addcslashes(substru($monthNominative, 0, 3), "A..Za..z"), $format);
- $format = preg_replace("/(?<!\\\)D/", addcslashes(substru($weekday, 0, 3), "A..Za..z"), $format);
- $format = preg_replace("/(?<!\\\)l/", addcslashes($weekday, "A..Za..z"), $format);
- $format = preg_replace("/(?<!\\\)T/", addcslashes($timeZoneAbbreviation, "A..Za..z"), $format);
- return date($format, $timestamp);
+ // Check if URL is a well-known URL scheme
+ public function isSafeUrl($url) {
+ return preg_match("/^(http|https|ftp|mailto|tel):/", $url);
}
- // Return human readable date, relative to today
- public function getDateRelative($timestamp, $format, $daysLimit, $language = "") {
- $timeDifference = time() - $timestamp;
- $days = abs(intval($timeDifference/86400));
- $key = $timeDifference>=0 ? "coreDatePast" : "coreDateFuture";
- $tokens = preg_split("/\s*,\s*/", $this->getText($key, $language));
- if (count($tokens)>=8) {
- if ($days<=$daysLimit || $daysLimit==0) {
- if ($days==0) {
- $output = $tokens[0];
- } elseif ($days==1) {
- $output = $tokens[1];
- } elseif ($days>=2 && $days<=29) {
- $output = preg_replace("/@x/i", $days, $tokens[2]);
- } elseif ($days>=30 && $days<=59) {
- $output = $tokens[3];
- } elseif ($days>=60 && $days<=364) {
- $output = preg_replace("/@x/i", intval($days/30), $tokens[4]);
- } elseif ($days>=365 && $days<=729) {
- $output = $tokens[5];
- } else {
- $output = preg_replace("/@x/i", intval($days/365.25), $tokens[6]);
- }
- } else {
- $output = preg_replace("/@x/i", $this->getDateFormatted($timestamp, $format, $language), $tokens[7]);
- }
- } else {
- $output = "[$key]";
- }
- return $output;
+ // Check if file is a well-known file type
+ public function isSafeFile($fileName) {
+ return preg_match("/\.(css|gif|ico|js|jpg|png|svg|woff|woff2)$/", $fileName);
}
- // Return language settings
- public function getSettings($filterStart = "", $filterEnd = "", $language = "") {
- $settings = array();
- if (is_string_empty($language)) $language = $this->language;
- if (isset($this->settings[$language])) {
- if (is_string_empty($filterStart) && is_string_empty($filterEnd)) {
- $settings = $this->settings[$language]->getArrayCopy();
- } else {
- foreach ($this->settings[$language] as $key=>$value) {
- if (!is_string_empty($filterStart) && substru($key, 0, strlenu($filterStart))==$filterStart) $settings[$key] = $value;
- if (!is_string_empty($filterEnd) && substru($key, -strlenu($filterEnd))==$filterEnd) $settings[$key] = $value;
- }
- }
- }
- return $settings;
+ // Check if file is valid
+ public function isValidFile($fileName) {
+ $contentDirectoryLength = strlenu($this->yellow->system->get("coreContentDirectory"));
+ $mediaDirectoryLength = strlenu($this->yellow->system->get("coreMediaDirectory"));
+ $systemDirectoryLength = strlenu($this->yellow->system->get("coreSystemDirectory"));
+ return strposu($fileName, "/")===false ||
+ substru($fileName, 0, $contentDirectoryLength)==$this->yellow->system->get("coreContentDirectory") ||
+ substru($fileName, 0, $mediaDirectoryLength)==$this->yellow->system->get("coreMediaDirectory") ||
+ substru($fileName, 0, $systemDirectoryLength)==$this->yellow->system->get("coreSystemDirectory");
}
- // Return language settings modification date, Unix time or HTTP format
- public function getModified($httpFormat = false) {
- return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($this->modified) : $this->modified;
+ // Check if content file
+ public function isContentFile($fileName) {
+ $contentDirectoryLength = strlenu($this->yellow->system->get("coreContentDirectory"));
+ return substru($fileName, 0, $contentDirectoryLength)==$this->yellow->system->get("coreContentDirectory");
}
- // Normalise date into known format
- public function normaliseDate($text, $language = "") {
- if (preg_match("/^\d+\-\d+$/", $text)) {
- $output = $this->getDateFormatted(strtotime($text), $this->getText("coreDateFormatShort", $language), $language);
- } elseif (preg_match("/^\d+\-\d+\-\d+$/", $text)) {
- $output = $this->getDateFormatted(strtotime($text), $this->getText("coreDateFormatMedium", $language), $language);
- } elseif (preg_match("/^\d+\-\d+\-\d+ \d+\:\d+$/", $text)) {
- $output = $this->getDateFormatted(strtotime($text), $this->getText("coreDateFormatLong", $language), $language);
- } else {
- $output = $text;
- }
- return $output;
+ // Check if media file
+ public function isMediaFile($fileName) {
+ $mediaDirectoryLength = strlenu($this->yellow->system->get("coreMediaDirectory"));
+ return substru($fileName, 0, $mediaDirectoryLength)==$this->yellow->system->get("coreMediaDirectory");
}
- // Check if language setting exists
- public function isText($key, $language = "") {
- if (is_string_empty($language)) $language = $this->language;
- return isset($this->settings[$language]) && isset($this->settings[$language][$key]);
- }
-
- // Check if language exists
- public function isExisting($language) {
- return isset($this->settings[$language]);
+ // Check if system file
+ public function isSystemFile($fileName) {
+ $systemDirectoryLength = strlenu($this->yellow->system->get("coreSystemDirectory"));
+ return substru($fileName, 0, $systemDirectoryLength)==$this->yellow->system->get("coreSystemDirectory");
}
}
-class YellowUser {
- public $yellow; // access to API
- public $modified; // user modification date
- public $settings; // user settings
- public $email; // current email
+class YellowToolbox {
+ public $yellow; // access to API
public function __construct($yellow) {
$this->yellow = $yellow;
- $this->modified = 0;
- $this->settings = new YellowArray();
- $this->email = "";
- }
-
- // Load user settings from file
- public function load($fileName) {
- $this->modified = $this->yellow->toolbox->getFileModified($fileName);
- $fileData = $this->yellow->toolbox->readFile($fileName);
- $this->settings = $this->yellow->toolbox->getTextSettings($fileData, "email");
- if ($this->yellow->system->get("coreDebugMode")>=2) echo "YellowUser::load file:$fileName<br/>\n";
}
-
- // Save user settings to file
- public function save($fileName, $email, $settings) {
- $this->modified = time();
- $settingsNew = new YellowArray();
- $settingsNew["email"] = $email;
- foreach ($settings as $key=>$value) {
- if (!is_string_empty($key) && !is_string_empty($value)) {
- $this->setUser($key, $value, $email);
- $settingsNew[$key] = $value;
- }
- }
- $fileData = $this->yellow->toolbox->readFile($fileName);
- $fileData = $this->yellow->toolbox->setTextSettings($fileData, "email", $email, $settingsNew);
- return $this->yellow->toolbox->createFile($fileName, $fileData);
+
+ // Return browser cookie from from current HTTP request
+ public function getCookie($key) {
+ return isset($_COOKIE[$key]) ? $_COOKIE[$key] : "";
}
- // Remove user settings from file
- public function remove($fileName, $email) {
- $this->modified = time();
- if (isset($this->settings[$email])) unset($this->settings[$email]);
- $fileData = $this->yellow->toolbox->readFile($fileName);
- $fileData = $this->yellow->toolbox->unsetTextSettings($fileData, "email", $email);
- return $this->yellow->toolbox->createFile($fileName, $fileData);
+ // Return server argument from current HTTP request
+ public function getServer($key) {
+ return isset($_SERVER[$key]) ? $_SERVER[$key] : "";
}
- // Set current email
- public function set($email) {
- $this->email = $email;
+ // Return location arguments from current HTTP request
+ public function getLocationArguments() {
+ return $this->getServer("LOCATION_ARGUMENTS");
}
- // Set user setting
- public function setUser($key, $value, $email) {
- if (!isset($this->settings[$email])) $this->settings[$email] = new YellowArray();
- $this->settings[$email][$key] = $value;
+ // Return location arguments from current HTTP request, modify existing arguments
+ public function getLocationArgumentsNew($key, $value) {
+ $locationArguments = "";
+ $found = false;
+ $separator = $this->getLocationArgumentsSeparator();
+ foreach (explode("/", $this->getServer("LOCATION_ARGUMENTS")) as $token) {
+ if (preg_match("/^(.*?)$separator(.*)$/", $token, $matches)) {
+ if ($matches[1]==$key) {
+ $matches[2] = $value;
+ $found = true;
+ }
+ if (!is_string_empty($matches[1]) && !is_string_empty($matches[2])) {
+ if (!is_string_empty($locationArguments)) $locationArguments .= "/";
+ $locationArguments .= "$matches[1]:$matches[2]";
+ }
+ }
+ }
+ if (!$found && !is_string_empty($key) && !is_string_empty($value)) {
+ if (!is_string_empty($locationArguments)) $locationArguments .= "/";
+ $locationArguments .= "$key:$value";
+ }
+ if (!is_string_empty($locationArguments)) {
+ $locationArguments = $this->yellow->lookup->normaliseArguments($locationArguments, false, false);
+ if (!$this->isLocationArgumentsPagination($locationArguments)) $locationArguments .= "/";
+ }
+ return $locationArguments;
}
- // Return user setting
- public function getUser($key, $email = "") {
- if (is_string_empty($email)) $email = $this->email;
- return isset($this->settings[$email]) && isset($this->settings[$email][$key]) ? $this->settings[$email][$key] : "";
- }
-
- // Return user setting, HTML encoded
- public function getUserHtml($key, $email = "") {
- return htmlspecialchars($this->getUser($key, $email));
+ // Return location arguments from current HTTP request, convert form parameters
+ public function getLocationArgumentsCleanUrl() {
+ $locationArguments = "";
+ foreach (array_merge($_GET, $_POST) as $key=>$value) {
+ if (!is_string_empty($key) && !is_string_empty($value)) {
+ if (!is_string_empty($locationArguments)) $locationArguments .= "/";
+ $key = str_replace(array("/", ":", "="), array("\x1c", "\x1d", "\x1e"), $key);
+ $value = str_replace(array("/", ":", "="), array("\x1c", "\x1d", "\x1e"), $value);
+ $locationArguments .= "$key:$value";
+ }
+ }
+ if (!is_string_empty($locationArguments)) {
+ $locationArguments = $this->yellow->lookup->normaliseArguments($locationArguments, false, false);
+ if (!$this->isLocationArgumentsPagination($locationArguments)) $locationArguments .= "/";
+ }
+ return $locationArguments;
}
- // Return user settings
- public function getSettings($email = "") {
- $settings = array();
- if (is_string_empty($email)) $email = $this->email;
- if (isset($this->settings[$email])) $settings = $this->settings[$email]->getArrayCopy();
- return $settings;
+ // Return location arguments separator
+ public function getLocationArgumentsSeparator() {
+ return (strtoupperu(substru(PHP_OS, 0, 3))!="WIN") ? ":" : "=";
}
- // Return user settings modification date, Unix time or HTTP format
- public function getModified($httpFormat = false) {
- return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($this->modified) : $this->modified;
+ // Return human readable HTTP date
+ public function getHttpDateFormatted($timestamp) {
+ return gmdate("D, d M Y H:i:s", $timestamp)." GMT";
}
- // Check if user setting exists
- public function isUser($key, $email = "") {
- if (is_string_empty($email)) $email = $this->email;
- return isset($this->settings[$email]) && isset($this->settings[$email][$key]);
+ // Return human readable HTTP server status
+ public function getHttpStatusFormatted($statusCode, $shortFormat = false) {
+ switch ($statusCode) {
+ case 0: $text = "No data"; break;
+ case 200: $text = "OK"; break;
+ case 301: $text = "Moved permanently"; break;
+ case 302: $text = "Moved temporarily"; break;
+ case 303: $text = "Reload please"; break;
+ case 304: $text = "Not modified"; break;
+ case 400: $text = "Bad request"; break;
+ case 403: $text = "Forbidden"; break;
+ case 404: $text = "Not found"; break;
+ case 420: $text = "Not public"; break;
+ case 430: $text = "Login failed"; break;
+ case 434: $text = "Can create"; break;
+ case 435: $text = "Can restore"; break;
+ case 450: $text = "Update error"; break;
+ case 500: $text = "Server error"; break;
+ case 503: $text = "Service unavailable"; break;
+ default: $text = "Error $statusCode";
+ }
+ $serverProtocol = $this->getServer("SERVER_PROTOCOL");
+ if (!preg_match("/^HTTP\//", $serverProtocol)) $serverProtocol = "HTTP/1.1";
+ return $shortFormat ? $text : "$serverProtocol $statusCode $text";
}
- // Check if user exists
- public function isExisting($email) {
- return isset($this->settings[$email]);
+ // Return MIME content type
+ public function getMimeContentType($fileName) {
+ $contentType = "";
+ $contentTypes = array(
+ "css" => "text/css",
+ "gif" => "image/gif",
+ "html" => "text/html; charset=utf-8",
+ "ico" => "image/x-icon",
+ "js" => "application/javascript",
+ "json" => "application/json",
+ "jpg" => "image/jpeg",
+ "md" => "text/markdown",
+ "png" => "image/png",
+ "svg" => "image/svg+xml",
+ "txt" => "text/plain",
+ "woff" => "application/font-woff",
+ "woff2" => "application/font-woff2",
+ "xml" => "text/xml; charset=utf-8");
+ $fileType = $this->getFileType($fileName);
+ if (is_string_empty($fileType)) {
+ $contentType = $contentTypes["html"];
+ } elseif (array_key_exists($fileType, $contentTypes)) {
+ $contentType = $contentTypes[$fileType];
+ }
+ return $contentType;
}
-}
-class YellowExtension {
- public $yellow; // access to API
- public $modified; // extension modification date
- public $data; // extension data
-
- public function __construct($yellow) {
- $this->yellow = $yellow;
- $this->modified = 0;
- $this->data = array();
+ // Send HTTP header
+ public function sendHttpHeader($text) {
+ if (!headers_sent()) header($text);
}
- // Load extensions
- public function load($path) {
- foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/^.*\.php$/", true, false) as $entry) {
- $this->modified = max($this->modified, filemtime($entry));
- require_once($entry);
- $name = $this->yellow->lookup->normaliseName(basename($entry), true, true);
- $this->register(lcfirst($name), "Yellow".ucfirst($name));
- if ($this->yellow->system->get("coreDebugMode")>=3) echo "YellowExtension::load file:$entry<br/>\n";
- }
- $callback = function ($a, $b) {
- return $a["priority"] - $b["priority"];
- };
- uasort($this->data, $callback);
- foreach ($this->data as $key=>$value) {
- if (method_exists($this->data[$key]["object"], "onLoad")) $this->data[$key]["object"]->onLoad($this->yellow);
+ // Return files and directories
+ public function getDirectoryEntries($path, $regex = "/.*/", $sort = true, $directories = true, $includePath = true) {
+ $entries = array();
+ $directoryHandle = @opendir($path);
+ if ($directoryHandle) {
+ $path = rtrim($path, "/");
+ while (($entry = readdir($directoryHandle))!==false) {
+ if (substru($entry, 0, 1)==".") continue;
+ $entry = $this->yellow->lookup->normaliseUnicode($entry);
+ if (preg_match($regex, $entry)) {
+ if ($directories) {
+ if (is_dir("$path/$entry")) array_push($entries, $includePath ? "$path/$entry" : $entry);
+ } else {
+ if (is_file("$path/$entry")) array_push($entries, $includePath ? "$path/$entry" : $entry);
+ }
+ }
+ }
+ if ($sort) natcasesort($entries);
+ closedir($directoryHandle);
}
+ return $entries;
}
- // Register extension
- public function register($key, $class) {
- if (!$this->isExisting($key) && class_exists($class)) {
- $this->data[$key] = array();
- $this->data[$key]["object"] = $class=="YellowCore" ? new stdClass : new $class;
- $this->data[$key]["class"] = $class;
- $this->data[$key]["version"] = defined("$class::VERSION") ? $class::VERSION : 0;
- $this->data[$key]["priority"] = defined("$class::PRIORITY") ? $class::PRIORITY : count($this->data) + 10;
+ // Return files and directories recursively
+ public function getDirectoryEntriesRecursive($path, $regex = "/.*/", $sort = true, $directories = true, $levelMax = 0) {
+ --$levelMax;
+ $entries = $this->getDirectoryEntries($path, $regex, $sort, $directories);
+ if ($levelMax!=0) {
+ foreach ($this->getDirectoryEntries($path, "/.*/", $sort, true) as $entry) {
+ $entries = array_merge($entries, $this->getDirectoryEntriesRecursive($entry, $regex, $sort, $directories, $levelMax));
+ }
}
+ return $entries;
}
- // Return extension
- public function get($key) {
- return $this->data[$key]["object"];
- }
-
- // Return extensions modification date, Unix time or HTTP format
- public function getModified($httpFormat = false) {
- return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($this->modified) : $this->modified;
- }
-
- // Check if extension exists
- public function isExisting($key) {
- return isset($this->data[$key]);
+ // Read file, empty string if not found
+ public function readFile($fileName, $sizeMax = 0) {
+ $fileData = "";
+ $fileHandle = @fopen($fileName, "rb");
+ if ($fileHandle) {
+ clearstatcache(true, $fileName);
+ $fileSize = $sizeMax ? $sizeMax : filesize($fileName);
+ if ($fileSize) $fileData = fread($fileHandle, $fileSize);
+ fclose($fileHandle);
+ }
+ return $fileData;
}
-}
-
-class YellowLookup {
- public $yellow; // access to API
- public $requestHandler; // request handler name
- public $commandHandler; // command handler name
- public $layoutArguments; // layout arguments
- public function __construct($yellow) {
- $this->yellow = $yellow;
+ // Create file
+ public function createFile($fileName, $fileData, $mkdir = false) {
+ $ok = false;
+ if ($mkdir) {
+ $path = dirname($fileName);
+ if (!is_string_empty($path) && !is_dir($path)) @mkdir($path, 0777, true);
+ }
+ $fileHandle = @fopen($fileName, "wb");
+ if ($fileHandle) {
+ clearstatcache(true, $fileName);
+ if (flock($fileHandle, LOCK_EX)) {
+ ftruncate($fileHandle, 0);
+ fwrite($fileHandle, $fileData);
+ flock($fileHandle, LOCK_UN);
+ }
+ fclose($fileHandle);
+ $ok = true;
+ }
+ return $ok;
}
- // Return file system information
- public function findFileSystemInformation() {
- $pathInstall = substru(__DIR__, 0, 1-strlenu($this->yellow->system->get("coreExtensionDirectory")));
- $pathBase = $this->yellow->system->get("coreContentDirectory");
- $pathRoot = $this->yellow->system->get("coreMultiLanguageMode") ? "default/" : "";
- $pathHome = "home/";
- if (!is_string_empty($pathRoot)) {
- $firstRoot = "";
- $token = $root = rtrim($pathRoot, "/");
- foreach ($this->yellow->toolbox->getDirectoryEntries($pathBase, "/.*/", true, true, false) as $entry) {
- if (is_string_empty($firstRoot)) $firstRoot = $token = $entry;
- if ($this->normaliseToken($entry)==$root) {
- $token = $entry;
- break;
- }
- }
- $pathRoot = $this->normaliseToken($token)."/";
- $pathBase .= "$firstRoot/";
+ // Append file
+ public function appendFile($fileName, $fileData, $mkdir = false) {
+ $ok = false;
+ if ($mkdir) {
+ $path = dirname($fileName);
+ if (!is_string_empty($path) && !is_dir($path)) @mkdir($path, 0777, true);
}
- if (!is_string_empty($pathHome)) {
- $firstHome = "";
- $token = $home = rtrim($pathHome, "/");
- foreach ($this->yellow->toolbox->getDirectoryEntries($pathBase, "/.*/", true, true, false) as $entry) {
- if (is_string_empty($firstHome)) $firstHome = $token = $entry;
- if ($this->normaliseToken($entry)==$home) {
- $token = $entry;
- break;
- }
+ $fileHandle = @fopen($fileName, "ab");
+ if ($fileHandle) {
+ clearstatcache(true, $fileName);
+ if (flock($fileHandle, LOCK_EX)) {
+ fwrite($fileHandle, $fileData);
+ flock($fileHandle, LOCK_UN);
}
- $pathHome = $this->normaliseToken($token)."/";
+ fclose($fileHandle);
+ $ok = true;
}
- return array($pathInstall, $pathRoot, $pathHome);
+ return $ok;
}
- // Return content language
- public function findContentLanguage($fileName, $languageDefault) {
- $language = $languageDefault;
- $pathBase = $this->yellow->system->get("coreContentDirectory");
- $pathRoot = $this->yellow->system->get("coreServerRootDirectory");
- if (!is_string_empty($pathRoot)) {
- $fileName = substru($fileName, strlenu($pathBase));
- if (preg_match("/^(.+?)\//", $fileName, $matches)) {
- $name = $this->normaliseToken($matches[1]);
- if (strlenu($name)==2) $language = $name;
- }
+ // Copy file
+ public function copyFile($fileNameSource, $fileNameDestination, $mkdir = false) {
+ clearstatcache();
+ if ($mkdir) {
+ $path = dirname($fileNameDestination);
+ if (!is_string_empty($path) && !is_dir($path)) @mkdir($path, 0777, true);
}
- return $language;
+ return @copy($fileNameSource, $fileNameDestination);
}
-
- // Return content root locations
- public function findContentRootLocations() {
- $rootLocations = array();
- $pathBase = $this->yellow->system->get("coreContentDirectory");
- $pathRoot = $this->yellow->system->get("coreServerRootDirectory");
- if (!is_string_empty($pathRoot)) {
- foreach ($this->yellow->toolbox->getDirectoryEntries($pathBase, "/.*/", true, true, false) as $entry) {
- $token = $this->normaliseToken($entry)."/";
- if ($token==$pathRoot) $token = "";
- $rootLocations["root/$token"] = "$pathBase$entry/";
- }
- } else {
- $rootLocations["root/"] = "$pathBase";
- }
- if ($this->yellow->system->get("coreDebugMode")>=3) {
- foreach ($rootLocations as $key=>$key) {
- echo "YellowLookup::findContentRootLocations $key -> $value<br/>\n";
- }
+
+ // Rename file
+ public function renameFile($fileNameSource, $fileNameDestination, $mkdir = false) {
+ clearstatcache();
+ if ($mkdir) {
+ $path = dirname($fileNameDestination);
+ if (!is_string_empty($path) && !is_dir($path)) @mkdir($path, 0777, true);
}
- return $rootLocations;
+ return @rename($fileNameSource, $fileNameDestination);
}
- // Return content location from file path
- public function findContentLocationFromFile($fileName) {
- $invalid = false;
- $location = "/";
- $pathBase = $this->yellow->system->get("coreContentDirectory");
- $pathRoot = $this->yellow->system->get("coreServerRootDirectory");
- $pathHome = $this->yellow->system->get("coreServerHomeDirectory");
- $fileDefault = $this->yellow->system->get("coreContentDefaultFile");
- $fileExtension = $this->yellow->system->get("coreContentExtension");
- if (substru($fileName, 0, strlenu($pathBase))==$pathBase && mb_check_encoding($fileName, "UTF-8")) {
- $fileName = substru($fileName, strlenu($pathBase));
- $tokens = explode("/", $fileName);
- if (!is_string_empty($pathRoot)) {
- $token = $this->normaliseToken($tokens[0])."/";
- if ($token!=$pathRoot) $location .= $token;
- array_shift($tokens);
- }
- for ($i=0; $i<count($tokens)-1; ++$i) {
- $token = $this->normaliseToken($tokens[$i])."/";
- if ($i || $token!=$pathHome) $location .= $token;
- }
- $token = $this->normaliseToken($tokens[$i], $fileExtension);
- if ($token!=$fileDefault) {
- $location .= $this->normaliseToken($tokens[$i], $fileExtension, true);
- }
- $extension = ($pos = strrposu($fileName, ".")) ? substru($fileName, $pos) : "";
- if ($extension!=$fileExtension) $invalid = true;
+ // Rename directory
+ public function renameDirectory($pathSource, $pathDestination, $mkdir = false) {
+ return $pathSource==$pathDestination || $this->renameFile($pathSource, $pathDestination, $mkdir);
+ }
+
+ // Delete file
+ public function deleteFile($fileName, $pathTrash = "") {
+ clearstatcache();
+ if (is_string_empty($pathTrash)) {
+ $ok = @unlink($fileName);
} else {
- $invalid = true;
- }
- if ($this->yellow->system->get("coreDebugMode")>=2) {
- $debug = ($invalid ? "INVALID" : $location)." <- $pathBase$fileName";
- echo "YellowLookup::findContentLocationFromFile $debug<br/>\n";
+ if (!is_dir($pathTrash)) @mkdir($pathTrash, 0777, true);
+ $fileNameDestination = $pathTrash;
+ $fileNameDestination .= pathinfo($fileName, PATHINFO_FILENAME);
+ $fileNameDestination .= "-".str_replace(array(" ", ":"), "-", date("Y-m-d H:i:s"));
+ $fileNameDestination .= ".".pathinfo($fileName, PATHINFO_EXTENSION);
+ $ok = @rename($fileName, $fileNameDestination);
}
- return $invalid ? "" : $location;
+ return $ok;
}
- // Return file path from content location
- public function findFileFromContentLocation($location, $directory = false) {
- $found = $invalid = false;
- $path = $this->yellow->system->get("coreContentDirectory");
- $pathRoot = $this->yellow->system->get("coreServerRootDirectory");
- $pathHome = $this->yellow->system->get("coreServerHomeDirectory");
- $fileDefault = $this->yellow->system->get("coreContentDefaultFile");
- $fileExtension = $this->yellow->system->get("coreContentExtension");
- $tokens = explode("/", $location);
- if ($this->isRootLocation($location)) {
- if (!is_string_empty($pathRoot)) {
- $token = (count($tokens)>2) ? $tokens[1] : rtrim($pathRoot, "/");
- $path .= $this->findFileDirectory($path, $token, "", true, true, $found, $invalid);
- }
- } else {
- if (!is_string_empty($pathRoot)) {
- if (count($tokens)>2) {
- if ($this->normaliseToken($tokens[1])==$this->normaliseToken(rtrim($pathRoot, "/"))) $invalid = true;
- $path .= $this->findFileDirectory($path, $tokens[1], "", true, false, $found, $invalid);
- if ($found) array_shift($tokens);
- }
- if (!$found) {
- $path .= $this->findFileDirectory($path, rtrim($pathRoot, "/"), "", true, true, $found, $invalid);
- }
- }
- if (count($tokens)>2) {
- if ($this->normaliseToken($tokens[1])==$this->normaliseToken(rtrim($pathHome, "/"))) $invalid = true;
- for ($i=1; $i<count($tokens)-1; ++$i) {
- $path .= $this->findFileDirectory($path, $tokens[$i], "", true, true, $found, $invalid);
- }
- } else {
- $i = 1;
- $tokens[0] = rtrim($pathHome, "/");
- $path .= $this->findFileDirectory($path, $tokens[0], "", true, true, $found, $invalid);
- }
- if (!$directory) {
- if (!is_string_empty($tokens[$i])) {
- $token = $tokens[$i].$fileExtension;
- if ($token==$fileDefault) $invalid = true;
- $path .= $this->findFileDirectory($path, $token, $fileExtension, false, true, $found, $invalid);
+ // Delete directory
+ public function deleteDirectory($path, $pathTrash = "") {
+ clearstatcache();
+ if (is_string_empty($pathTrash)) {
+ $iterator = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
+ $files = new RecursiveIteratorIterator($iterator, RecursiveIteratorIterator::CHILD_FIRST);
+ foreach ($files as $file) {
+ if ($file->getType()=="dir") {
+ @rmdir($file->getPathname());
} else {
- $path .= $this->findFileDefault($path, $fileDefault, $fileExtension, false);
- }
- if ($this->yellow->system->get("coreDebugMode")>=2) {
- $debug = "$location -> ".($invalid ? "INVALID" : $path);
- echo "YellowLookup::findFileFromContentLocation $debug<br/>\n";
+ @unlink($file->getPathname());
}
}
+ $ok = @rmdir($path);
+ } else {
+ if (!is_dir($pathTrash)) @mkdir($pathTrash, 0777, true);
+ $pathDestination = $pathTrash;
+ $pathDestination .= basename($path);
+ $pathDestination .= "-".str_replace(array(" ", ":"), "-", date("Y-m-d H:i:s"));
+ $ok = @rename($path, $pathDestination);
}
- return $invalid ? "" : $path;
+ return $ok;
}
- // Return children from content location
- public function findChildrenFromContentLocation($location) {
- $fileNames = array();
- if (!$this->isFileLocation($location)) {
- $path = $this->findFileFromContentLocation($location, true);
- $fileDefault = $this->yellow->system->get("coreContentDefaultFile");
- $fileExtension = $this->yellow->system->get("coreContentExtension");
- foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/.*/", true, true, false) as $entry) {
- $token = $this->findFileDefault($path.$entry, $fileDefault, $fileExtension, false);
- array_push($fileNames, $path.$entry."/".$token);
- }
- if (!$this->isRootLocation($location)) {
- $regex = "/^.*\\".$fileExtension."$/";
- foreach ($this->yellow->toolbox->getDirectoryEntries($path, $regex, true, false, false) as $entry) {
- if ($this->normaliseToken($entry, $fileExtension)==$fileDefault) continue;
- array_push($fileNames, $path.$entry);
- }
- }
- }
- return $fileNames;
+ // Set file/directory modification date, Unix time
+ public function modifyFile($fileName, $modified) {
+ clearstatcache(true, $fileName);
+ return @touch($fileName, $modified);
}
- // Return media location from file path
- public function findMediaLocationFromFile($fileName) {
- $location = "";
- $extensionDirectoryLength = strlenu($this->yellow->system->get("coreExtensionDirectory"));
- $themeDirectoryLength = strlenu($this->yellow->system->get("coreThemeDirectory"));
- $mediaDirectoryLength = strlenu($this->yellow->system->get("coreMediaDirectory"));
- if (substru($fileName, 0, $extensionDirectoryLength)==$this->yellow->system->get("coreExtensionDirectory")) {
- if ($this->yellow->toolbox->isSafeFile($fileName)) {
- $location = $this->yellow->system->get("coreExtensionLocation").substru($fileName, $extensionDirectoryLength);
- }
- } elseif (substru($fileName, 0, $themeDirectoryLength)==$this->yellow->system->get("coreThemeDirectory")) {
- if ($this->yellow->toolbox->isSafeFile($fileName)) {
- $location = $this->yellow->system->get("coreThemeLocation").substru($fileName, $themeDirectoryLength);
- }
- } elseif (substru($fileName, 0, $mediaDirectoryLength)==$this->yellow->system->get("coreMediaDirectory")) {
- $location = "/".$fileName;
- }
- return $location;
- }
-
- // Return file path from media location
- public function findFileFromMediaLocation($location) {
- $fileName = "";
- $extensionLocationLength = strlenu($this->yellow->system->get("coreExtensionLocation"));
- $themeLocationLength = strlenu($this->yellow->system->get("coreThemeLocation"));
- $mediaLocationLength = strlenu($this->yellow->system->get("coreMediaLocation"));
- if (substru($location, 0, $extensionLocationLength)==$this->yellow->system->get("coreExtensionLocation")) {
- if ($this->yellow->toolbox->isSafeFile($location)) {
- $fileName = $this->yellow->system->get("coreExtensionDirectory").substru($location, $extensionLocationLength);
- }
- } elseif (substru($location, 0, $themeLocationLength)==$this->yellow->system->get("coreThemeLocation")) {
- if ($this->yellow->toolbox->isSafeFile($location)) {
- $fileName = $this->yellow->system->get("coreThemeDirectory").substru($location, $themeLocationLength);
- }
- } elseif (substru($location, 0, $mediaLocationLength)==$this->yellow->system->get("coreMediaLocation")) {
- $fileName = substru($location, 1);
- }
- return $fileName;
+ // Return file/directory modification date, Unix time
+ public function getFileModified($fileName) {
+ return (is_file($fileName) || is_dir($fileName)) ? filemtime($fileName) : 0;
}
- // Return children from media location
- public function findChildrenFromMediaLocation($location) {
- $fileNames = array();
- if (!$this->isFileLocation($location)) {
- $path = $this->findFileFromMediaLocation($location);
- foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/.*/", true, true, true) as $entry) {
- array_push($fileNames, $entry."/");
- }
- foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/.*/", true, false, true) as $entry) {
- array_push($fileNames, $entry);
- }
+ // Return file/directory deletion date, Unix time
+ public function getFileDeleted($fileName) {
+ $deleted = 0;
+ $text = basename($fileName);
+ $text = ($pos = strrposu($text, ".")) ? substru($text, 0, $pos) : $text;
+ if (preg_match("#^(.+)-(\d\d\d\d-\d\d-\d\d)-(\d\d)-(\d\d)-(\d\d)$#", $text, $matches)) {
+ $deleted = strtotime("$matches[2] $matches[3]:$matches[4]:$matches[5]");
}
- return $fileNames;
+ return $deleted;
}
- // Return media directory from a system setting
- public function findMediaDirectory($key) {
- return substru($key, -8, 8)=="Location" ? $this->findFileFromMediaLocation($this->yellow->system->get($key)) : "";
+ // Return file type
+ public function getFileType($fileName) {
+ return strtoloweru(($pos = strrposu($fileName, ".")) ? substru($fileName, $pos+1) : "");
}
- // Return file or directory that matches token
- public function findFileDirectory($path, $token, $fileExtension, $directory, $default, &$found, &$invalid) {
- if ($this->normaliseToken($token, $fileExtension)!=$token) $invalid = true;
- if (!$invalid) {
- $regex = "/^[\d\-\_\.]*".str_replace("-", ".", $token)."$/";
- foreach ($this->yellow->toolbox->getDirectoryEntries($path, $regex, false, $directory, false) as $entry) {
- if ($this->normaliseToken($entry, $fileExtension)==$token) {
- $token = $entry;
- $found = true;
- break;
- }
- }
- }
- if ($directory) $token .= "/";
- return ($default || $found) ? $token : "";
+ // Return file group
+ public function getFileGroup($fileName, $path) {
+ $group = "none";
+ if (preg_match("#^$path(.+?)\/#", $fileName, $matches)) $group = strtoloweru($matches[1]);
+ return $group;
}
- // Return default file in directory
- public function findFileDefault($path, $fileDefault, $fileExtension, $includePath = true) {
- $token = $fileDefault;
- if (!is_file($path."/".$fileDefault)) {
- $regex = "/^[\d\-\_\.]*($fileDefault)$/";
- foreach ($this->yellow->toolbox->getDirectoryEntries($path, $regex, true, false, false) as $entry) {
- if ($this->normaliseToken($entry, $fileExtension)==$fileDefault) {
- $token = $entry;
- break;
- }
- }
+ // Return number of bytes
+ public function getNumberBytes($text) {
+ $bytes = intval($text);
+ switch (strtoupperu(substru($text, -1))) {
+ case "G": $bytes *= 1024*1024*1024; break;
+ case "M": $bytes *= 1024*1024; break;
+ case "K": $bytes *= 1024; break;
}
- return $includePath ? "$path/$token" : $token;
- }
-
- // Normalise file/directory token
- public function normaliseToken($text, $fileExtension = "", $removeExtension = false) {
- if (!is_string_empty($fileExtension)) $text = ($pos = strrposu($text, ".")) ? substru($text, 0, $pos) : $text;
- if (preg_match("/^[\d\-\_\.]+(.*)$/", $text, $matches) && !is_string_empty($matches[1])) $text = $matches[1];
- return preg_replace("/[^\pL\d\-\_]/u", "-", $text).($removeExtension ? "" : $fileExtension);
- }
-
- // Normalise name
- public function normaliseName($text, $removePrefix = false, $removeExtension = false, $filterStrict = false) {
- if ($removeExtension) $text = ($pos = strrposu($text, ".")) ? substru($text, 0, $pos) : $text;
- if ($removePrefix && preg_match("/^[\d\-\_\.]+(.*)$/", $text, $matches) && !is_string_empty($matches[1])) $text = $matches[1];
- if ($filterStrict) $text = strtoloweru($text);
- return preg_replace("/[^\pL\d\-\_]/u", "-", $text);
+ return $bytes;
}
- // Normalise prefix
- public function normalisePrefix($text) {
- $prefix = "";
- if (preg_match("/^([\d\-\_\.]*)(.*)$/", $text, $matches)) $prefix = $matches[1];
- if (!is_string_empty($prefix) && !preg_match("/[\-\_\.]$/", $prefix)) $prefix .= "-";
- return $prefix;
+ // Return lines from text, including newline
+ public function getTextLines($text) {
+ $lines = preg_split("/\n/", $text);
+ foreach ($lines as &$line) {
+ $line = $line."\n";
+ }
+ if (is_string_empty($text) || substru($text, -1, 1)=="\n") array_pop($lines);
+ return $lines;
}
- // Normalise array, make keys with same upper/lower case
- public function normaliseUpperLower($input) {
- $array = array();
- foreach ($input as $key=>$value) {
- if (is_string_empty($key) || is_string_empty($value)) continue;
- $keySearch = strtoloweru($key);
- foreach ($array as $keyNew=>$valueNew) {
- if (strtoloweru($keyNew)==$keySearch) {
- $key = $keyNew;
- break;
+ //Â Return settings from text
+ function getTextSettings($text, $blockStart) {
+ $settings = new YellowArray();
+ if (is_string_empty($blockStart)) {
+ foreach ($this->getTextLines($text) as $line) {
+ if (preg_match("/^\#/", $line)) continue;
+ if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) {
+ if (!is_string_empty($matches[1]) && !is_string_empty($matches[2])) {
+ $settings[$matches[1]] = $matches[2];
+ }
+ }
+ }
+ } else {
+ $blockKey = "";
+ foreach ($this->getTextLines($text) as $line) {
+ if (preg_match("/^\#/", $line)) continue;
+ if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) {
+ if (lcfirst($matches[1])==$blockStart && !is_string_empty($matches[2])) {
+ $blockKey = $matches[2];
+ $settings[$blockKey] = new YellowArray();
+ }
+ if (!is_string_empty($blockKey) && !is_string_empty($matches[1]) && !is_string_empty($matches[2])) {
+ $settings[$blockKey][$matches[1]] = $matches[2];
+ }
}
}
- if (!isset($array[$key])) $array[$key] = 0;
- $array[$key] += $value;
}
- return $array;
+ return $settings;
}
- // Normalise location, make absolute location
- public function normaliseLocation($location, $pageLocation, $filterStrict = true) {
- if (!preg_match("/^\w+:/", trim(html_entity_decode($location, ENT_QUOTES, "UTF-8")))) {
- $pageBase = $this->yellow->page->base;
- $mediaBase = $this->yellow->system->get("coreServerBase").$this->yellow->system->get("coreMediaLocation");
- if (!preg_match("/^\#/", $location)) {
- if (!preg_match("/^\//", $location)) {
- $location = $this->getDirectoryLocation($pageBase.$pageLocation).$location;
- } elseif (!preg_match("#^($pageBase|$mediaBase)#", $location)) {
- $location = $pageBase.$location;
+ //Â Set settings in text
+ function setTextSettings($text, $blockStart, $blockKey, $settings) {
+ $textNew = "";
+ if (is_string_empty($blockStart)) {
+ foreach ($this->getTextLines($text) as $line) {
+ if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) {
+ if (!is_string_empty($matches[1]) && isset($settings[$matches[1]])) {
+ $textNew .= "$matches[1]: ".$settings[$matches[1]]."\n";
+ unset($settings[$matches[1]]);
+ continue;
+ }
}
+ $textNew .= $line;
+ }
+ foreach ($settings as $key=>$value) {
+ $textNew .= (strposu($key, "/") ? $key : ucfirst($key)).": $value\n";
}
- $location = str_replace("/./", "/", $location);
- $location = str_replace(":", $this->yellow->toolbox->getLocationArgumentsSeparator(), $location);
} else {
- if ($filterStrict && !$this->yellow->toolbox->isSafeUrl($location)) $location = "error-xss-filter";
+ $scan = false;
+ $textStart = $textMiddle = $textEnd = "";
+ foreach ($this->getTextLines($text) as $line) {
+ if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) {
+ if (lcfirst($matches[1])==$blockStart && !is_string_empty($matches[2])) {
+ $scan = lcfirst($matches[2])==lcfirst($blockKey);
+ }
+ }
+ if (!$scan && is_string_empty($textMiddle)) {
+ $textStart .= $line;
+ } elseif ($scan) {
+ $textMiddle .= $line;
+ } else {
+ $textEnd .= $line;
+ }
+ }
+ $textSettings = "";
+ foreach ($this->getTextLines($textMiddle) as $line) {
+ if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) {
+ if (!is_string_empty($matches[1]) && isset($settings[$matches[1]])) {
+ $textSettings .= "$matches[1]: ".$settings[$matches[1]]."\n";
+ unset($settings[$matches[1]]);
+ continue;
+ }
+ $textSettings .= $line;
+ }
+ }
+ foreach ($settings as $key=>$value) {
+ $textSettings .= (strposu($key, "/") ? $key : ucfirst($key)).": $value\n";
+ }
+ if (!is_string_empty($textMiddle)) {
+ $textMiddle = $textSettings;
+ if (!is_string_empty($textEnd)) $textMiddle .= "\n";
+ } else {
+ if (!is_string_empty($textStart)) $textEnd .= "\n";
+ $textEnd .= $textSettings;
+ }
+ $textNew = $textStart.$textMiddle.$textEnd;
}
- return $location;
+ return $textNew;
}
-
- // Normalise URL, make absolute URL
- public function normaliseUrl($scheme, $address, $base, $location, $filterStrict = true) {
- if (!preg_match("/^\w+:/", $location)) {
- $url = "$scheme://$address$base$location";
- } else {
- if ($filterStrict && !$this->yellow->toolbox->isSafeUrl($location)) $location = "error-xss-filter";
- $url = $location;
+
+ //Â Remove settings from text
+ function unsetTextSettings($text, $blockStart, $blockKey) {
+ $textNew = "";
+ if (!is_string_empty($blockStart)) {
+ $scan = false;
+ $textStart = $textMiddle = $textEnd = "";
+ foreach ($this->getTextLines($text) as $line) {
+ if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) {
+ if (lcfirst($matches[1])==$blockStart && !is_string_empty($matches[2])) {
+ $scan = lcfirst($matches[2])==lcfirst($blockKey);
+ }
+ }
+ if (!$scan && is_string_empty($textMiddle)) {
+ $textStart .= $line;
+ } elseif ($scan) {
+ $textMiddle .= $line;
+ } else {
+ $textEnd .= $line;
+ }
+ }
+ $textNew = rtrim($textStart.$textEnd)."\n";
}
- return $url;
+ return $textNew;
}
- // Return URL information
- public function getUrlInformation($url) {
- $scheme = $address = $base = "";
- if (preg_match("#^(\w+)://([^/]+)(.*)$#", rtrim($url, "/"), $matches)) {
- $scheme = $matches[1];
- $address = $matches[2];
- $base = $matches[3];
- }
- return array($scheme, $address, $base);
+ // Return array of specific size from text
+ public function getTextList($text, $separator, $size) {
+ $tokens = explode($separator, $text, $size);
+ return array_pad($tokens, $size, "");
}
- // Return directory location
- public function getDirectoryLocation($location) {
- return ($pos = strrposu($location, "/")) ? substru($location, 0, $pos+1) : "/";
+ // Return array of variable size from text, space separated
+ public function getTextArguments($text, $optional = "-", $sizeMin = 9) {
+ $text = preg_replace("/\s+/s", " ", trim($text));
+ $tokens = str_getcsv($text, " ", "\"");
+ foreach ($tokens as $key=>$value) {
+ if (is_null($value) || $value==$optional) $tokens[$key] = "";
+ }
+ return array_pad($tokens, $sizeMin, "");
}
- // Return redirect location
- public function getRedirectLocation($location) {
- if ($this->isFileLocation($location)) {
- $location = "$location/";
- } else {
- $languageDefault = $this->yellow->system->get("language");
- $language = $this->yellow->toolbox->detectBrowserLanguage($this->yellow->content->getLanguages(), $languageDefault);
- $location = "/$language/";
+ // Return text from array, space separated
+ public function getTextString($tokens, $optional = "-") {
+ $text = "";
+ foreach ($tokens as $token) {
+ if (preg_match("/\s/", $token)) $token = "\"$token\"";
+ if (is_string_empty($token)) $token = $optional;
+ if (!is_string_empty($text)) $text .= " ";
+ $text .= $token;
}
- return $location;
+ return $text;
}
-
- // Check if clean URL is requested
- public function isRequestCleanUrl($location) {
- return isset($_REQUEST["clean-url"]) && substru($location, -1, 1)=="/";
+
+ // Return number of words in text
+ public function getTextWords($text) {
+ $text = preg_replace("/([\p{Han}\p{Hiragana}\p{Katakana}]{3})/u", "$1 ", $text);
+ $text = preg_replace("/(\pL|\p{N})/u", "x", $text);
+ return str_word_count($text);
}
- // Check if location is specifying root
- public function isRootLocation($location) {
- return substru($location, 0, 1)!="/";
+ // Return text truncated at word boundary
+ public function getTextTruncated($text, $lengthMax) {
+ if (strlenu($text)>$lengthMax-1) {
+ $text = substru($text, 0, $lengthMax);
+ $pos = strrposu($text, " ");
+ $text = substru($text, 0, $pos ? $pos : $lengthMax-1)."…";
+ }
+ return $text;
}
- // Check if location is specifying file or directory
- public function isFileLocation($location) {
- return substru($location, -1, 1)!="/";
+ // Create text description, with or without HTML
+ public function createTextDescription($text, $lengthMax = 0, $removeHtml = true, $endMarker = "", $endMarkerText = "") {
+ $output = "";
+ $elementsBlock = array("blockquote", "br", "div", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "li", "ol", "p", "pre", "ul");
+ $elementsVoid = array("area", "br", "col", "embed", "hr", "img", "input", "param", "source", "wbr");
+ if ($lengthMax==0) $lengthMax = strlenu($text);
+ if ($removeHtml) {
+ $hiddenLevel = 0;
+ $offsetBytes = 0;
+ while (true) {
+ $elementFound = preg_match("/<(\/?)([\!\?\w]+)(.*?)(\/?)>/s", $text, $matches, PREG_OFFSET_CAPTURE, $offsetBytes);
+ $elementBefore = $elementFound ? substrb($text, $offsetBytes, $matches[0][1] - $offsetBytes) : substrb($text, $offsetBytes);
+ $elementRawData = isset($matches[0][0]) ? $matches[0][0] : "";
+ $elementStart = isset($matches[1][0]) ? $matches[1][0] : "";
+ $elementName = isset($matches[2][0]) ? $matches[2][0] : "";
+ $elementAttributes = isset($matches[3][0]) ? $matches[3][0] : "";
+ $elementEnd = isset($matches[4][0]) ? $matches[4][0] : "";
+ if (!is_string_empty($elementBefore) && !$hiddenLevel) {
+ $rawText = preg_replace("/\s+/s", " ", html_entity_decode($elementBefore, ENT_QUOTES, "UTF-8"));
+ if (is_string_empty($elementStart) && in_array(strtolower($elementName), $elementsBlock)) $rawText = rtrim($rawText)." ";
+ if (substru($rawText, 0, 1)==" " && (is_string_empty($output) || substru($output, -1)==" ")) $rawText = ltrim($rawText);
+ $output .= $this->getTextTruncated($rawText, $lengthMax);
+ $lengthMax -= strlenu($rawText);
+ }
+ if (!is_string_empty($elementRawData) && $elementRawData==$endMarker) {
+ $output .= $endMarkerText;
+ $lengthMax = 0;
+ }
+ if ($lengthMax<=0 || !$elementFound) break;
+ if ($hiddenLevel>0 || preg_match("/aria-hidden=\"true\"/i", $elementAttributes)) {
+ if (!is_string_empty($elementName) && is_string_empty($elementEnd) && !in_array(strtolower($elementName), $elementsVoid)) {
+ if (is_string_empty($elementStart)) {
+ ++$hiddenLevel;
+ } else {
+ --$hiddenLevel;
+ }
+ }
+ }
+ $offsetBytes = $matches[0][1] + strlenb($matches[0][0]);
+ }
+ $output = preg_replace("/\s+\…$/s", "…", $output);
+ } else {
+ $elementsOpen = array();
+ $offsetBytes = 0;
+ while (true) {
+ $elementFound = preg_match("/&.*?\;|<(\/?)([\!\?\w]+)(.*?)(\/?)>/s", $text, $matches, PREG_OFFSET_CAPTURE, $offsetBytes);
+ $elementBefore = $elementFound ? substrb($text, $offsetBytes, $matches[0][1] - $offsetBytes) : substrb($text, $offsetBytes);
+ $elementRawData = isset($matches[0][0]) ? $matches[0][0] : "";
+ $elementStart = isset($matches[1][0]) ? $matches[1][0] : "";
+ $elementName = isset($matches[2][0]) ? $matches[2][0] : "";
+ $elementEnd = isset($matches[4][0]) ? $matches[4][0] : "";
+ if (!is_string_empty($elementBefore)) {
+ $output .= $this->getTextTruncated($elementBefore, $lengthMax);
+ $lengthMax -= strlenu($elementBefore);
+ }
+ if (!is_string_empty($elementRawData) && $elementRawData==$endMarker) {
+ $output .= $endMarkerText;
+ $lengthMax = 0;
+ }
+ if ($lengthMax<=0 || !$elementFound) break;
+ if (!is_string_empty($elementName) && is_string_empty($elementEnd) && !in_array(strtolower($elementName), $elementsVoid)) {
+ if (is_string_empty($elementStart)) {
+ array_push($elementsOpen, $elementName);
+ } else {
+ array_pop($elementsOpen);
+ }
+ }
+ $output .= $elementRawData;
+ if ($elementRawData[0]=="&") --$lengthMax;
+ $offsetBytes = $matches[0][1] + strlenb($matches[0][0]);
+ }
+ $output = preg_replace("/\s+\…$/s", "…", $output);
+ for ($i=count($elementsOpen)-1; $i>=0; --$i) {
+ $output .= "</".$elementsOpen[$i].">";
+ }
+ }
+ return trim($output);
}
- // Check if location can be redirected into directory
- public function isRedirectLocation($location) {
- $redirect = false;
- if ($this->isFileLocation($location)) {
- $redirect = is_dir($this->findFileFromContentLocation("$location/", true));
- } elseif ($location=="/") {
- $redirect = $this->yellow->system->get("coreMultiLanguageMode");
+ // Create title from text
+ public function createTextTitle($text) {
+ if (preg_match("/^.*\/([\pL\d\-\_]+)/u", $text, $matches)) $text = str_replace("-", " ", ucfirst($matches[1]));
+ return $text;
+ }
+
+ // Create random text for cryptography
+ public function createSalt($length, $bcryptFormat = false) {
+ $dataBuffer = $salt = "";
+ $dataBufferSize = $bcryptFormat ? intval(ceil($length/4) * 3) : intval(ceil($length/2));
+ if (is_string_empty($dataBuffer) && function_exists("random_bytes")) {
+ $dataBuffer = @random_bytes($dataBufferSize);
}
- return $redirect;
+ if (is_string_empty($dataBuffer) && function_exists("openssl_random_pseudo_bytes")) {
+ $dataBuffer = @openssl_random_pseudo_bytes($dataBufferSize);
+ }
+ if (strlenb($dataBuffer)==$dataBufferSize) {
+ if ($bcryptFormat) {
+ $salt = substrb(base64_encode($dataBuffer), 0, $length);
+ $base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ $bcrypt64Chars = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+ $salt = strtr($salt, $base64Chars, $bcrypt64Chars);
+ } else {
+ $salt = substrb(bin2hex($dataBuffer), 0, $length);
+ }
+ }
+ return $salt;
}
- // Check if location contains nested directories
- public function isNestedLocation($location, $fileName, $checkHomeLocation = false) {
- $nested = false;
- if (!$checkHomeLocation || $location==$this->yellow->content->getHomeLocation($location)) {
- $path = dirname($fileName);
- if (!is_array_empty($this->yellow->toolbox->getDirectoryEntries($path, "/.*/", true, true, false))) $nested = true;
+ // Create hash with random salt, bcrypt or sha256
+ public function createHash($text, $algorithm, $cost = 0) {
+ $hash = "";
+ switch ($algorithm) {
+ case "bcrypt": $prefix = sprintf("$2y$%02d$", $cost);
+ $salt = $this->createSalt(22, true);
+ $hash = crypt($text, $prefix.$salt);
+ if (is_string_empty($salt) || strlenb($hash)!=60) $hash = "";
+ break;
+ case "sha256": $prefix = "$5y$";
+ $salt = $this->createSalt(32);
+ $hash = "$prefix$salt".hash("sha256", $salt.$text);
+ if (is_string_empty($salt) || strlenb($hash)!=100) $hash = "";
+ break;
}
- return $nested;
+ return $hash;
}
- // Check if location is available
- public function isAvailableLocation($location, $fileName) {
- $available = true;
- $pathBase = $this->yellow->system->get("coreContentDirectory");
- if (substru($fileName, 0, strlenu($pathBase))==$pathBase) {
- $sharedLocation = $this->yellow->content->getHomeLocation($location)."shared/";
- if (substru($location, 0, strlenu($sharedLocation))==$sharedLocation) $available = false;
+ // Verify that text matches hash
+ public function verifyHash($text, $algorithm, $hash) {
+ $hashCalculated = "";
+ switch ($algorithm) {
+ case "bcrypt": if (substrb($hash, 0, 4)=="$2y$" || substrb($hash, 0, 4)=="$2a$") {
+ $hashCalculated = crypt($text, $hash);
+ }
+ break;
+ case "sha256": if (substrb($hash, 0, 4)=="$5y$") {
+ $prefix = "$5y$";
+ $salt = substrb($hash, 4, 32);
+ $hashCalculated = "$prefix$salt".hash("sha256", $salt.$text);
+ }
+ break;
}
- return $available;
+ return $this->verifyToken($hashCalculated, $hash);
}
- // Check if location is within current HTTP request
- public function isActiveLocation($location, $currentLocation) {
- if ($this->isFileLocation($location)) {
- $active = $currentLocation==$location;
- } else {
- if ($location==$this->yellow->content->getHomeLocation($location)) {
- $active = $this->getDirectoryLocation($currentLocation)==$location;
- } else {
- $active = substru($currentLocation, 0, strlenu($location))==$location;
+ // Verify that token is not empty and identical, timing attack safe string comparison
+ public function verifyToken($tokenExpected, $tokenReceived) {
+ $ok = false;
+ $lengthExpected = strlenb($tokenExpected);
+ $lengthReceived = strlenb($tokenReceived);
+ if ($lengthExpected!=0 && $lengthReceived!=0) {
+ $ok = $lengthExpected==$lengthReceived;
+ for ($i=0; $i<$lengthReceived; ++$i) {
+ $ok &= $tokenExpected[$i<$lengthExpected ? $i : 0]==$tokenReceived[$i];
}
}
- return $active;
- }
-
- // Check if file is valid
- public function isValidFile($fileName) {
- $contentDirectoryLength = strlenu($this->yellow->system->get("coreContentDirectory"));
- $mediaDirectoryLength = strlenu($this->yellow->system->get("coreMediaDirectory"));
- $systemDirectoryLength = strlenu($this->yellow->system->get("coreSystemDirectory"));
- return strposu($fileName, "/")===false ||
- substru($fileName, 0, $contentDirectoryLength)==$this->yellow->system->get("coreContentDirectory") ||
- substru($fileName, 0, $mediaDirectoryLength)==$this->yellow->system->get("coreMediaDirectory") ||
- substru($fileName, 0, $systemDirectoryLength)==$this->yellow->system->get("coreSystemDirectory");
+ return $ok;
}
- // Check if content file
- public function isContentFile($fileName) {
- $contentDirectoryLength = strlenu($this->yellow->system->get("coreContentDirectory"));
- return substru($fileName, 0, $contentDirectoryLength)==$this->yellow->system->get("coreContentDirectory");
+ //Â Return meta data from raw data
+ public function getMetaData($rawData, $key) {
+ $value = "";
+ if (preg_match("/^(\xEF\xBB\xBF)?\-\-\-[\r\n]+(.+?)\-\-\-[\r\n]+(.*)$/s", $rawData, $parts)) {
+ $key = lcfirst($key);
+ foreach ($this->getTextLines($parts[2]) as $line) {
+ if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) {
+ if (lcfirst($matches[1])==$key && !is_string_empty($matches[2])) {
+ $value = $matches[2];
+ break;
+ }
+ }
+ }
+ }
+ return $value;
}
- // Check if media file
- public function isMediaFile($fileName) {
- $mediaDirectoryLength = strlenu($this->yellow->system->get("coreMediaDirectory"));
- return substru($fileName, 0, $mediaDirectoryLength)==$this->yellow->system->get("coreMediaDirectory");
- }
-
- // Check if system file
- public function isSystemFile($fileName) {
- $systemDirectoryLength = strlenu($this->yellow->system->get("coreSystemDirectory"));
- return substru($fileName, 0, $systemDirectoryLength)==$this->yellow->system->get("coreSystemDirectory");
- }
-}
-
-class YellowToolbox {
-
- // Return browser cookie from from current HTTP request
- public function getCookie($key) {
- return isset($_COOKIE[$key]) ? $_COOKIE[$key] : "";
- }
-
- // Return server argument from current HTTP request
- public function getServer($key) {
- return isset($_SERVER[$key]) ? $_SERVER[$key] : "";
- }
-
- // Return location arguments from current HTTP request
- public function getLocationArguments() {
- return $this->getServer("LOCATION_ARGUMENTS");
- }
-
- // Return location arguments from current HTTP request, modify existing arguments
- public function getLocationArgumentsNew($key, $value) {
- $locationArguments = "";
- $found = false;
- $separator = $this->getLocationArgumentsSeparator();
- foreach (explode("/", $this->getServer("LOCATION_ARGUMENTS")) as $token) {
- if (preg_match("/^(.*?)$separator(.*)$/", $token, $matches)) {
- if ($matches[1]==$key) {
- $matches[2] = $value;
- $found = true;
- }
- if (!is_string_empty($matches[1]) && !is_string_empty($matches[2])) {
- if (!is_string_empty($locationArguments)) $locationArguments .= "/";
- $locationArguments .= "$matches[1]:$matches[2]";
+ //Â Set meta data in raw data
+ public function setMetaData($rawData, $key, $value) {
+ if (preg_match("/^(\xEF\xBB\xBF)?\-\-\-[\r\n]+(.+?)\-\-\-[\r\n]+(.*)$/s", $rawData, $parts)) {
+ $found = false;
+ $key = lcfirst($key);
+ $rawDataMiddle = "";
+ foreach ($this->getTextLines($parts[2]) as $line) {
+ if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) {
+ if (lcfirst($matches[1])==$key) {
+ $rawDataMiddle .= "$matches[1]: $value\n";
+ $found = true;
+ continue;
+ }
}
+ $rawDataMiddle .= $line;
}
+ if (!$found) $rawDataMiddle .= (strposu($key, "/") ? $key : ucfirst($key)).": $value\n";
+ $rawDataNew = $parts[1]."---\n".$rawDataMiddle."---\n".$parts[3];
+ } else {
+ $rawDataNew = $rawData;
}
- if (!$found && !is_string_empty($key) && !is_string_empty($value)) {
- if (!is_string_empty($locationArguments)) $locationArguments .= "/";
- $locationArguments .= "$key:$value";
- }
- if (!is_string_empty($locationArguments)) {
- $locationArguments = $this->normaliseArguments($locationArguments, false, false);
- if (!$this->isLocationArgumentsPagination($locationArguments)) $locationArguments .= "/";
- }
- return $locationArguments;
+ return $rawDataNew;
}
- // Return location arguments from current HTTP request, convert form parameters
- public function getLocationArgumentsCleanUrl() {
- $locationArguments = "";
- foreach (array_merge($_GET, $_POST) as $key=>$value) {
- if (!is_string_empty($key) && !is_string_empty($value)) {
- if (!is_string_empty($locationArguments)) $locationArguments .= "/";
- $key = str_replace(array("/", ":", "="), array("\x1c", "\x1d", "\x1e"), $key);
- $value = str_replace(array("/", ":", "="), array("\x1c", "\x1d", "\x1e"), $value);
- $locationArguments .= "$key:$value";
+ //Â Remove meta data in raw data
+ public function unsetMetaData($rawData, $key) {
+ if (preg_match("/^(\xEF\xBB\xBF)?\-\-\-[\r\n]+(.+?)\-\-\-[\r\n]+(.*)$/s", $rawData, $parts)) {
+ $key = lcfirst($key);
+ $rawDataMiddle = "";
+ foreach ($this->getTextLines($parts[2]) as $line) {
+ if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) {
+ if (lcfirst($matches[1])==$key) continue;
+ }
+ $rawDataMiddle .= $line;
}
+ $rawDataNew = $parts[1]."---\n".$rawDataMiddle."---\n".$parts[3];
+ } else {
+ $rawDataNew = $rawData;
}
- if (!is_string_empty($locationArguments)) {
- $locationArguments = $this->normaliseArguments($locationArguments, false, false);
- if (!$this->isLocationArgumentsPagination($locationArguments)) $locationArguments .= "/";
- }
- return $locationArguments;
+ return $rawDataNew;
}
- // Return location arguments separator
- public function getLocationArgumentsSeparator() {
- return (strtoupperu(substru(PHP_OS, 0, 3))!="WIN") ? ":" : "=";
+ // Detect server URL
+ public function detectServerUrl() {
+ $scheme = "http";
+ if ($this->getServer("REQUEST_SCHEME")=="https" || $this->getServer("HTTPS")=="on") $scheme = "https";
+ if ($this->getServer("HTTP_X_FORWARDED_PROTO")=="https") $scheme = "https";
+ $address = $this->getServer("SERVER_NAME");
+ $port = $this->getServer("SERVER_PORT");
+ if ($port!=80 && $port!=443) $address .= ":$port";
+ $base = "";
+ if (preg_match("/^(.*)\/.*\.php$/", $this->getServer("SCRIPT_NAME"), $matches)) $base = $matches[1];
+ return "$scheme://$address$base/";
}
- // Return human readable HTTP date
- public function getHttpDateFormatted($timestamp) {
- return gmdate("D, d M Y H:i:s", $timestamp)." GMT";
+ // Detect server location
+ public function detectServerLocation() {
+ if (isset($_SERVER["REQUEST_URI"])) {
+ $location = $_SERVER["REQUEST_URI"];
+ $location = rawurldecode(($pos = strposu($location, "?")) ? substru($location, 0, $pos) : $location);
+ $location = $this->yellow->lookup->normalisePath($location);
+ if (substru($location, 0, 1)!="/") $location = "/".$location;
+ $separator = $this->getLocationArgumentsSeparator();
+ if (preg_match("/^(.*?\/)([^\/]+$separator.*)$/", $location, $matches)) {
+ $_SERVER["LOCATION"] = $location = $matches[1];
+ $_SERVER["LOCATION_ARGUMENTS"] = $matches[2];
+ foreach (explode("/", $matches[2]) as $token) {
+ if (preg_match("/^(.*?)$separator(.*)$/", $token, $matches)) {
+ if (!is_string_empty($matches[1]) && !is_string_empty($matches[2])) {
+ $matches[1] = str_replace(array("\x1c", "\x1d", "\x1e"), array("/", ":", "="), $matches[1]);
+ $matches[2] = str_replace(array("\x1c", "\x1d", "\x1e"), array("/", ":", "="), $matches[2]);
+ $_REQUEST[$matches[1]] = $matches[2];
+ }
+ }
+ }
+ } else {
+ $_SERVER["LOCATION"] = $location;
+ $_SERVER["LOCATION_ARGUMENTS"] = "";
+ }
+ }
+ return $this->getServer("LOCATION");
}
- // Return human readable HTTP server status
- public function getHttpStatusFormatted($statusCode, $shortFormat = false) {
- switch ($statusCode) {
- case 0: $text = "No data"; break;
- case 200: $text = "OK"; break;
- case 301: $text = "Moved permanently"; break;
- case 302: $text = "Moved temporarily"; break;
- case 303: $text = "Reload please"; break;
- case 304: $text = "Not modified"; break;
- case 400: $text = "Bad request"; break;
- case 403: $text = "Forbidden"; break;
- case 404: $text = "Not found"; break;
- case 420: $text = "Not public"; break;
- case 430: $text = "Login failed"; break;
- case 434: $text = "Can create"; break;
- case 435: $text = "Can restore"; break;
- case 450: $text = "Update error"; break;
- case 500: $text = "Server error"; break;
- case 503: $text = "Service unavailable"; break;
- default: $text = "Error $statusCode";
+ // Detect server sitename
+ public function detectServerSitename() {
+ $sitename = "Localhost";
+ if (preg_match("#^(www\.)?([\w\-]+)#", $this->getServer("SERVER_NAME"), $matches)) {
+ $sitename = ucfirst($matches[2]);
}
- $serverProtocol = $this->getServer("SERVER_PROTOCOL");
- if (!preg_match("/^HTTP\//", $serverProtocol)) $serverProtocol = "HTTP/1.1";
- return $shortFormat ? $text : "$serverProtocol $statusCode $text";
+ return $sitename;
}
- // Return MIME content type
- public function getMimeContentType($fileName) {
- $contentType = "";
- $contentTypes = array(
- "css" => "text/css",
- "gif" => "image/gif",
- "html" => "text/html; charset=utf-8",
- "ico" => "image/x-icon",
- "js" => "application/javascript",
- "json" => "application/json",
- "jpg" => "image/jpeg",
- "md" => "text/markdown",
- "png" => "image/png",
- "svg" => "image/svg+xml",
- "txt" => "text/plain",
- "woff" => "application/font-woff",
- "woff2" => "application/font-woff2",
- "xml" => "text/xml; charset=utf-8");
- $fileType = $this->getFileType($fileName);
- if (is_string_empty($fileType)) {
- $contentType = $contentTypes["html"];
- } elseif (array_key_exists($fileType, $contentTypes)) {
- $contentType = $contentTypes[$fileType];
+ // Detect server timezone
+ public function detectServerTimezone() {
+ $timezone = ini_get("date.timezone");
+ if (is_string_empty($timezone)) {
+ if (PHP_OS=="Darwin") {
+ if (preg_match("#zoneinfo/(.*)#", @readlink("/etc/localtime"), $matches)) $timezone = $matches[1];
+ } else {
+ if (preg_match("/^(\S+)\/(\S+)/", $this->readFile("/etc/timezone"), $matches)) $timezone = $matches[1];
+ }
}
- return $contentType;
+ if (!in_array($timezone, timezone_identifiers_list())) $timezone = "UTC";
+ return $timezone;
}
- // Send HTTP header
- public function sendHttpHeader($text) {
- if (!headers_sent()) header($text);
+ // Detect server name, version and operating system
+ public function detectServerInformation() {
+ $name = "Unknown";
+ $version = "x.x.x";
+ $os = PHP_OS;
+ if (preg_match("/^(\S+)\/(\S+)/", $this->getServer("SERVER_SOFTWARE"), $matches)) {
+ $name = $matches[1];
+ $version = $matches[2];
+ } elseif (preg_match("/^(\S+)/", $this->getServer("SERVER_SOFTWARE"), $matches)) {
+ $name = $matches[1];
+ }
+ if (PHP_SAPI=="cli" || PHP_SAPI=="cli-server") {
+ $name = "Built-in";
+ $version = PHP_VERSION;
+ }
+ if (PHP_OS=="Darwin") {
+ $os = "Mac";
+ } elseif (strtoupperu(substru(PHP_OS, 0, 3))=="WIN") {
+ $os = "Windows";
+ }
+ return array($name, $version, $os);
}
- // Return files and directories
- public function getDirectoryEntries($path, $regex = "/.*/", $sort = true, $directories = true, $includePath = true) {
- $entries = array();
- $directoryHandle = @opendir($path);
- if ($directoryHandle) {
- $path = rtrim($path, "/");
- while (($entry = readdir($directoryHandle))!==false) {
- if (substru($entry, 0, 1)==".") continue;
- $entry = $this->normaliseUnicode($entry);
- if (preg_match($regex, $entry)) {
- if ($directories) {
- if (is_dir("$path/$entry")) array_push($entries, $includePath ? "$path/$entry" : $entry);
- } else {
- if (is_file("$path/$entry")) array_push($entries, $includePath ? "$path/$entry" : $entry);
- }
- }
+ // Detect browser language
+ public function detectBrowserLanguage($languages, $languageDefault) {
+ $languageFound = $languageDefault;
+ foreach (preg_split("/\s*,\s*/", $this->getServer("HTTP_ACCEPT_LANGUAGE")) as $text) {
+ list($language, $dummy) = $this->getTextList($text, ";", 2);
+ if (!is_string_empty($language) && in_array($language, $languages)) {
+ $languageFound = $language;
+ break;
}
- if ($sort) natcasesort($entries);
- closedir($directoryHandle);
}
- return $entries;
+ return $languageFound;
}
- // Return files and directories recursively
- public function getDirectoryEntriesRecursive($path, $regex = "/.*/", $sort = true, $directories = true, $levelMax = 0) {
- --$levelMax;
- $entries = $this->getDirectoryEntries($path, $regex, $sort, $directories);
- if ($levelMax!=0) {
- foreach ($this->getDirectoryEntries($path, "/.*/", $sort, true) as $entry) {
- $entries = array_merge($entries, $this->getDirectoryEntriesRecursive($entry, $regex, $sort, $directories, $levelMax));
+ // Detect terminal width and height
+ public function detectTerminalInformation() {
+ $width = $height = 0;
+ if (strtoupperu(substru(PHP_OS, 0, 3))=="WIN") {
+ exec("powershell \$Host.UI.RawUI.WindowSize.Width", $outputLines, $returnStatus);
+ if ($returnStatus==0 && !is_array_empty($outputLines)) {
+ $width = intval(end($outputLines));
+ }
+ exec("powershell \$Host.UI.RawUI.WindowSize.Height", $outputLines, $returnStatus);
+ if ($returnStatus==0 && !is_array_empty($outputLines)) {
+ $height = intval(end($outputLines));
+ }
+ } else {
+ exec("stty size", $outputLines, $returnStatus);
+ if ($returnStatus==0 && preg_match("/^(\d+)\s+(\d+)/", implode("\n", $outputLines), $matches)) {
+ $width = intval($matches[2]);
+ $height = intval($matches[1]);
}
}
- return $entries;
+ return array($width, $height);
}
- // Read file, empty string if not found
- public function readFile($fileName, $sizeMax = 0) {
- $fileData = "";
+ // Detect image width, height, orientation and type for GIF/JPG/PNG/SVG
+ public function detectImageInformation($fileName, $fileType = "") {
+ $width = $height = $orientation = 0;
+ $type = "";
$fileHandle = @fopen($fileName, "rb");
if ($fileHandle) {
- clearstatcache(true, $fileName);
- $fileSize = $sizeMax ? $sizeMax : filesize($fileName);
- if ($fileSize) $fileData = fread($fileHandle, $fileSize);
- fclose($fileHandle);
- }
- return $fileData;
- }
-
- // Create file
- public function createFile($fileName, $fileData, $mkdir = false) {
- $ok = false;
- if ($mkdir) {
- $path = dirname($fileName);
- if (!is_string_empty($path) && !is_dir($path)) @mkdir($path, 0777, true);
- }
- $fileHandle = @fopen($fileName, "wb");
- if ($fileHandle) {
- clearstatcache(true, $fileName);
- if (flock($fileHandle, LOCK_EX)) {
- ftruncate($fileHandle, 0);
- fwrite($fileHandle, $fileData);
- flock($fileHandle, LOCK_UN);
+ if (is_string_empty($fileType)) $fileType = $this->getFileType($fileName);
+ if ($fileType=="gif") {
+ $dataSignature = fread($fileHandle, 6);
+ $dataHeader = fread($fileHandle, 7);
+ if (!feof($fileHandle) && ($dataSignature=="GIF87a" || $dataSignature=="GIF89a")) {
+ $width = (ord($dataHeader[1])<<8) + ord($dataHeader[0]);
+ $height = (ord($dataHeader[3])<<8) + ord($dataHeader[2]);
+ $type = $fileType;
+ }
+ } elseif ($fileType=="jpg") {
+ $dataBufferSizeMax = filesize($fileName);
+ $dataBufferSize = min($dataBufferSizeMax, 4096);
+ if ($dataBufferSize) $dataBuffer = fread($fileHandle, $dataBufferSize);
+ $dataSignature = substrb($dataBuffer, 0, 4);
+ if (!feof($fileHandle) && ($dataSignature=="\xff\xd8\xff\xe0" || $dataSignature=="\xff\xd8\xff\xe1")) {
+ for ($pos=2; $pos+8<$dataBufferSize; $pos+=$length) {
+ if ($dataBuffer[$pos]!="\xff") break;
+ $dataMarker = $dataBuffer[$pos+1];
+ if ($dataMarker=="\xe1") {
+ $orientation = $this->getImageOrientationFromBuffer($dataBuffer, $pos+4, $dataBufferSize);
+ }
+ if (($dataMarker>="\xc0" && $dataMarker<="\xc3") ||
+ ($dataMarker>="\xc5" && $dataMarker<="\xc7") ||
+ ($dataMarker>="\xc9" && $dataMarker<="\xcb") ||
+ ($dataMarker>="\xcd" && $dataMarker<="\xcf")) {
+ $width = (ord($dataBuffer[$pos+7])<<8) + ord($dataBuffer[$pos+8]);
+ $height = (ord($dataBuffer[$pos+5])<<8) + ord($dataBuffer[$pos+6]);
+ $type = $fileType;
+ break;
+ }
+ $length = (ord($dataBuffer[$pos+2])<<8) + ord($dataBuffer[$pos+3]) + 2;
+ while ($pos+$length+8>=$dataBufferSize) {
+ if ($dataBufferSize==$dataBufferSizeMax) break;
+ $dataBufferDiff = min($dataBufferSizeMax, $dataBufferSize*2) - $dataBufferSize;
+ $dataBufferSize += $dataBufferDiff;
+ $dataBufferChunk = fread($fileHandle, $dataBufferDiff);
+ if (feof($fileHandle) || $dataBufferChunk===false) {
+ $dataBufferSize = 0;
+ break;
+ }
+ $dataBuffer .= $dataBufferChunk;
+ }
+ }
+ }
+ } elseif ($fileType=="png") {
+ $dataSignature = fread($fileHandle, 8);
+ $dataHeader = fread($fileHandle, 16);
+ if (!feof($fileHandle) && $dataSignature=="\x89PNG\r\n\x1a\n") {
+ $width = (ord($dataHeader[10])<<8) + ord($dataHeader[11]);
+ $height = (ord($dataHeader[14])<<8) + ord($dataHeader[15]);
+ $type = $fileType;
+ }
+ } elseif ($fileType=="svg") {
+ $dataBufferSizeMax = filesize($fileName);
+ $dataBufferSize = min($dataBufferSizeMax, 4096);
+ if ($dataBufferSize) $dataBuffer = fread($fileHandle, $dataBufferSize);
+ if (!feof($fileHandle) && preg_match("/<svg(\s.*?)>/s", $dataBuffer, $matches)) {
+ if (preg_match("/\swidth=\"(\d+)\"/s", $matches[1], $tokens)) $width = $tokens[1];
+ if (preg_match("/\sheight=\"(\d+)\"/s", $matches[1], $tokens)) $height = $tokens[1];
+ $type = $fileType;
+ }
}
fclose($fileHandle);
- $ok = true;
}
- return $ok;
+ return array($width, $height, $orientation, $type);
}
- // Append file
- public function appendFile($fileName, $fileData, $mkdir = false) {
- $ok = false;
- if ($mkdir) {
- $path = dirname($fileName);
- if (!is_string_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);
+ // Return image orientation from Exif
+ public function getImageOrientationFromBuffer($dataBuffer, $pos, $size) {
+ $orientation = 0;
+ $dataSignature = substrb($dataBuffer, $pos, 6);
+ if ($dataSignature=="\x45\x78\x69\x66\x00\x00" && $pos+14<=$size) {
+ $startPos = $pos+6;
+ $bigEndian = $dataBuffer[$startPos]=="M";
+ $ifdOffset = $this->getLongFromBuffer($dataBuffer, $startPos+4, $bigEndian);
+ $ifdStartPos = $startPos+$ifdOffset;
+ $ifdCount = $ifdStartPos+2<=$size ? $this->getShortFromBuffer($dataBuffer, $ifdStartPos, $bigEndian) : 0;
+ $pos = $ifdStartPos+2;
+ while ($ifdCount && $pos+12<=$size) {
+ $ifdTag = $this->getShortFromBuffer($dataBuffer, $pos, $bigEndian);
+ $ifdFormat = $this->getShortFromBuffer($dataBuffer, $pos+2, $bigEndian);
+ if ($ifdTag==0x8769 && $ifdFormat==4) {
+ $ifdOffset = $this->getLongFromBuffer($dataBuffer, $pos+8, $bigEndian);
+ $ifdStartPos = $startPos+$ifdOffset;
+ $ifdCount = $ifdStartPos+2<=$size ? $this->getShortFromBuffer($dataBuffer, $ifdStartPos, $bigEndian) : 0;
+ $pos = $ifdStartPos+2;
+ continue;
+ }
+ if ($ifdTag==0x0112 && $ifdFormat==3) {
+ $orientation = $this->getShortFromBuffer($dataBuffer, $pos+8, $bigEndian);
+ break;
+ }
+ --$ifdCount;
+ $pos += 12;
}
- fclose($fileHandle);
- $ok = true;
}
- return $ok;
+ return $orientation;
}
- // Copy file
- public function copyFile($fileNameSource, $fileNameDestination, $mkdir = false) {
- clearstatcache();
- if ($mkdir) {
- $path = dirname($fileNameDestination);
- if (!is_string_empty($path) && !is_dir($path)) @mkdir($path, 0777, true);
+ // Return unsigned short value from buffer
+ public function getShortFromBuffer($dataBuffer, $pos, $bigEndian) {
+ if ($bigEndian) {
+ $value = (ord($dataBuffer[$pos])<<8) + ord($dataBuffer[$pos+1]);
+ } else {
+ $value = (ord($dataBuffer[$pos+1])<<8) + ord($dataBuffer[$pos]);
}
- return @copy($fileNameSource, $fileNameDestination);
+ return $value;
}
- // Rename file
- public function renameFile($fileNameSource, $fileNameDestination, $mkdir = false) {
- clearstatcache();
- if ($mkdir) {
- $path = dirname($fileNameDestination);
- if (!is_string_empty($path) && !is_dir($path)) @mkdir($path, 0777, true);
+ // Return unsigned long value from buffer
+ public function getLongFromBuffer($dataBuffer, $pos, $bigEndian) {
+ if ($bigEndian) {
+ $value = (ord($dataBuffer[$pos])<<24) + (ord($dataBuffer[$pos+1])<<16) +
+ (ord($dataBuffer[$pos+2])<<8) + ord($dataBuffer[$pos+3]);
+ } else {
+ $value = (ord($dataBuffer[$pos+3])<<24) + (ord($dataBuffer[$pos+2])<<16) +
+ (ord($dataBuffer[$pos+1])<<8) + ord($dataBuffer[$pos]);
}
- return @rename($fileNameSource, $fileNameDestination);
- }
-
- // Rename directory
- public function renameDirectory($pathSource, $pathDestination, $mkdir = false) {
- return $pathSource==$pathDestination || $this->renameFile($pathSource, $pathDestination, $mkdir);
+ return $value;
}
- // Delete file
- public function deleteFile($fileName, $pathTrash = "") {
- clearstatcache();
- if (is_string_empty($pathTrash)) {
- $ok = @unlink($fileName);
- } else {
- if (!is_dir($pathTrash)) @mkdir($pathTrash, 0777, true);
- $fileNameDestination = $pathTrash;
- $fileNameDestination .= pathinfo($fileName, PATHINFO_FILENAME);
- $fileNameDestination .= "-".str_replace(array(" ", ":"), "-", date("Y-m-d H:i:s"));
- $fileNameDestination .= ".".pathinfo($fileName, PATHINFO_EXTENSION);
- $ok = @rename($fileName, $fileNameDestination);
- }
- return $ok;
+ // Start timer
+ public function timerStart(&$time) {
+ $time = microtime(true);
}
- // Delete directory
- public function deleteDirectory($path, $pathTrash = "") {
- clearstatcache();
- if (is_string_empty($pathTrash)) {
- $iterator = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
- $files = new RecursiveIteratorIterator($iterator, RecursiveIteratorIterator::CHILD_FIRST);
- foreach ($files as $file) {
- if ($file->getType()=="dir") {
- @rmdir($file->getPathname());
- } else {
- @unlink($file->getPathname());
- }
- }
- $ok = @rmdir($path);
- } else {
- if (!is_dir($pathTrash)) @mkdir($pathTrash, 0777, true);
- $pathDestination = $pathTrash;
- $pathDestination .= basename($path);
- $pathDestination .= "-".str_replace(array(" ", ":"), "-", date("Y-m-d H:i:s"));
- $ok = @rename($path, $pathDestination);
- }
- return $ok;
+ // Stop timer and calculate elapsed time in milliseconds
+ public function timerStop(&$time) {
+ $time = intval((microtime(true)-$time) * 1000);
}
- // Set file/directory modification date, Unix time
- public function modifyFile($fileName, $modified) {
- clearstatcache(true, $fileName);
- return @touch($fileName, $modified);
+ // Check if there are location arguments in current HTTP request
+ public function isLocationArguments($location = "") {
+ if (is_string_empty($location)) $location = $this->getServer("LOCATION").$this->getServer("LOCATION_ARGUMENTS");
+ $separator = $this->getLocationArgumentsSeparator();
+ return preg_match("/[^\/]+$separator.*$/", $location);
}
- // Return file/directory modification date, Unix time
- public function getFileModified($fileName) {
- return (is_file($fileName) || is_dir($fileName)) ? filemtime($fileName) : 0;
+ // Check if there are pagination arguments in current HTTP request
+ public function isLocationArgumentsPagination($location) {
+ $separator = $this->getLocationArgumentsSeparator();
+ return preg_match("/^(.*\/)?page$separator.*$/", $location);
}
-
- // Return file/directory deletion date, Unix time
- public function getFileDeleted($fileName) {
- $deleted = 0;
- $text = basename($fileName);
- $text = ($pos = strrposu($text, ".")) ? substru($text, 0, $pos) : $text;
- if (preg_match("#^(.+)-(\d\d\d\d-\d\d-\d\d)-(\d\d)-(\d\d)-(\d\d)$#", $text, $matches)) {
- $deleted = strtotime("$matches[2] $matches[3]:$matches[4]:$matches[5]");
- }
- return $deleted;
+
+ // Check if unmodified since last HTTP request
+ public function isNotModified($lastModifiedFormatted) {
+ return $this->getServer("HTTP_IF_MODIFIED_SINCE")==$lastModifiedFormatted;
}
- // Return file type
- public function getFileType($fileName) {
- return strtoloweru(($pos = strrposu($fileName, ".")) ? substru($fileName, $pos+1) : "");
+ public function normaliseArguments($text, $appendSlash = true, $filterStrict = true) { return $this->yellow->lookup->normaliseArguments($text, $appendSlash, $filterStrict); } //TODO: remove later, for backwards compatibility
+}
+
+class YellowPage {
+ public $yellow; // access to API
+ public $scheme; // server scheme
+ public $address; // server address
+ public $base; // base location
+ public $location; // page location
+ public $fileName; // content file name
+ public $rawData; // raw data of page
+ public $metaDataOffsetBytes; // meta data offset
+ public $metaData; // meta data
+ public $pageCollections; // additional pages
+ public $sharedPages; // shared pages
+ public $headerData; // response header
+ public $outputData; // response output
+ public $parser; // content parser
+ public $parserData; // content data of page
+ public $statusCode; // status code
+ public $errorMessage; // error message
+ public $lastModified; // last modification date
+ public $available; // page is available? (boolean)
+ public $visible; // page is visible location? (boolean)
+ public $active; // page is active location? (boolean)
+ public $cacheable; // page is cacheable? (boolean)
+
+ public function __construct($yellow) {
+ $this->yellow = $yellow;
+ $this->scheme = "";
+ $this->address = "";
+ $this->base = "";
+ $this->location = "";
+ $this->fileName = "";
+ $this->metaData = new YellowArray();
+ $this->pageCollections = array();
+ $this->sharedPages = array();
+ $this->headerData = array();
+ }
+
+ // Set request information
+ public function setRequestInformation($scheme, $address, $base, $location, $fileName, $cacheable) {
+ $this->scheme = $scheme;
+ $this->address = $address;
+ $this->base = $base;
+ $this->location = $location;
+ $this->fileName = $fileName;
+ $this->cacheable = $cacheable;
}
- // Return file group
- public function getFileGroup($fileName, $path) {
- $group = "none";
- if (preg_match("#^$path(.+?)\/#", $fileName, $matches)) $group = strtoloweru($matches[1]);
- return $group;
+ // Parse page meta
+ public function parseMeta($rawData, $statusCode = 0, $errorMessage = "") {
+ $this->rawData = $rawData;
+ $this->parser = null;
+ $this->parserData = "";
+ $this->statusCode = $statusCode;
+ $this->errorMessage = $errorMessage;
+ $this->lastModified = 0;
+ $this->available = $this->yellow->lookup->isAvailableLocation($this->location, $this->fileName);
+ $this->visible = true;
+ $this->active = $this->yellow->lookup->isActiveLocation($this->location, $this->yellow->page->location);
+ $this->parseMetaData();
}
- // Return number of bytes
- public function getNumberBytes($text) {
- $bytes = intval($text);
- switch (strtoupperu(substru($text, -1))) {
- case "G": $bytes *= 1024*1024*1024; break;
- case "M": $bytes *= 1024*1024; break;
- case "K": $bytes *= 1024; break;
+ // Parse page meta update
+ public function parseMetaUpdate() {
+ if ($this->statusCode==0) {
+ $this->rawData = $this->yellow->toolbox->readFile($this->fileName);
+ $this->statusCode = 200;
+ $this->parseMetaData();
}
- return $bytes;
}
- // Return lines from text, including newline
- public function getTextLines($text) {
- $lines = preg_split("/\n/", $text);
- foreach ($lines as &$line) {
- $line = $line."\n";
+ // Parse page meta data
+ public function parseMetaData() {
+ $this->metaData = new YellowArray();
+ $this->metaDataOffsetBytes = 0;
+ if (!is_null($this->rawData)) {
+ $this->set("title", $this->yellow->toolbox->createTextTitle($this->location));
+ $this->set("language", $this->yellow->lookup->findContentLanguage($this->fileName, $this->yellow->system->get("language")));
+ $this->set("modified", date("Y-m-d H:i:s", $this->yellow->toolbox->getFileModified($this->fileName)));
+ $this->parseMetaDataRaw(array("sitename", "author", "layout", "theme", "parser", "status"));
+ $titleHeader = ($this->location==$this->yellow->content->getHomeLocation($this->location)) ?
+ $this->get("sitename") : $this->get("title")." - ".$this->get("sitename");
+ if (!$this->isExisting("titleContent")) $this->set("titleContent", $this->get("title"));
+ if (!$this->isExisting("titleNavigation")) $this->set("titleNavigation", $this->get("title"));
+ if (!$this->isExisting("titleHeader")) $this->set("titleHeader", $titleHeader);
+ if ($this->get("status")=="unlisted") $this->visible = false;
+ if ($this->get("status")=="shared") $this->available = false;
+ $this->parseMetaDataShared();
+ } else {
+ $this->set("size", filesize($this->fileName));
+ $this->set("type", $this->yellow->toolbox->getFileType($this->fileName));
+ $this->set("group", $this->yellow->toolbox->getFileGroup($this->fileName, $this->yellow->system->get("coreMediaDirectory")));
+ $this->set("modified", date("Y-m-d H:i:s", $this->yellow->toolbox->getFileModified($this->fileName)));
+ }
+ foreach ($this->yellow->extension->data as $key=>$value) {
+ if (method_exists($value["object"], "onParseMetaData")) $value["object"]->onParseMetaData($this);
}
- if (is_string_empty($text) || substru($text, -1, 1)=="\n") array_pop($lines);
- return $lines;
}
- //Â Return settings from text
- function getTextSettings($text, $blockStart) {
- $settings = new YellowArray();
- if (is_string_empty($blockStart)) {
- foreach ($this->getTextLines($text) as $line) {
- if (preg_match("/^\#/", $line)) continue;
- if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) {
- if (!is_string_empty($matches[1]) && !is_string_empty($matches[2])) {
- $settings[$matches[1]] = $matches[2];
- }
- }
- }
- } else {
- $blockKey = "";
- foreach ($this->getTextLines($text) as $line) {
- if (preg_match("/^\#/", $line)) continue;
+ // Parse page meta data from raw data
+ public function parseMetaDataRaw($defaultKeys) {
+ foreach ($defaultKeys as $key) {
+ $value = $this->yellow->system->get($key);
+ if (!is_string_empty($key) && !is_string_empty($value)) $this->set($key, $value);
+ }
+ if (preg_match("/^(\xEF\xBB\xBF)?\-\-\-[\r\n]+(.+?)\-\-\-[\r\n]+/s", $this->rawData, $parts)) {
+ $this->metaDataOffsetBytes = strlenb($parts[0]);
+ foreach (preg_split("/[\r\n]+/", $parts[2]) as $line) {
if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) {
- if (lcfirst($matches[1])==$blockStart && !is_string_empty($matches[2])) {
- $blockKey = $matches[2];
- $settings[$blockKey] = new YellowArray();
- }
- if (!is_string_empty($blockKey) && !is_string_empty($matches[1]) && !is_string_empty($matches[2])) {
- $settings[$blockKey][$matches[1]] = $matches[2];
- }
+ if (!is_string_empty($matches[1]) && !is_string_empty($matches[2])) $this->set($matches[1], $matches[2]);
}
}
+ } elseif (preg_match("/^(\xEF\xBB\xBF)?([^\r\n]+)[\r\n]+=+[\r\n]+/", $this->rawData, $parts)) {
+ $this->metaDataOffsetBytes = strlenb($parts[0]);
+ $this->set("title", $parts[2]);
}
- return $settings;
}
- //Â Set settings in text
- function setTextSettings($text, $blockStart, $blockKey, $settings) {
- $textNew = "";
- if (is_string_empty($blockStart)) {
- foreach ($this->getTextLines($text) as $line) {
- if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) {
- if (!is_string_empty($matches[1]) && isset($settings[$matches[1]])) {
- $textNew .= "$matches[1]: ".$settings[$matches[1]]."\n";
- unset($settings[$matches[1]]);
- continue;
- }
- }
- $textNew .= $line;
- }
- foreach ($settings as $key=>$value) {
- $textNew .= (strposu($key, "/") ? $key : ucfirst($key)).": $value\n";
- }
- } else {
- $scan = false;
- $textStart = $textMiddle = $textEnd = "";
- foreach ($this->getTextLines($text) as $line) {
- if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) {
- if (lcfirst($matches[1])==$blockStart && !is_string_empty($matches[2])) {
- $scan = lcfirst($matches[2])==lcfirst($blockKey);
- }
- }
- if (!$scan && is_string_empty($textMiddle)) {
- $textStart .= $line;
- } elseif ($scan) {
- $textMiddle .= $line;
- } else {
- $textEnd .= $line;
+ // Parse page meta data for shared pages
+ public function parseMetaDataShared() {
+ $this->sharedPages["main"] = $this;
+ if ($this->available && $this->statusCode!=0) {
+ foreach ($this->yellow->content->getShared($this->location) as $page) {
+ if ($page->get("status")=="shared") {
+ $this->sharedPages[basename($page->location)] = $page;
+ $page->sharedPages["main"] = $this;
}
}
- $textSettings = "";
- foreach ($this->getTextLines($textMiddle) as $line) {
- if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) {
- if (!is_string_empty($matches[1]) && isset($settings[$matches[1]])) {
- $textSettings .= "$matches[1]: ".$settings[$matches[1]]."\n";
- unset($settings[$matches[1]]);
- continue;
+ }
+ }
+
+ // Parse page content on demand
+ public function parseContent() {
+ if (!is_null($this->rawData) && !is_object($this->parser)) {
+ if ($this->yellow->extension->isExisting($this->get("parser"))) {
+ $value = $this->yellow->extension->data[$this->get("parser")];
+ if (method_exists($value["object"], "onParseContentRaw")) {
+ $this->parser = $value["object"];
+ $this->parserData = $this->getContent(true);
+ $this->parserData = $this->parser->onParseContentRaw($this, $this->parserData);
+ foreach ($this->yellow->extension->data as $key=>$value) {
+ if (method_exists($value["object"], "onParseContentHtml")) {
+ $output = $value["object"]->onParseContentHtml($this, $this->parserData);
+ if (!is_null($output)) $this->parserData = $output;
+ }
}
- $textSettings .= $line;
}
+ } else {
+ $this->parserData = $this->getContent(true);
+ $this->parserData = preg_replace("/\[yellow error\]/i", $this->errorMessage, $this->parserData);
}
- foreach ($settings as $key=>$value) {
- $textSettings .= (strposu($key, "/") ? $key : ucfirst($key)).": $value\n";
+ if (!$this->isExisting("description")) {
+ $description = $this->yellow->toolbox->createTextDescription($this->parserData, 150);
+ $this->set("description", !is_string_empty($description) ? $description : $this->get("title"));
}
- if (!is_string_empty($textMiddle)) {
- $textMiddle = $textSettings;
- if (!is_string_empty($textEnd)) $textMiddle .= "\n";
- } else {
- if (!is_string_empty($textStart)) $textEnd .= "\n";
- $textEnd .= $textSettings;
+ if ($this->yellow->system->get("coreDebugMode")>=3) {
+ echo "YellowPage::parseContent location:".$this->location."<br/>\n";
}
- $textNew = $textStart.$textMiddle.$textEnd;
}
- return $textNew;
}
-
- //Â Remove settings from text
- function unsetTextSettings($text, $blockStart, $blockKey) {
- $textNew = "";
- if (!is_string_empty($blockStart)) {
- $scan = false;
- $textStart = $textMiddle = $textEnd = "";
- foreach ($this->getTextLines($text) as $line) {
- if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) {
- if (lcfirst($matches[1])==$blockStart && !is_string_empty($matches[2])) {
- $scan = lcfirst($matches[2])==lcfirst($blockKey);
- }
- }
- if (!$scan && is_string_empty($textMiddle)) {
- $textStart .= $line;
- } elseif ($scan) {
- $textMiddle .= $line;
- } else {
- $textEnd .= $line;
- }
+
+ // Parse page content shortcut
+ public function parseContentShortcut($name, $text, $type) {
+ $output = null;
+ foreach ($this->yellow->extension->data as $key=>$value) {
+ if (method_exists($value["object"], "onParseContentShortcut")) {
+ $output = $value["object"]->onParseContentShortcut($this, $name, $text, $type);
+ if (!is_null($output)) break;
}
- $textNew = rtrim($textStart.$textEnd)."\n";
}
- return $textNew;
+ if (is_null($output)) {
+ if ($name=="yellow" && $type=="inline" && $text=="error") {
+ $output = $this->errorMessage;
+ }
+ }
+ if ($this->yellow->system->get("coreDebugMode")>=3 && !is_string_empty($name)) {
+ echo "YellowPage::parseContentShortcut name:$name type:$type<br/>\n";
+ }
+ return $output;
}
- // Return attributes from text
- public function getTextAttributes($text, $attributesAllowEmptyString) {
- $tokens = array();
- $posStart = $posQuote = 0;
- $textLength = strlenb($text);
- for ($pos=0; $pos<$textLength; ++$pos) {
- if ($text[$pos]==" " && !$posQuote) {
- if ($pos>$posStart) array_push($tokens, substrb($text, $posStart, $pos-$posStart));
- $posStart = $pos+1;
- }
- if ($text[$pos]=="=" && !$posQuote) {
- if ($pos>$posStart) array_push($tokens, substrb($text, $posStart, $pos-$posStart));
- array_push($tokens, "=");
- $posStart = $pos+1;
+ // Parse page
+ public function parsePage() {
+ $this->parsePageLayout($this->get("layout"));
+ if (!$this->isCacheable()) $this->setHeader("Cache-Control", "no-cache, no-store");
+ if (!$this->isHeader("Content-Type")) $this->setHeader("Content-Type", "text/html; charset=utf-8");
+ if (!$this->isHeader("Content-Modified")) $this->setHeader("Content-Modified", $this->getModified(true));
+ if (!$this->isHeader("Last-Modified")) $this->setHeader("Last-Modified", $this->getLastModified(true));
+ $fileNameTheme = $this->yellow->system->get("coreThemeDirectory").$this->yellow->lookup->normaliseName($this->get("theme")).".css";
+ if (!is_file($fileNameTheme)) {
+ $this->error(500, "Theme '".$this->get("theme")."' does not exist!");
+ }
+ if (!$this->yellow->language->isExisting($this->get("language"))) {
+ $this->error(500, "Language '".$this->get("language")."' does not exist!");
+ }
+ if (!is_object($this->parser)) {
+ $this->error(500, "Parser '".$this->get("parser")."' does not exist!");
+ }
+ if ($this->yellow->lookup->isNestedLocation($this->location, $this->fileName, true)) {
+ $this->error(500, "Folder '".dirname($this->fileName)."' may not contain subfolders!");
+ }
+ if ($this->yellow->getRequestHandler()=="core" && $this->isExisting("redirect") && $this->statusCode==200) {
+ $location = $this->yellow->lookup->normaliseLocation($this->get("redirect"), $this->location);
+ $location = $this->yellow->lookup->normaliseUrl($this->scheme, $this->address, "", $location);
+ $this->status(301, $location);
+ }
+ if ($this->yellow->getRequestHandler()=="core" && !$this->isAvailable() && $this->statusCode==200) {
+ $this->error(404);
+ }
+ if ($this->isExisting("pageClean")) $this->outputData = null;
+ foreach ($this->yellow->extension->data as $key=>$value) {
+ if (method_exists($value["object"], "onParsePageOutput")) {
+ $output = $value["object"]->onParsePageOutput($this, $this->outputData);
+ if (!is_null($output)) $this->outputData = $output;
}
- if ($text[$pos]=="\"") {
- if ($posQuote) {
- if ($pos>$posQuote) array_push($tokens, substrb($text, $posQuote+1, $pos-$posQuote-1));
- $posQuote = 0;
- $posStart = $pos+1;
- } else {
- if ($pos==$posStart) $posQuote = $pos;
- }
+ }
+ }
+
+ // Parse page layout
+ public function parsePageLayout($name) {
+ $this->outputData = null;
+ foreach ($this->yellow->extension->data as $key=>$value) {
+ if (method_exists($value["object"], "onParsePageLayout")) {
+ $value["object"]->onParsePageLayout($this, $name);
}
}
- if ($pos>$posStart && !$posQuote) {
- array_push($tokens, substrb($text, $posStart, $pos-$posStart));
+ if (is_null($this->outputData)) {
+ ob_start();
+ $this->includeLayout($name);
+ $this->outputData = ob_get_contents();
+ ob_end_clean();
}
- $attributes = array();
- for ($i=0; $i<count($tokens); ++$i) {
- if ($i+2<count($tokens) && $tokens[$i+1]=="=") {
- $key = $tokens[$i];
- $value = $tokens[$i+2];
- $i += 2;
- } else {
- $key = $value = $tokens[$i];
+ }
+
+ // Include page layout
+ public function includeLayout($name) {
+ $fileNameLayoutNormal = $this->yellow->system->get("coreLayoutDirectory").$this->yellow->lookup->normaliseName($name).".html";
+ $fileNameLayoutTheme = $this->yellow->system->get("coreLayoutDirectory").
+ $this->yellow->lookup->normaliseName($this->get("theme"))."-".$this->yellow->lookup->normaliseName($name).".html";
+ if (is_file($fileNameLayoutTheme)) {
+ if ($this->yellow->system->get("coreDebugMode")>=2) {
+ echo "YellowPage::includeLayout file:$fileNameLayoutTheme<br/>\n";
}
- if (!is_string_empty($key) && (!is_string_empty($value) || in_array(strtolower($key), $attributesAllowEmptyString))) {
- $attributes[$key] = $value;
+ $this->setLastModified(filemtime($fileNameLayoutTheme));
+ require($fileNameLayoutTheme);
+ } elseif (is_file($fileNameLayoutNormal)) {
+ if ($this->yellow->system->get("coreDebugMode")>=2) {
+ echo "YellowPage::includeLayout file:$fileNameLayoutNormal<br/>\n";
}
+ $this->setLastModified(filemtime($fileNameLayoutNormal));
+ require($fileNameLayoutNormal);
+ } else {
+ $this->error(500, "Layout '$name' does not exist!");
+ echo "Layout error<br/>\n";
}
- return $attributes;
}
- // Return array of specific size from text
- public function getTextList($text, $separator, $size) {
- $tokens = explode($separator, $text, $size);
- return array_pad($tokens, $size, "");
+ // Set page setting
+ public function set($key, $value) {
+ $this->metaData[$key] = $value;
}
- // Return array of variable size from text, space separated
- public function getTextArguments($text, $optional = "-", $sizeMin = 9) {
- $text = preg_replace("/\s+/s", " ", trim($text));
- $tokens = str_getcsv($text, " ", "\"");
- foreach ($tokens as $key=>$value) {
- if (is_null($value) || $value==$optional) $tokens[$key] = "";
- }
- return array_pad($tokens, $sizeMin, "");
+ // Return page setting
+ public function get($key) {
+ return $this->isExisting($key) ? $this->metaData[$key] : "";
+ }
+
+ // Return page setting, HTML encoded
+ public function getHtml($key) {
+ return htmlspecialchars($this->get($key));
}
- // Return text from array, space separated
- public function getTextString($tokens, $optional = "-") {
- $text = "";
- foreach ($tokens as $token) {
- if (preg_match("/\s/", $token)) $token = "\"$token\"";
- if (is_string_empty($token)) $token = $optional;
- if (!is_string_empty($text)) $text .= " ";
- $text .= $token;
+ // Return page setting as language specific date
+ public function getDate($key, $format = "") {
+ if (!is_string_empty($format)) {
+ $format = $this->yellow->language->getText($format);
+ } else {
+ $format = $this->yellow->language->getText("coreDateFormatMedium");
}
- return $text;
+ return $this->yellow->language->getDateFormatted(strtotime($this->get($key)), $format);
}
- // Return number of words in text
- public function getTextWords($text) {
- $text = preg_replace("/([\p{Han}\p{Hiragana}\p{Katakana}]{3})/u", "$1 ", $text);
- $text = preg_replace("/(\pL|\p{N})/u", "x", $text);
- return str_word_count($text);
+ // Return page setting as language specific date, HTML encoded
+ public function getDateHtml($key, $format = "") {
+ return htmlspecialchars($this->getDate($key, $format));
+ }
+
+ // Return page setting as language specific date, relative to today
+ public function getDateRelative($key, $format = "", $daysLimit = 30) {
+ if (!is_string_empty($format)) {
+ $format = $this->yellow->language->getText($format);
+ } else {
+ $format = $this->yellow->language->getText("coreDateFormatMedium");
+ }
+ return $this->yellow->language->getDateRelative(strtotime($this->get($key)), $format, $daysLimit);
}
- // Return text truncated at word boundary
- public function getTextTruncated($text, $lengthMax) {
- if (strlenu($text)>$lengthMax-1) {
- $text = substru($text, 0, $lengthMax);
- $pos = strrposu($text, " ");
- $text = substru($text, 0, $pos ? $pos : $lengthMax-1)."…";
+ // Return page setting as language specific date, relative to today, HTML encoded
+ public function getDateRelativeHtml($key, $format = "", $daysLimit = 30) {
+ return htmlspecialchars($this->getDateRelative($key, $format, $daysLimit));
+ }
+
+ // Return page setting as date
+ public function getDateFormatted($key, $format) {
+ return $this->yellow->language->getDateFormatted(strtotime($this->get($key)), $format);
+ }
+
+ // Return page setting as date, HTML encoded
+ public function getDateFormattedHtml($key, $format) {
+ return htmlspecialchars($this->getDateFormatted($key, $format));
+ }
+
+ // Return page content, HTML encoded or raw format
+ public function getContent($rawFormat = false) {
+ if ($rawFormat) {
+ $this->parseMetaUpdate();
+ $text = substrb($this->rawData, $this->metaDataOffsetBytes);
+ } else {
+ $this->parseContent();
+ $text = $this->parserData;
}
return $text;
}
- // Create text description, with or without HTML
- public function createTextDescription($text, $lengthMax = 0, $removeHtml = true, $endMarker = "", $endMarkerText = "") {
- $output = "";
- $elementsBlock = array("blockquote", "br", "div", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "li", "ol", "p", "pre", "ul");
- $elementsVoid = array("area", "br", "col", "embed", "hr", "img", "input", "param", "source", "wbr");
- if ($lengthMax==0) $lengthMax = strlenu($text);
- if ($removeHtml) {
- $hiddenLevel = 0;
- $offsetBytes = 0;
- while (true) {
- $elementFound = preg_match("/<(\/?)([\!\?\w]+)(.*?)(\/?)>/s", $text, $matches, PREG_OFFSET_CAPTURE, $offsetBytes);
- $elementBefore = $elementFound ? substrb($text, $offsetBytes, $matches[0][1] - $offsetBytes) : substrb($text, $offsetBytes);
- $elementRawData = isset($matches[0][0]) ? $matches[0][0] : "";
- $elementStart = isset($matches[1][0]) ? $matches[1][0] : "";
- $elementName = isset($matches[2][0]) ? $matches[2][0] : "";
- $elementAttributes = isset($matches[3][0]) ? $matches[3][0] : "";
- $elementEnd = isset($matches[4][0]) ? $matches[4][0] : "";
- if (!is_string_empty($elementBefore) && !$hiddenLevel) {
- $rawText = preg_replace("/\s+/s", " ", html_entity_decode($elementBefore, ENT_QUOTES, "UTF-8"));
- if (is_string_empty($elementStart) && in_array(strtolower($elementName), $elementsBlock)) $rawText = rtrim($rawText)." ";
- if (substru($rawText, 0, 1)==" " && (is_string_empty($output) || substru($output, -1)==" ")) $rawText = ltrim($rawText);
- $output .= $this->getTextTruncated($rawText, $lengthMax);
- $lengthMax -= strlenu($rawText);
- }
- if (!is_string_empty($elementRawData) && $elementRawData==$endMarker) {
- $output .= $endMarkerText;
- $lengthMax = 0;
- }
- if ($lengthMax<=0 || !$elementFound) break;
- if ($hiddenLevel>0 || preg_match("/aria-hidden=\"true\"/i", $elementAttributes)) {
- if (!is_string_empty($elementName) && is_string_empty($elementEnd) && !in_array(strtolower($elementName), $elementsVoid)) {
- if (is_string_empty($elementStart)) {
- ++$hiddenLevel;
- } else {
- --$hiddenLevel;
- }
- }
- }
- $offsetBytes = $matches[0][1] + strlenb($matches[0][0]);
- }
- $output = preg_replace("/\s+\…$/s", "…", $output);
- } else {
- $elementsOpen = array();
- $offsetBytes = 0;
- while (true) {
- $elementFound = preg_match("/&.*?\;|<(\/?)([\!\?\w]+)(.*?)(\/?)>/s", $text, $matches, PREG_OFFSET_CAPTURE, $offsetBytes);
- $elementBefore = $elementFound ? substrb($text, $offsetBytes, $matches[0][1] - $offsetBytes) : substrb($text, $offsetBytes);
- $elementRawData = isset($matches[0][0]) ? $matches[0][0] : "";
- $elementStart = isset($matches[1][0]) ? $matches[1][0] : "";
- $elementName = isset($matches[2][0]) ? $matches[2][0] : "";
- $elementEnd = isset($matches[4][0]) ? $matches[4][0] : "";
- if (!is_string_empty($elementBefore)) {
- $output .= $this->getTextTruncated($elementBefore, $lengthMax);
- $lengthMax -= strlenu($elementBefore);
- }
- if (!is_string_empty($elementRawData) && $elementRawData==$endMarker) {
- $output .= $endMarkerText;
- $lengthMax = 0;
- }
- if ($lengthMax<=0 || !$elementFound) break;
- if (!is_string_empty($elementName) && is_string_empty($elementEnd) && !in_array(strtolower($elementName), $elementsVoid)) {
- if (is_string_empty($elementStart)) {
- array_push($elementsOpen, $elementName);
- } else {
- array_pop($elementsOpen);
- }
- }
- $output .= $elementRawData;
- if ($elementRawData[0]=="&") --$lengthMax;
- $offsetBytes = $matches[0][1] + strlenb($matches[0][0]);
- }
- $output = preg_replace("/\s+\…$/s", "…", $output);
- for ($i=count($elementsOpen)-1; $i>=0; --$i) {
- $output .= "</".$elementsOpen[$i].">";
- }
- }
- return trim($output);
+ // Return parent page, null if none
+ public function getParent() {
+ $parentLocation = $this->yellow->content->getParentLocation($this->location);
+ return $this->yellow->content->find($parentLocation);
}
- // Create title from text
- public function createTextTitle($text) {
- if (preg_match("/^.*\/([\pL\d\-\_]+)/u", $text, $matches)) $text = str_replace("-", " ", ucfirst($matches[1]));
- return $text;
- }
-
- // Create random text for cryptography
- public function createSalt($length, $bcryptFormat = false) {
- $dataBuffer = $salt = "";
- $dataBufferSize = $bcryptFormat ? intval(ceil($length/4) * 3) : intval(ceil($length/2));
- if (is_string_empty($dataBuffer) && function_exists("random_bytes")) {
- $dataBuffer = @random_bytes($dataBufferSize);
- }
- if (is_string_empty($dataBuffer) && function_exists("openssl_random_pseudo_bytes")) {
- $dataBuffer = @openssl_random_pseudo_bytes($dataBufferSize);
- }
- if (strlenb($dataBuffer)==$dataBufferSize) {
- if ($bcryptFormat) {
- $salt = substrb(base64_encode($dataBuffer), 0, $length);
- $base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
- $bcrypt64Chars = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
- $salt = strtr($salt, $base64Chars, $bcrypt64Chars);
- } else {
- $salt = substrb(bin2hex($dataBuffer), 0, $length);
- }
+ // Return top-level parent page, null if none
+ public function getParentTop($homeFallback = false) {
+ $parentTopLocation = $this->yellow->content->getParentTopLocation($this->location);
+ if (!$this->yellow->content->find($parentTopLocation) && $homeFallback) {
+ $parentTopLocation = $this->yellow->content->getHomeLocation($this->location);
}
- return $salt;
+ return $this->yellow->content->find($parentTopLocation);
}
- // Create hash with random salt, bcrypt or sha256
- public function createHash($text, $algorithm, $cost = 0) {
- $hash = "";
- switch ($algorithm) {
- case "bcrypt": $prefix = sprintf("$2y$%02d$", $cost);
- $salt = $this->createSalt(22, true);
- $hash = crypt($text, $prefix.$salt);
- if (is_string_empty($salt) || strlenb($hash)!=60) $hash = "";
- break;
- case "sha256": $prefix = "$5y$";
- $salt = $this->createSalt(32);
- $hash = "$prefix$salt".hash("sha256", $salt.$text);
- if (is_string_empty($salt) || strlenb($hash)!=100) $hash = "";
- break;
- }
- return $hash;
+ // Return page collection with pages on the same level
+ public function getSiblings($showInvisible = false) {
+ $parentLocation = $this->yellow->content->getParentLocation($this->location);
+ return $this->yellow->content->getChildren($parentLocation, $showInvisible);
}
- // Verify that text matches hash
- public function verifyHash($text, $algorithm, $hash) {
- $hashCalculated = "";
- switch ($algorithm) {
- case "bcrypt": if (substrb($hash, 0, 4)=="$2y$" || substrb($hash, 0, 4)=="$2a$") {
- $hashCalculated = crypt($text, $hash);
- }
- break;
- case "sha256": if (substrb($hash, 0, 4)=="$5y$") {
- $prefix = "$5y$";
- $salt = substrb($hash, 4, 32);
- $hashCalculated = "$prefix$salt".hash("sha256", $salt.$text);
- }
- break;
- }
- return $this->verifyToken($hashCalculated, $hash);
+ // Return page collection with child pages
+ public function getChildren($showInvisible = false) {
+ return $this->yellow->content->getChildren($this->location, $showInvisible);
+ }
+
+ // Return page collection with child pages recursively
+ public function getChildrenRecursive($showInvisible = false, $levelMax = 0) {
+ return $this->yellow->content->getChildrenRecursive($this->location, $showInvisible, $levelMax);
}
- // Verify that token is not empty and identical, timing attack safe string comparison
- public function verifyToken($tokenExpected, $tokenReceived) {
- $ok = false;
- $lengthExpected = strlenb($tokenExpected);
- $lengthReceived = strlenb($tokenReceived);
- if ($lengthExpected!=0 && $lengthReceived!=0) {
- $ok = $lengthExpected==$lengthReceived;
- for ($i=0; $i<$lengthReceived; ++$i) {
- $ok &= $tokenExpected[$i<$lengthExpected ? $i : 0]==$tokenReceived[$i];
- }
- }
- return $ok;
+ // Set page collection with additional pages
+ public function setPages($key, $pages) {
+ $this->pageCollections[$key] = $pages;
+ }
+
+ // Return page collection with additional pages
+ public function getPages($key) {
+ return isset($this->pageCollections[$key]) ? $this->pageCollections[$key] : new YellowPageCollection($this->yellow);
}
- //Â Return meta data from raw data
- public function getMetaData($rawData, $key) {
- $value = "";
- if (preg_match("/^(\xEF\xBB\xBF)?\-\-\-[\r\n]+(.+?)\-\-\-[\r\n]+(.*)$/s", $rawData, $parts)) {
- $key = lcfirst($key);
- foreach ($this->getTextLines($parts[2]) as $line) {
- if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) {
- if (lcfirst($matches[1])==$key && !is_string_empty($matches[2])) {
- $value = $matches[2];
- break;
- }
- }
- }
- }
- return $value;
+ // Set shared page
+ public function setPage($key, $page) {
+ $this->sharedPages[$key] = $page;
}
- //Â Set meta data in raw data
- public function setMetaData($rawData, $key, $value) {
- if (preg_match("/^(\xEF\xBB\xBF)?\-\-\-[\r\n]+(.+?)\-\-\-[\r\n]+(.*)$/s", $rawData, $parts)) {
- $found = false;
- $key = lcfirst($key);
- $rawDataMiddle = "";
- foreach ($this->getTextLines($parts[2]) as $line) {
- if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) {
- if (lcfirst($matches[1])==$key) {
- $rawDataMiddle .= "$matches[1]: $value\n";
- $found = true;
- continue;
- }
- }
- $rawDataMiddle .= $line;
- }
- if (!$found) $rawDataMiddle .= (strposu($key, "/") ? $key : ucfirst($key)).": $value\n";
- $rawDataNew = $parts[1]."---\n".$rawDataMiddle."---\n".$parts[3];
- } else {
- $rawDataNew = $rawData;
- }
- return $rawDataNew;
+ // Return shared page
+ public function getPage($key) {
+ return isset($this->sharedPages[$key]) ? $this->sharedPages[$key] : new YellowPage($this->yellow);
}
- //Â Remove meta data in raw data
- public function unsetMetaData($rawData, $key) {
- if (preg_match("/^(\xEF\xBB\xBF)?\-\-\-[\r\n]+(.+?)\-\-\-[\r\n]+(.*)$/s", $rawData, $parts)) {
- $key = lcfirst($key);
- $rawDataMiddle = "";
- foreach ($this->getTextLines($parts[2]) as $line) {
- if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) {
- if (lcfirst($matches[1])==$key) continue;
- }
- $rawDataMiddle .= $line;
- }
- $rawDataNew = $parts[1]."---\n".$rawDataMiddle."---\n".$parts[3];
- } else {
- $rawDataNew = $rawData;
- }
- return $rawDataNew;
+ // Return page URL
+ public function getUrl() {
+ return $this->yellow->lookup->normaliseUrl($this->scheme, $this->address, $this->base, $this->location);
}
-
- // Detect server URL
- public function detectServerUrl() {
- $scheme = "http";
- if ($this->getServer("REQUEST_SCHEME")=="https" || $this->getServer("HTTPS")=="on") $scheme = "https";
- if ($this->getServer("HTTP_X_FORWARDED_PROTO")=="https") $scheme = "https";
- $address = $this->getServer("SERVER_NAME");
- $port = $this->getServer("SERVER_PORT");
- if ($port!=80 && $port!=443) $address .= ":$port";
- $base = "";
- if (preg_match("/^(.*)\/.*\.php$/", $this->getServer("SCRIPT_NAME"), $matches)) $base = $matches[1];
- return "$scheme://$address$base/";
+
+ // Return page base
+ public function getBase($multiLanguage = false) {
+ return $multiLanguage ? rtrim($this->base.$this->yellow->content->getHomeLocation($this->location), "/") : $this->base;
}
- // Detect server location
- public function detectServerLocation() {
- if (isset($_SERVER["REQUEST_URI"])) {
- $location = $_SERVER["REQUEST_URI"];
- $location = rawurldecode(($pos = strposu($location, "?")) ? substru($location, 0, $pos) : $location);
- $location = $this->normalisePath($location);
- if (substru($location, 0, 1)!="/") $location = "/".$location;
- $separator = $this->getLocationArgumentsSeparator();
- if (preg_match("/^(.*?\/)([^\/]+$separator.*)$/", $location, $matches)) {
- $_SERVER["LOCATION"] = $location = $matches[1];
- $_SERVER["LOCATION_ARGUMENTS"] = $matches[2];
- foreach (explode("/", $matches[2]) as $token) {
- if (preg_match("/^(.*?)$separator(.*)$/", $token, $matches)) {
- if (!is_string_empty($matches[1]) && !is_string_empty($matches[2])) {
- $matches[1] = str_replace(array("\x1c", "\x1d", "\x1e"), array("/", ":", "="), $matches[1]);
- $matches[2] = str_replace(array("\x1c", "\x1d", "\x1e"), array("/", ":", "="), $matches[2]);
- $_REQUEST[$matches[1]] = $matches[2];
- }
- }
- }
- } else {
- $_SERVER["LOCATION"] = $location;
- $_SERVER["LOCATION_ARGUMENTS"] = "";
- }
- }
- return $this->getServer("LOCATION");
+ // Return page location
+ public function getLocation($absoluteLocation = false) {
+ return $absoluteLocation ? $this->base.$this->location : $this->location;
}
- // Detect server sitename
- public function detectServerSitename() {
- $sitename = "Localhost";
- if (preg_match("#^(www\.)?([\w\-]+)#", $this->getServer("SERVER_NAME"), $matches)) {
- $sitename = ucfirst($matches[2]);
- }
- return $sitename;
+ // Set page request argument
+ public function setRequest($key, $value) {
+ $_REQUEST[$key] = $value;
}
- // Detect server timezone
- public function detectServerTimezone() {
- $timezone = ini_get("date.timezone");
- if (is_string_empty($timezone)) {
- if (PHP_OS=="Darwin") {
- if (preg_match("#zoneinfo/(.*)#", @readlink("/etc/localtime"), $matches)) $timezone = $matches[1];
- } else {
- if (preg_match("/^(\S+)\/(\S+)/", $this->readFile("/etc/timezone"), $matches)) $timezone = $matches[1];
+ // Return page request argument
+ public function getRequest($key) {
+ return isset($_REQUEST[$key]) ? $_REQUEST[$key] : "";
+ }
+
+ // Return page request argument, HTML encoded
+ public function getRequestHtml($key) {
+ return htmlspecialchars($this->getRequest($key));
+ }
+
+ // Set page response header
+ public function setHeader($key, $value) {
+ $this->headerData[$key] = $value;
+ }
+
+ // Return page response header
+ public function getHeader($key) {
+ return $this->isHeader($key) ? $this->headerData[$key] : "";
+ }
+
+ // Return page extra data
+ public function getExtra($name) {
+ $output = "";
+ foreach ($this->yellow->extension->data as $key=>$value) {
+ if (method_exists($value["object"], "onParsePageExtra")) {
+ $outputExtension = $value["object"]->onParsePageExtra($this, $name);
+ if (!is_null($outputExtension)) $output .= $outputExtension;
}
}
- if (!in_array($timezone, timezone_identifiers_list())) $timezone = "UTC";
- return $timezone;
+ if ($name=="header") {
+ $fileNameTheme = $this->yellow->system->get("coreThemeDirectory").$this->yellow->lookup->normaliseName($this->get("theme")).".css";
+ if (is_file($fileNameTheme)) {
+ $locationTheme = $this->yellow->system->get("coreServerBase").
+ $this->yellow->system->get("coreThemeLocation").$this->yellow->lookup->normaliseName($this->get("theme")).".css";
+ $output .= "<link rel=\"stylesheet\" type=\"text/css\" media=\"all\" href=\"$locationTheme\" />\n";
+ }
+ $fileNameScript = $this->yellow->system->get("coreThemeDirectory").$this->yellow->lookup->normaliseName($this->get("theme")).".js";
+ if (is_file($fileNameScript)) {
+ $locationScript = $this->yellow->system->get("coreServerBase").
+ $this->yellow->system->get("coreThemeLocation").$this->yellow->lookup->normaliseName($this->get("theme")).".js";
+ $output .= "<script type=\"text/javascript\" src=\"$locationScript\"></script>\n";
+ }
+ $fileNameFavicon = $this->yellow->system->get("coreThemeDirectory").$this->yellow->lookup->normaliseName($this->get("theme")).".png";
+ if (is_file($fileNameFavicon)) {
+ $locationFavicon = $this->yellow->system->get("coreServerBase").
+ $this->yellow->system->get("coreThemeLocation").$this->yellow->lookup->normaliseName($this->get("theme")).".png";
+ $output .= "<link rel=\"icon\" type=\"image/png\" href=\"$locationFavicon\" />\n";
+ }
+ }
+ return $output;
}
- // Detect server name, version and operating system
- public function detectServerInformation() {
- $name = "Unknown";
- $version = "x.x.x";
- $os = PHP_OS;
- if (preg_match("/^(\S+)\/(\S+)/", $this->getServer("SERVER_SOFTWARE"), $matches)) {
- $name = $matches[1];
- $version = $matches[2];
- } elseif (preg_match("/^(\S+)/", $this->getServer("SERVER_SOFTWARE"), $matches)) {
- $name = $matches[1];
- }
- if (PHP_SAPI=="cli" || PHP_SAPI=="cli-server") {
- $name = "Built-in";
- $version = PHP_VERSION;
+ // Set page response output
+ public function setOutput($output) {
+ $this->outputData = $output;
+ }
+
+ // Return page modification date, Unix time or HTTP format
+ public function getModified($httpFormat = false) {
+ $modified = strtotime($this->get("modified"));
+ return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($modified) : $modified;
+ }
+
+ // Set last modification date, Unix time
+ public function setLastModified($modified) {
+ $this->lastModified = max($this->lastModified, $modified);
+ }
+
+ // Return last modification date, Unix time or HTTP format
+ public function getLastModified($httpFormat = false) {
+ $lastModified = max($this->lastModified, $this->getModified(), $this->yellow->system->getModified(),
+ $this->yellow->language->getModified(), $this->yellow->extension->getModified());
+ foreach ($this->pageCollections as $pages) $lastModified = max($lastModified, $pages->getModified());
+ foreach ($this->sharedPages as $page) $lastModified = max($lastModified, $page->getModified());
+ return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($lastModified) : $lastModified;
+ }
+
+ // Return raw data for error page
+ public function getRawDataError() {
+ $statusCode = $this->statusCode;
+ $sharedLocation = $this->yellow->content->getHomeLocation($this->location)."shared/";
+ $fileNameError = $this->yellow->lookup->findFileFromContentLocation($sharedLocation, true).$this->yellow->system->get("coreContentErrorFile");
+ $fileNameError = str_replace("(.*)", $statusCode, $fileNameError);
+ $languageError = $this->yellow->lookup->findContentLanguage($this->fileName, $this->yellow->system->get("language"));
+ if (is_file($fileNameError)) {
+ $rawData = $this->yellow->toolbox->readFile($fileNameError);
+ } elseif ($this->yellow->language->isText("coreError{$statusCode}Title", $languageError)) {
+ $rawData = "---\nTitle: ".$this->yellow->language->getText("coreError{$statusCode}Title", $languageError)."\n";
+ $rawData .= "Layout: error\n---\n".$this->yellow->language->getText("coreError{$statusCode}Text", $languageError);
+ } else {
+ $rawData = "---\nTitle:".$this->yellow->toolbox->getHttpStatusFormatted($statusCode, true)."\n";
+ $rawData .= "Layout:error\n---\n".$this->errorMessage;
}
- if (PHP_OS=="Darwin") {
- $os = "Mac";
- } elseif (strtoupperu(substru(PHP_OS, 0, 3))=="WIN") {
- $os = "Windows";
+ return $rawData;
+ }
+
+ // Return page status code, number or HTTP format
+ public function getStatusCode($httpFormat = false) {
+ $statusCode = $this->statusCode;
+ if ($httpFormat) {
+ $statusCode = $this->yellow->toolbox->getHttpStatusFormatted($statusCode);
+ if (!is_string_empty($this->errorMessage)) $statusCode .= ": ".$this->errorMessage;
}
- return array($name, $version, $os);
+ return $statusCode;
}
- // Detect browser language
- public function detectBrowserLanguage($languages, $languageDefault) {
- $languageFound = $languageDefault;
- foreach (preg_split("/\s*,\s*/", $this->getServer("HTTP_ACCEPT_LANGUAGE")) as $text) {
- list($language, $dummy) = $this->getTextList($text, ";", 2);
- if (!is_string_empty($language) && in_array($language, $languages)) {
- $languageFound = $language;
- break;
+ // Respond with status code, no page content
+ public function status($statusCode, $location = "") {
+ if ($statusCode>0 && !$this->isExisting("pageClean")) {
+ $this->statusCode = $statusCode;
+ $this->lastModified = 0;
+ $this->headerData = array();
+ if (!is_string_empty($location)) {
+ $this->setHeader("Location", $location);
+ $this->setHeader("Cache-Control", "no-cache, no-store");
}
+ $this->set("pageClean", (string)$statusCode);
}
- return $languageFound;
}
- // Detect terminal width and height
- public function detectTerminalInformation() {
- $width = $height = 0;
- if (strtoupperu(substru(PHP_OS, 0, 3))=="WIN") {
- exec("powershell \$Host.UI.RawUI.WindowSize.Width", $outputLines, $returnStatus);
- if ($returnStatus==0 && !is_array_empty($outputLines)) {
- $width = intval(end($outputLines));
- }
- exec("powershell \$Host.UI.RawUI.WindowSize.Height", $outputLines, $returnStatus);
- if ($returnStatus==0 && !is_array_empty($outputLines)) {
- $height = intval(end($outputLines));
- }
- } else {
- exec("stty size", $outputLines, $returnStatus);
- if ($returnStatus==0 && preg_match("/^(\d+)\s+(\d+)/", implode("\n", $outputLines), $matches)) {
- $width = intval($matches[2]);
- $height = intval($matches[1]);
- }
+ // Respond with error page
+ public function error($statusCode, $errorMessage = "") {
+ if ($statusCode>=400 && is_string_empty($this->errorMessage)) {
+ $this->statusCode = $statusCode;
+ $this->errorMessage = is_string_empty($errorMessage) ? "Page error!" : $errorMessage;
}
- return array($width, $height);
}
- // Detect image width, height, orientation and type for GIF/JPG/PNG/SVG
- public function detectImageInformation($fileName, $fileType = "") {
- $width = $height = $orientation = 0;
- $type = "";
- $fileHandle = @fopen($fileName, "rb");
- if ($fileHandle) {
- if (is_string_empty($fileType)) $fileType = $this->getFileType($fileName);
- if ($fileType=="gif") {
- $dataSignature = fread($fileHandle, 6);
- $dataHeader = fread($fileHandle, 7);
- if (!feof($fileHandle) && ($dataSignature=="GIF87a" || $dataSignature=="GIF89a")) {
- $width = (ord($dataHeader[1])<<8) + ord($dataHeader[0]);
- $height = (ord($dataHeader[3])<<8) + ord($dataHeader[2]);
- $type = $fileType;
- }
- } elseif ($fileType=="jpg") {
- $dataBufferSizeMax = filesize($fileName);
- $dataBufferSize = min($dataBufferSizeMax, 4096);
- if ($dataBufferSize) $dataBuffer = fread($fileHandle, $dataBufferSize);
- $dataSignature = substrb($dataBuffer, 0, 4);
- if (!feof($fileHandle) && ($dataSignature=="\xff\xd8\xff\xe0" || $dataSignature=="\xff\xd8\xff\xe1")) {
- for ($pos=2; $pos+8<$dataBufferSize; $pos+=$length) {
- if ($dataBuffer[$pos]!="\xff") break;
- $dataMarker = $dataBuffer[$pos+1];
- if ($dataMarker=="\xe1") {
- $orientation = $this->getImageOrientationFromBuffer($dataBuffer, $pos+4, $dataBufferSize);
- }
- if (($dataMarker>="\xc0" && $dataMarker<="\xc3") ||
- ($dataMarker>="\xc5" && $dataMarker<="\xc7") ||
- ($dataMarker>="\xc9" && $dataMarker<="\xcb") ||
- ($dataMarker>="\xcd" && $dataMarker<="\xcf")) {
- $width = (ord($dataBuffer[$pos+7])<<8) + ord($dataBuffer[$pos+8]);
- $height = (ord($dataBuffer[$pos+5])<<8) + ord($dataBuffer[$pos+6]);
- $type = $fileType;
- break;
- }
- $length = (ord($dataBuffer[$pos+2])<<8) + ord($dataBuffer[$pos+3]) + 2;
- while ($pos+$length+8>=$dataBufferSize) {
- if ($dataBufferSize==$dataBufferSizeMax) break;
- $dataBufferDiff = min($dataBufferSizeMax, $dataBufferSize*2) - $dataBufferSize;
- $dataBufferSize += $dataBufferDiff;
- $dataBufferChunk = fread($fileHandle, $dataBufferDiff);
- if (feof($fileHandle) || $dataBufferChunk===false) {
- $dataBufferSize = 0;
- break;
- }
- $dataBuffer .= $dataBufferChunk;
- }
+ // Check if page is available
+ public function isAvailable() {
+ return $this->available;
+ }
+
+ // Check if page is visible
+ public function isVisible() {
+ return $this->visible;
+ }
+
+ // Check if page is within current HTTP request
+ public function isActive() {
+ return $this->active;
+ }
+
+ // Check if page is cacheable
+ public function isCacheable() {
+ return $this->cacheable;
+ }
+
+ // Check if page with error
+ public function isError() {
+ return $this->statusCode>=400;
+ }
+
+ // Check if page setting exists
+ public function isExisting($key) {
+ return isset($this->metaData[$key]);
+ }
+
+ // Check if request argument exists
+ public function isRequest($key) {
+ return isset($_REQUEST[$key]);
+ }
+
+ // Check if response header exists
+ public function isHeader($key) {
+ return isset($this->headerData[$key]);
+ }
+
+ // Check if shared page exists
+ public function isPage($key) {
+ return isset($this->sharedPages[$key]);
+ }
+}
+
+class YellowPageCollection extends ArrayObject {
+ public $yellow; // access to API
+ public $filterValue; // current page filter value
+ public $paginationNumber; // current page number in pagination
+ public $paginationCount; // highest page number in pagination
+
+ public function __construct($yellow) {
+ parent::__construct(array());
+ $this->yellow = $yellow;
+ }
+
+ // Append to end of page collection
+ #[\ReturnTypeWillChange]
+ public function append($page) {
+ parent::append($page);
+ }
+
+ // Prepend to start of page collection
+ #[\ReturnTypeWillChange]
+ public function prepend($page) {
+ $array = $this->getArrayCopy();
+ array_unshift($array, $page);
+ $this->exchangeArray($array);
+ }
+
+ // Filter page collection by page setting
+ public function filter($key, $value, $exactMatch = true): YellowPageCollection {
+ $array = array();
+ $value = str_replace(" ", "-", strtoloweru($value));
+ $valueLength = strlenu($value);
+ $this->filterValue = "";
+ foreach ($this->getArrayCopy() as $page) {
+ if ($page->isExisting($key)) {
+ foreach (preg_split("/\s*,\s*/", $page->get($key)) as $pageValue) {
+ $pageValueLength = $exactMatch ? strlenu($pageValue) : $valueLength;
+ if ($value==substru(str_replace(" ", "-", strtoloweru($pageValue)), 0, $pageValueLength)) {
+ if (is_string_empty($this->filterValue)) $this->filterValue = substru($pageValue, 0, $pageValueLength);
+ array_push($array, $page);
+ break;
}
}
- } elseif ($fileType=="png") {
- $dataSignature = fread($fileHandle, 8);
- $dataHeader = fread($fileHandle, 16);
- if (!feof($fileHandle) && $dataSignature=="\x89PNG\r\n\x1a\n") {
- $width = (ord($dataHeader[10])<<8) + ord($dataHeader[11]);
- $height = (ord($dataHeader[14])<<8) + ord($dataHeader[15]);
- $type = $fileType;
- }
- } elseif ($fileType=="svg") {
- $dataBufferSizeMax = filesize($fileName);
- $dataBufferSize = min($dataBufferSizeMax, 4096);
- if ($dataBufferSize) $dataBuffer = fread($fileHandle, $dataBufferSize);
- if (!feof($fileHandle) && preg_match("/<svg(\s.*?)>/s", $dataBuffer, $matches)) {
- if (preg_match("/\swidth=\"(\d+)\"/s", $matches[1], $tokens)) $width = $tokens[1];
- if (preg_match("/\sheight=\"(\d+)\"/s", $matches[1], $tokens)) $height = $tokens[1];
- $type = $fileType;
- }
}
- fclose($fileHandle);
}
- return array($width, $height, $orientation, $type);
+ $this->exchangeArray($array);
+ return $this;
}
- // Return image orientation from Exif
- public function getImageOrientationFromBuffer($dataBuffer, $pos, $size) {
- $orientation = 0;
- $dataSignature = substrb($dataBuffer, $pos, 6);
- if ($dataSignature=="\x45\x78\x69\x66\x00\x00" && $pos+14<=$size) {
- $startPos = $pos+6;
- $bigEndian = $dataBuffer[$startPos]=="M";
- $ifdOffset = $this->getLongFromBuffer($dataBuffer, $startPos+4, $bigEndian);
- $ifdStartPos = $startPos+$ifdOffset;
- $ifdCount = $ifdStartPos+2<=$size ? $this->getShortFromBuffer($dataBuffer, $ifdStartPos, $bigEndian) : 0;
- $pos = $ifdStartPos+2;
- while ($ifdCount && $pos+12<=$size) {
- $ifdTag = $this->getShortFromBuffer($dataBuffer, $pos, $bigEndian);
- $ifdFormat = $this->getShortFromBuffer($dataBuffer, $pos+2, $bigEndian);
- if ($ifdTag==0x8769 && $ifdFormat==4) {
- $ifdOffset = $this->getLongFromBuffer($dataBuffer, $pos+8, $bigEndian);
- $ifdStartPos = $startPos+$ifdOffset;
- $ifdCount = $ifdStartPos+2<=$size ? $this->getShortFromBuffer($dataBuffer, $ifdStartPos, $bigEndian) : 0;
- $pos = $ifdStartPos+2;
- continue;
- }
- if ($ifdTag==0x0112 && $ifdFormat==3) {
- $orientation = $this->getShortFromBuffer($dataBuffer, $pos+8, $bigEndian);
- break;
+ // Filter page collection by location or file
+ public function match($regex = "/.*/", $filterByLocation = true): YellowPageCollection {
+ $array = array();
+ $this->filterValue = $regex;
+ foreach ($this->getArrayCopy() as $page) {
+ if (preg_match($regex, $filterByLocation ? $page->location : $page->fileName)) array_push($array, $page);
+ }
+ $this->exchangeArray($array);
+ return $this;
+ }
+
+ // Sort page collection by page setting
+ public function sort($key, $ascendingOrder = true): YellowPageCollection {
+ $array = $this->getArrayCopy();
+ $sortIndex = 0;
+ foreach ($array as $page) {
+ $page->set("sortIndex", ++$sortIndex);
+ }
+ $callback = function ($a, $b) use ($key, $ascendingOrder) {
+ $result = $ascendingOrder ?
+ strnatcasecmp($a->get($key), $b->get($key)) :
+ strnatcasecmp($b->get($key), $a->get($key));
+ return $result==0 ? $a->get("sortIndex") - $b->get("sortIndex") : $result;
+ };
+ usort($array, $callback);
+ $this->exchangeArray($array);
+ return $this;
+ }
+
+ // Sort page collection by settings similarity
+ public function similar($page, $ascendingOrder = false): YellowPageCollection {
+ $location = $page->location;
+ $keywords = strtoloweru($page->get("title").",".$page->get("tag").",".$page->get("author"));
+ $tokens = array_unique(array_filter(preg_split("/[,\s\(\)\+\-]/", $keywords), "strlen"));
+ if (!is_array_empty($tokens)) {
+ $array = array();
+ foreach ($this->getArrayCopy() as $page) {
+ $sortScore = 0;
+ foreach ($tokens as $token) {
+ if (stristr($page->get("title"), $token)) $sortScore += 50;
+ if (stristr($page->get("tag"), $token)) $sortScore += 5;
+ if (stristr($page->get("author"), $token)) $sortScore += 2;
+ }
+ if ($page->location!=$location) {
+ $page->set("sortScore", $sortScore);
+ array_push($array, $page);
}
- --$ifdCount;
- $pos += 12;
}
+ $this->exchangeArray($array);
+ $this->sort("modified", $ascendingOrder)->sort("sortScore", $ascendingOrder);
}
- return $orientation;
+ return $this;
+ }
+
+ // Calculate union, merge page collection
+ public function merge($input): YellowPageCollection {
+ $this->exchangeArray(array_merge($this->getArrayCopy(), (array)$input));
+ return $this;
}
- // Return unsigned short value from buffer
- public function getShortFromBuffer($dataBuffer, $pos, $bigEndian) {
- if ($bigEndian) {
- $value = (ord($dataBuffer[$pos])<<8) + ord($dataBuffer[$pos+1]);
- } else {
- $value = (ord($dataBuffer[$pos+1])<<8) + ord($dataBuffer[$pos]);
- }
- return $value;
+ // Calculate intersection, remove pages that are not present in another page collection
+ public function intersect($input): YellowPageCollection {
+ $callback = function ($a, $b) {
+ return strcmp(spl_object_hash($a), spl_object_hash($b));
+ };
+ $this->exchangeArray(array_uintersect($this->getArrayCopy(), (array)$input, $callback));
+ return $this;
+ }
+
+ // Calculate difference, remove pages that are present in another page collection
+ public function diff($input): YellowPageCollection {
+ $callback = function ($a, $b) {
+ return strcmp(spl_object_hash($a), spl_object_hash($b));
+ };
+ $this->exchangeArray(array_udiff($this->getArrayCopy(), (array)$input, $callback));
+ return $this;
}
- // Return unsigned long value from buffer
- public function getLongFromBuffer($dataBuffer, $pos, $bigEndian) {
- if ($bigEndian) {
- $value = (ord($dataBuffer[$pos])<<24) + (ord($dataBuffer[$pos+1])<<16) +
- (ord($dataBuffer[$pos+2])<<8) + ord($dataBuffer[$pos+3]);
- } else {
- $value = (ord($dataBuffer[$pos+3])<<24) + (ord($dataBuffer[$pos+2])<<16) +
- (ord($dataBuffer[$pos+1])<<8) + ord($dataBuffer[$pos]);
- }
- return $value;
+ // Limit the number of pages in page collection
+ public function limit($pagesMax): YellowPageCollection {
+ $this->exchangeArray(array_slice($this->getArrayCopy(), 0, $pagesMax));
+ return $this;
}
- // Normalise location arguments
- public function normaliseArguments($text, $appendSlash = true, $filterStrict = true) {
- if ($appendSlash) $text .= "/";
- if ($filterStrict) $text = str_replace(" ", "-", strtoloweru($text));
- $text = str_replace(":", $this->getLocationArgumentsSeparator(), $text);
- return str_replace(array("%2F","%3A","%3D"), array("/",":","="), rawurlencode($text));
+ // Reverse page collection
+ public function reverse(): YellowPageCollection {
+ $this->exchangeArray(array_reverse($this->getArrayCopy()));
+ return $this;
}
- // Normalise elements and attributes in HTML/SVG data
- public function normaliseData($text, $type = "html", $filterStrict = true) {
- $output = "";
- $elementsHtml = array(
- "a", "abbr", "acronym", "address", "area", "article", "aside", "audio", "b", "bdi", "bdo", "big", "blink", "blockquote", "body", "br", "button", "canvas", "caption", "center", "cite", "code", "col", "colgroup", "content", "data", "datalist", "dd", "decorator", "del", "details", "dfn", "dir", "div", "dl", "dt", "element", "em", "fieldset", "figcaption", "figure", "font", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hgroup", "hr", "html", "i", "iframe", "image", "img", "input", "ins", "kbd", "label", "legend", "li", "main", "map", "mark", "marquee", "menu", "menuitem", "meta", "meter", "nav", "nobr", "ol", "optgroup", "option", "output", "p", "picture", "pre", "progress", "q", "rp", "rt", "ruby", "s", "samp", "section", "select", "shadow", "small", "source", "spacer", "span", "strike", "strong", "style", "sub", "summary", "sup", "table", "tbody", "td", "template", "textarea", "tfoot", "th", "thead", "time", "title", "tr", "track", "tt", "u", "ul", "var", "video", "wbr");
- $elementsSvg = array(
- "svg", "altglyph", "altglyphdef", "altglyphitem", "animatecolor", "animatemotion", "animatetransform", "circle", "clippath", "defs", "desc", "ellipse", "feblend", "fecolormatrix", "fecomponenttransfer", "fecomposite", "feconvolvematrix", "fediffuselighting", "fedisplacementmap", "fedistantlight", "feflood", "fefunca", "fefuncb", "fefuncg", "fefuncr", "fegaussianblur", "femerge", "femergenode", "femorphology", "feoffset", "fepointlight", "fespecularlighting", "fespotlight", "fetile", "feturbulence", "filter", "font", "g", "glyph", "glyphref", "hkern", "image", "line", "lineargradient", "marker", "mask", "metadata", "mpath", "path", "pattern", "polygon", "polyline", "radialgradient", "rect", "stop", "switch", "symbol", "text", "textpath", "title", "tref", "tspan", "use", "view", "vkern");
- $attributesHtml = array(
- "accept", "action", "align", "allow", "allowfullscreen", "alt", "autocomplete", "autoplay", "background", "bgcolor", "border", "cellpadding", "cellspacing", "charset", "checked", "cite", "class", "clear", "color", "cols", "colspan", "content", "contenteditable", "controls", "coords", "crossorigin", "datetime", "default", "dir", "disabled", "download", "enctype", "face", "for", "frameborder", "headers", "height", "hidden", "high", "href", "hreflang", "id", "integrity", "ismap", "label", "lang", "list", "loading", "loop", "low", "max", "maxlength", "media", "method", "min", "multiple", "muted", "name", "noshade", "novalidate", "nowrap", "open", "optimum", "pattern", "placeholder", "poster", "prefix", "preload", "property", "pubdate", "radiogroup", "readonly", "rel", "required", "rev", "reversed", "role", "rows", "rowspan", "spellcheck", "scope", "selected", "shape", "size", "sizes", "span", "srclang", "start", "src", "srcset", "step", "style", "summary", "tabindex", "target", "title", "type", "usemap", "valign", "value", "width", "xmlns");
- $attributesSvg = array(
- "accent-height", "accumulate", "additivive", "alignment-baseline", "ascent", "attributename", "attributetype", "azimuth", "basefrequency", "baseline-shift", "begin", "bias", "by", "class", "clip", "clip-path", "clip-rule", "color", "color-interpolation", "color-interpolation-filters", "color-profile", "color-rendering", "cx", "cy", "d", "datenstrom", "dx", "dy", "diffuseconstant", "direction", "display", "divisor", "dur", "edgemode", "elevation", "end", "fill", "fill-opacity", "fill-rule", "filter", "flood-color", "flood-opacity", "font-family", "font-size", "font-size-adjust", "font-stretch", "font-style", "font-variant", "font-weight", "fx", "fy", "g1", "g2", "glyph-name", "glyphref", "gradientunits", "gradienttransform", "height", "href", "id", "image-rendering", "in", "in2", "k", "k1", "k2", "k3", "k4", "kerning", "keypoints", "keysplines", "keytimes", "lang", "lengthadjust", "letter-spacing", "kernelmatrix", "kernelunitlength", "lighting-color", "local", "marker-end", "marker-mid", "marker-start", "markerheight", "markerunits", "markerwidth", "maskcontentunits", "maskunits", "max", "mask", "media", "method", "mode", "min", "name", "numoctaves", "offset", "operator", "opacity", "order", "orient", "orientation", "origin", "overflow", "paint-order", "path", "pathlength", "patterncontentunits", "patterntransform", "patternunits", "points", "preservealpha", "preserveaspectratio", "r", "rx", "ry", "radius", "refx", "refy", "repeatcount", "repeatdur", "restart", "result", "rotate", "scale", "seed", "shape-rendering", "specularconstant", "specularexponent", "spreadmethod", "stddeviation", "stitchtiles", "stop-color", "stop-opacity", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke", "stroke-width", "style", "surfacescale", "tabindex", "targetx", "targety", "transform", "text-anchor", "text-decoration", "text-rendering", "textlength", "type", "u1", "u2", "unicode", "values", "viewbox", "visibility", "vert-adv-y", "vert-origin-x", "vert-origin-y", "width", "word-spacing", "wrap", "writing-mode", "xchannelselector", "ychannelselector", "x", "x1", "x2", "xlink:href", "xml:id", "xml:space", "xmlns", "y", "y1", "y2", "z", "zoomandpan");
- $attributesAllowEmptyString = array("alt", "value");
- $elementsSafe = $elementsHtml;
- $attributesSafe = $attributesHtml;
- if ($type=="svg") {
- $elementsSafe = array_merge($elementsHtml, $elementsSvg);
- $attributesSafe = array_merge($attributesHtml, $attributesSvg);
- }
- $offsetBytes = 0;
- while (true) {
- $elementFound = preg_match("/<(\/?)([\!\?\w]+)(.*?)(\/?)>/s", $text, $matches, PREG_OFFSET_CAPTURE, $offsetBytes);
- $elementBefore = $elementFound ? substrb($text, $offsetBytes, $matches[0][1] - $offsetBytes) : substrb($text, $offsetBytes);
- $elementStart = $elementFound ? $matches[1][0] : "";
- $elementName = $elementFound ? $matches[2][0]: "";
- $elementMiddle = $elementFound ? $matches[3][0]: "";
- $elementEnd = $elementFound ? $matches[4][0]: "";
- $output .= $elementBefore;
- if (substrb($elementName, 0, 1)=="!") {
- $output .= "<$elementName$elementMiddle>";
- } elseif (in_array(strtolower($elementName), $elementsSafe)) {
- $elementAttributes = $this->getTextAttributes($elementMiddle, $attributesAllowEmptyString);
- foreach ($elementAttributes as $key=>$value) {
- if (!in_array(strtolower($key), $attributesSafe) && !preg_match("/^(aria|data)-/i", $key)) {
- unset($elementAttributes[$key]);
- }
- }
- if ($filterStrict) {
- $href = isset($elementAttributes["href"]) ? $elementAttributes["href"] : "";
- if (preg_match("/^\w+:/", $href) && !$this->isSafeUrl($href)) {
- $elementAttributes["href"] = "error-xss-filter";
- }
- $href = isset($elementAttributes["xlink:href"]) ? $elementAttributes["xlink:href"] : "";
- if (preg_match("/^\w+:/", $href) && !$this->isSafeUrl($href)) {
- $elementAttributes["xlink:href"] = "error-xss-filter";
- }
- }
- $output .= "<$elementStart$elementName";
- foreach ($elementAttributes as $key=>$value) $output .= " $key=\"$value\"";
- if (!is_string_empty($elementEnd)) $output .= " ";
- $output .= "$elementEnd>";
- }
- if (!$elementFound) break;
- $offsetBytes = $matches[0][1] + strlenb($matches[0][0]);
- }
- return $output;
+ // Randomize page collection
+ public function shuffle(): YellowPageCollection {
+ $array = $this->getArrayCopy();
+ shuffle($array);
+ $this->exchangeArray($array);
+ return $this;
}
- // Normalise relative path tokens
- public function normalisePath($text) {
- $textFiltered = "";
- $textLength = strlenb($text);
- for ($pos=0; $pos<$textLength; ++$pos) {
- if ($text[$pos]=="." && ($pos==0 || $text[$pos-1]=="/")) {
- while ($text[$pos]==".") ++$pos;
- if ($text[$pos]=="/") ++$pos;
- --$pos;
- continue;
+ // Paginate page collection
+ public function paginate($limit): YellowPageCollection {
+ if (!$this->isPagination() && $limit!=0) {
+ $this->paginationNumber = 1;
+ $this->paginationCount = ceil($this->count() / $limit);
+ if ($this->yellow->page->isRequest("page")) {
+ $this->paginationNumber = intval($this->yellow->page->getRequest("page"));
+ }
+ if ($this->paginationNumber<0 || $this->paginationNumber>$this->paginationCount) $this->paginationNumber = 0;
+ if ($this->paginationNumber) {
+ $this->exchangeArray(array_slice($this->getArrayCopy(), ($this->paginationNumber - 1) * $limit, $limit));
+ } else {
+ $this->yellow->page->error(404);
}
- $textFiltered .= $text[$pos];
}
- return $textFiltered;
+ return $this;
}
- // Normalise text lines, convert line endings
- public function normaliseLines($text, $endOfLine = "lf") {
- if ($endOfLine=="lf") {
- $text = preg_replace("/\R/u", "\n", $text);
- } else {
- $text = preg_replace("/\R/u", "\r\n", $text);
+ // Return current page number in pagination
+ public function getPaginationNumber() {
+ return $this->paginationNumber;
+ }
+
+ // Return highest page number in pagination
+ public function getPaginationCount() {
+ return $this->paginationCount;
+ }
+
+ // Return location for a page in pagination
+ public function getPaginationLocation($absoluteLocation = true, $pageNumber = 1) {
+ $location = $locationArguments = "";
+ if ($pageNumber>=1 && $pageNumber<=$this->paginationCount) {
+ $location = $this->yellow->page->getLocation($absoluteLocation);
+ $locationArguments = $this->yellow->toolbox->getLocationArgumentsNew("page", $pageNumber>1 ? "$pageNumber" : "");
}
- return $text;
+ return $location.$locationArguments;
}
- // Normalise text into UTF-8 NFC
- public function normaliseUnicode($text) {
- if (PHP_OS=="Darwin" && !mb_check_encoding($text, "ASCII")) {
- $utf8nfc = preg_match("//u", $text) && !preg_match("/[^\\x00-\\x{2FF}]/u", $text);
- if (!$utf8nfc) $text = iconv("UTF-8-MAC", "UTF-8", $text);
+ // Return location for previous page in pagination
+ public function getPaginationPrevious($absoluteLocation = true) {
+ $pageNumber = $this->paginationNumber-1;
+ return $this->getPaginationLocation($absoluteLocation, $pageNumber);
+ }
+
+ // Return location for next page in pagination
+ public function getPaginationNext($absoluteLocation = true) {
+ $pageNumber = $this->paginationNumber+1;
+ return $this->getPaginationLocation($absoluteLocation, $pageNumber);
+ }
+
+ // Return current page number in collection
+ public function getPageNumber($page) {
+ $pageNumber = 0;
+ foreach ($this->getIterator() as $key=>$value) {
+ if ($page->getLocation()==$value->getLocation()) {
+ $pageNumber = $key+1;
+ break;
+ }
}
- return $text;
+ return $pageNumber;
}
- // Start timer
- public function timerStart(&$time) {
- $time = microtime(true);
+ // Return page in collection, null if none
+ public function getPage($pageNumber = 1) {
+ return ($pageNumber>=1 && $pageNumber<=$this->count()) ? $this->offsetGet($pageNumber-1) : null;
}
- // Stop timer and calculate elapsed time in milliseconds
- public function timerStop(&$time) {
- $time = intval((microtime(true)-$time) * 1000);
+ // Return previous page in collection, null if none
+ public function getPagePrevious($page) {
+ $pageNumber = $this->getPageNumber($page)-1;
+ return $this->getPage($pageNumber);
}
- // Check if file is a well-known file type
- public function isSafeFile($fileName) {
- return preg_match("/\.(css|gif|ico|js|jpg|png|svg|woff|woff2)$/", $fileName);
+ // Return next page in collection, null if none
+ public function getPageNext($page) {
+ $pageNumber = $this->getPageNumber($page)+1;
+ return $this->getPage($pageNumber);
}
- // Check if URL is a well-known URL scheme
- public function isSafeUrl($url) {
- return preg_match("/^(http|https|ftp|mailto|tel):/", $url);
+ // Return current page filter
+ public function getFilter() {
+ return $this->filterValue;
}
- // Check if there are location arguments in current HTTP request
- public function isLocationArguments($location = "") {
- if (is_string_empty($location)) $location = $this->getServer("LOCATION").$this->getServer("LOCATION_ARGUMENTS");
- $separator = $this->getLocationArgumentsSeparator();
- return preg_match("/[^\/]+$separator.*$/", $location);
+ // Return page collection modification date, Unix time or HTTP format
+ public function getModified($httpFormat = false) {
+ $modified = 0;
+ foreach ($this->getIterator() as $page) {
+ $modified = max($modified, $page->getModified());
+ }
+ return $httpFormat ? $this->yellow->toolbox->getHttpDateFormatted($modified) : $modified;
}
- // Check if there are pagination arguments in current HTTP request
- public function isLocationArgumentsPagination($location) {
- $separator = $this->getLocationArgumentsSeparator();
- return preg_match("/^(.*\/)?page$separator.*$/", $location);
+ // Check if there is a pagination
+ public function isPagination() {
+ return $this->paginationCount>1;
}
-
- // Check if unmodified since last HTTP request
- public function isNotModified($lastModifiedFormatted) {
- return $this->getServer("HTTP_IF_MODIFIED_SINCE")==$lastModifiedFormatted;
+
+ // Check if page collection is empty
+ public function isEmpty() {
+ return empty($this->getArrayCopy());
}
}
-
+
class YellowArray extends ArrayObject {
public function __construct($array = []) {
parent::__construct($array);
diff --git a/system/extensions/edit.php b/system/extensions/edit.php
@@ -2,7 +2,7 @@
// Edit extension, https://github.com/annaesvensson/yellow-edit
class YellowEdit {
- const VERSION = "0.8.68";
+ const VERSION = "0.8.69";
public $yellow; // access to API
public $response; // web response
public $merge; // text merge
@@ -1075,7 +1075,7 @@ class YellowEditResponse {
// Return new page
public function getPageNew($scheme, $address, $base, $location, $fileName, $rawData, $endOfLine) {
- $rawData = $this->yellow->toolbox->normaliseLines($rawData, $endOfLine);
+ $rawData = $this->yellow->lookup->normaliseLines($rawData, $endOfLine);
$page = new YellowPage($this->yellow);
$page->setRequestInformation($scheme, $address, $base, $location, $fileName, false);
$page->parseMeta($rawData);
@@ -1085,7 +1085,7 @@ class YellowEditResponse {
$page->fileName = $this->getPageNewFile($page->location, $page->fileName, $page->get("published"));
while ($this->yellow->content->find($page->location) || is_string_empty($page->fileName)) {
$page->rawData = $this->yellow->toolbox->setMetaData($page->rawData, "title", $this->getTitleNext($page->rawData));
- $page->rawData = $this->yellow->toolbox->normaliseLines($page->rawData, $endOfLine);
+ $page->rawData = $this->yellow->lookup->normaliseLines($page->rawData, $endOfLine);
$page->location = $this->getPageNewLocation($page->rawData, $page->location, $page->get("editNewLocation"));
$page->fileName = $this->getPageNewFile($page->location, $page->fileName, $page->get("published"));
if (++$pageCounter>999) break;
@@ -1104,9 +1104,9 @@ class YellowEditResponse {
// Return modified page
public function getPageEdit($scheme, $address, $base, $location, $fileName, $rawDataSource, $rawDataEdit, $rawDataFile, $endOfLine) {
- $rawDataSource = $this->yellow->toolbox->normaliseLines($rawDataSource, $endOfLine);
- $rawDataEdit = $this->yellow->toolbox->normaliseLines($rawDataEdit, $endOfLine);
- $rawDataFile = $this->yellow->toolbox->normaliseLines($rawDataFile, $endOfLine);
+ $rawDataSource = $this->yellow->lookup->normaliseLines($rawDataSource, $endOfLine);
+ $rawDataEdit = $this->yellow->lookup->normaliseLines($rawDataEdit, $endOfLine);
+ $rawDataFile = $this->yellow->lookup->normaliseLines($rawDataFile, $endOfLine);
$rawData = $this->extension->merge->merge($rawDataSource, $rawDataEdit, $rawDataFile);
$page = new YellowPage($this->yellow);
$page->setRequestInformation($scheme, $address, $base, $location, $fileName, false);
@@ -1132,7 +1132,7 @@ class YellowEditResponse {
// Return deleted page
public function getPageDelete($scheme, $address, $base, $location, $fileName, $rawData, $endOfLine) {
- $rawData = $this->yellow->toolbox->normaliseLines($rawData, $endOfLine);
+ $rawData = $this->yellow->lookup->normaliseLines($rawData, $endOfLine);
$page = new YellowPage($this->yellow);
$page->setRequestInformation($scheme, $address, $base, $location, $fileName, false);
$page->parseMeta($rawData);
@@ -1157,7 +1157,7 @@ class YellowEditResponse {
// Return preview page
public function getPagePreview($scheme, $address, $base, $location, $fileName, $rawData, $endOfLine) {
- $rawData = $this->yellow->toolbox->normaliseLines($rawData, $endOfLine);
+ $rawData = $this->yellow->lookup->normaliseLines($rawData, $endOfLine);
$page = new YellowPage($this->yellow);
$page->setRequestInformation($scheme, $address, $base, $location, $fileName, false);
$page->parseMeta($rawData, 200);
@@ -1181,7 +1181,7 @@ class YellowEditResponse {
$file->set("type", $this->yellow->toolbox->getFileType($fileNameShort));
if ($file->get("type")=="html" || $file->get("type")=="svg") {
$fileData = $this->yellow->toolbox->readFile($fileNameTemp);
- $fileData = $this->yellow->toolbox->normaliseData($fileData, $file->get("type"));
+ $fileData = $this->yellow->lookup->normaliseData($fileData, $file->get("type"));
if (is_string_empty($fileData) || !$this->yellow->toolbox->createFile($fileNameTemp, $fileData)) {
$file->error(500, "Can't write file '$fileNameTemp'!");
}
@@ -1581,7 +1581,7 @@ class YellowEditResponse {
$expire = time() + 60*60*24;
$actionToken = $this->createActionToken($email, $action, $expire);
$locationArguments = "/action:$action/email:$email/expire:$expire/language:$userLanguage/actiontoken:$actionToken/";
- $url = "$scheme://$address$base".$this->yellow->toolbox->normaliseArguments($locationArguments, false, false);
+ $url = "$scheme://$address$base".$this->yellow->lookup->normaliseArguments($locationArguments, false, false);
}
$prefix = "edit".ucfirst($action);
$message = $this->yellow->language->getText("{$prefix}Message", $userLanguage);
diff --git a/system/extensions/install-blog.bin b/system/extensions/install-blog.bin
Binary files differ.
diff --git a/system/extensions/install-wiki.bin b/system/extensions/install-wiki.bin
Binary files differ.
diff --git a/system/extensions/install.php b/system/extensions/install.php
@@ -98,7 +98,7 @@ class YellowInstall {
if ($statusCode==200) $statusCode = 0;
if ($statusCode>=400) {
echo "ERROR installing files: ".$this->yellow->page->errorMessage."\n";
- echo "Detected ZIP-files, 0 extensions installed. Please run command again.\n";
+ echo "Detected ZIP files, 0 extensions installed. Please run command again.\n";
}
}
return $statusCode;
diff --git a/system/extensions/markdown.php b/system/extensions/markdown.php
@@ -2,7 +2,7 @@
// Markdown extension, https://github.com/annaesvensson/yellow-markdown
class YellowMarkdown {
- const VERSION = "0.8.23";
+ const VERSION = "0.8.24";
public $yellow; // access to API
// Handle initialisation
@@ -14,7 +14,7 @@ class YellowMarkdown {
public function onParseContentRaw($page, $text) {
$markdown = new YellowMarkdownParser($this->yellow, $page);
$text = $markdown->transform($text);
- $text = $this->yellow->toolbox->normaliseData($text, "html");
+ $text = $this->yellow->lookup->normaliseData($text, "html");
return $text;
}
}
diff --git a/system/extensions/update-current.ini b/system/extensions/update-current.ini
@@ -11,11 +11,11 @@ Tag: feature
system/extensions/command.php: command.php, create, update
Extension: Core
-Version: 0.8.100
+Version: 0.8.101
Description: Core functionality of the website.
DocumentationUrl: https://github.com/annaesvensson/yellow-core
DownloadUrl: https://github.com/datenstrom/yellow-extensions/raw/main/downloads/core.zip
-Published: 2022-11-05 11:21:33
+Published: 2022-11-10 15:28:28
Developer: Mark Seuffert, David Fehrmann
Tag: feature
system/extensions/core.php: core.php, create, update
@@ -27,11 +27,11 @@ system/layouts/navigation.html: navigation.html, create, update, careful
system/layouts/pagination.html: pagination.html, create, update, careful
Extension: Edit
-Version: 0.8.68
+Version: 0.8.69
Description: Edit your website in a web browser.
DocumentationUrl: https://github.com/annaesvensson/yellow-edit
DownloadUrl: https://github.com/datenstrom/yellow-extensions/raw/main/downloads/edit.zip
-Published: 2022-11-04 17:39:21
+Published: 2022-11-10 14:21:05
Developer: Anna Svensson
Tag: feature
system/extensions/edit.php: edit.php, create, update
@@ -76,11 +76,11 @@ media/downloads/yellow.pdf: yellow.pdf, create
./robots.txt: robots.txt, create
Extension: Markdown
-Version: 0.8.23
+Version: 0.8.24
Description: Text formatting for humans.
DocumentationUrl: https://github.com/annaesvensson/yellow-markdown
DownloadUrl: https://github.com/datenstrom/yellow-extensions/raw/main/downloads/markdown.zip
-Published: 2022-11-03 18:21:05
+Published: 2022-11-10 14:17:27
Developer: Anna Svensson
Tag: feature
system/extensions/markdown.php: markdown.php, create, update
@@ -121,11 +121,11 @@ system/themes/stockholm-opensans-light.woff: stockholm-opensans-light.woff, crea
system/themes/stockholm-opensans-regular.woff: stockholm-opensans-regular.woff, create, update, careful
Extension: Update
-Version: 0.8.90
+Version: 0.8.91
Description: Keep your website up to date.
DocumentationUrl: https://github.com/annaesvensson/yellow-update
DownloadUrl: https://github.com/datenstrom/yellow-extensions/raw/main/downloads/update.zip
-Published: 2022-11-05 11:55:13
+Published: 2022-11-10 15:40:13
Developer: Anna Svensson
Tag: feature
system/extensions/update.php: update.php, create, update
diff --git a/system/extensions/update.php b/system/extensions/update.php
@@ -2,7 +2,7 @@
// Update extension, https://github.com/annaesvensson/yellow-update
class YellowUpdate {
- const VERSION = "0.8.90";
+ const VERSION = "0.8.91";
const PRIORITY = "2";
public $yellow; // access to API
public $extensions; // number of extensions
@@ -317,7 +317,7 @@ class YellowUpdate {
// Update extension from file
public function updateExtensionFile($fileName, $fileData, $newModified, $oldModified, $lastModified, $flags, $extension) {
$statusCode = 200;
- $fileName = $this->yellow->toolbox->normalisePath($fileName);
+ $fileName = $this->yellow->lookup->normalisePath($fileName);
if ($this->yellow->lookup->isValidFile($fileName)) {
$create = $update = $delete = false;
if (preg_match("/create/i", $flags) && !is_file($fileName) && !is_string_empty($fileData)) $create = true;
@@ -630,7 +630,7 @@ class YellowUpdate {
// Remove extension file
public function removeExtensionFile($fileName) {
$statusCode = 200;
- $fileName = $this->yellow->toolbox->normalisePath($fileName);
+ $fileName = $this->yellow->lookup->normalisePath($fileName);
if ($this->yellow->lookup->isValidFile($fileName) && is_file($fileName)) {
if (!$this->yellow->toolbox->deleteFile($fileName, $this->yellow->system->get("coreTrashDirectory"))) {
$statusCode = 500;
@@ -804,7 +804,7 @@ class YellowUpdate {
$invalid = false;
foreach ($settings as $key=>$value) {
if (strposu($key, "/")) {
- $fileName = $this->yellow->toolbox->normalisePath($key);
+ $fileName = $this->yellow->lookup->normalisePath($key);
if (!$this->yellow->lookup->isValidFile($fileName)) $invalid = true;
if ($oldModified==0) $oldModified = $this->yellow->toolbox->getFileModified($fileName);
}