Current oav website

This commit is contained in:
Charlie Root
2023-03-20 12:18:38 +01:00
commit a096ce07cf
3270 changed files with 261778 additions and 0 deletions

View File

@ -0,0 +1,461 @@
<?php
/**
* @class template
*
* @package Clearbricks
* @subpackage Template
*
* @copyright Olivier Meunier & Association Dotclear
* @copyright GPL-2.0-only
*/
class template
{
private $self_name;
public $use_cache = true;
protected $blocks = [];
protected $values = [];
protected $remove_php = true;
protected $unknown_value_handler = null;
protected $unknown_block_handler = null;
protected $tpl_path = [];
protected $cache_dir;
protected $parent_file;
protected $compile_stack = [];
protected $parent_stack = [];
# Inclusion variables
protected static $superglobals = ['GLOBALS', '_SERVER', '_GET', '_POST', '_COOKIE', '_FILES', '_ENV', '_REQUEST', '_SESSION'];
protected static $_k;
protected static $_n;
protected static $_r;
public function __construct($cache_dir, $self_name)
{
$this->setCacheDir($cache_dir);
$this->self_name = $self_name;
$this->addValue('include', [$this, 'includeFile']);
$this->addBlock('Block', [$this, 'blockSection']);
}
public function includeFile($attr)
{
if (!isset($attr['src'])) {return;}
$src = path::clean($attr['src']);
$tpl_file = $this->getFilePath($src);
if (!$tpl_file) {return;}
if (in_array($tpl_file, $this->compile_stack)) {return;}
return
'<?php try { ' .
'echo ' . $this->self_name . "->getData('" . str_replace("'", "\'", $src) . "'); " .
'} catch (Exception $e) {} ?>' . "\n";
}
public function blockSection($attr, $content)
{
return $content;
}
public function setPath()
{
$path = [];
foreach (func_get_args() as $v) {
if (is_array($v)) {
$path = array_merge($path, array_values($v));
} else {
$path[] = $v;
}
}
foreach ($path as $k => $v) {
if (($v = path::real($v)) === false) {
unset($path[$k]);
}
}
$this->tpl_path = array_unique($path);
}
public function getPath()
{
return $this->tpl_path;
}
public function setCacheDir($dir)
{
if (!is_dir($dir)) {
throw new Exception($dir . ' is not a valid directory.');
}
if (!is_writable($dir)) {
throw new Exception($dir . ' is not writable.');
}
$this->cache_dir = path::real($dir) . '/';
}
public function addBlock($name, $callback)
{
if (!is_callable($callback)) {
throw new Exception('No valid callback for ' . $name);
}
$this->blocks[$name] = $callback;
}
public function addValue($name, $callback)
{
if (!is_callable($callback)) {
throw new Exception('No valid callback for ' . $name);
}
$this->values[$name] = $callback;
}
public function blockExists($name)
{
return isset($this->blocks[$name]);
}
public function valueExists($name)
{
return isset($this->values[$name]);
}
public function tagExists($name)
{
return $this->blockExists($name) || $this->valueExists($name);
}
public function getValueCallback($name)
{
if ($this->valueExists($name)) {
return $this->values[$name];
}
return false;
}
public function getBlockCallback($name)
{
if ($this->blockExists($name)) {
return $this->blocks[$name];
}
return false;
}
public function getBlocksList()
{
return array_keys($this->blocks);
}
public function getValuesList()
{
return array_keys($this->values);
}
public function getFile($file)
{
$tpl_file = $this->getFilePath($file);
if (!$tpl_file) {
throw new Exception('No template found for ' . $file);
return false;
}
$file_md5 = md5($tpl_file);
$dest_file = sprintf('%s/%s/%s/%s/%s.php',
$this->cache_dir,
'cbtpl',
substr($file_md5, 0, 2),
substr($file_md5, 2, 2),
$file_md5
);
clearstatcache();
$stat_f = $stat_d = false;
if (file_exists($dest_file)) {
$stat_f = stat($tpl_file);
$stat_d = stat($dest_file);
}
# We create template if:
# - dest_file doest not exists
# - we don't want cache
# - dest_file size == 0
# - tpl_file is more recent thant dest_file
if (!$stat_d || !$this->use_cache || $stat_d['size'] == 0 || $stat_f['mtime'] > $stat_d['mtime']) {
files::makeDir(dirname($dest_file), true);
if (($fp = @fopen($dest_file, 'wb')) === false) {
throw new Exception('Unable to create cache file');
}
$fc = $this->compileFile($tpl_file);
fwrite($fp, $fc);
fclose($fp);
files::inheritChmod($dest_file);
}
return $dest_file;
}
public function getFilePath($file)
{
foreach ($this->tpl_path as $p) {
if (file_exists($p . '/' . $file)) {
return $p . '/' . $file;
}
}
return false;
}
public function getParentFilePath($previous_path, $file)
{
$check_file = false;
foreach ($this->tpl_path as $p) {
if ($check_file && file_exists($p . '/' . $file)) {
return $p . "/" . $file;
}
if ($p == $previous_path) {
$check_file = true;
}
}
return false;
}
public function getData($________)
{
self::$_k = array_keys($GLOBALS);
foreach (self::$_k as self::$_n) {
if (!in_array(self::$_n, self::$superglobals)) {
global ${self::$_n};
}
}
$dest_file = $this->getFile($________);
ob_start();
if (ini_get('display_errors') == true) {
include $dest_file;
} else {
@include $dest_file;
}
self::$_r = ob_get_contents();
ob_end_clean();
return self::$_r;
}
protected function getCompiledTree($file, &$err)
{
$fc = file_get_contents($file);
$this->compile_stack[] = $file;
# Remove every PHP tags
if ($this->remove_php) {
$fc = preg_replace('/<\?(?=php|=|\s).*?\?>/ms', '', $fc);
}
# Transform what could be considered as PHP short tags
$fc = preg_replace('/(<\?(?!php|=|\s))(.*?)(\?>)/ms',
'<?php echo "$1"; ?>$2<?php echo "$3"; ?>', $fc);
# Remove template comments <!-- #... -->
$fc = preg_replace('/(^\s*)?<!-- #(.*?)-->/ms', '', $fc);
# Lexer part : split file into small pieces
# each array entry will be either a tag or plain text
$blocks = preg_split(
'#(<tpl:\w+[^>]*>)|(</tpl:\w+>)|({{tpl:\w+[^}]*}})#msu', $fc, -1,
PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
# Next : build semantic tree from tokens.
$rootNode = new tplNode();
$node = $rootNode;
$errors = [];
$this->parent_file = '';
foreach ($blocks as $id => $block) {
$isblock = preg_match('#<tpl:(\w+)(?:(\s+.*?)>|>)|</tpl:(\w+)>|{{tpl:(\w+)(\s(.*?))?}}#ms', $block, $match);
if ($isblock == 1) {
if (substr($match[0], 1, 1) == '/') {
// Closing tag, check if it matches current opened node
$tag = $match[3];
if (($node instanceof tplNodeBlock) && $node->getTag() == $tag) {
$node->setClosing();
$node = $node->getParent();
} else {
// Closing tag does not match opening tag
// Search if it closes a parent tag
$search = $node;
while ($search->getTag() != 'ROOT' && $search->getTag() != $tag) {
$search = $search->getParent();
}
if ($search->getTag() == $tag) {
$errors[] = sprintf(
__('Did not find closing tag for block <tpl:%s>. Content has been ignored.'),
html::escapeHTML($node->getTag()));
$search->setClosing();
$node = $search->getParent();
} else {
$errors[] = sprintf(
__('Unexpected closing tag </tpl:%s> found.'),
$tag);
}
}
} elseif (substr($match[0], 0, 1) == '{') {
// Value tag
$tag = $match[4];
$str_attr = '';
$attr = [];
if (isset($match[6])) {
$str_attr = $match[6];
$attr = $this->getAttrs($match[6]);
}
if (strtolower($tag) == "extends") {
if (isset($attr['parent']) && $this->parent_file == "") {
$this->parent_file = $attr['parent'];
}
} elseif (strtolower($tag) == "parent") {
$node->addChild(new tplNodeValueParent($tag, $attr, $str_attr));
} else {
$node->addChild(new tplNodeValue($tag, $attr, $str_attr));
}
} else {
// Opening tag, create new node and dive into it
$tag = $match[1];
if ($tag == "Block") {
$newnode = new tplNodeBlockDefinition($tag, isset($match[2]) ? $this->getAttrs($match[2]) : []);
} else {
$newnode = new tplNodeBlock($tag, isset($match[2]) ? $this->getAttrs($match[2]) : []);
}
$node->addChild($newnode);
$node = $newnode;
}
} else {
// Simple text
$node->addChild(new tplNodeText($block));
}
}
if (($node instanceof tplNodeBlock) && !$node->isClosed()) {
$errors[] = sprintf(
__('Did not find closing tag for block <tpl:%s>. Content has been ignored.'),
html::escapeHTML($node->getTag()));
}
$err = "";
if (count($errors) > 0) {
$err = "\n\n<!-- \n" .
__('WARNING: the following errors have been found while parsing template file :') .
"\n * " .
join("\n * ", $errors) .
"\n -->\n";
}
return $rootNode;
}
protected function compileFile($file)
{
$tree = null;
while (true) {
if ($file && !in_array($file, $this->parent_stack)) {
$tree = $this->getCompiledTree($file, $err);
if ($this->parent_file == "__parent__") {
$this->parent_stack[] = $file;
$newfile = $this->getParentFilePath(dirname($file), basename($file));
if (!$newfile) {
throw new Exception('No template found for ' . basename($file));
return false;
}
$file = $newfile;
} elseif ($this->parent_file != "") {
$this->parent_stack[] = $file;
$file = $this->getFilePath($this->parent_file);
if (!$file) {
throw new Exception('No template found for ' . $this->parent_file);
return false;
}
} else {
return $tree->compile($this) . $err;
}
} else {
if ($tree != null) {
return $tree->compile($this) . $err;
} else {
return '';
}
}
}
}
public function compileBlockNode($tag, $attr, $content)
{
$res = '';
if (isset($this->blocks[$tag])) {
$res .= call_user_func($this->blocks[$tag], $attr, $content);
} elseif ($this->unknown_block_handler != null) {
$res .= call_user_func($this->unknown_block_handler, $tag, $attr, $content);
}
return $res;
}
public function compileValueNode($tag, $attr, $str_attr)
{
$res = '';
if (isset($this->values[$tag])) {
$res .= call_user_func($this->values[$tag], $attr, ltrim($str_attr));
} elseif ($this->unknown_value_handler != null) {
$res .= call_user_func($this->unknown_value_handler, $tag, $attr, $str_attr);
}
return $res;
}
protected function compileValue($match)
{
$v = $match[1];
$attr = isset($match[2]) ? $this->getAttrs($match[2]) : [];
$str_attr = isset($match[2]) ? $match[2] : null;
return call_user_func($this->values[$v], $attr, ltrim($str_attr));
}
public function setUnknownValueHandler($callback)
{
if (is_callable($callback)) {
$this->unknown_value_handler = $callback;
}
}
public function setUnknownBlockHandler($callback)
{
if (is_callable($callback)) {
$this->unknown_block_handler = $callback;
}
}
protected function getAttrs($str)
{
$res = [];
if (preg_match_all('|([a-zA-Z0-9_:-]+)="([^"]*)"|ms', $str, $m) > 0) {
foreach ($m[1] as $i => $v) {
$res[$v] = $m[2][$i];
}
}
return $res;
}
}

View File

@ -0,0 +1,72 @@
<?php
/**
* @class tplNode
* @brief Template nodes, for parsing purposes
*
* Generic list node, this one may only be instanciated once for root element
*
* @package Clearbricks
* @subpackage Template
*
* @copyright Olivier Meunier & Association Dotclear
* @copyright GPL-2.0-only
*/
class tplNode
{
# Basic tree structure : links to parent, children forrest
protected $parentNode;
protected $children;
public function __construct()
{
$this->children = new ArrayObject();
$this->parentNode = null;
}
// Returns compiled block
public function compile($tpl)
{
$res = '';
foreach ($this->children as $child) {
$res .= $child->compile($tpl);
}
return $res;
}
# Add a children to current node
public function addChild($child)
{
$this->children[] = $child;
$child->setParent($this);
}
# Set current node children
public function setChildren($children)
{
$this->children = $children;
foreach ($this->children as $child) {
$child->setParent($this);
}
}
# Defines parent for current node
protected function setParent($parent)
{
$this->parentNode = $parent;
}
# Retrieves current node parent.
# If parent is root node, null is returned
public function getParent()
{
return $this->parentNode;
}
# Current node tag
public function getTag()
{
return "ROOT";
}
}

View File

@ -0,0 +1,49 @@
<?php
/**
* @class tplNodeBlock
* @brief Block node, for all <tpl:Tag>...</tpl:Tag>
*
* @package Clearbricks
* @subpackage Template
*
* @copyright Olivier Meunier & Association Dotclear
* @copyright GPL-2.0-only
*/
class tplNodeBlock extends tplNode
{
protected $attr;
protected $tag;
protected $closed;
public function __construct($tag, $attr)
{
parent::__construct();
$this->content = '';
$this->tag = $tag;
$this->attr = $attr;
$this->closed = false;
}
public function setClosing()
{
$this->closed = true;
}
public function isClosed()
{
return $this->closed;
}
public function compile($tpl)
{
if ($this->closed) {
$content = parent::compile($tpl);
return $tpl->compileBlockNode($this->tag, $this->attr, $content);
} else {
// if tag has not been closed, silently ignore its content...
return '';
}
}
public function getTag()
{
return $this->tag;
}
}

View File

@ -0,0 +1,121 @@
<?php
/**
* @class tplNodeBlockDefinition
* @brief Block node, for all <tpl:Tag>...</tpl:Tag>
*
* @package Clearbricks
* @subpackage Template
*
* @copyright Olivier Meunier & Association Dotclear
* @copyright GPL-2.0-only
*/
class tplNodeBlockDefinition extends tplNodeBlock
{
protected static $stack = [];
protected static $current_block = null;
protected static $c = 1;
protected $name;
/**
* Renders the parent block of currently being displayed block
* @param template $tpl the current template engine instance
* @return string the compiled parent block
*/
public static function renderParent($tpl)
{
return self::getStackBlock(self::$current_block, $tpl);
}
/**
* resets blocks stack
*/
public static function reset()
{
self::$stack = [];
self::$current_block = null;
}
/**
* Retrieves block defined in call stack
* @param string $name the block name
* @param template $tpl the template engine instance
* @return string the block (empty string if unavailable)
*/
public static function getStackBlock($name, $tpl)
{
$stack = &self::$stack[$name];
$pos = $stack['pos'];
// First check if block position is correct
if (isset($stack['blocks'][$pos])) {
$saved_current_block = self::$current_block;
self::$current_block = $name;
if (!is_string($stack['blocks'][$pos])) {
// Not a string ==> need to compile the tree
// Go deeper 1 level in stack, to enable calls to parent
$stack['pos']++;
$ret = '';
// Compile each and every children
foreach ($stack['blocks'][$pos] as $child) {
$ret .= $child->compile($tpl);
}
$stack['pos']--;
$stack['blocks'][$pos] = $ret;
} else {
// Already compiled, nice ! Simply return string
$ret = $stack['blocks'][$pos];
}
return $ret;
} else {
// Not found => return empty
return '';
}
}
/**
* Block definition specific constructor : keep block name in mind
* @param string $tag Current tag (might be "Block")
* @param array $attr Tag attributes (must contain "name" attribute)
*/
public function __construct($tag, $attr)
{
parent::__construct($tag, $attr);
$this->name = '';
if (isset($attr['name'])) {
$this->name = $attr['name'];
}
}
/**
* Override tag closing processing. Here we enrich the block stack to
* keep block history.
*/
public function setClosing()
{
if (!isset(self::$stack[$this->name])) {
self::$stack[$this->name] = [
'pos' => 0, // pos is the pointer to the current block being rendered
'blocks' => []];
}
parent::setClosing();
self::$stack[$this->name]['blocks'][] = $this->children;
$this->children = new ArrayObject();
}
/**
* Compile the block definition : grab latest block content being defined
* @param template $tpl current template engine instance
* @return string the compiled block
*/
public function compile($tpl)
{
return $tpl->compileBlockNode(
$this->tag,
$this->attr,
self::getStackBlock($this->name, $tpl)
);
}
}

View File

@ -0,0 +1,33 @@
<?php
/**
* @class tplNodeText
* @brief Text node, for any non-tpl content
*
* @package Clearbricks
* @subpackage Template
*
* @copyright Olivier Meunier & Association Dotclear
* @copyright GPL-2.0-only
*/
class tplNodeText extends tplNode
{
// Simple text node, only holds its content
protected $content;
public function __construct($text)
{
parent::__construct();
$this->content = $text;
}
public function compile($tpl)
{
return $this->content;
}
public function getTag()
{
return "TEXT";
}
}

View File

@ -0,0 +1,37 @@
<?php
/**
* @class tplNodeValue
* @brief Value node, for all {{tpl:Tag}}
*
* @package Clearbricks
* @subpackage Template
*
* @copyright Olivier Meunier & Association Dotclear
* @copyright GPL-2.0-only
*/
class tplNodeValue extends tplNode
{
protected $attr;
protected $str_attr;
protected $tag;
public function __construct($tag, $attr, $str_attr)
{
parent::__construct();
$this->content = '';
$this->tag = $tag;
$this->attr = $attr;
$this->str_attr = $str_attr;
}
public function compile($tpl)
{
return $tpl->compileValueNode($this->tag, $this->attr, $this->str_attr);
}
public function getTag()
{
return $this->tag;
}
}

View File

@ -0,0 +1,20 @@
<?php
/**
* @class tplNodeValueParent
* @brief Value node, for all {{tpl:Tag}}
*
* @package Clearbricks
* @subpackage Template
*
* @copyright Olivier Meunier & Association Dotclear
* @copyright GPL-2.0-only
*/
class tplNodeValueParent extends tplNodeValue
{
public function compile($tpl)
{
// simply ask currently being displayed to display itself!
return tplNodeBlockDefinition::renderParent($tpl);
}
}