first attempt of syntax plugins

The first version of the new plugin system. Syntax plugins only yet. A very simple
example plugin called info (doing nothig useful yet) is included.

Missing Features

  - Doku_Block_Handler needs work (doesn't honur plugins yet)
  - there is no way to specify the order of plugins and other modes yet
  - useful output from the info plugin
  - bug testing and fixing
  - code cleanup
  - documentation

darcs-hash:20050519201009-9977f-f793dbfc6a39d8a9643b610927d93cd3288bdd6b.gz
This commit is contained in:
andi 2005-05-19 22:10:09 +02:00
parent 64f50cdbe2
commit ee20e7d166
11 changed files with 350 additions and 48 deletions

View File

@ -364,7 +364,7 @@ function html_search(){
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
function html_locked($ip){
function html_locked(){
global $ID;
global $conf;
global $lang;

View File

@ -10,6 +10,7 @@
error_reporting(E_ALL ^ E_NOTICE);
//prepare config array()
global $conf;
$conf = array();
// load the config file(s)
@ -17,12 +18,16 @@
@include_once(DOKU_INC.'conf/local.php');
//prepare language array
global $lang;
$lang = array();
// define baseURL
if(!defined('DOKU_BASE')) define('DOKU_BASE',getBaseURL());
if(!defined('DOKU_URL')) define('DOKU_URL',getBaseURL(true));
// define Plugin dir
if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'inc/plugins/');
// define main script
if(!defined('DOKU_SCRIPT')) define('DOKU_SCRIPT','doku.php');

View File

@ -54,6 +54,25 @@ class Doku_Handler {
}
return FALSE;
}
/**
* Special plugin handler
*
* This handler is called for all modes starting with 'plugin_'.
* An additional parameter with the plugin name is passed
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
function plugin($match, $state, $pos, $pluginname){
$data = array($match);
$plugin = null;
if(plugin_load('syntax',$pluginname,$plugin)){
$data = $plugin->handle($match, $state, $pos, $handler);
}
$this->_addCall('plugin',array($pluginname,$data,$pos),$pos);
return TRUE;
}
function base($match, $state, $pos) {
switch ( $state ) {

View File

@ -471,6 +471,13 @@ class Doku_Lexer {
if (isset($this->_mode_handlers[$handler])) {
$handler = $this->_mode_handlers[$handler];
}
// modes starting with plugin_ are all handled by the same
// handler but with an additional parameter
if(substr($handler,0,7)=='plugin_'){
list($handler,$plugin) = split('_',$handler,2);
return $this->_parser->$handler($content, $is_match, $pos, $plugin);
}
return $this->_parser->$handler($content, $is_match, $pos);
}

View File

@ -5,6 +5,45 @@ if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../')
require_once DOKU_INC . 'inc/parser/lexer.php';
require_once DOKU_INC . 'inc/parser/handler.php';
/**
* Define various types of modes used by the parser - they are used to
* populate the list of modes another mode accepts
*/
global $PARSER_MODES;
$PARSER_MODES = array(
// containers are complex modes that can contain many other modes
// hr breaks the principle but they shouldn't be used in tables / lists
// so they are put here
'container' => array('listblock','table','quote','hr'),
// some mode are allowed inside the base mode only
'baseonly' => array('header'),
// modes for styling text -- footnote behaves similar to styling
'formatting' => array('strong', 'emphasis', 'underline', 'monospace',
'subscript', 'superscript', 'deleted', 'footnote'),
// modes where the token is simply replaced - they can not contain any
// other modes
'substition' => array('acronym','smiley','wordblock','entity',
'camelcaselink', 'internallink','media',
'externallink','linebreak','emaillink',
'windowssharelink','filelink','notoc',
'nocache','multiplyentity','quotes','rss'),
// modes which have a start and end token but inside which
// no other modes should be applied
'protected' => array('preformatted','code','file','php','html'),
// inside this mode no wiki markup should be applied but lineendings
// and whitespace isn't preserved
'disabled' => array('unformatted'),
// used to mark paragraph boundaries
'paragraphs' => array('eol')
);
//-------------------------------------------------------------------
/**
@ -101,7 +140,7 @@ class Doku_Parser_Mode {
var $Lexer;
var $allowedModes = array();
// Called before any calls to connectTo
function preConnect() {}
@ -120,15 +159,16 @@ class Doku_Parser_Mode {
class Doku_Parser_Mode_Base extends Doku_Parser_Mode {
function Doku_Parser_Mode_Base() {
global $PARSER_MODES;
$this->allowedModes = array_merge (
Doku_Parser_BlockContainers(),
Doku_Parser_BaseOnly(),
Doku_Parser_Paragraphs(),
Doku_Parser_Formatting(),
Doku_Parser_Substition(),
Doku_Parser_Protected(),
Doku_Parser_Disabled()
$PARSER_MODES['container'],
$PARSER_MODES['baseonly'],
$PARSER_MODES['paragraphs'],
$PARSER_MODES['formatting'],
$PARSER_MODES['substition'],
$PARSER_MODES['protected'],
$PARSER_MODES['disabled']
);
}
}
@ -137,13 +177,14 @@ class Doku_Parser_Mode_Base extends Doku_Parser_Mode {
class Doku_Parser_Mode_Footnote extends Doku_Parser_Mode {
function Doku_Parser_Mode_Footnote() {
global $PARSER_MODES;
$this->allowedModes = array_merge (
Doku_Parser_BlockContainers(),
Doku_Parser_Formatting(),
Doku_Parser_Substition(),
Doku_Parser_Protected(),
Doku_Parser_Disabled()
$PARSER_MODES['container'],
$PARSER_MODES['formatting'],
$PARSER_MODES['substition'],
$PARSER_MODES['protected'],
$PARSER_MODES['disabled']
);
}
@ -237,7 +278,6 @@ class Doku_Parser_Mode_HR extends Doku_Parser_Mode {
//-------------------------------------------------------------------
class Doku_Parser_Mode_Formatting extends Doku_Parser_Mode {
var $type;
var $formatting = array (
@ -278,6 +318,7 @@ class Doku_Parser_Mode_Formatting extends Doku_Parser_Mode {
);
function Doku_Parser_Mode_Formatting($type) {
global $PARSER_MODES;
if ( !array_key_exists($type, $this->formatting) ) {
trigger_error('Invalid formatting type '.$type, E_USER_WARNING);
@ -285,12 +326,18 @@ class Doku_Parser_Mode_Formatting extends Doku_Parser_Mode {
$this->type = $type;
// formatting may contain other formatting but not it self
$modes = $PARSER_MODES['formatting'];
$key = array_search($type, $modes);
if ( is_int($key) ) {
unset($modes[$key]);
}
$this->allowedModes = array_merge (
Doku_Parser_Formatting($type),
Doku_Parser_Substition(),
Doku_Parser_Disabled()
$modes,
$PARSER_MODES['substition'],
$PARSER_MODES['disabled']
);
}
function connectTo($mode) {
@ -321,20 +368,16 @@ class Doku_Parser_Mode_Formatting extends Doku_Parser_Mode {
class Doku_Parser_Mode_ListBlock extends Doku_Parser_Mode {
function Doku_Parser_Mode_ListBlock() {
global $PARSER_MODES;
$this->allowedModes = array_merge (
Doku_Parser_Formatting(),
Doku_Parser_Substition(),
Doku_Parser_Disabled()
$PARSER_MODES['formatting'],
$PARSER_MODES['substition'],
$PARSER_MODES['disabled'],
$PARSER_MODES['protected'] #XXX new
);
$this->allowedModes[] = 'footnote';
$this->allowedModes[] = 'preformatted';
$this->allowedModes[] = 'unformatted';
$this->allowedModes[] = 'html';
$this->allowedModes[] = 'php';
$this->allowedModes[] = 'code';
$this->allowedModes[] = 'file';
// $this->allowedModes[] = 'footnote';
}
function connectTo($mode) {
@ -355,15 +398,17 @@ class Doku_Parser_Mode_ListBlock extends Doku_Parser_Mode {
class Doku_Parser_Mode_Table extends Doku_Parser_Mode {
function Doku_Parser_Mode_Table() {
global $PARSER_MODES;
$this->allowedModes = array_merge (
Doku_Parser_Formatting(),
Doku_Parser_Substition(),
Doku_Parser_Disabled()
$PARSER_MODES['formatting'],
$PARSER_MODES['substition'],
$PARSER_MODES['disabled'],
$PARSER_MODES['protected'] #XXX new
);
$this->allowedModes[] = 'footnote';
$this->allowedModes[] = 'preformatted';
$this->allowedModes[] = 'unformatted';
#$this->allowedModes[] = 'footnote';
#$this->allowedModes[] = 'preformatted';
#$this->allowedModes[] = 'unformatted';
}
function connectTo($mode) {
@ -473,15 +518,17 @@ class Doku_Parser_Mode_File extends Doku_Parser_Mode {
class Doku_Parser_Mode_Quote extends Doku_Parser_Mode {
function Doku_Parser_Mode_Quote() {
global $PARSER_MODES;
$this->allowedModes = array_merge (
Doku_Parser_Formatting(),
Doku_Parser_Substition(),
Doku_Parser_Disabled()
$PARSER_MODES['formatting'],
$PARSER_MODES['substition'],
$PARSER_MODES['disabled'],
$PARSER_MODES['protected'] #XXX new
);
$this->allowedModes[] = 'footnote';
$this->allowedModes[] = 'preformatted';
$this->allowedModes[] = 'unformatted';
#$this->allowedModes[] = 'footnote';
#$this->allowedModes[] = 'preformatted';
#$this->allowedModes[] = 'unformatted';
}
function connectTo($mode) {
@ -774,9 +821,14 @@ class Doku_Parser_Mode_EmailLink extends Doku_Parser_Mode {
}
//-------------------------------------------------------------------
//
// XXX deprecated - replace by $PARSER_MODES
//
// Help fns to keep mode lists - used to make it easier to populate
// the list of modes another mode accepts
/*
// Can contain many other modes
// E.g. a footnote can containing formatting etc.
function Doku_Parser_BlockContainers() {
@ -848,6 +900,8 @@ function Doku_Parser_Disabled() {
);
return $modes;
}
*/
// --------------------------------------------------------------------------
//Setup VIM: ex: et ts=4 enc=utf-8 :

View File

@ -19,6 +19,7 @@ if ( !defined('DOKU_TAB') ) {
}
require_once DOKU_INC . 'inc/parser/renderer.php';
require_once DOKU_INC . 'inc/pluginutils.php';
/**
* The Renderer
@ -41,7 +42,6 @@ class Doku_Renderer_xhtml extends Doku_Renderer {
var $store = '';
function document_start() {
}
@ -57,6 +57,14 @@ class Doku_Renderer_xhtml extends Doku_Renderer {
$this->doc .= '</div>'.DOKU_LF;
}
}
//handles plugin rendering
function plugin($name,$data){
$plugin = null;
if(plugin_load('syntax',$name,$plugin)){
$plugin->render('xhtml',$this,$data);
}
}
function toc_open() {
global $lang;

View File

@ -11,6 +11,7 @@
require_once(DOKU_INC.'inc/confutils.php');
require_once(DOKU_INC.'inc/pageutils.php');
require_once(DOKU_INC.'inc/pluginutils.php');
/**
* Returns the parsed Wikitext in XHTML for the given id and revision.
@ -155,14 +156,26 @@ function p_cached_instructions($file,$cacheonly=false){
function p_get_instructions($text){
global $conf;
//import parser classes and mode definitions
require_once DOKU_INC . 'inc/parser/parser.php';
// load syntax plugins
$pluginlist = plugin_list('syntax');
if(count($pluginlist)){
global $PARSER_MODES;
$plugins = array();
foreach($pluginlist as $p){
plugin_load('syntax',$p,$plugin[$p]); //load plugin into $plugin array
$PARSER_MODES[$plugin[$p]->getType()][] = "plugin_$p"; //register mode type
}
}
// Create the parser
$Parser = & new Doku_Parser();
// Add the Handler
$Parser->Handler = & new Doku_Handler();
// Load all the modes
$Parser->addMode('listblock',new Doku_Parser_Mode_ListBlock());
$Parser->addMode('preformatted',new Doku_Parser_Mode_Preformatted());
@ -192,7 +205,7 @@ function p_get_instructions($text){
$Parser->addMode('smiley',new Doku_Parser_Mode_Smiley(array_keys(getSmileys())));
$Parser->addMode('acronym',new Doku_Parser_Mode_Acronym(array_keys(getAcronyms())));
#$Parser->addMode('wordblock',new Doku_Parser_Mode_Wordblock(getBadWords()));
#$Parser->addMode('wordblock',new Doku_Parser_Mode_Wordblock($Modes,getBadWords()));
$Parser->addMode('entity',new Doku_Parser_Mode_Entity(array_keys(getEntities())));
$Parser->addMode('multiplyentity',new Doku_Parser_Mode_MultiplyEntity());
@ -202,6 +215,11 @@ function p_get_instructions($text){
$Parser->addMode('camelcaselink',new Doku_Parser_Mode_CamelCaseLink());
}
//add plugins FIXME since order is important we, need to find a better way!
foreach ( array_keys($plugin) as $p ) {
$Parser->addMode("plugin_$p",$plugin[$p]);
}
$Parser->addMode('internallink',new Doku_Parser_Mode_InternalLink());
$Parser->addMode('rss',new Doku_Parser_Mode_RSS());
$Parser->addMode('media',new Doku_Parser_Mode_Media());
@ -210,10 +228,10 @@ function p_get_instructions($text){
$Parser->addMode('windowssharelink',new Doku_Parser_Mode_WindowsShareLink());
//$Parser->addMode('filelink',new Doku_Parser_Mode_FileLink()); //FIXME ???
$Parser->addMode('eol',new Doku_Parser_Mode_Eol());
// Do the parsing
$p = $Parser->parse($text);
# dbg($p);
// dbg($p);
return $p;
}

View File

@ -0,0 +1,63 @@
<?php
/**
* Info Plugin: Displays information about various DokuWiki internals
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author Andreas Gohr <andi@splitbrain.org>
*/
if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'inc/plugins/');
require_once(DOKU_PLUGIN.'syntax.php');
/**
* All DokuWiki plugins to extend the parser/rendering mechanism
* need to inherit from this class
*/
class syntax_plugin_info extends DokuWiki_Syntax_Plugin {
/**
* What kind of syntax are we?
*/
function getType(){
return 'substition';
}
/**
* Connect pattern to lexer
*/
function connectTo($mode) {
$this->Lexer->addSpecialPattern('~~INFO:\w+~~',$mode,'plugin_info');
}
/**
* Handle the match
*/
function handle($match, $state, $pos, &$handler){
$match = substr($match,7,-2); //strip ~~INFO: from start and ~~ from end
return array(strtolower($match));
}
/**
* Create output
*/
function render($mode, &$renderer, $data) {
if($mode == 'xhtml'){
//handle various info stuff
switch ($data[0]){
case 'foo':
$renderer->doc .= "foo is foo";
break;
default:
$renderer->doc .= "no info about ".htmlspecialchars($data[0]);
}
return true;
}
return false;
}
}
//Setup VIM: ex: et ts=4 enc=utf-8 :

66
inc/plugins/syntax.php Normal file
View File

@ -0,0 +1,66 @@
<?php
/**
* Syntax Plugin Prototype
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author Andreas Gohr <andi@splitbrain.org>
*/
if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../').'/');
if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'inc/plugins/');
require_once(DOKU_INC.'inc/parser/parser.php');
/**
* All DokuWiki plugins to extend the parser/rendering mechanism
* need to inherit from this class
*/
class DokuWiki_Syntax_Plugin extends Doku_Parser_Mode {
/**
* Needs to return one of the mode types defined in $PARSER_MODES in parser.php
*/
function getType(){
trigger_error('getType() not implemented in '.get_class($this), E_USER_WARNING);
}
/**
* Handler to prepare matched data for the rendering process
*
* Usually you should only need the $match param.
*
* @param $match string The text matched by the patterns
* @param $state int The lexer state for the match
* @param $pos int The character position of the matched text
* @param $handler ref Reference to the Doku_Handler object
* @return array Return an array with all data you want to use in render
*/
function handle($match, $state, $pos, &$handler){
trigger_error('handle() not implemented in '.get_class($this), E_USER_WARNING);
}
/**
* Handles the actual output creation.
*
* The function should always check for the given mode and return false
* when a mode isn't supported.
*
* $renderer contains a reference to the renderer object which is
* currently handling the rendering. You need to use it for writing
* the output. How this is done depends on the renderer used (specified
* by $mode
*
* The contents of the $data array depends on what the handler() function above
* created
*
* @param $mode string current Rendermode
* @param $renderer ref reference to the current renderer object
* @param $data array data created by handler()
* @return boolean rendered correctly?
*/
function render($mode, &$renderer, $data) {
trigger_error('render() not implemented in '.get_class($this), E_USER_WARNING);
}
}
//Setup VIM: ex: et ts=4 enc=utf-8 :

