mirror of
https://github.com/boostorg/website.git
synced 2026-01-19 04:42:17 +00:00
boost_archive.php: For mime types, the current list seems fine, and a full list doesn't seem feasable. boost_filter_text.php: For supporting other character sets, could possibly use mb_detect_encoding to guess the file's encoding, but it's not currently installed on the server, and I think guesses are probably no better than just assuming everything is UTF-8. The alternative would be some mechanism to specify a file's (path's?) encoding, but I'm sure that can be done if the need arises. boost_library.php: If it turns out that a better exception is needed, then it will be handled then, so I'm not concerned with that TODO note. boost_pages.php: Sourceforge is redirecting downloads to the right place, so I'll not bother with '/download' at the end. It's probably better if the download URLs have the right filename. boost_simple_template.php: I don't think the simple template class really needs to support tricky edge cases, so I'll just leave that alone. doc/libraries.php: I don't think anyone's desperate to see the library list for ancient versions. If they are, they can get the it in json format using: http://www.boost.org/doc/libraries.json.php?version=1.11.1 site-tools/git-prep-beta.sh: Coming back to this, I think the answer is no. A change on master is only really made when it has been pushed to remote, if it's only local then it might get rebased. This script doesn't actually get much use now that there are long standing unmerged changes in beta, but it used to work well for me. site-tools/update-doc-list.php: Other TODO note was to make the script a little more automatic when run against a local git tree, but I don't think that's a use case to support in general. The tree might not be fully synced, or might be checked out from a tag, which would be harder to check.
391 lines
12 KiB
PHP
391 lines
12 KiB
PHP
<?php
|
|
|
|
require_once(__DIR__.'/boost.php');
|
|
|
|
/* Simple mustache-like template library.
|
|
* Does not implement:
|
|
*
|
|
* Lambdas
|
|
*/
|
|
class BoostSimpleTemplate {
|
|
static function render_file($path, $params) {
|
|
$context = new BoostSimpleTemplate_Context();
|
|
$context->path = $path;
|
|
$context->params = $params;
|
|
$context->partial_loader = new BoostSimpleTemplate_PartialLoader();
|
|
|
|
$nodes = $context->partial_loader->load($path);
|
|
if (!$nodes) {
|
|
throw new BoostSimpleTemplate_Exception("File not found: {$path}");
|
|
}
|
|
|
|
return self::interpret($context, $nodes);
|
|
}
|
|
|
|
static function render($template, $params, $partials = null) {
|
|
$nodes = self::parse_template($template);
|
|
|
|
$context = new BoostSimpleTemplate_Context();
|
|
$context->params = $params;
|
|
$context->path = '(text_file).mustache';
|
|
$context->partial_loader = new BoostSimpleTemplate_PartialArray();
|
|
if ($partials) foreach($partials as $symbol => $partial) {
|
|
$context->partial_loader->add($symbol, self::parse_template($partial));
|
|
}
|
|
|
|
return self::interpret($context, $nodes);
|
|
}
|
|
|
|
static function get_regexp($open_delim, $close_delim) {
|
|
$open_delim = preg_quote($open_delim, '@');
|
|
$close_delim = preg_quote($close_delim, '@');
|
|
|
|
return "@
|
|
(?P<leading_whitespace>^[ \\t]*)?
|
|
(?P<tag>{$open_delim}(?:
|
|
!(?P<comment>.*?){$close_delim} |
|
|
(?P<symbol_operator>[#/^&>]?)\\s*(?P<symbol>[\\w?!/.-]*)\\s*{$close_delim} |
|
|
{\\s*(?P<unescaped>[\\w?!\\/.-]+)\\s*}{$close_delim} |
|
|
=\\s*(?P<open_delim>[^=\\s]+?)\\s*(?P<close_delim>[^=\\s]+?)\\s*={$close_delim} |
|
|
(?P<error>)
|
|
))
|
|
(?P<trailing_whitespace>[ \\t]*(?:\\r?\\n|\\Z))?
|
|
@xsm";
|
|
}
|
|
|
|
static function parse_template($template) {
|
|
$nodes = array();
|
|
$stack = array();
|
|
$tokenizer = self::get_regexp('{{', '}}');
|
|
|
|
$offset = 0;
|
|
while(preg_match($tokenizer, $template, $match, PREG_OFFSET_CAPTURE, $offset)) {
|
|
$text_node = array(
|
|
'offset' => $offset,
|
|
'type' => '(text)',
|
|
'indent_start' => $offset == 0 || $template[$offset - 1] == "\n",
|
|
);
|
|
|
|
$node = array(
|
|
'offset' => $match['tag'][1],
|
|
);
|
|
|
|
if (self::match_exists($match, 'error')) {
|
|
throw new BoostSimpleTemplate_Exception("Invalid/unsupported tag", $match['tag'][1]);
|
|
}
|
|
else if (self::match_exists($match, 'unescaped')) {
|
|
$node['type'] = '&';
|
|
$node['symbol'] = $match['unescaped'][0];
|
|
}
|
|
else if (self::match_exists($match, 'open_delim')) {
|
|
$node['type'] = '=';
|
|
$node['open'] = $match['open_delim'][0];
|
|
$node['close'] = $match['close_delim'][0];
|
|
}
|
|
else if (self::match_exists($match, 'symbol')) {
|
|
$node['type'] = $match['symbol_operator'][0] ?: '(variable)';
|
|
$node['symbol'] = $match['symbol'][0];
|
|
}
|
|
else if (self::match_exists($match, 'comment')) {
|
|
$node['type'] = '!';
|
|
$node['content'] = $match['comment'][0];
|
|
}
|
|
else {
|
|
assert(false);
|
|
}
|
|
|
|
$standalone = $node['type'] != '&' && $node['type'] != '(variable)' &&
|
|
self::match_exists($match, 'leading_whitespace') &&
|
|
self::match_exists($match, 'trailing_whitespace');
|
|
|
|
if ($standalone) {
|
|
$text_node['content'] = substr($template, $offset, $match[0][1] - $offset);
|
|
$node['indent_start'] = false;
|
|
$node['indentation'] = $match['leading_whitespace'][0];
|
|
$offset = $match[0][1] + strlen($match[0][0]);
|
|
}
|
|
else {
|
|
$text_node['content'] = substr($template, $offset, $match['tag'][1] - $offset);
|
|
$node['indent_start'] = $match['tag'][1] == 0 || $template[$match['tag'][1] - 1] == "\n";
|
|
$node['indentation'] = '';
|
|
$offset = $match['tag'][1] + strlen($match['tag'][0]);
|
|
}
|
|
|
|
if ($text_node['content']) {
|
|
$nodes[] = $text_node;
|
|
}
|
|
|
|
switch($node['type']) {
|
|
case '!':
|
|
break;
|
|
case '#':
|
|
case '^':
|
|
$stack[] = array(
|
|
'nodes' => $nodes,
|
|
'node' => $node,
|
|
);
|
|
$nodes = array();
|
|
break;
|
|
case '/':
|
|
$top = array_pop($stack);
|
|
if (!$top || $top['node']['symbol'] !== $node['symbol']) {
|
|
throw new BoostSimpleTemplate_Exception("Mismatched close tag", $node['offset']);
|
|
}
|
|
$node = $top['node'];
|
|
$node['contents'] = $nodes;
|
|
$nodes = $top['nodes'];
|
|
$nodes[] = $node;
|
|
break;
|
|
case '(variable)':
|
|
case '&':
|
|
$nodes[] = $node;
|
|
break;
|
|
case '=':
|
|
$tokenizer = self::get_regexp($node['open'], $node['close']);
|
|
break;
|
|
case '>':
|
|
$nodes[] = $node;
|
|
break;
|
|
default:
|
|
assert(false); exit(1);
|
|
}
|
|
}
|
|
|
|
if ($stack) {
|
|
$top = end($stack);
|
|
throw new BoostSimpleTemplate_Exception("Unclosed tag: {$top['node']['symbol']}", $top['node']['offset']);
|
|
}
|
|
|
|
$end = substr($template, $offset);
|
|
if ($end || !$nodes) {
|
|
$nodes[] = array(
|
|
'offset' => $offset,
|
|
'type' => '(text)',
|
|
'indent_start' => $offset == 0 || $template[$offset - 1] == "\n",
|
|
'content' => $end,
|
|
);
|
|
}
|
|
|
|
return $nodes;
|
|
}
|
|
|
|
static function interpret($context, $nodes) {
|
|
$output = '';
|
|
|
|
foreach($nodes as $node) {
|
|
if (!empty($node['indent_start'])) {
|
|
$output .= $context->indentation;
|
|
}
|
|
|
|
switch($node['type']) {
|
|
case '(text)':
|
|
$output .= preg_replace('@^(?!\A)@m', $context->indentation, $node['content']);
|
|
break;
|
|
case '(variable)':
|
|
$value = self::lookup($context, $node['symbol']);
|
|
if ($value) {
|
|
$output .= html_encode($value);
|
|
}
|
|
break;
|
|
case '&':
|
|
$value = self::lookup($context, $node['symbol']);
|
|
if ($value) {
|
|
$output .= $value;
|
|
}
|
|
break;
|
|
case '#':
|
|
$value = self::lookup($context, $node['symbol']);
|
|
if ($value) {
|
|
$output .= self::interpret_nested_content(
|
|
$context,
|
|
$node['contents'],
|
|
$value);
|
|
}
|
|
break;
|
|
case '^':
|
|
$value = self::lookup($context, $node['symbol']);
|
|
if (!$value) {
|
|
$output .= self::interpret(
|
|
$context,
|
|
$node['contents']);
|
|
}
|
|
break;
|
|
case '>':
|
|
$new_path = $node['symbol'][0] == '/' ? $node['symbol'] : dirname($context->path).'/'.$node['symbol'];
|
|
$partial = $context->partial_loader->load($new_path);
|
|
if ($partial) {
|
|
$output .= self::interpret(
|
|
$context->create_partial_context($new_path, $node['indentation']),
|
|
$partial);
|
|
}
|
|
break;
|
|
default:
|
|
assert(false); exit(1);
|
|
}
|
|
}
|
|
|
|
return $output;
|
|
}
|
|
|
|
static function lookup($context, $symbol) {
|
|
// Deal with some special cases to make life easier.
|
|
if (strpos($symbol, '..') !== false) {
|
|
return null;
|
|
}
|
|
else if ($symbol == '.') {
|
|
return $context->params;
|
|
}
|
|
|
|
// Look up the first part of the symbol from stack.
|
|
$symbol_parts = explode('.', $symbol);
|
|
$first_symbol = array_shift($symbol_parts);
|
|
if (!$first_symbol) {
|
|
$value = $context->params;
|
|
}
|
|
else {
|
|
$value = null;
|
|
for($x = $context; $x; $x = $x->parent) {
|
|
if (is_array($x->params) && array_key_exists($first_symbol, $x->params)) {
|
|
$value = $x->params[$first_symbol];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Iterate over the rest of the symbol, looking up members.
|
|
foreach($symbol_parts as $symbol_part) {
|
|
if (is_array($value) && array_key_exists($symbol_part, $value)) {
|
|
$value = $value[$symbol_part];
|
|
}
|
|
else if (is_object($value) && property_exists($value, $symbol_part)) {
|
|
$value = $value->$symbol_part;
|
|
}
|
|
else {
|
|
$value = null;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
|
|
static function interpret_nested_content($context, $nodes, $value) {
|
|
if (is_array($value)) {
|
|
// Just checking the first key to see if this is a list or object.
|
|
// Should probably check every key?
|
|
reset($value);
|
|
if (is_int(key($value))) {
|
|
$output = '';
|
|
foreach($value as $x) {
|
|
$output .= self::interpret($context->create_child_context($x), $nodes);
|
|
}
|
|
return $output;
|
|
}
|
|
}
|
|
|
|
return self::interpret($context->create_child_context($value), $nodes);
|
|
}
|
|
|
|
static function match_exists($match, $key) {
|
|
return array_key_exists($key, $match) && $match[$key][1] != -1;
|
|
}
|
|
}
|
|
|
|
class BoostSimpleTemplate_PartialArray {
|
|
var $partials;
|
|
|
|
function __construct($partials = null) {
|
|
$this->partials = Array();
|
|
$partials ?: Array();
|
|
}
|
|
|
|
function add($path, $x) {
|
|
$this->partials[self::normalize_path($path)] = $x;
|
|
}
|
|
|
|
function load($path) {
|
|
$path = $this->normalize_path($path);
|
|
return array_key_exists($path, $this->partials) ? $this->partials[$path] : null;
|
|
}
|
|
|
|
function normalize_path($path) {
|
|
$path = preg_replace('@//+@', '/', $path);
|
|
|
|
$new_path = array();
|
|
foreach(explode('/', $path) as $part) {
|
|
switch($part) {
|
|
case '':
|
|
case '.':
|
|
break;
|
|
case '..':
|
|
array_pop($new_path);
|
|
break;
|
|
default:
|
|
$new_path[] = $part;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return implode('/', $new_path);
|
|
}
|
|
}
|
|
|
|
class BoostSimpleTemplate_PartialLoader {
|
|
var $cache = array();
|
|
|
|
function load($path) {
|
|
$realpath = $this->normalize_path($path);
|
|
if ($realpath) {
|
|
if (array_key_exists($realpath, $this->cache)) {
|
|
return $this->cache[$realpath];
|
|
}
|
|
else if (is_file($realpath)) {
|
|
return $this->cache[$realpath] = BoostSimpleTemplate::parse_template(file_get_contents($realpath));
|
|
}
|
|
else {
|
|
return $this->cache[$realpath] = null;
|
|
}
|
|
}
|
|
else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function normalize_path($path) {
|
|
$realpath = realpath($path);
|
|
if (!$realpath && !pathinfo($path, PATHINFO_EXTENSION)) { $realpath = realpath("{$path}.mustache"); }
|
|
return $realpath;
|
|
}
|
|
}
|
|
|
|
class BoostSimpleTemplate_Context {
|
|
var $partial_loader = Array();
|
|
var $path = null;
|
|
var $params = null;
|
|
var $parent = null;
|
|
var $indentation = '';
|
|
|
|
function create_child_context($params) {
|
|
$x = clone $this;
|
|
$x->params = is_object($params) ? (array) $params : $params;
|
|
$x->parent = $this;
|
|
return $x;
|
|
}
|
|
|
|
function create_partial_context($path, $indentation) {
|
|
$x = clone $this;
|
|
$x->path = $path;
|
|
$x->indentation .= $indentation;
|
|
return $x;
|
|
}
|
|
}
|
|
|
|
class BoostSimpleTemplate_Exception extends BoostException {
|
|
var $offset;
|
|
|
|
function __construct($message, $offset = null) {
|
|
parent::__construct($message);
|
|
$this->offset = $offset;
|
|
}
|
|
}
|