ewiki internals and extension howto =================================== This part of the [README] series describes a bit of the internals of the ewiki script and its plugin system. It lists variables, coding guidelines and a few recommendations. These informations are useful if you'd like to change some of the hardcoded behaviour, fix annoying bugs you've found or to write your own extensions. You do not need to read this, if you just want to setup and use a Wiki. README.programming ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ 1 ewiki_ functions() 2 $GLOBALS pollution ($ewiki_ variables) 3 internal coding explained 3.1 how ewiki operates 3.2 used variables 4 Extension HowTo 4.1 the PlugInterface 4.2 plugin tasks 4.2.1 mpi plugins 4.2.2 authentication/permission plugins 4.3 writing your own plugin 4.4 format_* / rendering plugins 4.4.1 ewiki_format() internals 4.4.2 the format_ plugin hooks 4.4.3 $iii[] and $ooo[] block flags 4.4.4 your own block markup plugin 4.5 xpi plugin system 5 mysql database structure 6 Just using the wiki source transformation -------------------------------------------------------------------- 1 -- ewiki_ functions() ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ Some of the core functions of ewiki.php can be used separate from the others and some of them were designed to be replaced by different implementations. Btw, all the functions, constants and variables start with "ewiki_" to make it easier to mix it into other projects (reduces function name conflicts and similar problems, that usually arise if you join two or more scripts into one program). ewiki_page($id) --------------- This is the main function which fetches the selected WikiPage (or the one given with $id) via ewiki_database to transform with ewiki_format(). If the requested page does not exist it returns the edit screen. It also includes some virtual pages (InfoAboutThisPage, NewestPages, SearchPage, ReferencesToThisPage, ...). ewiki_page_...() ---------------- These functions were separated out from the main ewiki_page() to make it more readable. Most of them contain code to generate the few special/internal WikiPages (Search, Newest, Info, and the Edit
, ...) ewiki_control_links($id, $data) ------------------------------- Prints the line with the EditThisPage and PageInfo, ... links. ewiki_format($wiki_source, $params) ---------------------------------------------------------- This returns the formatted (HTML) output for the given WikiSource (with all the WikiMarkup in it). The second param is an array with various config overrides. An entry of "scan_links"=>1 for example tells ewiki_format to lookup the referenced WikiPages in the database (see ewiki_scan_wikiwords) for filling up $ewiki_links. Another $params entry is "html"=>0, which controls interpetation of the ... page content blocks. ewiki_render_wiki_links(&$o) ---------------------------- Transforms WikiLinks and square brackets in a page into html links. ewiki_scan_wikiwords(&$wiki_source, &$ewiki_links) -------------------------------------------------- work with regex on the wiki source text, to find valid WikiWords, the $ewiki_links will be filled with informations if the found page names exist in the DB. ewiki_link_regex_callback() --------------------------- Called from ewiki_format(). To separate the ewiki_format() from the database this function will utilize the global $ewiki_links (generated on demand by ewiki_format) to output either a normal link or a question-mark after the WikiPageName to signal a non-existent page. ewiki_script() -------------- Builds the complete URL needed to access the given resource. This function replaces/enhances the static EWIKI_SCRIPT constant and unifies the generated URLs (less bugs). It also helps around various design flaws (like nice looking URL strings), that made some parts of ewiki a bit weird and unreliable in the past. Btw, now the base URL is stored in $ewiki_config["script"]. ewiki_script_binary() --------------------- Is just a ewiki_script() wrapper, but can additionally distinguish between binary download and upload URLs, which could be utilized by (database external) plain file storages (see plugins/binary_store). ewiki_binary() -------------- Gets called automatically for requests with the ?binary= trailer which is used to reference cached and uploaded images (or not yet cached ones). ewiki_author() -------------- returns a string with REMOTE_ADDR and the $ewiki_author or a default string incorporated ewiki_auth() ------------ Is a simple interface to a probably large collection of plugins, which should to actual user and permission management. Support for this in the core is however still sporadic. ewiki_auth_user() ----------------- Queries all registered user databases, and is usually itself called from within an auth_method/auth_query plugin. ewiki_t() --------- Fetches a text string from the $ewiki_t[] array and additionally adds some text pieces into it (given as second param). It can retrieve translations for registered abbreviations, or searches for complete text fragment replacements. It also understands _{...} to recursively translate a text snippet inside of larger text blocks. This is probably a bit slower and less readable than the previous usage of EWIKI_T_ constants, but it saves some memory and allows to extend translations or additional text constants (of plugins) a lot more easier (previously one had to edit inside a function, which is almost impossible to do from outside / per configuration). ewiki_make_title() ------------------ Returns a string enclosing (the generated) page title (as link) into the html title markup "

