From ee20e7d16637625e900f518b6b46a61bfa30fe6e Mon Sep 17 00:00:00 2001 From: andi <andi@splitbrain.org> Date: Thu, 19 May 2005 22:10:09 +0200 Subject: [PATCH] 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 --- inc/html.php | 2 +- inc/init.php | 5 ++ inc/parser/handler.php | 19 +++++ inc/parser/lexer.php | 7 ++ inc/parser/parser.php | 134 +++++++++++++++++++++++++----------- inc/parser/xhtml.php | 10 ++- inc/parserutils.php | 28 ++++++-- inc/plugins/info/syntax.php | 63 +++++++++++++++++ inc/plugins/syntax.php | 66 ++++++++++++++++++ inc/pluginutils.php | 62 +++++++++++++++++ inc/template.php | 2 +- 11 files changed, 350 insertions(+), 48 deletions(-) create mode 100644 inc/plugins/info/syntax.php create mode 100644 inc/plugins/syntax.php create mode 100644 inc/pluginutils.php diff --git a/inc/html.php b/inc/html.php index c49a2b220..370754c52 100644 --- a/inc/html.php +++ b/inc/html.php @@ -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; diff --git a/inc/init.php b/inc/init.php index 150e07d83..af1d1eb9c 100644 --- a/inc/init.php +++ b/inc/init.php @@ -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'); diff --git a/inc/parser/handler.php b/inc/parser/handler.php index e1ded183a..2379d60ff 100644 --- a/inc/parser/handler.php +++ b/inc/parser/handler.php @@ -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 ) { diff --git a/inc/parser/lexer.php b/inc/parser/lexer.php index e7961932d..1cacaeed5 100644 --- a/inc/parser/lexer.php +++ b/inc/parser/lexer.php @@ -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); } diff --git a/inc/parser/parser.php b/inc/parser/parser.php index dd2b72aa0..87a29328c 100644 --- a/inc/parser/parser.php +++ b/inc/parser/parser.php @@ -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 : diff --git a/inc/parser/xhtml.php b/inc/parser/xhtml.php index f9f6be1e6..b58c24435 100644 --- a/inc/parser/xhtml.php +++ b/inc/parser/xhtml.php @@ -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; diff --git a/inc/parserutils.php b/inc/parserutils.php index 802ae8a0d..9c0e4fd9c 100644 --- a/inc/parserutils.php +++ b/inc/parserutils.php @@ -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; } diff --git a/inc/plugins/info/syntax.php b/inc/plugins/info/syntax.php new file mode 100644 index 000000000..4567de685 --- /dev/null +++ b/inc/plugins/info/syntax.php @@ -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 : diff --git a/inc/plugins/syntax.php b/inc/plugins/syntax.php new file mode 100644 index 000000000..bb5680cd3 --- /dev/null +++ b/inc/plugins/syntax.php @@ -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 : diff --git a/inc/pluginutils.php b/inc/pluginutils.php new file mode 100644 index 000000000..3be57f2d7 --- /dev/null +++ b/inc/pluginutils.php @@ -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; +} + + diff --git a/inc/template.php b/inc/template.php index 2baa6f77e..0826ba6f8 100644 --- a/inc/template.php +++ b/inc/template.php @@ -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(); -- GitLab