1
+ − 1
<?php
536
+ − 2
+ − 3
/*
+ − 4
* Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
801
eb8b23f11744
Two big commits in one day I know, but redid password storage to use HMAC-SHA1. Consolidated much AES processing to three core methods in session that should handle everything automagically. Installation works; upgrades should. Rebranded as 1.1.6.
Dan
diff
changeset
+ − 5
* Version 1.1.6 (Caoineag beta 1)
536
+ − 6
* Copyright (C) 2006-2008 Dan Fuhry
+ − 7
*
+ − 8
* This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License
+ − 9
* as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
+ − 10
*
+ − 11
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ − 12
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ − 13
*/
+ − 14
1
+ − 15
/**
1027
+ − 16
* Framework for parsing and rendering various formats. In Enano by default, this is MediaWiki-style wikitext being
+ − 17
* rendered to XHTML, but this framework allows other formats to be supported as well.
1
+ − 18
*
1027
+ − 19
* @package Enano
+ − 20
* @subpackage Content
+ − 21
* @author Dan Fuhry <dan@enanocms.org>
+ − 22
* @copyright (C) 2009 Enano CMS Project
+ − 23
* @license GNU General Public License, version 2 or later <http://www.gnu.org/licenses/gpl-2.0.html>
1
+ − 24
*/
+ − 25
1027
+ − 26
class Carpenter
+ − 27
{
+ − 28
/**
+ − 29
* Parser token
+ − 30
* @const string
+ − 31
*/
+ − 32
+ − 33
const PARSER_TOKEN = "\xFF";
+ − 34
+ − 35
/**
+ − 36
* Parsing engine
+ − 37
* @var string
+ − 38
*/
+ − 39
+ − 40
private $parser = 'mediawiki';
+ − 41
+ − 42
/**
+ − 43
* Rendering engine
+ − 44
* @var string
+ − 45
*/
+ − 46
+ − 47
private $renderer = 'xhtml';
+ − 48
+ − 49
/**
+ − 50
* Rendering flags
+ − 51
*/
+ − 52
+ − 53
public $flags = RENDER_WIKI_DEFAULT;
+ − 54
+ − 55
/**
+ − 56
* List of rendering rules
+ − 57
* @var array
+ − 58
*/
+ − 59
+ − 60
private $rules = array(
+ − 61
'lang',
+ − 62
'templates',
+ − 63
'tables',
+ − 64
'heading',
+ − 65
// note: can't be named list ("list" is a PHP language construct)
+ − 66
'multilist',
+ − 67
'bold',
+ − 68
'italic',
+ − 69
'underline',
+ − 70
'externalwithtext',
+ − 71
'externalnotext',
+ − 72
'image',
+ − 73
'internallink',
+ − 74
'paragraph'
1
+ − 75
);
1027
+ − 76
+ − 77
/**
+ − 78
* List of render hooks
+ − 79
* @var array
+ − 80
*/
+ − 81
+ − 82
private $hooks = array();
+ − 83
+ − 84
/* private $rules = array('prefilter', 'delimiter', 'code', 'function', 'html', 'raw', 'include', 'embed', 'anchor',
+ − 85
'heading', 'toc', 'horiz', 'break', 'blockquote', 'list', 'deflist', 'table', 'image',
+ − 86
'phplookup', 'center', 'newline', 'paragraph', 'url', 'freelink', 'interwiki',
+ − 87
'wikilink', 'colortext', 'strong', 'bold', 'emphasis', 'italic', 'underline', 'tt',
+ − 88
'superscript', 'subscript', 'revise', 'tighten'); */
+ − 89
+ − 90
/**
+ − 91
* Render the text!
+ − 92
* @param string Text to render
+ − 93
* @return string
+ − 94
*/
+ − 95
+ − 96
public function render($text)
+ − 97
{
+ − 98
$parser_class = "Carpenter_Parse_" . ucwords($this->parser);
+ − 99
$renderer_class = "Carpenter_Render_" . ucwords($this->renderer);
+ − 100
+ − 101
// include files, if we haven't already
+ − 102
if ( !class_exists($parser_class) )
+ − 103
{
+ − 104
require_once( ENANO_ROOT . "/includes/wikiengine/parse_{$this->parser}.php");
+ − 105
}
+ − 106
+ − 107
if ( !class_exists($renderer_class) )
1
+ − 108
{
1027
+ − 109
require_once( ENANO_ROOT . "/includes/wikiengine/render_{$this->renderer}.php");
+ − 110
}
+ − 111
+ − 112
$parser = new $parser_class;
+ − 113
$renderer = new $renderer_class;
+ − 114
+ − 115
// run prehooks
+ − 116
foreach ( $this->hooks as $hook )
+ − 117
{
+ − 118
if ( $hook['when'] === PO_FIRST )
+ − 119
{
+ − 120
$text = call_user_func($hook['callback'], $text);
+ − 121
if ( !is_string($text) || empty($text) )
+ − 122
{
+ − 123
trigger_error("Hook returned empty/invalid text: " . print_r($hook['callback'], true), E_USER_WARNING);
+ − 124
// *sigh*
+ − 125
$text = '';
1
+ − 126
}
1027
+ − 127
}
+ − 128
}
+ − 129
+ − 130
// perform render
+ − 131
foreach ( $this->rules as $rule )
+ − 132
{
+ − 133
// run prehooks
+ − 134
foreach ( $this->hooks as $hook )
+ − 135
{
+ − 136
if ( $hook['when'] === PO_BEFORE && $hook['rule'] === $rule )
407
+ − 137
{
1027
+ − 138
$text = call_user_func($hook['callback'], $text);
+ − 139
if ( !is_string($text) || empty($text) )
438
+ − 140
{
1027
+ − 141
trigger_error("Hook returned empty/invalid text: " . print_r($hook['callback'], true), E_USER_WARNING);
+ − 142
// *sigh*
+ − 143
$text = '';
438
+ − 144
}
407
+ − 145
}
1
+ − 146
}
1027
+ − 147
+ − 148
// execute rule
+ − 149
$text = $this->perform_render_step($text, $rule, $parser, $renderer);
+ − 150
+ − 151
// run posthooks
+ − 152
foreach ( $this->hooks as $hook )
+ − 153
{
+ − 154
if ( $hook['when'] === PO_AFTER && $hook['rule'] === $rule )
+ − 155
{
+ − 156
$text = call_user_func($hook['callback'], $text);
+ − 157
if ( !is_string($text) || empty($text) )
+ − 158
{
+ − 159
trigger_error("Hook returned empty/invalid text: " . print_r($hook['callback'], true), E_USER_WARNING);
+ − 160
// *sigh*
+ − 161
$text = '';
1
+ − 162
}
+ − 163
}
+ − 164
}
+ − 165
}
1027
+ − 166
+ − 167
// run posthooks
+ − 168
foreach ( $this->hooks as $hook )
1
+ − 169
{
1027
+ − 170
if ( $hook['when'] === PO_LAST )
+ − 171
{
+ − 172
$text = call_user_func($hook['callback'], $text);
+ − 173
if ( !is_string($text) || empty($text) )
+ − 174
{
+ − 175
trigger_error("Hook returned empty/invalid text: " . print_r($hook['callback'], true), E_USER_WARNING);
+ − 176
// *sigh*
+ − 177
$text = '';
1
+ − 178
}
+ − 179
}
+ − 180
}
1027
+ − 181
+ − 182
return (( defined('ENANO_DEBUG') && isset($_GET['parserdebug']) ) ? '<pre>' . htmlspecialchars($text) . '</pre>' : $text) . "\n\n";
+ − 183
}
+ − 184
+ − 185
/**
+ − 186
* Performs a step in the rendering process.
+ − 187
* @param string Text to render
+ − 188
* @param string Rule to execute
+ − 189
* @param object Parser instance
+ − 190
* @param object Renderer instance
+ − 191
* @return string
+ − 192
* @access private
+ − 193
*/
+ − 194
+ − 195
private function perform_render_step($text, $rule, $parser, $renderer)
+ − 196
{
+ − 197
// First look for a direct function
+ − 198
if ( function_exists("parser_{$this->parser}_{$this->renderer}_{$rule}") )
1
+ − 199
{
1027
+ − 200
return call_user_func("parser_{$this->parser}_{$this->renderer}_{$rule}", $text, $this->flags);
1
+ − 201
}
1027
+ − 202
+ − 203
// We don't have that, so start looking for other ways or means of doing this
+ − 204
if ( method_exists($parser, $rule) && method_exists($renderer, $rule) )
+ − 205
{
+ − 206
// Both the parser and render have callbacks they want to use.
+ − 207
$pieces = $parser->$rule($text);
+ − 208
$text = call_user_func(array($renderer, $rule), $text, $pieces);
+ − 209
}
+ − 210
else if ( method_exists($parser, $rule) && !method_exists($renderer, $rule) && isset($renderer->rules[$rule]) )
1
+ − 211
{
1027
+ − 212
// The parser has a callback, but the renderer does not
+ − 213
$pieces = $parser->$rule($text);
+ − 214
$text = $this->generic_render($text, $pieces, $renderer->rules[$rule]);
1
+ − 215
}
1027
+ − 216
else if ( !method_exists($parser, $rule) && isset($parser->rules[$rule]) && method_exists($renderer, $rule) )
1
+ − 217
{
1027
+ − 218
// The parser has no callback, but the renderer does
+ − 219
$text = preg_replace_callback($parser->rules[$rule], array($renderer, $rule), $text);
+ − 220
}
+ − 221
else if ( isset($parser->rules[$rule]) && isset($renderer->rules[$rule]) )
+ − 222
{
+ − 223
// This is a straight-up regex only rule
+ − 224
$text = preg_replace($parser->rules[$rule], $renderer->rules[$rule], $text);
+ − 225
}
+ − 226
else
+ − 227
{
+ − 228
// Either the renderer or parser does not support this rule, ignore it
1
+ − 229
}
1027
+ − 230
+ − 231
return $text;
+ − 232
}
+ − 233
+ − 234
/**
+ − 235
* Generic renderer
+ − 236
* @access protected
+ − 237
*/
+ − 238
+ − 239
protected function generic_render($text, $pieces, $rule)
+ − 240
{
+ − 241
foreach ( $pieces as $i => $piece )
1
+ − 242
{
1027
+ − 243
$replacement = $rule;
+ − 244
+ − 245
// if the piece is an array, replace $1, $2, etc. in the rule with each value in the piece
+ − 246
if ( is_array($piece) )
+ − 247
{
+ − 248
$j = 0;
+ − 249
foreach ( $piece as $part )
+ − 250
{
+ − 251
$j++;
+ − 252
$replacement = str_replace(array("\\$j", "\${$j}"), $part, $replacement);
1
+ − 253
}
1027
+ − 254
}
+ − 255
// else, just replace \\1 or $1 in the rule with the piece
+ − 256
else
+ − 257
{
+ − 258
$replacement = str_replace(array("\\1", "\$1"), $piece, $replacement);
+ − 259
}
+ − 260
+ − 261
$text = str_replace(self::generate_token($i), $replacement, $text);
1
+ − 262
}
1027
+ − 263
+ − 264
return $text;
+ − 265
}
+ − 266
+ − 267
/**
+ − 268
* Add a hook into the parser.
+ − 269
* @param callback Function to call
+ − 270
* @param int PO_* constant
+ − 271
* @param string If PO_{BEFORE,AFTER} used, rule
+ − 272
*/
+ − 273
+ − 274
public function hook($callback, $when, $rule = false)
+ − 275
{
+ − 276
if ( !is_int($when) )
+ − 277
return null;
+ − 278
if ( ($when == PO_BEFORE || $when == PO_AFTER) && !is_string($rule) )
+ − 279
return null;
+ − 280
if ( ( is_string($callback) && !function_exists($callback) ) || ( is_array($callback) && !method_exists($callback[0], $callback[1]) ) || ( !is_string($callback) && !is_array($callback) ) )
1
+ − 281
{
1027
+ − 282
trigger_error("Attempt to hook with undefined function/method " . print_r($callback, true), E_USER_ERROR);
+ − 283
return null;
1
+ − 284
}
1027
+ − 285
+ − 286
$this->hooks[] = array(
+ − 287
'callback' => $callback,
+ − 288
'when' => $when,
+ − 289
'rule' => $rule
+ − 290
);
+ − 291
}
+ − 292
+ − 293
/**
+ − 294
* Generate a token
+ − 295
* @param int Token index
+ − 296
* @return string
+ − 297
* @static
+ − 298
*/
+ − 299
+ − 300
public static function generate_token($i)
+ − 301
{
+ − 302
return self::PARSER_TOKEN . $i . self::PARSER_TOKEN;
+ − 303
}
+ − 304
+ − 305
/**
+ − 306
* Tokenize string
+ − 307
* @param string
+ − 308
* @param array Matches
+ − 309
* @return string
+ − 310
* @static
+ − 311
*/
+ − 312
+ − 313
public static function tokenize($text, $matches)
+ − 314
{
+ − 315
$matches = array_values($matches);
+ − 316
foreach ( $matches as $i => $match )
1
+ − 317
{
1027
+ − 318
$text = str_replace_once($match, self::generate_token($i), $text);
1
+ − 319
}
1027
+ − 320
+ − 321
return $text;
+ − 322
}
1
+ − 323
}
+ − 324