". The $class parameter actually tells from which plugin sort it was called, and this decides if a link will be generated or the title will be unclickable plain text (the setting in $ewiki_config["print_title"] is used to determine that). $go_action tells which action to link the title to. ewiki_chunked_page(...) ----------------------- Is a helper function to split large results into multiple click-through pages, and is used by info/ and some search functions/plugins. It only produces the click-through links for inclusion on other dynamic pages, allows overlapping of page chunk ranges. ewiki_in_array($value, &$array, $dn=0) -------------------------------------- Case-insensitive variant of PHPs` in_array(), returns the $value if found. The $array will be all-lowercased afterwards (except when $dn was set). ewiki_array($array, $index=false, $am=1) ---------------------------------------- Returns input-array lowercased (indices), or just the entry for the $index if searched for. The $am decides if multiple entries should be merged together (uppercase+lowercase merging produces overlaps). ewiki_db:: ---------- This static class provides the interface to the database backends. It abstracts the database as a simple flat store for named entries (file or page names). Therefore it is that easy to switch from a SQL backend to a flat file based data store. Actually, this rude form of "database abstraction" has the drawback, that it knows only little about the data it maintains. But this also allows to internally extend the used structures if necessary. You call the individual functions like " ewiki_db::GET() ", the atomic features are: ::GET($id) Fetches the latest version of the "$id" page from the database. ::GET($id, $version) Retrieves a given version instead. Counting starts at 1. ::WRITE($data) Saves the contents of the given data array in the database, does _never_ overwrite an existing entry (you must keep track of the {version} yourself) unless a second paremeter (1) was given. ::GETALL($fieldnames, $mask, $filter) Fetches an array of all existing pages in the database, but returns it as ewiki_dbquery_result object, which will throw the requested columns on ->get(), where the entries 'id', 'version' and 'flags' are always present. ::FIND($list_of_pagenames) Searches the database for the queried page names, returns an array which associates the boolean value (if pages found) with their names ::SEARCH($field, $content, $caseinsensitive, $regex, $mask, $filter) Returns only those pages, where the database COLUMN has a content that matches the requested value; the list of pages is returned as ewiki_dbquery_result object, where you can access the individual entries using the ->get() call, which will return the columns 'id', 'version', 'flags' and the scanned COLUMN of course unless you ->get("_ALL=1"). The following three actions are not required for correct operation, but provide additional functionality for some plugins or tools. ::HIT($id) Increases the hit counter of the given wiki page by 1, what is not implemented in db_flat_file. ::DELETE($id, $version) Removes the specified page (only the given version) from the database; implemented in all database plugins but should be used from within the tools/ only. ::INIT() For SQL database backends this creates the required tables. There are also a few virtual functions, that only provide utility code or make use of the atomic funtions itself: ::APPEND($id, $text) Adds the given $text at the bottom of the named page. ::UPDATE(&$data) Refreshes all the meta data fields in the given page hash, but of the {version} field. This is usefully be called before any ::WRITE call. ::CREATE($id, $flags, $author) Returns a fresh page $data hash. Other functions are usually used internally only, as for example the ->ALLFILES() command in dbff or dba/dbm plugins. $ewiki_dbquery_result --------------------- Has the member variables $keys and $entries, where the latter contains an array of page names that where triggered by your GETALL or SEARCH request, and the $keys array contains the column names that each subsequent "$result->get()" will return. ->get() Returns the database entry array (see GET above), but only the fields the database query should return (at minimum these are 'id', 'version' and 'flags' and the searched column for SEARCH). ->get("_ALL=1") Instead returns the complete entry. ->get(0, $mask) The second parameter is for filtering out content: 0x0001 strips out _HIDDEN pages from the result set 0x0002 removes any _DISABLED pages 0x0020 performs the _PROTECTED_MODE_HIDING checks ->get(0, $mask, $type) if the $type parameter is supplied, then the results are filtered by the given page type (_DB_F_TYPE mask) ->count() Returns the number of found database entries. ->add($row) [internal] This is used by the ewiki_database() core functions to initialize the $result object with the found entries. -------------------------------------------------------------------- 2 -- $GLOBALS pollution ($ewiki_ variables) ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ At least the ewiki_page() function produces variables in the global namespace. Of course they also were named to not interfere with anything from yoursite.php: $ewiki_id - Contains the current page name, after ewiki_page() was called. $ewiki_action - Contains the $action/ForTheCurrentPage. $ewiki_title - Will be set after the first call to ewiki_page(), it is most useful to be printed inside the tags inside <HEAD>. So if you want to use it you should call ewiki_page() very early, but save its output into a variable for later use. This way you can make the current wiki pages` title available (the _title may be different from the pages _id). $ewiki_data - Contains the page data hash as retrieved from the database, but that {content} has been removed. $ewiki_errmsg - Sometimes used to pass error notices back (ewiki_auth does so for example). $ewiki_links - Is an array produced by ewiki_format() that associates all found WikiPageNames with a value of 0 or 1, depending on if the referred page exists in the database. $ewiki_author - The content of this variable is saved in the author field of newly created wiki pages (it will be filled with IP:PORT if not set from outside). This is only an informational setting, and does not directly correspond to the _PROTECTED_MODE. You should set it, whenever yoursite.php notes a logged in user (so his login gets saved in the wiki pages 'author' column). But you should REALLY NOT SPAM IT with your own name or ad words. $ewiki_auth_user - Is set by ewiki_auth_user() whenever it successfully authenticates a user in _PROTECTED_MODE. This variable is then used as reliable state setting, which affects permission granting. $ewiki_ring - Holds the permission level ('ring') of the currently authenticated user (or else will be unset). This value tells only about the user, many plugin functions have built-in requirements which will be compared against this value (no value or zero means full permissions). While this is the built-in way to grant permissions and often also suits the needs to do it, the _auth() plugin interface allows to work at a much finer degree of access granting. values: 0=administrator, 1=moderator, 2=editor, 3=guest See also plugins/auth/README.auth for more informations. $ewiki_plugins - Is an array which connects task names (say "database" or "image_resize" for example) to function names. You can utilize this if you decide to extend ewiki. There is an own chapter on this. $ewiki_config - Imports some configuration settings from older constants, and introduces newer ones, which can then be overridden at runtime. Also holds some work and markup transform data. $ewiki_t - Text definitions and translations for all possible messages. -------------------------------------------------------------------- 3 -- internal coding explained ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ This section is to explain some of the coding style of ewiki, and how some things work. While many parts of ewiki carry good source code comments, it is always difficult to quickly understand larger scripts like ewiki.php by just reading through it. how ewiki operates ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ ewiki_page() - decodes the $id and $action from the GET or POST parameters - tries to load the page from ewiki_database() - if this failed then it calls the database init function - calls some init plugins, calls the _auth() interface - chains to ["page"] plugins which activate for registered $id's - alternatively calls a plugin that was registered for $action - the default however is to render the current page via _page_view() - adds a page title - sends the generated output (view page or plugin output) back to caller (for output into yoursite.php) ewiki_page_view() - feeds the current page $data's ["content"] into ewiki_format() - also decodes a few formatting parameters (e.g. if html allowed) - returns the gererated html back ewiki_format() - beatifies the source code (unifies to plain UNIX newlines) - calls init plugins (wiki source mangling) - splits source into blocks, calls block plugins - then goes through each line of the wiki source to generate html - there is line-start, in-line and complete-markup - afterwards everything went from source into the $ooo-output var - first calls the link_pre_scan_regex (which searches for wikiwords and stores that information into $ewiki_links) - then calls the wiki-link transformation regex function - then calls post plugins and returns generated <html> ewiki_render_wiki_links() - searches for all (pre-fetched) $ewiki_links via ewiki_db::FIND() - transforms the wikiwords into html-links - with the regex and callback func: returns output back to - ewiki_format() ewiki_link_regex_callback() - transform the wiki source snippet returned from the preg_replace() call into a html link string - (complicated) ewiki_$page_plugin_*() - page plugins return html output, which usually is hardcoded as strings into them - provide some interactivity ewiki_$action_plugins_*() - activate on pages with special registered $action's - provide some interactivity (for page editing for example) used variables ŻŻŻŻŻŻŻŻŻŻŻŻŻŻ Variables in ewiki often have similar names, and are also regularily passed by reference from one function to another (so it is in fact the same variable). $id - Is often the name of the current page (which is to be returned as output. The content of this variable is also available via the global $ewiki_id [[for plugins that do not have the common ($id,$data,$action) interface parameters]]. $data - Contains the entry fetched with the initial ewiki_database() call. This is an array of the form: array( "id" => "CurrentPageName", "version" => 1, # 1 is the lowest possible "flags" => 1, "created" => 1002056301, "lastmodified" => 1002171711, "hits" => 235, "author" => "localhost (127.0.0.1:4981), "meta" => array("Http-Header"=>"X", "setting"=>"val"), "content" => "wiki source...", ) $action - The $action with wich the current page was requested (most often "view", but everybody also knows "edit"). $uu - Short for "unused". Is used as temporary variable, especially with preg_match() and string functions. $result - Used for database queries SEARCH and GETALL. $row - Holds temporarily fetched entries from the databases (like $data), if page lists are to be generated. -------------------------------------------------------------------- 4 -- Extension HowTo ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ Best way to extend ewiki is to read the man page on vi or emacs ;-> However the tool that made this all possible was: joe. the PlugInterface ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ The $ewiki_plugins array holds an array of "task names" connected to function names (that of course should do something senseful). As an example: $ewiki_plugins["image_resize"][0] = "ewiki_binary_image_resize_gd"; connects the task name "image_resize" to function already inside ewiki.php, and the task "image_resize" will be called for every uploaded or to be cached image. The function name here does not say anything about the parameters the function will be called with later. You have to look up the original function implementation in ewiki.php to see which parameters will be passed; so you could write your own task plugin. The [0] in the example above shows that this is the very first registered function for the task "image_resize", there could be others as well. So if you write a plugin you should take care to add your function name using $ewiki_plugins["task"][] = "my_func" so you won't overwrite a previous function name ''registration''. There are of course tasks like ["database"] where only one of the plugin functions will be called, in this case you should of course overwrite [0]. Two special case "tasks" are ["page"] and ["action"], because they aren't counted with numerical indices, but instead carry WikiPageNames or other idf strings as array/hash index. plugin tasks ŻŻŻŻŻŻŻŻŻŻŻŻ Here's a short summary of current PlugInterface "tasks" and (recommended) function interface definitions (the "= function (..." lines). A plugin hook with [] means there can be multiple, and each one would be tried. basic ----- ["page"][$PageName] - called for requests to page "$PageName" (like "SearchPage", "NewestPages") = function ( $id, $data, $action ) ["action"][$ACTION] - called for requests with url "?id=$ACTION/pagename" (core actions are "edit", "links", "info", "view") = function ( $id, &$data, $action ) ["handler"][] - called from ewiki_page() on start-up, if it returns a string the page was handled and no further processing takes place, the plugins output is used = function ( $id, &$data, $action ) rendering --------- ["render"][0] - alias for ewiki_format() - our "WikiKernel" ["format_source"][] - called inside the format function for the wiki source, implement this or the following ones to use complex wiki markup = function ( &$wiki_source ) ["format_line"][] - generic call from inside wiki format engine for every line, you may need to use static vars inside your plugin function = function ( &$o, &$line, &$post ) ["format_tbl"][0] - called to handle "wiki|table|markup" (the first and last | are already stripped) = function ( &$o, &$line, &$post, $tbl_open=0 ) ["format_final"][] - call after wiki source was transformed into html (WikiPageLinks were already interpolated too) = function ( &$html ) ["format_block"][] - called, with the page fragment extracted using the string patterns of the according $ewiki_config["format_block"] entry = function (&$currbuf, &$in, &$iii, &$s, $btype); ["format_para"][] - called, if the $para (text enclosed in <p></p>) is to be written into the output stream $ooo[$in][0] = function (&$para, &$ooo, &$s); ["link_url"][] - called to transform wiki source references = function ( $href, $title ) ["link_final"][] - called from ewiki_link_regex_callback to transform the final <a href> = function ( &$str,, $type, $href, $title ) special tasks ------------- ["database"][0] - only [0] will be called in favour of the ewiki.php internal ewiki_database_mysql() = function ( $action, $args=array() ) ["image_resize"][] - all [] registered functions will be invoked = function ( &$content, &$mime, $return=0 ) ["mime_magic"][0] - hooks before save_binary/image to fetch the correct mime type for non-image files; nowadays just an always-available get_content_type() = function ( &$content ) ["binary_get"][0] - the binary_repository handles large/binary content (to separate it out of the standard sql-database), usually just sending it to stdout = function ( $id, $meta ) page lists ---------- ["list_pages"][0] - <li>st generating callback function = function ( $lines ) ["list_dict"][0] - special variant of the above one (called just for / from within PageIndex and WordIndex listings) = ??? ["list_transform"][] - works on the given list of links (text transformations) = function ( &$lines ) ["make_title"][0] - allows to chain a replacement function for ewiki_make_title() = function ($title, $class, $action, $go_action, $may_split) ["title_transform"] - changing the currently linked title (called from within _make_title) = function ($id, &$title, &$go_action) page transform / additions -------------------------- ["view_append"][] - output will be printed below a rendered page = function ( $id, $data, $action ) ["view_final"][] - can rework the full html of the rendered page = function ( &$html, $id, $data, $action ) ["view_append"][] - add <html> code at the end of the currently viewed page = function ($id, $data, $action) ["view_final"][] - filter hook for final processing of "view/"ed pages = function ($o, $id, $data, $action) ["page_final"][] - filter hook for final processing of any shown page (any action: edit/, view/, info/, ...) = function ($o, $id, $data, $action) edit/ hooks ----------- ["edit_preview"][] - called if edit pages [preview] button pressed = function ( $data ) ["edit_form_final"][] - add <html>/<form>s to the edit/ page = function (&$o, $id, &$data, $action) ["edit_form_append"][] - insert other <input> fields between <textarea> and <submit> button on the edit/ page = function ($id, &$data, $action) ["edit_hook"][] - chains into before the edit box is printed (to allow security checks, pre-edit-tweaking, ...) any output terminates the current edit/ attemp = function (&$id, &$data, &$hidden_postdata) ["edit_save"][] - immediately called before saving the currently "edit/"ed page into the database, allows last transformations or rejection (unsetting $data) = function (&$data, &$old_data) ["edit_patch"][0] - special hook for the patchsaving plugin = function ($id, &$data) bloat extensions ---------------- ["auth_*"][] - plugin tasks used with ewiki_auth() = see the plugins/auth/README.auth ["mpi"][...] - markup plugins, see next paragraph ["init"][] - run once, when the main script is included() ["page_init"][...] - init functions, called when ewiki_page() is called the very first time aliases and variants -------------------- ["action_always"][$ACTION] - are called with precedence over ["page"] plugins (for example "links" which also works for registered page plugins) ["action_binary"][$ACTION] - action/admin plugins which do not care, if the current page actually is binary data Some other entries have been re-extracted into $ewiki_config, because they were falsely in $ewiki_plugins. See the paragraph at the start of the README on the $ewiki_config array. This list will probably not stay up-to-date, so please grep the ewiki.php script for all occurrences of 'ewiki_plugins["', and you can of course invent some new and tell the author how it helped you to implement something very different. mpi plugins ŻŻŻŻŻŻŻŻŻŻŻ Plugins of the class "mpi" extend the wiki markup with html like calls to dynamic content generating functions. They were taken from the ewiki adaption of Hans B Pufal and are very similar to the plugins found in PhpWiki. In order to use them you must first load their generic PlugInterface file using include("plugins/mpi.php"); As an exception to all other ewiki plugins, the mpi plugins are then loaded only as needed (the mpi interface takes care). You could however still load all the "mpi_*.php" scripts yourself beforehand. You can then call those plugins from the wiki markup with: <?plugin Calendar ?> There are many different plugins available (not all included with this ewiki distribution), and the allowed arguments differ widely (must all be noted inside the < > and are written in arg=value style separated by semicolon): <?plugin Insert WikiPageName ?> # this includes the referenced WikiPage in a box # into the current one, Note the ! to prevent that # WikiWord from getting rendered (before mpi sees # it) <?plugin BackLinks page="ForGivenPageName" ?> <?plugin SparseTable columns="c1,c2,c3" c1="column 1" c2="column 2" c3="column 3" c1="#1" c2="2.0" c1="#2" c3="3.0" c2="2.5" c3="--" ?> See the distributed page "MpiPlugins" for a complete list. The API is very simple, so one could easily write a custom code block extension. (The mpi_backlinks plugin makes a nicely short example.) authentication/permission plugins ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ The paragraph and descriptions about the _auth interfaces have gone into plugins/auth/README.auth writing your own plugin ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ Using the list of current plugin tasks, you could (hopefully) write your own extension with ease. It is probably most simple to write a dynamic ["page"] plugin, so we start with this as example. All you need is a function definition like: function my_page_plugin($id, $data, $action) { return("This is the returned page <b>content</b>."); } And then just register it as ["page"] plugin, using your defined function name (yes, this does NOT need to start with the usual "ewiki_" prefix!). You also need to tell ewiki about the WikiPageName under which your plugin should be made available: $ewiki_plugins["page"]["MyPagePlugin"] = "my_page_plugin"; That's it. But of course your function should do something more useful, than just returning a hardcoded html string - even if this all, what's necessary here. The parameters to your ["page"] plugin function you'll often just want to ignore, and implement your plugin functionality (hard disk formation e.g.) independently from such things. It is likewise easy to write an ["action"] plugin; but this type of plugin should then process some parts of the $data entry and work for nearly any page (extracting contents or presenting the current page differently). So this kind of plugin could be used to initialize a download for the current page or to allow to email it to someone else (beware of the spammers!). format_* / rendering plugins ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ It is rather simple to add WikiMarkup using the $ewiki_config["wm_..."] settings, but for some tasks you need stronger weapons like rendering and markup plugins. The ewiki_format() function (often blatantly referred to as "rendering kernel") recently got rather complicated to add support for the 'block' plugins. Therefore we'll first need to discuss the variables structures and names used inside of it: ewiki_format() internals ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ When the function receives the input string (WikiSource), it first escapes all html tags using & < > to replace the & < > chars (because HTML is not allowed within Wiki, initially). Then it runs optional ["format_source"] plugins on the whole wiki page (still one source string). Afterwards ewiki_format() starts to split that wikisource into fragments meant to get handled by ["format_block"] plugins AND/OR the Wiki -> HTML transformation code inside of it. It creates an array called $iii[] from it - each fragment being an array on its own: $iii[0] = array( 0 => "WikiSource ...", 1 => 0x0FFF, 2 => "core", ); Initially we here just have one fragment [0] - and often this remains the only one (if no block plugin activates for the current WikiPage). The 0=> entry contains the body (wiki source) of a fragment, while at the 1=> index you'll find the block flags and 2=> is just the name of the block plugin to handle that fragment. If there is some block code, like <htm>...</htm> or <pre>...</pre> in the WikiPage, you'll end up with a larger $iii[] input array: $iii[0] = array("WikiSource...",0xFFFF,"core"), $iii[1] = array("<b>....",0x0002,"html"), $iii[2] = array("text",0x0FFF,""), Besides the $iii[] input array, we'll also have an array containing rendering status variables called $s[]. The most important entry there is $s["in"], which is the index into the currently accessed $iii[] wiki page source or block fragment. And ewiki_format() then uses various alias variable names for entries of the status var $s[] array - for example $in and $s["in"] are the same index number. After ewiki_format() separated the input source into the block fragments of $iii[], it will then run the actual ["fragment_block"] plugins on it. These can then apply regexs on them, or strip the fragments completely out of $iii[] or transform then into regular wiki source. Then the large wiki transform loop comes into action, but only for $iii[] fragments, whose flags (in the $iii[...][1] number) have the bit 0x0001 set. Other blocks/fragments of $iii[] remain untouched. While transforming the $iii[] arrays WikiSource a new fragments array will be created, called $ooo[] - the output array, which has exactly the same layout. And every $iii[$in] directly maps to the $ooo[$in] - same index number! But the later then already contains <html> instead of wiki source. Inside of the transformation loop, two other plugin types are activated (besides applying the $ewiki_config["wm_..."] ruleset): the ["format_line"] and ["format_para"] plugin groups. After all $iii[] blocks have been transformed into $ooo[], a second loop will check the flags (this time $ooo[...][1]) for the bit 0x0002 which tells, if WikiWords should be transformed into html <a href=> links. After link conversion (on the selected fragments), all blocks (the content entries $ooo[...][0] of course) of $ooo[] are merged together into one <html> string. After the ["format_final"] plugins run over this, ewiki_format() returns the resulting <html> page. the format_ plugin hooks ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ As denoted above, the ["format_source"] and ["format_final"] plugin hooks are the simplest to work with, as both only get one parameter (passed by reference) containing either the full WikiPage source text or the already fully rendered <html> output. The ["format_line"] and ["format_tbl"] hooks are also rather simple, lookup their interface to know which variables you could modify. The ["format_para"] and ["format_block"] plugins both recieve either the $iii[] or $ooo[] and status $s[] array variables (plus a few aliases and shortcomings). This makes these hooks look a bit more complicated, but allows great flexibility - a "_block" plugin could for example merge its $iii[] fragment with another one, or replace itself with nothing. To write a markup plugin, you should lookup the actual interface in the 'ewiki.php' script or in this README. And don't forget that most parameters are meant to be passed by reference to be useful! $iii[] and $ooo[] block flags ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ The $iii[...][1] and $ooo[...][1] hold the flags (as defined by $ewiki_config["format_block"][...][2]) for each fragment of the WikiPage. The "core" blocks (plain WikiSource) always have the default 0x137F assigned. currently used bit values are: 0x0001 - render WikiMarkup (inline / text style) 0x0002 - render WikiLinks 0x0004 - international character &#htmlentities; allowed 0x0008 - enable BlockMarkup (lists, tables, headlines, ...) *NEW* 0x0400 - decode < > htmlentities again before calling block plugin 0x1000 - fragment further (other block plugins can split it) pseudo-values (OR checks / behaviour): 0x0010 - consider to be inline block between WikiSourceBlocks (prevents paragraph breaks between plugin and wiki block) 0x0022 - scan for WikiWords in this paragraph your own block markup plugin ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ Of course a _block plugin is a bit more complicated than a action or page plugin, but generally you only need this small stub: $ewiki_plugins["format_block"]["your"] = "your_block_func"; $ewiki_config["format_block"]["your"] = array( "<your>", "</your>", false, // no special filter rule/flag 0x0014 // see above, often also just 0x0000 ); function your_block_func(&$str, &$in, &$iii, &$s, $btype) { ... } Where you would just rework the conents of "$str", which will be the text part in between the <your> and </your> tags as defined by the $ewiki_config[] block plugin settings. The other variables in that API aren't normally important. You only must take care, that "<", ">" and "&" are still encoded in htmlentities() at this stage. xpi plugin system ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ A newer extension of ewiki (namely plugins/feature/xpi) allows to store addon functions in the database (the code is saved as _BINARY db entry where usually page text was stored). The "PlugInstall" page allows to load the extension code from (even remotely located) .xpi files into the database, from where it can be used later. In order to use once installed xpi code, only the "feature/xpi0.php" plugin was needed. Through the design of the xpi feature extension nearly all plugins could be converted and installed in the .xpi format. Though this doesn't make sense for database backends, and converting all to-date plugins is silly. So this feature is further merely used for fun and mini-extensions. You could easily write your own .xpi plugins using the description in the "tools/mkxpi" script. First you need to create a .src file with the plugin code as usual (write it like an ordinary plugin script, except for page plugins), and a few meta: informations on top of that file. Then the mkxpi tool converts this in the binary .xpi format for distribution and installation. Typically this would look like: id: UniqueExtensionName type: actionlink_and_textdata license: Free to use author: PutYourNameHere description: adds the search/ action to the control-links line icon: data:image/png;base64,iVBORw0KGgoAAAANS... code: <?php // just look'n'feel settings, no real plugin code here... $ewiki_config["action_links"]["view"]["search"] = "SEARCH_ACTION"; $ewiki_t["en"]["SEARCH_ACTION"] = "search in other pages"; $ewiki_t["de"]["SEARCH_ACTION"] = "woanders suchen"; ?> Most of the fields are optional (author, license, description and of course icon) but senseful. The type: is required, but only a value of "page" has a special meaning here (otherwise is merely descriptive). The id: gives that plugin an internal identifier which will later be used for storing the extension with a database-wide unique name; for "page" type plugins it of course would be the name under which it was installed. The code: contains the usual plugin registration and function definition code. But for "page" xpi plugins it may not have a function body, but lots of 'echo' calls to output the dynamic pages contents when called (no return allowed in this incarnation of page plugins). After this no other fields should appear. Future .xpi versions may allow to embed other files for instant installation (maybe image files for storage in database). But until now onle the code: entry carries data which will be installed as database page. All the other meta entries (id, type, author, ...) will however be stored in the xpi registry database entry, which is also used to control the enable/disable flag for each of it (only page plugins cannot be disabled). To share your own extensions simply send them to one of the core project developers (Andy or Mario) or just leave a link on the "XpiPlugins" page: http://erfurtwiki.sourceforge.net/?XpiPlugins because that way users will immediately see it when remotely installing .xpi plugins from that default xpi plugin repository. It may be an security risk for users if they install code from unchecked sources, but the complete URL is always displayed right before storing the external code into the database. The most interesting part of the .xpi system (PlugInstall page) is the possibility to later disable or remove extensions easily. You only need to setup the administrator password to install and control click-and-run extensions without hacking the 'config.php'. ------------------------------------------------------------------ 5 --- mysql database structure ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ The MySQL database table structure is (to a certain degree) compatible with that of the well known ğPHPWikiĞ v1.2.x (you only need to change EWIKI_DB_TABLE_NAME to "wiki" to use it). This is the MySQL statement which creates our database table (you can find it at the bottom of the "ewiki.php" script): CREATE TABLE ewiki ( pagename VARCHAR(160) NOT NULL, version INTEGER UNSIGNED NOT NULL DEFAULT 0, flags INTEGER UNSIGNED DEFAULT 0, content MEDIUMTEXT, author VARCHAR(100) DEFAULT 'ewiki', created INTEGER UNSIGNED DEFAULT 0, lastmodified INTEGER UNSIGNED DEFAULT 0, refs MEDIUMTEXT, meta MEDIUMTEXT, hits INTEGER UNSIGNED DEFAULT 0, PRIMARY KEY id (pagename, version) ) I didn't like the column name {pagename} but as I've seen this was the only difference I renamed it, therefore now the ewiki_database() function translates it from "pagename" to "id" and vice versa most of the time - else this would be really slim and nice code :) The columns {version} holds the different saved page versions. Other Wikis require a secondary "backup" or "history" table for old versions, but as I couldn't imagine what this is for, there is just one table in ewiki; and it seems this is really enough. The first {version} of a wiki page is always numbered 1. An existing page {version} will never get overwritten => very secure MySQL usage. For what's in the {flags}, see the README section about constants. The {content} of course holds the wiki pages source. The {created} and {lastmodified} should be clear too. {refs} contain a "\n" separated list of referenced WikiPages. The code to generate that list is rather unclean, so it often contains GhostPages. However this does not hurt ewiki and the few functions that utilize {refs}, so there is currently no need to slow it down by fixing this. {meta} can hold additional informations, which allows to extend ewiki without requiring to ALTER and convert the ewiki database table. It currently holds some mime headers for binary content and some other useful informations for images and uploaded files. {hits} should have gone into {meta} really. But having it separate allows us to use the very fast mysql UPDATE function. Note, that the ewiki database table can hold other things than wiki pages - binary content (images) for example, depending on the setting of the {flags} field. And last comment about this, the one-table-concept also made it really easy to implement the flat file based "database backend". ------------------------------------------------------------------ 6 --- Just using the wiki source transformation ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ The ewiki_format function was designed to be used independently from the ewiki database. ewiki_format($wiki_source, 0); It just needs the "wiki_source" as argument and generates a nicely formatted page from it. All you need to take care about is the $ewiki_links variable. Set the $ewiki_links=true ("true" and not "1" or anything else) to enforce ewiki_format() to treat all references as existing. To separate the ewiki_format() function out of recent ewiki versions, you'll also need ewiki_script(), ewiki_link_regex_callback(), ... and a lot of constants to take with. It is often much easier to just include("ewiki.php") for using ewiki_format(). You then should however take care, that the _binary part doesn't get activated by accident. To prevent this, just put following before the include() statement: unset($_REQUEST["binary"]); include("ewiki.php"); If you need it more quickly, or don't want to load the whole ewiki.php file, then just try the fragments/wiki_format.inc, which is a stripped down version of an older rendering core function (no WikiLinks, no binary stuff). Contributed by Frank Luithle.