Hello application interface
This commit is contained in:
parent
5171263f2d
commit
673944a4a9
8 changed files with 604 additions and 287 deletions
|
@ -9,7 +9,7 @@ How do I install this?
|
|||
Copy all files to your web server hosting.
|
||||
Open your website in a browser, that's it!
|
||||
|
||||
Installation requirements are Apache, mod_rewrite and PHP 5.3.
|
||||
Installation requirements are Apache, mod_rewrite, PHP 5.3 and multibyte support.
|
||||
|
||||
Need help? Have a question?
|
||||
---------------------------
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
---
|
||||
Title: Page does not exist
|
||||
---
|
||||
You can [create this page](javascript:yellow.onShow('yellowpaneedit');).
|
||||
You can [create this page](javascript:yellow.onShow('yellow-paneedit');).
|
|
@ -10,7 +10,6 @@ webinterfaceLoginEmail = Email:
|
|||
webinterfaceLoginPassword = Password:
|
||||
webinterfaceLoginButton = Login
|
||||
webinterfaceSaveButton = Save
|
||||
webinterfaceCancelButton = Cancel
|
||||
webinterfaceEdit = Edit
|
||||
webinterfaceShow = Show
|
||||
webinterfaceUser = User
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<?php
|
||||
// Copyright (c) 2013 Datenstrom, http://www.datenstrom.se
|
||||
// This file may be used and distributed under the terms of the public license.
|
||||
|
||||
|
||||
// Yellow main class
|
||||
class Yellow
|
||||
{
|
||||
const Version = "0.1.0";
|
||||
const Version = "0.1.1";
|
||||
var $page; //current page data
|
||||
var $pages; //current page tree, top level
|
||||
var $pages; //current page tree from file system
|
||||
var $toolbox; //toolbox with helpers
|
||||
var $config; //site configuration
|
||||
var $text; //site text strings
|
||||
|
@ -122,7 +122,7 @@ class Yellow
|
|||
{
|
||||
header($this->toolbox->getHttpStatusFormated($statusCode));
|
||||
header("Content-Type: text/html; charset=UTF-8");
|
||||
$fileName = str_replace("(.*)", $statusCode, $this->config->get("configDir").$this->config->get("errorPageFile"));
|
||||
$fileName = strreplaceu("(.*)", $statusCode, $this->config->get("configDir").$this->config->get("errorPageFile"));
|
||||
$fileHandle = @fopen($fileName, "r");
|
||||
if($fileHandle)
|
||||
{
|
||||
|
@ -132,7 +132,7 @@ class Yellow
|
|||
die("Configuration problem: Can't open file '$fileName'!");
|
||||
}
|
||||
}
|
||||
if($fileData != "") $this->sendPage($baseLocation, $location, $fileName, $fileData, $statusCode);
|
||||
if(!empty($fileData)) $this->sendPage($baseLocation, $location, $fileName, $fileData, $statusCode);
|
||||
if(defined("DEBUG") && DEBUG>=1) echo "Yellow::processRequestFile base:$baseLocation file:$fileName<br>\n";
|
||||
return $statusCode;
|
||||
}
|
||||
|
@ -141,45 +141,16 @@ class Yellow
|
|||
function sendStatus($statusCode, $text = "")
|
||||
{
|
||||
header($this->toolbox->getHttpStatusFormated($statusCode));
|
||||
if($text != "") header($text);
|
||||
if(!empty($text)) header($text);
|
||||
}
|
||||
|
||||
// Send page response
|
||||
function sendPage($baseLocation, $location, $fileName, $fileData, $statusCode)
|
||||
{
|
||||
$this->pages = new Yellow_Pages($baseLocation, $this->toolbox, $this->config);
|
||||
$this->page = new Yellow_Page($baseLocation, $location, $fileName, $fileData, $this->toolbox, $this->config);
|
||||
$this->pages = new Yellow_Pages($baseLocation, $this->toolbox, $this->config, $this->plugins);
|
||||
$this->page = new Yellow_Page($baseLocation, $location, $fileName, $fileData, $this->pages, true);
|
||||
$this->text->setLanguage($this->page->get("language"));
|
||||
|
||||
$text = $this->page->getContentRawText();
|
||||
foreach($this->plugins->plugins as $key=>$value)
|
||||
{
|
||||
if(method_exists($value["obj"], "onParseBefore"))
|
||||
{
|
||||
$text = $value["obj"]->onParseBefore($text, $statusCode);
|
||||
}
|
||||
}
|
||||
if(!$this->plugins->isExisting($this->page->get("parser"))) die("Parser '".$this->page->get("parser")."' does not exist!");
|
||||
$this->page->parser = $this->plugins->plugins[$this->page->get("parser")]["obj"];
|
||||
$text = $this->page->parser->parse($text);
|
||||
foreach($this->plugins->plugins as $key=>$value)
|
||||
{
|
||||
if(method_exists($value["obj"], "onParseAfter"))
|
||||
{
|
||||
$text = $value["obj"]->onParseAfter($text, $statusCode);
|
||||
}
|
||||
}
|
||||
$this->page->setContent($text);
|
||||
|
||||
if(!$this->page->isExisting("description"))
|
||||
{
|
||||
$this->page->set("description", $this->toolbox->createTextDescription($this->page->getContent(), 150));
|
||||
}
|
||||
if(!$this->page->isExisting("keywords"))
|
||||
{
|
||||
$this->page->set("keywords", $this->toolbox->createTextKeywords($this->page->get("title"), 10));
|
||||
}
|
||||
|
||||
$fileName = $this->config->get("templateDir").$this->page->get("template").$this->config->get("systemExtension");
|
||||
if(!is_file($fileName)) die("Template '".$this->page->get("template")."' does not exist!");
|
||||
global $yellow;
|
||||
|
@ -211,23 +182,22 @@ class Yellow
|
|||
{
|
||||
$location = $this->toolbox->getRequestLocation();
|
||||
$location = $this->toolbox->normaliseLocation($location);
|
||||
$position = strlen($baseLocation);
|
||||
return substr($location, $position);
|
||||
return substru($location, strlenu($baseLocation));
|
||||
}
|
||||
|
||||
// Return content file name from location
|
||||
function getContentFileName($location)
|
||||
{
|
||||
return $this->toolbox->findFileFromLocation($location,
|
||||
$this->config->get("contentDir"), $this->config->get("contentHomeDir"),
|
||||
$this->config->get("contentDefaultFile"), $this->config->get("contentExtension"));
|
||||
$this->config->get("contentDir"), $this->config->get("contentHomeDir"),
|
||||
$this->config->get("contentDefaultFile"), $this->config->get("contentExtension"));
|
||||
}
|
||||
|
||||
// Return content directory from location
|
||||
function getContentDirectory($location)
|
||||
{
|
||||
return $this->toolbox->findFileFromLocation($location,
|
||||
$this->config->get("contentDir"), $this->config->get("contentHomeDir"), "", "");
|
||||
$this->config->get("contentDir"), $this->config->get("contentHomeDir"), "", "");
|
||||
}
|
||||
|
||||
// Register plugin
|
||||
|
@ -236,28 +206,31 @@ class Yellow
|
|||
$this->plugins->register($name, $class, $version);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Yellow page data
|
||||
class Yellow_Page
|
||||
{
|
||||
var $baseLocation; //base location
|
||||
var $location; //page location
|
||||
var $fileName; //content file name
|
||||
var $parser; //content parser
|
||||
var $metaData; //meta data of page
|
||||
var $rawData; //raw data of page (unparsed)
|
||||
var $rawTextPos; //raw text of page (unparsed)
|
||||
var $active; //page is active?
|
||||
var $hidden; //page is hidden?
|
||||
var $baseLocation; //base location
|
||||
var $location; //page location
|
||||
var $fileName; //content file name
|
||||
var $parser; //content parser
|
||||
var $metaData; //meta data of page
|
||||
var $rawData; //raw data of page (unparsed)
|
||||
var $rawTextOffsetBytes; //raw text of page (unparsed)
|
||||
var $pages; //access to file system
|
||||
var $active; //page is active?
|
||||
var $hidden; //page is hidden in navigation?
|
||||
|
||||
function __construct($baseLocation, $location, $fileName, $rawData, $toolbox, $config)
|
||||
function __construct($baseLocation, $location, $fileName, $rawData, $pages, $parseContent = false)
|
||||
{
|
||||
$this->baseLocation = $baseLocation;
|
||||
$this->location = $location;
|
||||
$this->fileName = $fileName;
|
||||
$this->setRawData($rawData, $toolbox, $config);
|
||||
$this->active = $toolbox->isActiveLocation($baseLocation, $location);
|
||||
$this->hidden = $toolbox->isHiddenLocation($baseLocation, $location, $fileName, $config->get("contentDir"));
|
||||
$this->setRawData($rawData, $pages->toolbox, $pages->config);
|
||||
$this->pages = $pages;
|
||||
$this->active = $pages->toolbox->isActiveLocation($baseLocation, $location);
|
||||
$this->hidden = $pages->toolbox->isHiddenLocation($baseLocation, $location, $fileName, $pages->config->get("contentDir"));
|
||||
if($parseContent) $this->parseContent();
|
||||
}
|
||||
|
||||
// Set page raw data
|
||||
|
@ -265,7 +238,7 @@ class Yellow_Page
|
|||
{
|
||||
$this->metaData = array();
|
||||
$this->rawData = $rawData;
|
||||
$this->rawTextPos = 0;
|
||||
$this->rawTextOffsetBytes = 0;
|
||||
$this->set("title", $toolbox->createTextTitle($this->location));
|
||||
$this->set("author", $config->get("author"));
|
||||
$this->set("language", $config->get("language"));
|
||||
|
@ -274,17 +247,45 @@ class Yellow_Page
|
|||
|
||||
if(preg_match("/^(\-\-\-[\r\n]+)(.+?)([\r\n]+\-\-\-[\r\n]+)/s", $rawData, $parsed))
|
||||
{
|
||||
$this->rawTextPos = strlen($parsed[1]) + strlen($parsed[2]) + strlen($parsed[3]);
|
||||
$this->rawTextOffsetBytes = strlenb($parsed[0]);
|
||||
preg_match_all("/([^\:\r\n]+)\s*\:\s*([^\r\n]+)/s", $parsed[2], $matches, PREG_SET_ORDER);
|
||||
foreach($matches as $match)
|
||||
{
|
||||
$this->set(strtolower($match[1]), $match[2]);
|
||||
}
|
||||
foreach($matches as $match) $this->set(strtoloweru($match[1]), $match[2]);
|
||||
} else if(preg_match("/^([^\r\n]+)([\r\n]+=+[\r\n]+)/", $rawData, $parsed)) {
|
||||
$this->rawTextPos = strlen($parsed[1]) + strlen($parsed[2]);
|
||||
$this->rawTextOffsetBytes = strlenb($parsed[0]);
|
||||
$this->set("title", $parsed[1]);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse page content on demand
|
||||
function parseContent()
|
||||
{
|
||||
if(empty($this->parser->html))
|
||||
{
|
||||
if(defined("DEBUG") && DEBUG>=2) echo "Yellow_Page::parseContent location:".$this->location."<br/>\n";
|
||||
$text = $this->getContentRawText();
|
||||
foreach($this->pages->plugins->plugins as $key=>$value)
|
||||
{
|
||||
if(method_exists($value["obj"], "onParseBefore")) $text = $value["obj"]->onParseBefore($text, $statusCode);
|
||||
}
|
||||
if(!$this->pages->plugins->isExisting($this->get("parser"))) die("Parser '".$this->get("parser")."' does not exist!");
|
||||
$this->parser = $this->pages->plugins->plugins[$this->get("parser")]["obj"];
|
||||
$text = $this->parser->parse($text);
|
||||
foreach($this->pages->plugins->plugins as $key=>$value)
|
||||
{
|
||||
if(method_exists($value["obj"], "onParseAfter")) $text = $value["obj"]->onParseAfter($text, $statusCode);
|
||||
}
|
||||
$this->setContent($text);
|
||||
|
||||
if(!$this->isExisting("description"))
|
||||
{
|
||||
$this->set("description", $this->pages->toolbox->createTextDescription($this->getContent(), 150));
|
||||
}
|
||||
if(!$this->isExisting("keywords"))
|
||||
{
|
||||
$this->set("keywords", $this->pages->toolbox->createTextKeywords($this->get("title"), 10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set page meta data
|
||||
function set($key, $value)
|
||||
|
@ -319,13 +320,14 @@ class Yellow_Page
|
|||
// Return page content, HTML encoded
|
||||
function getContent()
|
||||
{
|
||||
$this->parseContent();
|
||||
return $this->parser->html;
|
||||
}
|
||||
|
||||
// Return page content, raw text
|
||||
function getContentRawText()
|
||||
{
|
||||
return substr($this->rawData, $this->rawTextPos);
|
||||
return substrb($this->rawData, $this->rawTextOffsetBytes);
|
||||
}
|
||||
|
||||
// Return absolut page location
|
||||
|
@ -334,87 +336,264 @@ class Yellow_Page
|
|||
return $this->baseLocation.$this->location;
|
||||
}
|
||||
|
||||
// Return page modification time (Unix time UTC)
|
||||
function getModified()
|
||||
{
|
||||
return filemtime($this->fileName);
|
||||
}
|
||||
|
||||
// Return child pages relative to current page
|
||||
function getChildren($hidden = false)
|
||||
{
|
||||
return $this->pages->findChildren($this->location, $hidden);
|
||||
}
|
||||
|
||||
// Return pages on the same level as current page
|
||||
function getSiblings($hidden = false)
|
||||
{
|
||||
$parentLocation = $this->pages->getParentLocation($this->location);
|
||||
return $this->pages->findChildren($parentLocation, $hidden);
|
||||
}
|
||||
|
||||
// Return parent page relative to current page
|
||||
function getParent()
|
||||
{
|
||||
$parentLocation = $this->pages->getParentLocation($this->location);
|
||||
return $this->pages->findPage($parentLocation);
|
||||
}
|
||||
|
||||
// Check if meta data exists
|
||||
function isExisting($key)
|
||||
{
|
||||
return !is_null($this->metaData[$key]);
|
||||
}
|
||||
|
||||
// Check if page is active
|
||||
// Check if page is within current HTTP request
|
||||
function isActive()
|
||||
{
|
||||
return $this->active;
|
||||
}
|
||||
|
||||
// Check if page is active
|
||||
// Check if page is hidden in navigation
|
||||
function isHidden()
|
||||
{
|
||||
return $this->hidden;
|
||||
}
|
||||
}
|
||||
|
||||
// Yellow page collection as array
|
||||
class Yellow_PageCollection extends ArrayObject
|
||||
{
|
||||
var $baseLocation; //base location
|
||||
var $location; //collection location
|
||||
var $paginationPage; //current page number in pagination
|
||||
var $paginationCount; //highest page number in pagination
|
||||
var $toolbox; //access to toolbox
|
||||
|
||||
function __construct($input, $baseLocation, $location, $toolbox)
|
||||
{
|
||||
parent::__construct($input);
|
||||
$this->baseLocation = $baseLocation;
|
||||
$this->location = $location;
|
||||
$this->toolbox = $toolbox;
|
||||
}
|
||||
|
||||
// Filter page collection by meta data
|
||||
function filter($key, $value, $exactMatch = true)
|
||||
{
|
||||
if(!empty($key))
|
||||
{
|
||||
$array = array();
|
||||
$value = strtoloweru($value);
|
||||
$valueLength = strlenu($value);
|
||||
foreach($this->getArrayCopy() as $page)
|
||||
{
|
||||
if($page->isExisting($key))
|
||||
{
|
||||
foreach(preg_split("/,\s*/", strtoloweru($page->get($key))) as $valuePage)
|
||||
{
|
||||
$length = $exactMatch ? strlenu($valuePage) : $valueLength;
|
||||
if($value == substru($valuePage, 0, $length)) array_push($array, $page);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->exchangeArray($array);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Reverse page collection
|
||||
function reverse($entriesMax = 0)
|
||||
{
|
||||
$this->exchangeArray(array_reverse($this->getArrayCopy()));
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Paginate page collection
|
||||
function pagination($limit, $reverse = true)
|
||||
{
|
||||
$array = $this->getArrayCopy();
|
||||
if($reverse) $array = array_reverse($array);
|
||||
$this->paginationPage = 1;
|
||||
$this->paginationCount = ceil($this->count() / $limit);
|
||||
if($limit < $this->count() && isset($_REQUEST["page"])) $this->paginationPage = max(1, $_REQUEST["page"]);
|
||||
$this->exchangeArray(array_slice($array, ($this->paginationPage - 1) * $limit, $limit));
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Return current page number in pagination
|
||||
function getPaginationPage()
|
||||
{
|
||||
return $this->$paginationPage;
|
||||
}
|
||||
|
||||
// Return highest page number in pagination
|
||||
function getPaginationCount()
|
||||
{
|
||||
return $this->paginationCount;
|
||||
}
|
||||
|
||||
// Return absolut location for a page in pagination
|
||||
function getLocationPage($pageNumber)
|
||||
{
|
||||
if($pageNumber>=1 && $pageNumber<=$this->paginationCount)
|
||||
{
|
||||
$locationArgs = $this->toolbox->getRequestLocationArgs($pageNumber>1 ? "page:$pageNumber" : "page:");
|
||||
$location = $this->baseLocation.$this->location.$locationArgs;
|
||||
}
|
||||
return $location;
|
||||
}
|
||||
|
||||
// Return absolut location for previous page in pagination
|
||||
function getLocationPrevious()
|
||||
{
|
||||
$pageNumber = $this->paginationPage;
|
||||
$pageNumber = ($pageNumber>1 && $pageNumber<=$this->paginationCount) ? $pageNumber-1 : 0;
|
||||
return $this->getLocationPage($pageNumber);
|
||||
}
|
||||
|
||||
// Return absolut location for next page in pagination
|
||||
function getLocationNext()
|
||||
{
|
||||
$pageNumber = $this->paginationPage;
|
||||
$pageNumber = ($pageNumber>=1 && $pageNumber<$this->paginationCount) ? $pageNumber+1 : 0;
|
||||
return $this->getLocationPage($pageNumber);
|
||||
}
|
||||
|
||||
// Check if there is an active pagination
|
||||
function isPagination()
|
||||
{
|
||||
return $this->paginationCount > 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Yellow page tree from file system
|
||||
class Yellow_Pages
|
||||
{
|
||||
var $pages; //scanned pages
|
||||
var $baseLocation; //base location
|
||||
var $pages; //scanned pages
|
||||
var $toolbox; //access to toolbox
|
||||
var $config; //access to configuration
|
||||
var $plugins; //access to plugins
|
||||
|
||||
function __construct($baseLocation, $toolbox, $config)
|
||||
{
|
||||
$this->scan($baseLocation, $toolbox, $config);
|
||||
}
|
||||
|
||||
// Scan top-level pages
|
||||
function scan($baseLocation, $toolbox, $config)
|
||||
function __construct($baseLocation, $toolbox, $config, $plugins)
|
||||
{
|
||||
$this->baseLocation = $baseLocation;
|
||||
$this->pages = array();
|
||||
foreach($toolbox->getDirectoryEntries($config->get("contentDir"), "/.*/", true) as $entry)
|
||||
{
|
||||
$fileName = $config->get("contentDir").$entry."/".$config->get("contentDefaultFile");
|
||||
$location = $toolbox->findLocationFromFile($fileName, $config->get("contentDir"), $config->get("contentHomeDir"),
|
||||
$config->get("contentDefaultFile"), $config->get("contentExtension"));
|
||||
$fileHandle = @fopen($fileName, "r");
|
||||
if($fileHandle)
|
||||
{
|
||||
$fileData = fread($fileHandle, 4096);
|
||||
fclose($fileHandle);
|
||||
} else {
|
||||
$fileData = "";
|
||||
}
|
||||
$page = new Yellow_Page($baseLocation, $location, $fileName, $fileData, $toolbox, $config);
|
||||
array_push($this->pages, $page);
|
||||
}
|
||||
$this->toolbox = $toolbox;
|
||||
$this->config = $config;
|
||||
$this->plugins = $plugins;
|
||||
}
|
||||
|
||||
// Return top-level pages
|
||||
function root($showHidden = false)
|
||||
// Return top-level navigation pages
|
||||
function root($hidden = false)
|
||||
{
|
||||
$pages = array();
|
||||
foreach($this->pages as $page)
|
||||
return $this->findChildren("", $hidden);
|
||||
}
|
||||
|
||||
// Return child pages for a location
|
||||
function findChildren($location, $hidden = false)
|
||||
{
|
||||
$pages = new Yellow_PageCollection(array(), $this->baseLocation, $location, $this->toolbox);
|
||||
$this->scanChildren($location);
|
||||
foreach($this->pages[$location] as $page)
|
||||
{
|
||||
if($showHidden || !$page->isHidden()) array_push($pages, $page);
|
||||
if($hidden || !$page->isHidden()) $pages->append($page);
|
||||
}
|
||||
return $pages;
|
||||
}
|
||||
}
|
||||
|
||||
// Return page for a location, false if not found
|
||||
function findPage($location)
|
||||
{
|
||||
$parentLocation = $this->getParentLocation($location);
|
||||
$this->scanChildren($parentLocation);
|
||||
foreach($this->pages[$parentLocation] as $page)
|
||||
{
|
||||
if($this->baseLocation.$location == $page->getLocation()) return $page;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Scan child pages on demand
|
||||
function scanChildren($location)
|
||||
{
|
||||
if(is_null($this->pages[$location]))
|
||||
{
|
||||
if(defined("DEBUG") && DEBUG>=2) echo "Yellow_Pages::scanChildren location:$location<br/>\n";
|
||||
$this->pages[$location] = array();
|
||||
$path = $this->config->get("contentDir");
|
||||
if(!empty($location))
|
||||
{
|
||||
$path = $this->toolbox->findFileFromLocation($location,
|
||||
$this->config->get("contentDir"), $this->config->get("contentHomeDir"), "", "");
|
||||
}
|
||||
$fileNames = array();
|
||||
foreach($this->toolbox->getDirectoryEntries($path, "/.*/", true) as $entry)
|
||||
{
|
||||
array_push($fileNames, $path.$entry."/".$this->config->get("contentDefaultFile"));
|
||||
}
|
||||
$fileRegex = "/.*\\".$this->config->get("contentExtension")."/";
|
||||
foreach($this->toolbox->getDirectoryEntries($path, $fileRegex, true, false) as $entry)
|
||||
{
|
||||
if($entry == $this->config->get("contentDefaultFile")) continue;
|
||||
array_push($fileNames, $path.$entry);
|
||||
}
|
||||
foreach($fileNames as $fileName)
|
||||
{
|
||||
$childLocation = $this->toolbox->findLocationFromFile($fileName,
|
||||
$this->config->get("contentDir"), $this->config->get("contentHomeDir"),
|
||||
$this->config->get("contentDefaultFile"), $this->config->get("contentExtension"));
|
||||
$fileHandle = @fopen($fileName, "r");
|
||||
if($fileHandle)
|
||||
{
|
||||
$fileData = fread($fileHandle, 4096);
|
||||
fclose($fileHandle);
|
||||
} else {
|
||||
$fileData = "";
|
||||
}
|
||||
$page = new Yellow_Page($this->baseLocation, $childLocation, $fileName, $fileData, $this);
|
||||
array_push($this->pages[$location], $page);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return parent navigation location
|
||||
function getParentLocation($location)
|
||||
{
|
||||
$parentLocation = "";
|
||||
if(preg_match("/^(.*\/)(.+?)$/", $location, $matches))
|
||||
{
|
||||
$parentLocation = $matches[1]!="/" ? $matches[1] : "";
|
||||
}
|
||||
return $parentLocation;
|
||||
}
|
||||
}
|
||||
|
||||
// Yellow toolbox with helpers
|
||||
class Yellow_Toolbox
|
||||
{
|
||||
// Return location from current HTTP request
|
||||
static function getRequestLocation()
|
||||
{
|
||||
$uri = $_SERVER["REQUEST_URI"];
|
||||
return ($pos = strpos($uri, '?')) ? substr($uri, 0, $pos) : $uri;
|
||||
}
|
||||
|
||||
// Return arguments from current HTTP request
|
||||
static function getRequestLocationArguments()
|
||||
{
|
||||
$uri = $_SERVER["REQUEST_URI"];
|
||||
return ($pos = strpos($uri, '?')) ? substr($uri, $pos+1) : "";
|
||||
}
|
||||
|
||||
// Return base location
|
||||
// Return base location from current HTTP request
|
||||
static function getBaseLocation()
|
||||
{
|
||||
$baseLocation = "/";
|
||||
|
@ -422,27 +601,73 @@ class Yellow_Toolbox
|
|||
return $baseLocation;
|
||||
}
|
||||
|
||||
// Normalise location and remove incorrect path tokens
|
||||
static function normaliseLocation($location)
|
||||
// Return location from current HTTP request
|
||||
static function getRequestLocation()
|
||||
{
|
||||
$str = str_replace('\\', '/', rawurldecode($location));
|
||||
$location = ($str[0]=='/') ? '' : '/';
|
||||
for($pos=0; $pos<strlen($str); ++$pos)
|
||||
$uri = $_SERVER["REQUEST_URI"];
|
||||
return ($pos = strposu($uri, '?')) ? substru($uri, 0, $pos) : $uri;
|
||||
}
|
||||
|
||||
// Return arguments from current HTTP request
|
||||
static function getRequestLocationArgs($arg = "", $encodeArgs = true)
|
||||
{
|
||||
preg_match("/^(.*?):(.*)$/", $arg, $args);
|
||||
if(preg_match("/^(.*?\/)(\w+:.*)$/", rawurldecode(self::getRequestLocation()), $matches))
|
||||
{
|
||||
if($str[$pos] == '/')
|
||||
foreach(explode('/', $matches[2]) as $token)
|
||||
{
|
||||
if($str[$pos+1] == '/') continue;
|
||||
if($str[$pos+1] == '.')
|
||||
preg_match("/^(.*?):(.*)$/", $token, $matches);
|
||||
if($matches[1] == $args[1]) { $matches[2] = $args[2]; $found = true; }
|
||||
if(!empty($matches[1]) && !empty($matches[2]))
|
||||
{
|
||||
$posNew = $pos+1; while($str[$posNew] == '.') ++$posNew;
|
||||
if($str[$posNew]=='/' || $str[$posNew]=='')
|
||||
if(!empty($locationArgs)) $locationArgs .= '/';
|
||||
$locationArgs .= "$matches[1]:$matches[2]";
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!$found && !empty($args[1]) && !empty($args[2]))
|
||||
{
|
||||
if(!empty($locationArgs)) $locationArgs .= '/';
|
||||
$locationArgs .= "$args[1]:$args[2]";
|
||||
}
|
||||
if($encodeArgs)
|
||||
{
|
||||
$locationArgs = rawurlencode($locationArgs);
|
||||
$locationArgs = strreplaceu(array('%3A','%2F'), array(':','/'), $locationArgs);
|
||||
}
|
||||
return $locationArgs;
|
||||
}
|
||||
|
||||
// Normalise location and remove unwanted path tokens
|
||||
static function normaliseLocation($location, $removeArgs = true)
|
||||
{
|
||||
$string = strreplaceu('\\', '/', rawurldecode($location));
|
||||
$location = ($string[0]=='/') ? '' : '/';
|
||||
for($pos=0; $pos<strlenb($string); ++$pos)
|
||||
{
|
||||
if($string[$pos] == '/')
|
||||
{
|
||||
if($string[$pos+1] == '/') continue;
|
||||
if($string[$pos+1] == '.')
|
||||
{
|
||||
$posNew = $pos+1; while($string[$posNew] == '.') ++$posNew;
|
||||
if($string[$posNew]=='/' || $string[$posNew]=='')
|
||||
{
|
||||
$pos = $posNew-1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
$location .= $str[$pos];
|
||||
$location .= $string[$pos];
|
||||
}
|
||||
if($removeArgs && preg_match("/^(.*?\/)(\w+:.*)$/", $location, $matches))
|
||||
{
|
||||
$location = $matches[1];
|
||||
foreach(explode('/', $matches[2]) as $token)
|
||||
{
|
||||
preg_match("/^(.*?):(.*)$/", $token, $matches);
|
||||
if(!empty($matches[1]) && !empty($matches[2])) $_REQUEST[$matches[1]] = $matches[2];
|
||||
}
|
||||
}
|
||||
return $location;
|
||||
}
|
||||
|
@ -450,27 +675,27 @@ class Yellow_Toolbox
|
|||
// Check if location is specifying file or directory
|
||||
static function isFileLocation($location)
|
||||
{
|
||||
return substr($location,-1,1) != "/";
|
||||
return substru($location, -1, 1) != "/";
|
||||
}
|
||||
|
||||
// Check if location is within current HTTP request
|
||||
static function isActiveLocation($baseLocation, $location)
|
||||
{
|
||||
$currentLocation = substr(self::getRequestLocation(), strlen($baseLocation));
|
||||
$currentLocation = substru(self::getRequestLocation(), strlenu($baseLocation));
|
||||
if($location != "/")
|
||||
{
|
||||
$active = substr($currentLocation, 0, strlen($location))==$location;
|
||||
$active = substru($currentLocation, 0, strlenu($location))==$location;
|
||||
} else {
|
||||
$active = $currentLocation==$location;
|
||||
}
|
||||
return $active;
|
||||
}
|
||||
|
||||
// Check if location is within visible collection
|
||||
// Check if location is hidden in navigation
|
||||
static function isHiddenLocation($baseLocation, $location, $fileName, $pathBase)
|
||||
{
|
||||
$hidden = false;
|
||||
if(substr($fileName, 0, strlen($pathBase)) == $pathBase) $fileName = substr($fileName, strlen($pathBase));
|
||||
if(substru($fileName, 0, strlenu($pathBase)) == $pathBase) $fileName = substru($fileName, strlenu($pathBase));
|
||||
$tokens = explode('/', $fileName);
|
||||
for($i=0; $i<count($tokens)-1; ++$i)
|
||||
{
|
||||
|
@ -487,49 +712,48 @@ class Yellow_Toolbox
|
|||
static function findFileFromLocation($location, $pathBase, $pathHome, $fileDefault, $fileExtension)
|
||||
{
|
||||
$path = $pathBase;
|
||||
if($location != "/")
|
||||
$tokens = explode('/', $location);
|
||||
if(count($tokens) > 2)
|
||||
{
|
||||
$tokens = explode('/', $location);
|
||||
for($i=1; $i<count($tokens)-1; ++$i)
|
||||
{
|
||||
if(preg_match("/^[\d\-\.]+/", $tokens[$i])) $duplicate = true;
|
||||
$entries = self::getDirectoryEntries($path, "/^[\d\-\.]+".$tokens[$i]."$/");
|
||||
if(!empty($entries)) $tokens[$i] = $entries[0];
|
||||
$path .= "$tokens[$i]/";
|
||||
}
|
||||
if($tokens[$i] != "")
|
||||
{
|
||||
$path .= $tokens[$i].$fileExtension;
|
||||
} else {
|
||||
$path .= $fileDefault;
|
||||
$path .= empty($entries) ? "$tokens[$i]/" : "$entries[0]/";
|
||||
}
|
||||
if($path == $pathBase.$pathHome) $duplicate = true;
|
||||
|
||||
} else {
|
||||
$path .= $pathHome.$fileDefault;
|
||||
$i = 1;
|
||||
$path .= $pathHome;
|
||||
}
|
||||
return $path;
|
||||
if($tokens[$i] != "")
|
||||
{
|
||||
if(preg_match("/^[\d\-\.]+/", $tokens[$i])) $duplicate = true;
|
||||
$entries = self::getDirectoryEntries($path, "/^[\d\-\.]+".$tokens[$i].$fileExtension."$/", false, false);
|
||||
$path .= empty($entries) ? $tokens[$i].$fileExtension : $entries[0];
|
||||
} else {
|
||||
$path .= $fileDefault;
|
||||
}
|
||||
return $duplicate ? "" : $path;
|
||||
}
|
||||
|
||||
// Find location from file path
|
||||
static function findLocationFromFile($fileName, $pathBase, $pathHome, $fileDefault, $fileExtension)
|
||||
{
|
||||
$location = "/";
|
||||
if(substr($fileName, 0, strlen($pathBase)) == $pathBase) $fileName = substr($fileName, strlen($pathBase));
|
||||
if(substr($fileName, 0, strlen($pathHome)) != $pathHome)
|
||||
if(substru($fileName, 0, strlenu($pathBase)) == $pathBase) $fileName = substru($fileName, strlenu($pathBase));
|
||||
if(substru($fileName, 0, strlenu($pathHome)) == $pathHome) $fileName = substru($fileName, strlenu($pathHome));
|
||||
$tokens = explode('/', $fileName);
|
||||
for($i=0; $i<count($tokens)-1; ++$i)
|
||||
{
|
||||
$tokens = explode('/', $fileName);
|
||||
for($i=0; $i<count($tokens)-1; ++$i)
|
||||
{
|
||||
if(preg_match("/^[\d\-\.]+(.*)$/", $tokens[$i], $matches)) $tokens[$i] = $matches[1];
|
||||
$location .= "$tokens[$i]/";
|
||||
}
|
||||
if($tokens[$i] != $fileDefault)
|
||||
{
|
||||
$location .= substr($tokens[$i], 0, -strlen($fileExtension)-1);
|
||||
}
|
||||
} else {
|
||||
if($fileName != $pathHome.$fileDefault)
|
||||
{
|
||||
$location .= substr($fileName, $pathHome, -strlen($fileExtension)-1);
|
||||
}
|
||||
if(preg_match("/^[\d\-\.]+(.*)$/", $tokens[$i], $matches)) $tokens[$i] = $matches[1];
|
||||
$location .= "$tokens[$i]/";
|
||||
}
|
||||
if($tokens[$i] != $fileDefault)
|
||||
{
|
||||
if(preg_match("/^[\d\-\.]+(.*)$/", $tokens[$i], $matches)) $tokens[$i] = $matches[1];
|
||||
$location .= substru($tokens[$i], 0, -strlenu($fileExtension));
|
||||
}
|
||||
return $location;
|
||||
}
|
||||
|
@ -541,6 +765,7 @@ class Yellow_Toolbox
|
|||
{
|
||||
case 301: $text = "$_SERVER[SERVER_PROTOCOL] $statusCode Moved permanently"; break;
|
||||
case 302: $text = "$_SERVER[SERVER_PROTOCOL] $statusCode Moved temporarily"; break;
|
||||
case 303: $text = "$_SERVER[SERVER_PROTOCOL] $statusCode Reload please"; break;
|
||||
case 304: $text = "$_SERVER[SERVER_PROTOCOL] $statusCode Not modified"; break;
|
||||
case 401: $text = "$_SERVER[SERVER_PROTOCOL] $statusCode Unauthorised"; break;
|
||||
case 404: $text = "$_SERVER[SERVER_PROTOCOL] $statusCode Not found"; break;
|
||||
|
@ -559,7 +784,7 @@ class Yellow_Toolbox
|
|||
{
|
||||
while(($entry = readdir($dirHandle)) !== false)
|
||||
{
|
||||
if(substr($entry, 0, 1) == ".") continue;
|
||||
if(substru($entry, 0, 1) == ".") continue;
|
||||
if(preg_match($regex, $entry))
|
||||
{
|
||||
if($directories)
|
||||
|
@ -576,36 +801,67 @@ class Yellow_Toolbox
|
|||
return $entries;
|
||||
}
|
||||
|
||||
// Create description from text
|
||||
static function createTextDescription($text, $lengthMax)
|
||||
// Create description from text string
|
||||
static function createTextDescription($text, $lengthMax, $removeHtml = true)
|
||||
{
|
||||
$description = "";
|
||||
preg_match_all("/\<p\>(.+?\<\/p\>)/s", $text, $parsedDescription, PREG_SET_ORDER);
|
||||
foreach($parsedDescription as $matches)
|
||||
if(preg_match("/<h1>.*<\/h1>(.*)/si", $text, $matches)) $text = $matches[1];
|
||||
if($removeHtml)
|
||||
{
|
||||
preg_match_all("/([^\<]*)[^\>]+./s", $matches[1], $parsedUndoTag, PREG_SET_ORDER);
|
||||
if(count($parsedUndoTag) > 0)
|
||||
while(true)
|
||||
{
|
||||
if(!empty($description)) $description .= " ";
|
||||
foreach($parsedUndoTag as $matchTag)
|
||||
$elementFound = preg_match("/<\s*?(\/?\w*).*?\>/s", $text, $matches, PREG_OFFSET_CAPTURE, $offsetBytes);
|
||||
$element = $matches[0][0];
|
||||
$elementName = $matches[1][0];
|
||||
$elementOffsetBytes = $elementFound ? $matches[0][1] : strlenb($text);
|
||||
$string = html_entity_decode(substrb($text, $offsetBytes, $elementOffsetBytes - $offsetBytes), ENT_QUOTES, "UTF-8");
|
||||
if(preg_match("/^(blockquote|br|div|h\d|hr|li|ol|p|pre|ul)/i", $elementName)) $string .= ' ';
|
||||
$string = preg_replace("/\s+/s", " ", $string);
|
||||
if(substru($string, 0 , 1)==" " && (empty($output) || substru($output, -1)==' ')) $string = substru($string, 1);
|
||||
$length = strlenu($string);
|
||||
$output .= substru($string, 0, $length < $lengthMax ? $length : $lengthMax-1);
|
||||
$lengthMax -= $length;
|
||||
if($lengthMax<=0 || !$elementFound) break;
|
||||
$offsetBytes = $elementOffsetBytes + strlenb($element);
|
||||
}
|
||||
$output = rtrim($output);
|
||||
if($lengthMax <= 0) $output .= '…';
|
||||
} else {
|
||||
$elementsOpen = array();
|
||||
while(true)
|
||||
{
|
||||
$elementFound = preg_match("/&.*?\;|<\s*?(\/?\w*)\s*?(.*?)\s*?\>/s", $text, $matches, PREG_OFFSET_CAPTURE, $offsetBytes);
|
||||
$element = $matches[0][0];
|
||||
$elementName = $matches[1][0];
|
||||
$elementOffsetBytes = $elementFound ? $matches[0][1] : strlenb($text);
|
||||
$string = substrb($text, $offsetBytes, $elementOffsetBytes - $offsetBytes);
|
||||
$length = strlenu($string);
|
||||
$output .= substru($string, 0, $length < $lengthMax ? $length : $lengthMax-1);
|
||||
$lengthMax -= $length + ($element[0]=='&' ? 1 : 0);
|
||||
if($lengthMax<=0 || !$elementFound) break;
|
||||
if(!empty($elementName))
|
||||
{
|
||||
$description .= preg_replace("/[\\x00-\\x1f]+/s", " ", $matchTag[1]);
|
||||
if(!preg_match("/^(\/|area|br|col|hr|img|input|col|param)/i", $elementName))
|
||||
{
|
||||
if(substru($matches[2][0], -1) != '/') array_push($elementsOpen, $elementName);
|
||||
} else {
|
||||
array_pop($elementsOpen);
|
||||
}
|
||||
}
|
||||
$output .= $element;
|
||||
$offsetBytes = $elementOffsetBytes + strlenb($element);
|
||||
}
|
||||
if(strlen($description) > $lengthMax)
|
||||
{
|
||||
$description = substr($description, 0, $lengthMax-3)."...";
|
||||
break;
|
||||
}
|
||||
$output = rtrim($output);
|
||||
if($lengthMax <= 0) $output .= '…';
|
||||
for($t=count($elementsOpen)-1; $t>=0; --$t) $output .= "</".$elementsOpen[$t].">";
|
||||
}
|
||||
return $description;
|
||||
return $output;
|
||||
}
|
||||
|
||||
// Create keywords from text string
|
||||
static function createTextKeywords($text, $keywordsMax)
|
||||
{
|
||||
$tokens = preg_split("/[,\s\(\)]/", strtolower($text));
|
||||
foreach($tokens as $key => $value) if(strlen($value) < 3) unset($tokens[$key]);
|
||||
$tokens = preg_split("/[,\s\(\)]/", strtoloweru($text));
|
||||
foreach($tokens as $key=>$value) if(strlenu($value) < 3) unset($tokens[$key]);
|
||||
return implode(", ", array_slice(array_unique($tokens), 0, $keywordsMax));
|
||||
}
|
||||
|
||||
|
@ -617,14 +873,14 @@ class Yellow_Toolbox
|
|||
}
|
||||
|
||||
// Detect web browser language
|
||||
function detectBrowserLanguage($languagesAllowed, $languageDefault)
|
||||
static function detectBrowserLanguage($languagesAllowed, $languageDefault)
|
||||
{
|
||||
$language = $languageDefault;
|
||||
if(isset($_SERVER["HTTP_ACCEPT_LANGUAGE"]))
|
||||
{
|
||||
foreach(preg_split("/,\s*/", $_SERVER["HTTP_ACCEPT_LANGUAGE"]) as $string)
|
||||
{
|
||||
$tokens = split(";", $string, 2);
|
||||
$tokens = explode(';', $string, 2);
|
||||
if(in_array($tokens[0], $languagesAllowed))
|
||||
{
|
||||
$language = $tokens[0];
|
||||
|
@ -642,7 +898,7 @@ class Yellow_Toolbox
|
|||
$fileHandle = @fopen($fileName, "rb");
|
||||
if($fileHandle)
|
||||
{
|
||||
if(substr($fileName, -3) == "png")
|
||||
if(substru($fileName, -3) == "png")
|
||||
{
|
||||
$dataSignature = fread($fileHandle, 8);
|
||||
$dataHeader = fread($fileHandle, 25);
|
||||
|
@ -651,7 +907,7 @@ class Yellow_Toolbox
|
|||
$width = (ord($dataHeader[10])<<8) + ord($dataHeader[11]);
|
||||
$height = (ord($dataHeader[14])<<8) + ord($dataHeader[15]);
|
||||
}
|
||||
} else if(substr($fileName, -3) == "jpg") {
|
||||
} else if(substru($fileName, -3) == "jpg") {
|
||||
$dataSignature = fread($fileHandle, 11);
|
||||
$dataHeader = fread($fileHandle, 147);
|
||||
$dataHeader = fread($fileHandle, 16);
|
||||
|
@ -702,7 +958,7 @@ class Yellow_Config
|
|||
{
|
||||
if(preg_match("/^\//", $line)) continue;
|
||||
preg_match("/^\s*(.*?)\s*=\s*(.*?)\s*$/", $line, $matches);
|
||||
if($matches[1]!="" && $matches[2]!="")
|
||||
if(!empty($matches[1]) && !empty($matches[2]))
|
||||
{
|
||||
$this->set($matches[1], $matches[2]);
|
||||
if(defined("DEBUG") && DEBUG>=3) echo "Yellow_Config::load key:$matches[1] $matches[2]<br/>\n";
|
||||
|
@ -745,7 +1001,7 @@ class Yellow_Config
|
|||
} else {
|
||||
foreach($this->config as $key=>$value)
|
||||
{
|
||||
if(substr($key, -strlen($filterEnd)) == $filterEnd) $config[$key] = $value;
|
||||
if(substru($key, -strlenu($filterEnd)) == $filterEnd) $config[$key] = $value;
|
||||
}
|
||||
}
|
||||
return $config;
|
||||
|
@ -784,13 +1040,13 @@ class Yellow_Text
|
|||
foreach($fileData as $line)
|
||||
{
|
||||
preg_match("/^\s*(.*?)\s*=\s*(.*?)\s*$/", $line, $matches);
|
||||
if($matches[1]=="language" && $matches[2]!="") { $language = $matches[2]; break; }
|
||||
if($matches[1]=="language" && !empty($matches[2])) { $language = $matches[2]; break; }
|
||||
}
|
||||
foreach($fileData as $line)
|
||||
{
|
||||
if(preg_match("/^\//", $line)) continue;
|
||||
preg_match("/^\s*(.*?)\s*=\s*(.*?)\s*$/", $line, $matches);
|
||||
if($language!="" && $matches[1]!="" && $matches[2]!="")
|
||||
if(!empty($language) && !empty($matches[1]) && !empty($matches[2]))
|
||||
{
|
||||
$this->setLanguageText($language, $matches[1], $matches[2]);
|
||||
if(defined("DEBUG") && DEBUG>=3) echo "Yellow_Text::load key:$matches[1] $matches[2]<br/>\n";
|
||||
|
@ -837,8 +1093,8 @@ class Yellow_Text
|
|||
} else {
|
||||
foreach($this->text[$language] as $key=>$value)
|
||||
{
|
||||
if(substr($key, 0, strlen("language")) == "language") $text[$key] = $value;
|
||||
if(substr($key, 0, strlen($filterStart)) == $filterStart) $text[$key] = $value;
|
||||
if(substru($key, 0, strlenu("language")) == "language") $text[$key] = $value;
|
||||
if(substru($key, 0, strlenu($filterStart)) == $filterStart) $text[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -902,4 +1158,16 @@ class Yellow_Plugins
|
|||
return !is_null($this->plugins[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
// Unicode support for PHP 5
|
||||
mb_internal_encoding("UTF-8");
|
||||
function strlenu() { return call_user_func_array("mb_strlen", func_get_args()); }
|
||||
function strposu() { return call_user_func_array("mb_strpos", func_get_args()); }
|
||||
function strreplaceu() { return call_user_func_array("str_replace", func_get_args()); }
|
||||
function strtoloweru() { return call_user_func_array("mb_strtolower", func_get_args()); }
|
||||
function strtoupperu() { return call_user_func_array("mb_strtoupper", func_get_args()); }
|
||||
function substru() { return call_user_func_array("mb_substr", func_get_args()); }
|
||||
function strlenb() { return call_user_func_array("strlen", func_get_args()); }
|
||||
function strposb() { return call_user_func_array("strpos", func_get_args()); }
|
||||
function substrb() { return call_user_func_array("substr", func_get_args()); }
|
||||
?>
|
|
@ -1,25 +1,50 @@
|
|||
.yellowlogin { width:600px; position:absolute; top:5px; padding:30px; border:1px solid #ccc; background:#fff; color:#000; }
|
||||
.yellowlogin h1 { margin:0px; padding:0px; }
|
||||
.yellowlogin p { margin:0.5em; text-align:right; }
|
||||
.yellow-login {
|
||||
width:600px; position:absolute; top:5px; padding:30px;
|
||||
border:1px solid #ccc; border-radius:4px;
|
||||
background-color:#fff; color:#000; }
|
||||
.yellow-login h1 { margin:0px; padding:0px; }
|
||||
.yellow-login p { margin:0.5em; text-align:right; }
|
||||
|
||||
.yellowbar { width:600px; position:absolute; top:1px; background:#fff; color:#000; }
|
||||
.yellowbar img { vertical-align:top; }
|
||||
.yellowbar button { color:#05d; padding-left:0.5em; padding-right:0.5em; }
|
||||
.yellowbar button:hover { color:#f00; }
|
||||
.yellowbarleft { margin-left:0px; display:block; float:left; height:100%; }
|
||||
.yellowbarright { margin-right:0px; display:block; float:right; height:100%; }
|
||||
.yellowbarlink { cursor:pointer; font:inherit; background:none; border:none; margin:0px padding:0px; }
|
||||
.yellowbubble { -webkit-border-radius:4px; -moz-border-radius:4px; border-radius:4px; }
|
||||
.yellow-bar { width:600px; position:absolute; top:1px; background-color:#fff; color:#000; }
|
||||
.yellow-bar img { vertical-align:top; }
|
||||
.yellow-bar button { color:#05d; padding-left:0.5em; padding-right:0.5em; }
|
||||
.yellow-bar button:hover { color:#f00; }
|
||||
.yellow-barleft { margin-left:0px; display:block; float:left; height:100%; }
|
||||
.yellow-barright { margin-right:0px; display:block; float:right; height:100%; }
|
||||
.yellow-barlink { cursor:pointer; font:inherit; background:none; border:none; margin:0px padding:0px; }
|
||||
|
||||
.yellowpane { position:absolute; display:none; margin:0px; padding:5px; border:solid 1px #ccc; background:#fff; color:#000; z-index:10; }
|
||||
.yellowpane a { text-decoration:none; color:#000; }
|
||||
.yellowpane a:hover { text-decoration:none; color:#f00; }
|
||||
.yellowpane p { margin:0.5em; }
|
||||
.yellowpane ul { list-style:none; margin:0em 0.5em; padding:0px; }
|
||||
.yellow-pane {
|
||||
position:absolute; display:none; margin:0px; padding:5px; z-index:10;
|
||||
border:1px solid #ccc; border-radius:4px;
|
||||
background-color:#fff; color:#000; }
|
||||
.yellow-panebubble { }
|
||||
.yellow-pane a { text-decoration:none; color:#000; }
|
||||
.yellow-pane a:hover { text-decoration:none; color:#f00; }
|
||||
.yellow-pane p { margin:0.5em; }
|
||||
.yellow-pane ul { list-style:none; margin:0em 0.5em; padding:0px; }
|
||||
|
||||
#yellowpaneedit { }
|
||||
#yellowpaneshow { min-width:250px; overflow:auto; }
|
||||
#yellowpaneuser { }
|
||||
#yellowedittext { margin:0px; margin-bottom:5px; padding:5px; border:solid 1px #ccc; resize:none; font-size:0.9em }
|
||||
#yelloweditbuttons { margin-bottom:5px; width:100%; }
|
||||
#yelloweditbuttons input { margin-left:5px; }
|
||||
.yellow-btn {
|
||||
margin:0px;
|
||||
padding:4px 22px;
|
||||
font-size:0.9em;
|
||||
background-color:#eaeaea; color:#333333;
|
||||
background-image:linear-gradient(to bottom, #ffffff, #e1e1e1);
|
||||
background-repeat:repeat-x;
|
||||
border:1px solid #ccc;
|
||||
border-color:#d1d1d1 #d1d1d1 #aaaaaa;
|
||||
border-radius:4px;
|
||||
outline-offset:-2px; }
|
||||
.yellow-btn:hover, .yellow-btn:focus, .yellow-btn:active { background-image:none; }
|
||||
.yellow-btn:active { box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.1); }
|
||||
.yellow-btn-warn {
|
||||
text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);
|
||||
background-color:#c33c35; color:#ffffff;
|
||||
background-image:linear-gradient(to bottom, #ee5f5b, #bd362f);
|
||||
border-color:#b13121 #b13121 #802020; }
|
||||
.yellow-btn-warn:active { box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.3); }
|
||||
|
||||
#yellow-paneedit { }
|
||||
#yellow-paneshow { min-width:250px; overflow:auto; }
|
||||
#yellow-paneuser { }
|
||||
#yellow-edittext { margin:0px; margin-bottom:5px; padding:5px; border:1px solid #ccc; resize:none; font-size:0.9em }
|
||||
#yellow-editbuttons { margin-bottom:5px; width:100%; }
|
|
@ -4,11 +4,11 @@
|
|||
// Yellow main API
|
||||
var yellow =
|
||||
{
|
||||
version: "0.0.0", //Hello web interface!
|
||||
onClick: function(e) { yellow.webinterface.hidePanesOnClick(e); },
|
||||
onShow: function(id) { yellow.webinterface.showPane(id); },
|
||||
onReset: function(id) { yellow.webinterface.resetPane(id); },
|
||||
version: "0.1.0",
|
||||
onClick: function(e) { yellow.webinterface.hidePanesOnClick(yellow.toolbox.getEventElement(e)); },
|
||||
onKeydown: function(e) { yellow.webinterface.hidePanesOnKeydown(yellow.toolbox.getEventKeycode(e)); },
|
||||
onResize: function() { yellow.webinterface.resizePanes(); },
|
||||
onShow: function(id) { yellow.webinterface.showPane(id); },
|
||||
webinterface:{}, page:{}, pages:{}, toolbox:{}, config:{}, text:{}
|
||||
}
|
||||
|
||||
|
@ -23,8 +23,9 @@ yellow.webinterface =
|
|||
init: function()
|
||||
{
|
||||
this.intervalId = setInterval("yellow.webinterface.create()", 1);
|
||||
window.onresize = yellow.onResize;
|
||||
document.onclick = yellow.onClick;
|
||||
yellow.toolbox.addEvent(window, "resize", yellow.onResize);
|
||||
yellow.toolbox.addEvent(document, "click", yellow.onClick);
|
||||
yellow.toolbox.addEvent(document, "keydown", yellow.onKeydown);
|
||||
},
|
||||
|
||||
// Create action bar and panes
|
||||
|
@ -37,34 +38,34 @@ yellow.webinterface =
|
|||
{
|
||||
var location = yellow.config.baseLocation+yellow.config.pluginsLocation;
|
||||
var element = document.createElement("div");
|
||||
element.className = "yellowbar";
|
||||
element.setAttribute("id", "yellowbar");
|
||||
element.className = "yellow-bar";
|
||||
element.setAttribute("id", "yellow-bar");
|
||||
element.innerHTML =
|
||||
"<div class=\"yellowbarleft\">"+
|
||||
"<div class=\"yellow-barleft\">"+
|
||||
"<img src=\""+location+"core_webinterface.png\" width=\"16\" height=\"16\"> Yellow"+
|
||||
"<button class=\"yellowbarlink\" onclick=\"yellow.onShow('yellowpaneedit');\">"+this.getText("Edit")+"</button>"+
|
||||
"<button class=\"yellowbarlink\" onclick=\"yellow.onShow('yellowpaneshow');\">"+this.getText("Show")+"</button>"+
|
||||
"<button class=\"yellow-barlink\" onclick=\"yellow.onShow('yellow-paneedit');\">"+this.getText("Edit")+"</button>"+
|
||||
"<button class=\"yellow-barlink\" onclick=\"yellow.onShow('yellow-paneshow');\">"+this.getText("Show")+"</button>"+
|
||||
"</div>"+
|
||||
"<div class=\"yellowbarright\">"+
|
||||
"<button class=\"yellowbarlink\" onclick=\"yellow.onShow('yellowpaneuser');\" id=\"yellowusername\">"+this.getText("User")+"</button>"+
|
||||
"<div class=\"yellow-barright\">"+
|
||||
"<button class=\"yellow-barlink\" onclick=\"yellow.onShow('yellow-paneuser');\" id=\"yellow-username\">"+this.getText("User")+"</button>"+
|
||||
"</div>";
|
||||
body.insertBefore(element, body.firstChild);
|
||||
yellow.toolbox.insertAfter(this.createPane("yellowpaneedit"), body.firstChild);
|
||||
yellow.toolbox.insertAfter(this.createPane("yellowpaneshow", yellow.pages), body.firstChild);
|
||||
yellow.toolbox.insertAfter(this.createPane("yellowpaneuser"), body.firstChild);
|
||||
yellow.toolbox.setText(document.getElementById("yellowusername"), yellow.config.userName+" ↓");
|
||||
yellow.toolbox.setText(document.getElementById("yellowedittext"), yellow.page.rawData);
|
||||
yellow.toolbox.insertAfter(this.createPane("yellow-paneedit"), body.firstChild);
|
||||
yellow.toolbox.insertAfter(this.createPane("yellow-paneshow", yellow.pages), body.firstChild);
|
||||
yellow.toolbox.insertAfter(this.createPane("yellow-paneuser"), body.firstChild);
|
||||
yellow.toolbox.setText(document.getElementById("yellow-username"), yellow.config.userName+" ↓");
|
||||
yellow.toolbox.setText(document.getElementById("yellow-edittext"), yellow.page.rawData);
|
||||
} else {
|
||||
var element = document.createElement("div");
|
||||
element.className = "yellowlogin yellowbubble";
|
||||
element.setAttribute("id", "yellowlogin");
|
||||
element.className = "yellow-login";
|
||||
element.setAttribute("id", "yellow-login");
|
||||
element.innerHTML =
|
||||
"<form method=\"post\" name=\"formlogin\">"+
|
||||
"<input type=\"hidden\" name=\"action\" value=\"login\"/>"+
|
||||
"<h1>"+this.getText("LoginText")+"</h1>"+
|
||||
"<p>"+this.getText("LoginEmail")+" <input name=\"email\" maxlength=\"64\" /></p>"+
|
||||
"<p>"+this.getText("LoginPassword")+" <input type=\"password\" name=\"password\" maxlength=\"64\" /></p>"+
|
||||
"<p><input type=\"submit\" value=\""+this.getText("LoginButton")+"\"/></p>"+
|
||||
"<p><input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("LoginButton")+"\"/></p>"+
|
||||
"</form>";
|
||||
body.insertBefore(element, body.firstChild);
|
||||
}
|
||||
|
@ -78,19 +79,18 @@ yellow.webinterface =
|
|||
{
|
||||
if(yellow.debug) console.log("yellow.webinterface.createPane id:"+id);
|
||||
var outDiv = document.createElement("div");
|
||||
if(id == "yellowpaneedit")
|
||||
if(id == "yellow-paneedit")
|
||||
{
|
||||
outDiv.innerHTML =
|
||||
"<p>Editing page...</p>"+
|
||||
"<form method=\"post\" name=\"formeditor\">"+
|
||||
"<input type=\"hidden\" name=\"action\" value=\"edit\"/>"+
|
||||
"<textarea id=\"yellowedittext\" name=\"rawdata\"></textarea>"+
|
||||
"<div id=\"yelloweditbuttons\">"+
|
||||
"<input type=\"submit\" value=\""+this.getText("SaveButton")+"\"/>"+
|
||||
"<input type=\"button\" value=\""+this.getText("CancelButton")+"\" onclick=\"yellow.onReset('yellowpaneedit');\"/>"+
|
||||
"<textarea id=\"yellow-edittext\" name=\"rawdata\"></textarea>"+
|
||||
"<div id=\"yellow-editbuttons\">"+
|
||||
"<input class=\"yellow-btn\" type=\"submit\" value=\""+this.getText("SaveButton")+"\"/>"+
|
||||
"</div>"+
|
||||
"</form>";
|
||||
} else if(id == "yellowpaneshow") {
|
||||
} else if(id == "yellow-paneshow") {
|
||||
outDiv.innerHTML = "<p>Showing files...</p>";
|
||||
for(var n in data)
|
||||
{
|
||||
|
@ -103,7 +103,7 @@ yellow.webinterface =
|
|||
outUl.appendChild(outLi);
|
||||
outDiv.appendChild(outUl);
|
||||
}
|
||||
} else if(id == "yellowpaneuser") {
|
||||
} else if(id == "yellow-paneuser") {
|
||||
outDiv.innerHTML =
|
||||
"<p>"+yellow.config.userEmail+"</p>"+
|
||||
"<form method=\"post\" name=\"formlogout\">"+
|
||||
|
@ -112,68 +112,67 @@ yellow.webinterface =
|
|||
"</form>";
|
||||
}
|
||||
var element = document.createElement("div");
|
||||
element.className = "yellowpane yellowbubble";
|
||||
element.className = "yellow-pane yellow-panebubble";
|
||||
element.setAttribute("id", id);
|
||||
element.style.display = "none";
|
||||
element.appendChild(outDiv);
|
||||
return element;
|
||||
},
|
||||
|
||||
// Reset pane
|
||||
resetPane: function(id)
|
||||
{
|
||||
if(id == "yellowpaneedit")
|
||||
{
|
||||
document.formeditor.reset();
|
||||
yellow.toolbox.setText(document.getElementById("yellowedittext"), yellow.page.rawData);
|
||||
this.hidePane(id);
|
||||
}
|
||||
},
|
||||
|
||||
// Show pane
|
||||
showPane: function(id)
|
||||
{
|
||||
if(document.getElementById(id).style.display == "block")
|
||||
if(document.getElementById(id).style.display != "block")
|
||||
{
|
||||
this.hidePanes();
|
||||
} else {
|
||||
this.hidePanes();
|
||||
if(yellow.debug) console.log("yellow.webinterface.showPane id:"+id);
|
||||
document.getElementById(id).style.display = "block";
|
||||
this.resizePanes(true);
|
||||
} else {
|
||||
this.hidePane(id);
|
||||
}
|
||||
},
|
||||
|
||||
// Hide pane
|
||||
hidePane: function(id)
|
||||
{
|
||||
if(document.getElementById(id)) document.getElementById(id).style.display = "none";
|
||||
if(document.getElementById(id).style.display != "none")
|
||||
{
|
||||
if(yellow.debug) console.log("yellow.webinterface.hidePane id:"+id);
|
||||
document.getElementById(id).style.display = "none";
|
||||
}
|
||||
},
|
||||
|
||||
// Hide all panes
|
||||
hidePanes: function()
|
||||
{
|
||||
for(var element=document.getElementById("yellowbar"); element; element=element.nextSibling)
|
||||
for(var element=document.getElementById("yellow-bar"); element; element=element.nextSibling)
|
||||
{
|
||||
if(element.className && element.className.indexOf("yellowpane")>=0)
|
||||
if(element.className && element.className.indexOf("yellow-pane")>=0)
|
||||
{
|
||||
this.hidePane(element.getAttribute("id"));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Hide all panes on mouse click
|
||||
hidePanesOnClick: function(e)
|
||||
// Hide all panes on mouse click outside
|
||||
hidePanesOnClick: function(element)
|
||||
{
|
||||
var element = yellow.toolbox.getElementForEvent(e);
|
||||
while(element = element.parentNode)
|
||||
{
|
||||
if(element.className)
|
||||
{
|
||||
if(element.className.indexOf("yellowpane")>=0 || element.className.indexOf("yellowbar")>=0) return;
|
||||
if(element.className.indexOf("yellow-pane")>=0 || element.className.indexOf("yellow-bar")>=0) return;
|
||||
}
|
||||
}
|
||||
this.hidePanes();
|
||||
},
|
||||
|
||||
// Hide all panes on ESC key
|
||||
hidePanesOnKeydown: function(keycode)
|
||||
{
|
||||
if(keycode == 27) this.hidePanes();
|
||||
},
|
||||
|
||||
// Resize panes, recalculate height and width where needed
|
||||
resizePanes: function(force)
|
||||
|
@ -190,28 +189,28 @@ yellow.webinterface =
|
|||
interfaceHeight = window.document.body.clientHeight;
|
||||
}
|
||||
}
|
||||
if((interfaceHeight!=this.heightOld || force) && document.getElementById("yellowbar"))
|
||||
if((interfaceHeight!=this.heightOld || force) && document.getElementById("yellow-bar"))
|
||||
{
|
||||
this.heightOld = interfaceHeight;
|
||||
var elementBar = document.getElementById("yellowbar");
|
||||
var elementBar = document.getElementById("yellow-bar");
|
||||
var borderRadius = 6;
|
||||
var panePadding = 5;
|
||||
var editPadding = 5;
|
||||
var interfaceTop = elementBar.offsetHeight + 1;
|
||||
interfaceHeight -= interfaceTop + borderRadius*2;
|
||||
if(yellow.debug) console.log("yellow.webinterface.resizePanes windowY:"+interfaceHeight+" actionbarY:"+document.getElementById("yellowbar").offsetHeight+" buttonsY:"+document.getElementById("yelloweditbuttons").offsetHeight+" editorX:"+document.getElementById("yellowpaneedit").offsetWidth);
|
||||
if(yellow.debug) console.log("yellow.webinterface.resizePanes windowY:"+interfaceHeight+" actionbarY:"+document.getElementById("yellow-bar").offsetHeight+" buttonsY:"+document.getElementById("yellow-editbuttons").offsetHeight+" editorX:"+document.getElementById("yellow-paneedit").offsetWidth);
|
||||
|
||||
this.setPaneHeight(document.getElementById("yellowpaneedit"), interfaceHeight, null, interfaceTop);
|
||||
this.setPaneHeight(document.getElementById("yellowpaneshow"), null, interfaceHeight, interfaceTop);
|
||||
this.setPaneHeight(document.getElementById("yellowpaneuser"), null, null, interfaceTop);
|
||||
this.setPaneHeight(document.getElementById("yellow-paneedit"), interfaceHeight, null, interfaceTop);
|
||||
this.setPaneHeight(document.getElementById("yellow-paneshow"), null, interfaceHeight, interfaceTop);
|
||||
this.setPaneHeight(document.getElementById("yellow-paneuser"), null, null, interfaceTop);
|
||||
|
||||
var editTextHeight = interfaceHeight - panePadding*2 - editPadding*2 - 10
|
||||
- (document.getElementById("yellowedittext").offsetTop-document.getElementById("yellowpaneedit").getElementsByTagName("p")[0].offsetTop)
|
||||
- document.getElementById("yelloweditbuttons").offsetHeight;
|
||||
document.getElementById("yellowpaneedit").style.width = Math.max(0, elementBar.offsetWidth - panePadding*2) + "px";
|
||||
document.getElementById("yellowedittext").style.height = Math.max(0, editTextHeight) + "px";
|
||||
document.getElementById("yellowedittext").style.width = Math.max(0, document.getElementById("yellowpaneedit").offsetWidth - 2 - panePadding*2 - editPadding*2) + "px";
|
||||
document.getElementById("yellowpaneuser").style.marginLeft = Math.max(0, elementBar.offsetWidth - document.getElementById("yellowpaneuser").offsetWidth) + "px";
|
||||
- (document.getElementById("yellow-edittext").offsetTop-document.getElementById("yellow-paneedit").getElementsByTagName("p")[0].offsetTop)
|
||||
- document.getElementById("yellow-editbuttons").offsetHeight;
|
||||
document.getElementById("yellow-paneedit").style.width = Math.max(0, elementBar.offsetWidth - panePadding*2) + "px";
|
||||
document.getElementById("yellow-edittext").style.height = Math.max(0, editTextHeight) + "px";
|
||||
document.getElementById("yellow-edittext").style.width = Math.max(0, document.getElementById("yellow-paneedit").offsetWidth - 2 - panePadding*2 - editPadding*2) + "px";
|
||||
document.getElementById("yellow-paneuser").style.marginLeft = Math.max(0, elementBar.offsetWidth - document.getElementById("yellow-paneuser").offsetWidth) + "px";
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -250,11 +249,25 @@ yellow.toolbox =
|
|||
referenceElement.parentNode.insertBefore(newElement, referenceElement.nextSibling);
|
||||
},
|
||||
|
||||
// Return element for event
|
||||
getElementForEvent: function(e)
|
||||
// Add event handler
|
||||
addEvent: function(element, type, handler)
|
||||
{
|
||||
if(element.addEventListener) element.addEventListener(type, handler, false);
|
||||
else element.attachEvent('on'+type, handler);
|
||||
},
|
||||
|
||||
// Return element of event
|
||||
getEventElement: function(e)
|
||||
{
|
||||
e = e ? e : window.event;
|
||||
return e.target ? e.target : e.srcElement;
|
||||
},
|
||||
|
||||
// Return keycode of event
|
||||
getEventKeycode: function(e)
|
||||
{
|
||||
e = e ? e : window.event;
|
||||
return e.keyCode
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
// Web interface core plugin
|
||||
class Yellow_Webinterface
|
||||
{
|
||||
const Version = "0.0.0"; //Hello web interface!
|
||||
const Version = "0.1.0";
|
||||
var $yellow; //access to API
|
||||
var $users; //web interface users
|
||||
var $activeLocation; //web interface location? (boolean)
|
||||
|
@ -92,11 +92,10 @@ class Yellow_Webinterface
|
|||
{
|
||||
if($this->isWebinterfaceLocation() && $this->isUser())
|
||||
{
|
||||
$this->yellow->toolbox->timerStart($time);
|
||||
$baseLocation = $this->yellow->config->get("baseLocation");
|
||||
$webinterfaceLocation = rtrim($this->yellow->config->get("webinterfaceLocation"), '/');
|
||||
$text = preg_replace("#<a(.*?)href=\"$baseLocation(?!$webinterfaceLocation)(.*?)\"(.*?)>#",
|
||||
"<a$1href=\"$baseLocation$webinterfaceLocation$2\"$3>", $text);
|
||||
$webinterfaceLocation = trim($this->yellow->config->get("webinterfaceLocation"), '/');
|
||||
$text = preg_replace("#<a(.*?)href=\"$baseLocation/(?!$webinterfaceLocation)(.*?)\"(.*?)>#",
|
||||
"<a$1href=\"$baseLocation/$webinterfaceLocation/$2\"$3>", $text);
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
@ -107,7 +106,7 @@ class Yellow_Webinterface
|
|||
$statusCode = 0;
|
||||
if($_POST["action"] == "edit")
|
||||
{
|
||||
if(strlen($_POST["rawdata"]))
|
||||
if(!empty($_POST["rawdata"]))
|
||||
{
|
||||
$fileHandle = @fopen($fileName, "w");
|
||||
if($fileHandle)
|
||||
|
@ -117,7 +116,12 @@ class Yellow_Webinterface
|
|||
} else {
|
||||
die("Configuration problem: Can't write page '$fileName'!");
|
||||
}
|
||||
$statusCode = 303;
|
||||
$this->yellow->sendStatus($statusCode, "Location: http://$_SERVER[SERVER_NAME]$baseLocation$location");
|
||||
}
|
||||
} else if($_POST["action"]== "login") {
|
||||
$statusCode = 303;
|
||||
$this->yellow->sendStatus($statusCode, "Location: http://$_SERVER[SERVER_NAME]$baseLocation$location");
|
||||
} else if($_POST["action"]== "logout") {
|
||||
$this->users->destroyCookie("login");
|
||||
$this->activeUserEmail = "";
|
||||
|
@ -143,8 +147,8 @@ class Yellow_Webinterface
|
|||
// Check web interface location
|
||||
function checkWebinterfaceLocation($location)
|
||||
{
|
||||
$locationLength = strlen($this->yellow->config->get("webinterfaceLocation"));
|
||||
$this->activeLocation = substr($location, 0, $locationLength) == $this->yellow->config->get("webinterfaceLocation");
|
||||
$locationLength = strlenu($this->yellow->config->get("webinterfaceLocation"));
|
||||
$this->activeLocation = substru($location, 0, $locationLength) == $this->yellow->config->get("webinterfaceLocation");
|
||||
return $this->isWebinterfaceLocation();
|
||||
}
|
||||
|
||||
|
@ -193,7 +197,7 @@ class Yellow_Webinterface
|
|||
return !empty($this->activeUserEmail);
|
||||
}
|
||||
|
||||
// Return page tree with content/media information
|
||||
// Return page tree with content information (2 levels)
|
||||
function getPagesData()
|
||||
{
|
||||
$data = array();
|
||||
|
@ -201,7 +205,15 @@ class Yellow_Webinterface
|
|||
{
|
||||
$data[$page->fileName] = array();
|
||||
$data[$page->fileName]["location"] = $page->getLocation();
|
||||
$data[$page->fileName]["modified"] = $page->getModified();
|
||||
$data[$page->fileName]["title"] = $page->getTitle();
|
||||
foreach($page->getChildren(true) as $page)
|
||||
{
|
||||
$data[$page->fileName] = array();
|
||||
$data[$page->fileName]["location"] = $page->getLocation();
|
||||
$data[$page->fileName]["modified"] = $page->getModified();
|
||||
$data[$page->fileName]["title"] = $page->getTitle();
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
@ -236,8 +248,8 @@ class Yellow_WebinterfaceUsers
|
|||
foreach($fileData as $line)
|
||||
{
|
||||
if(preg_match("/^\//", $line)) continue;
|
||||
preg_match("/^(.*?)\s*,(.*?),\s*(.*?),\s*(.*?)\s*$/", $line, $matches);
|
||||
if($matches[1]!="" && $matches[2]!="" && $matches[3]!="" && $matches[4]!="")
|
||||
preg_match("/^(.*?),\s*(.*?),\s*(.*?),\s*(.*?)\s*$/", $line, $matches);
|
||||
if(!empty($matches[1]) && !empty($matches[2]) && !empty($matches[3]) && !empty($matches[4]))
|
||||
{
|
||||
$this->setUser($matches[1], $matches[2], $matches[3], $matches[4]);
|
||||
if(defined("DEBUG") && DEBUG>=3) echo "Yellow_WebinterfaceUsers::load email:$matches[1] $matches[3]<br/>\n";
|
||||
|
@ -254,7 +266,7 @@ class Yellow_WebinterfaceUsers
|
|||
$this->users[$email]["password"] = $password;
|
||||
$this->users[$email]["name"] = $name;
|
||||
$this->users[$email]["language"] = $language;
|
||||
$this->users[$email]["session"] = hash("sha256", $email.$password.strrev($email.$password));
|
||||
$this->users[$email]["session"] = hash("sha256", $email.$password.$password.$email);
|
||||
}
|
||||
|
||||
// Check user login
|
||||
|
|
|
@ -4,4 +4,4 @@
|
|||
<h1><?php echo $yellow->page->getTitle() ?></h1>
|
||||
<?php echo $yellow->page->getContent() ?>
|
||||
</div>
|
||||
<?php $yellow->snippet("footer") ?>
|
||||
<?php $yellow->snippet("footer") ?>
|
Loading…
Add table
Reference in a new issue