62
inc/pluginutils.php Normal file
View File

@ -0,0 +1,62 @@
<?php
/**
* Utilities for handling plugins
*
* @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author Andreas Gohr <andi@splitbrain.org>
*/
/**
* Returns a list of available plugins of given type
*
* @author Andreas Gohr <andi@splitbrain.org>
*/
function plugin_list($type){
$plugins = array();
if ($dh = opendir(DOKU_PLUGIN)) {
while (false !== ($file = readdir($dh))) {
if ($file == '.' || $file == '..') continue;
if (is_file(DOKU_PLUGIN.$file)) continue;
if (@file_exists(DOKU_PLUGIN.$file.'/'.$type.'.php')){
$plugins[] = $file;
}
}
closedir($dh);
}
return $plugins;
}
/**
* Loads the given plugin and creates an object of it
*
* @author Andreas Gohr <andi@splitbrain.org>
*
* @param $type string type of plugin to load
* @param $name string name of the plugin to load
* @param $ref ref will contain the plugin object
* @return boolean plugin loading successful?
*/
function plugin_load($type,$name,&$ref){
//we keep all loaded plugins available in global scope for reuse
global $DOKU_PLUGINS;
//plugin already loaded?
if($DOKU_PLUGINS[$type][$name] != null){
$ref = $DOKU_PLUGINS[$type][$name];
return true;
}
//try to load the wanted plugin file
if(!@include_once(DOKU_PLUGIN.$name.'/'.$type.'.php')){
return false;
}
//construct class and instanciate
$class = $type.'_plugin_'.$name;
$DOKU_PLUGINS[$type][$name] = new $class;
$ref = $DOKU_PLUGINS[$type][$name];
return true;
}

View File

@ -88,7 +88,7 @@ function tpl_content(){
html_diff(con($PRE,$TEXT,$SUF),false);
break;
case 'locked':
html_locked($lockedby);
html_locked();
break;
case 'login':
html_login();