View source for Toms modified Wakka API

API Reference

=== Wakka API for version 0.1.2 ==
[//Note//: This list actually describes the API for TomG's much-hacked personal version of Wakka 0.1.2, with additions as indicated by the markers ##[*]## (function added) and ##[=]## (functionality and/or API extended). The purpose for these additions was to make Wakka more usable as a general-purpose 'site engine' rather than solely as a (very good) wiki, and to make it backward-compatible to PHP 4.0.6 (which my ISP insists on using). Unlike the (also good) Wacko variant of Wakka, it's still compact and fast; and perhaps importantly, the additions require //no changes// to the Wakka 0.1.2 database structure. If you're interested in these additions, email me (tgraves) at tomgraves. com. au.]

{{toc}}

==== Main (index.php)===

These functions are in the root-file Wakka.php, listed in the same order as in the file.

==== DB ==

//int// **Query**(//string// query) -- general-purpose Mysql query
	##query## - query-string in Mysql syntax

//array// **Loadsingle**(//string// query) -- single-record select
	##query## - query-string in Mysql syntax
	array is one-dimension indexed by fieldname
	returns empty array if record not found

//array// **Loadall**(//string// query) -- multi-record select
	##query## - query-string in Mysql syntax
	array is two-dimension 0-indexed list of records with each record-array indexed by field

==== Misc ==

//float// **Getmicrotime**() -- return system micro-time

//string// **Includebuffered**(//string// filename, //string// notfoundtext=""''"", //string/array// vars=""''"", //string// path=""''"") -- return result of embedded code-fragment
	##notfoundtext## - returned if file is not found
	##vars## - optional array of variables to be passed (via ##extract()##) to the fragment
	##path## - ':'-separated list of directories to be searched
	returns result of all print/echo statements in included fragment
	//note// Includebuffered is called by Action(), Method(), Format()

==== Variables ==

//string// **Getpagetag**() -- return page-tag [shorthand for ##Getpagevalue('tag')##]

//string// **Getpagetime**() -- return page save-date [shorthand for ##Getpagevalue('time')##]

[*] //mixed// **Getpagevalue**(//string// id) -- return page-property stored in ##$page## array (usally a string)

[*] //null// **Setpagevalue**(//string// id, //mixed// value) -- set page-property in ##$page## array

[*] //mixed// **Altvalue**(//mixed// main, //mixed// default) -- return main if non-empty, default if main is empty
	is a possibly-easier 'shorthand' for the ##( ? : )## construct

//string// **Getmethod**() -- return method for current page 

//mixed// **Getconfigvalue**(//string// id) -- return configuration property in ##$config## array

//string// **Getwakkaname**() -- return site-name [shorthand for ##Getconfigvalue('wakka_name')##]

//string// **Getwakkaversion**() -- return current code-version id

==== Pages ==

//array// **Loadpage**(//string// tag, //timestamp// time=""''"", //bool// cache=1) -- load page-data
	##tag## - page-tag
	##time## - if set, gets specific version of page, else most recent version (default)
	##cache## - if set, tries page-cache first (default)
	returns ##$page## array, [=] including page-properties from ACLS table
	//note// if ##time## is not set (i.e. not an old version), stores result in page-cache
	[=] search for tag is case-independent (i.e. need not be strict wiki-name); tag may include underscores

[*] //array// **Loadpageoptions**(//string// tag=""''"") -- returns array of extended page-properties for ##tag## from ACLS table
	##tag## - page-tag, or current page if absent
	returns single-dimension array indexed by property-name, to merge into ##$page## array
	effectively, provides arbitrary extension of ##$page## fields

//array// **Getcachedpage**(//string// tag) -- retrieve page-array from page-cache

//null// **Cachepage**(//array// page) -- store page-array in page-cache
	//note// - uses 'tag' member in page-array as index in cache

//null// **Setpage**(//array// page) -- sets the page as the current 'this-page'
	//note// - will not change the current-page if tag (presumably whole array) is empty

//array// **Loadpagebyid**(//int// id) -- get page-array (without extra page-properties) for selected page-record
	id - unique id for page-record

//array// **Loadrevisions**(//string// tag) -- get array of page-arrays (without extra page-properties) for all versions of this page, sorted in reverse date-order

//array// **Loadpageslinkingto**(//string// tag) -- get array of array of page-tags linking to this page
	returns 0-index array of record-arrays each with single field ##'tag'##

//array// **Loadrecentlychanged**() -- get array of **all** current pages, sorted in reverse date-order
	returns array of page-record arrays
	page-record arrays are also stored in cache
	//warning// - this is likely to be //very// resource-intensive

//array// **Loadwantedpages**() -- get array of (intended) tags for links to non-existent pages
	returns 0-index array of record-arrays with fields ##'tag'## (missing page) and ##'count'## (no. of references)

//array// **Loadorphanedpages**() - get array of tags for pages without links to any other page
	returns 0-index array of record-arrays with single field ##'tag'##

//array// **Loadpagetitles**() - get array of tags for all pages
	returns 0-index array of record-arrays with single field ##'tag'##
	//note// tag is page-'title' (as per Wakka) - see also ##Loadpageoptions()## function

//array// **Loadallpages**() - get array of latest versions of **all** page-records
	returns 0-index array of record-arrays with all standard Wakka page-fields (but not extended page-properties)
	//warning// - this is likely to be //very// resource-intensive

//array// **Fulltextsearch**(//string// phrase) - get array of current versions of pages with tag or body containing the search-phrase
	returns 0-index array of page-record arrays with all standard Wakka page-fields (but not extended page-properties), sorted in reverse date-order
	//warning// - this is likely to be resource-intensive

[=] //null// **Savepage**(//string// tag, //string// body, comment_on=""''"", //array// extra=""''"") -- store new/edited page-content
	##tag## - page-tag to use (may or may not already exist)
	##body## - Wakka-format text for page-body
	##comment_on## - if set, body is a comment on the page with this field as tag
	[=] ##extra## - optional field-name indexed array of extended page-properties for non-comment pages
		extended page-properties are stored in ACLS table
		if ##'parent'## property exists, also updates ##'parents'##, ##'siblings'## and ##'children'## properties for self and related pages
	[=] this function also triggers optional flushing of outdated page-revisions as per ##'pages_purge_time'## config-parameter (moved from ##Maintenance()## function - same as in Wakka 0.1.2, but triggered on each save-page rather than each page-view)

[*] //array// **Getparentlist**(//string// tag=""''"", //string// parent=""''"") -- get array of tags in 'parent' tree, sorted highest-parent-first
	##tag## - page-tag of end-point of parent-child tree (i.e. usually self) - default is self
	##parent## - page-tag of this tag's parent, if known
	returns 0-index array of page-tags

[*] //array// **Getsiblinglist**(//string// tag, //string// parent) -- get array of 'sibling' tags, sorted in tag order
	##tag## - page-tag to be //excluded// from sibling-list (i.e. usually self)
	##parent## - tag of 'parent' for siblings of this tag
	returns 0-index array of page-tags

[*] //array// **Getchildlist**(//string// tag) -- get array of 'child' tags, sorted in tag order
	##tag## - page-tag as 'parent' (i.e. usually self) to identify 'children'
	returns 0-index array of page-tags

[*] //string// **Maketree**(//array// taglist, //string// fieldsep='|', //string// rowsep='~') - return serialised version of tag and shortname, for tags in taglist
	##taglist## - 0-index array of tags (i.e. as returned by ##Getparentlist()## etc.)
	##fieldsep## - field-separator in output string
	##rowsep## - row-separator in output string
	(with default fieldsep/rowsep, output is compatible with default settings of menu handlers - see ##Menuitem()## function)
	if ##'shortname'## property is absent for a page, standard ##'tag'## property is used as default (i.e. backward-compatible with Wakka)

==== Cookies ==

//null// **Setsessioncookie**(//string// name, //mixed// value) -- store a session-value
	stored as root-value for the domain; expires when the browser closes
	also stored in the local COOKIE array

//null// **Setpersistentcookie**(//string// name, //mixed// value) -- store a persistent client-value
	stored as root-value for the domain; expires in 90 days
	also stored in the local COOKIE array

//null// **Deletecookie**(//string// name) -- delete a session/client value
	stored as null value, 1-second expiry, for the current session
	also stored as empty (not unset) in the local COOKIE array

//mixed// **Getcookie**(//string// name) -- retrieve a session/client value from the local COOKIE array

==== HTTP / request / link related ==

//null// **Setmessage**(//string// message) -- set content for a popup message to be shown on opening the next Wakka page
	(actually just stores the session-value 'message')
	//note// - because it's displayed via a Javascript alert-box, the message can only be around 10-20 words maximum
	//warning// - because it's eventually embedded in Javascript in the page  tag, the string cannot include " characters, and must not end with a ' character

//string// **Getmessage**() -- retrieve and clear the session-value 'message'

//null// **Redirect**(//string// url) -- immediate redirect to the specified URL
	//note// - even though it's output as an HTTP Header, Wakka's output-buffering means that this function still works anywhere in a page

[=] //string// **Minihref**(//string// method=""''"",  //string// tag=""''"",  //string// anchor=""''"") -- return value for page 'wakka' parameter, in tag[/method][#anchor] format
	##method## - optional Wakka method (default ##'show'## method added in ##Run()## function)
	##tag## - optional tag - returns current-page tag if empty
	[=] ##anchor## - optional HTTP anchor-fragment (i.e. backward-compatible with Wakka 0.1.2)

[=] //string// **Href**(//string// method=""''"", //string// tag=""''"", //string// params=""''"", //string// anchor=""''"") -- return the full URL for a page/method, including any additional URL-parameters and/or anchor
	##method## - optional Wakka method (default ##'show'## method added in ##Run()## function)
	##tag## - optional tag - returns current-page tag if empty
	##params## - optional URL parameters in HTTP ##name=value[&name=value][...]## format
	[=] ##anchor## - optional HTTP anchor-fragment (i.e. backward-compatible with Wakka 0.1.2)
	returns HREF string adjusted for Apache rewrite_method setting (i.e. Wakka ##'rewrite_method'## config-parameter)
	[=] Wakka ##'base_url'## config-parameter stays the same regardless of ##'rewrite_method'## setting
	[=] script-name can be set via ##'script_name'## config-parameter (defaults to self, i.e. ##wakka.php##)
	[=] page-var can be set via ##'page_var'## config-parameter (defaults to ##wakka##)

[=] //string// **Link**(//string// pagetag, //string// method=""''"", //string// text=""''"", //bool// track=1) -- return full <A href="http://example.com/"> or <IMG/> HTML-tag
	##pagetag## - link content - may be Wakka //tag//, interwiki //wikiname:page// tag, ##http/ftp/https/mailto## URL, [=] local or remote image-file for <IMG/> link, or [=] local or remote doc-file; if pagetag is for an external link but not protocol is specified, ##""http://""## is prepended
	##method## - optional Wakka method (default ##'show'## method added in ##Run()## function)
	##text## - optional text or [=] image-file for HREF link (defaults to same as ##pagetag##)
	##track## - link-tracking used by Wakka's internal link-tracking (inter-page cross-references in LINKS table)
	[=] //credits// - some extension ideas and code adapted from Wacko R3.5 variant of Wakka (http://www.oversite.ru)
	[=] pagetag may include a '#' anchor-fragment
	[=] if pagetag begins with '#' and text is empty, link is an <A name=".."> tag (i.e. page-anchor)
	[=] if pagetag begins with '#' and text is non-empty, link is an intra-page link with text as content (text may be an image-file, for an intra-page image-link - e.g. for 'return to top')
	[=] if pagetag is an image- or doc-file and text is not an image-link (see next), text is content for link ALT parameter
	[=] text may be name of local or remote image-file - results in image-link
	[=] image- and document-files are identified by filetypes listed in ##'image_types'## and ##'doc_types'## config-parameters respectively
	[=] local image- or document-files are identified by no '/' in the filename, and must be in the folder pointed to by the config-parameter ##'image_path'## (defaults to ##/images##) or ##'doc_path'## (defaults to ##download##) respectively
	[=] if link-class separator (defined by config-parameter ##'link_class'##, defaults to ##""||""##) is found in text, text is split into text (before separator) and class-info (after); class-info is inserted into <IMG/> tag (if either pagetag or text is an image-file) or <A> tag (all other cases), must be HTML-appropriate content (e.g. ALT, HEIGHT, WIDTH, ALIGN etc) and cannot include < or > (to minimise hacking...); class-info may also include ##useicon## or ##noicon## directive (see next)
	[=] doc-file links optionally preceded by an icon for the respective doc-type; icons must be in folder pointed to by ##'icon_path'## (defaults to same as ##'image_path'##) and in GIF format with same name as doc-type (e.g. ##pdf.gif##); will be shown for all doc-files if ##'use_icons'## config-parameter is set (defaults to ##Y## - i.e. icon-display on); can be overruled by ##useicon## or ##noicon## directive in class-info

//bool// **Iswikiname**(//string// text) -- return true if text conforms to Wakka's 'camel-case' page-name convention

//null// **Tracklinkto**(//string// tag) -- add tag (i.e. page-reference) to current list stored in SESSION link-table
	//note// - used by Wakka's internal link-tracking - is effectively a private-function

//array// **Getlinktable**() -- return current content of SESSION link-table
	array is 0-index array of page-tags
	//note// - used by Wakka's internal link-tracking - is effectively a private-function

//null// **Clearlinktable**() -- clear and reset the current content of SESSION link-table
	//note// - used by Wakka's internal link-tracking - is effectively a private-function

//null// **Startlinktracking**() -- switch on Wakka's internal link-tracking
	//note// - used by Wakka's internal link-tracking - is effectively a private-function

//null// **Stoplinktracking**() -- switch off Wakka's internal link-tracking
	//note// - used by Wakka's internal link-tracking - is effectively a private-function

//null// **Writelinktable**() -- update (clear and refresh) the content of the LINKS table for the current page-tag
	//note// - used by Wakka's internal link-tracking - is effectively a private-function
	in the LINKS table, the ##from_tag## field is the current page-tag (i.e. from ##Getpagetag()## function), the ##to_tag## field is a reference from the SESSION link-table

//string// **Header**() -- return the buffered output for the page-header
	header-content is derived from the file pointed to by the 'header_action' config-parameter
	[=] link-tracking is disabled for the header (otherwise any page referenced in the header is listed as linked to //every// page)
	[=] header-action may be set individually for each sub-site or section, as selected by additional page-properties (see **//Configuration//** section)
	[=] header is now processed //after// the page-body (see ##Run()## function), so header-action may be selected in page-code (e.g. to output different headers for popup help-pages, or no header at all)

//string// **Footer**() -- return the buffered output for the page-footer
	footer-content is derived from the file pointed to by the 'footer_action' config-parameter
	[=] link-tracking is disabled for the footer (otherwise any page referenced in the footer is listed as linked to //every// page)
	[=] footer-action may be set individually for each sub-site or section, as selected by additional page-properties (see Configuration section)
	[=] footer is processed after the page-body, so footer-action may be selected in page-code (e.g. to output different footers for popup help-pages, or no header at all)

==== Forms ==

//string// **Formopen**(//string// method=""''"", //string// tag=""''"", //string// formmethod='POST') -- return a <FORM> HTML-tag for the selected method and tag
	##method## - optional (if empty, defaults to ##'show'## in ##Run()## function)
	##tag## - optional, defaults to current page-tag
	FORM 'action' parameter is adjusted for Apache rewrite_method setting (i.e. Wakka ##'rewrite_method'## config-parameter)
	[=] page-var for use when 'rewrite_method' is disabled can be set via ##'page_var'## config-parameter (defaults to ##wakka##)

//string// **Formclose**() -- return a </FORM> HTML-tag

==== Interwiki stuff ==

//null// **Readinterwiki**() -- read and store the content of the 'interwiki.conf' file, for use in interwiki links
	//note// - the ##interwiki.conf## file, if present, must be in the root-directory

//null// **Addinterwiki**(//string// name,  //string// url) -- add an interwiki reference to this page's interwiki list
	//note// - this list persists only for the duration of the processing for this page

//string// **Getinterwikiurl**(//string// name,  //string// tag) -- return a full interwiki URL
	##name## - name of interwiki reference
	##tag## - text to be appended to the interwiki URL
	//warning// - returns nothing if the interwiki reference is not found - if so, this will result in null (and possibly invisible) HREF links

==== Referrers ==

//null// **Logreferrers**(//string// tag=""''"", //string// referrer=""''"") -- if page-referrer is from outside this wiki, add it to the page's entries in the REFERRERS table
	##tag## - optional Wakka page-tag (defaults to current page)
	##referrer## - page-referrer (defaults to value of HTTP-server 'REFERER' (sic) parameter)
	[=] this function also triggers optional flushing of outdated page-referrers as per ##'referrers_purge_time'## config-parameter (moved from ##Maintenance()## function - same as in Wakka 0.1.2, but now triggered on each external-referrer reference rather than each page-view)

//array// **Loadreferrers**(//string// tag=""''"") -- return array of external referrers for this page-tag, sorted by number of references from each referrer, highest-first
	##tag## - optional Wakka page-tag (defaults to current page)
	returns 0-index array of record-arrays, each field-indexed as ##'referrer'## (URL) and ##'num'## (number of references)

==== Plug-ins ==

//string// **Action**(//string// action, //bool// forcelinktracking=true) -- return buffered output of a ##""{{..}}""## page-action
	##action## - name of action-file followed by optional arbitrary-length list of parameters in ##parameter=".."## format
	action-file is name of file (//without// ##.php## - that's added automatically here) in any directory listed in the ##'action_path'## config-parameter (see Includebuffered() function)
	parameter-names are case-sensitive and must be alphanumeric (i.e. '##_##' is not allowed); each parameter and its value is added to the symbol-table available to the action's code

//string// **Method**(//string// method) -- return buffered output of (usually) processing the current page with the selected method
	##method## - name of method-file (//without// ##.php## - that's added automatically here)
	the directory pointed to by the ##'handler'## config-parameter (defaults to ##page##) is prepended as part of the method, as the 'method-location'
	the 'method-location' (with '.php') may be in any directory listed in the ##'handler_path'## config-parameter (see ##Includebuffered()## function)

//string// **Format**(//string// text, //string// formatter='wakka') -- return buffered output of processing the 'text' parameter with the selected formatter
	##text## - text to be processed (formatted)
	##formatter## - name of formatter-file (//without// ##.php## - that's added automatically here)
	the file must be in the ##./formatters## directory
	the ##text## parameter and its value are added to the symbol-table available to the formatter's code

==== Users ==

//array// **Loaduser**(//string// name, //bool/string// password=0) -- return user-record for selected user, with optional password check
	##name## - user-name in USERS table
	##password## - optional; if non-zero, only return values if both user-name and password match
	returns fieldname-indexed array of all matching fields in USERS table, or empty array if user not found or if password provided but does not match

//array// **Loadusers**() -- return array of all current user-records, sorted by user-name
	returns 0-index array of user-records, each record as fieldname-indexed array of all matching fields in USERS table
	//warning// - this may be resource-intensive

//string// **Getusername**() -- return name of or identifier for current user
	returns user-name if user is logged-on, otherwise extracts a host-name or IP address from the HTTP 'SERVER_ADDR' parameter
	//warning// - on a closed intranet or local server, change the ##gethostbyaddr()## function in the code to a simple copy from 'SERVER_ADDR', as the failed ##gethostbyaddr()## search may add //many seconds// to the page runtime

//string// **Username**() -- same as Getusername() (//deprecated//)

//array// **Getuser**() -- return the user-info (if any) from the current session

//null// **Setuser**(//array// user) -- store the user-info for the current user in the current session
	//note// - adds the user-name and password to the user-info as ##'name'## and ##'password'## fields respectively
	//warning// - storing the password in a session-parameter may be a security issue where sessions are stored in cookies

//null// **Logoutuser**() -- logs out the current user by deleting the user-info from the current session
	//warning// - the current password is stored in a separate session-cookie - this may be a security issue

//bool// **Userwantscomments**() -- return true if the user is logged-on and has set their default ##'wants_comments'## user-info parameter to ##Y##

==== Comments ==

[=] //array// **Loadcomments**(//string// tag, //bool// reverse=false, //int// limit=0) -- return array of comment-'pages' associated with selected tag
	##tag## - name of page for which to search for associated comments
	[=] ##reverse## - optional sort-order selector: if false, sorts earliest-first (default, as per Wakka 0.1.2), if true, sorts most-recent-first
	[=] ##limit## - optional limit on number of comments - if non-zero, limits the comment-count as per the specified order
		combined, these two additions also support a simple weblog mechanism, with entries as 'comments' to a base-page sorted in reverse date-order and limited to the selected number of entries
	returns 0-indexed array of page-records, without additional page-properties (see ##Loadpage()## function)

//array// **Loadrecentcomments**() -- return array of all comments, in reverse date-order
	//note// - the time used for the sort-order is that of the comment, not the base-page
	//warning// - this function returns all content of **all** comments, and may be resource-intensive

//array// **Loadrecentlycommented**(//int// limit=50) -- return array of page-records sorted by most-recent-comment
	##limit## - optional limit on number of comments processed
	returns 0-indexed array of page-records, without additional page-properties (see ##LoadPage()## function), but with extra fields ##'comment_user'##, ##'comment_time'##, ##'comment_tag'## (respectively the user, time and page-tag of the respective comment-'page')

[*] //bool// **Allowscomments**() -- returns false if no user can write comments to the current page
	//note// - is used by a modified ##'show'## method to prevent the view/edit comments section of the footer from being displayed if no user can write comments

==== Access control ==

//bool// **Userisowner**(//string// tag) -- return true if the current user is the 'owner' of the selected page
	##tag## - optional page-tag - defaults to current page
	//note// - returns false if the current user is not logged on

[*] //bool// **Userisadmin**() -- return true if the current user is a system administrator
	system-admins are identified in the (additional) config-parameter ##'sysadmin_acl'## (comma-separated list of user-ids and/or user-groups - see ##Hasaccess()## function)

//string// **Getpageowner**(//string// tag, //string// time=""''"") -- return user-id of selected page or page-revision
	##tag## - optional page-tag - defaults to current page
	##time## - optional revision-version time

//null// **Setpageowner**(//string// tag, //string// user) -- set selected user as the 'owner' of the selected page (i.e. user 'claims' the page)
	##tag## - page-tag of page 'claimed'
	##user## - user-id of user 'claiming' the page

[=] //array// **Loadacl**(//string// tag, //string// privilege, //bool// usedefaults=true, //bool// forceload=false) -- return access-control/page-property record for selected page-privilege/property
	##tag## - page-tag for requested privilege/property
	##privilege## - name of privilege/property requested
	##usedefaults## - if true and respective page-privilege record not found, use respective default privilege from ##'default_read_acl'##, ##'default_write_acl'## or ##'default_comment_acl'## config-parameter ([=] not used for extended page-properties)
	[=] ##forceload## - if false, attempt to read from current page-properties (including privileges) first; if true, force privilege/property to be loaded from ACLS table (as per Wakka 0.1.2)
	returns fieldname-indexed array of ##'page'## (tag), ##'privilege'## and ##'list'## - latter is newline-separated list of names ([=] and/or user-groups) for access-control rights ([=] or page-property value for other 'privilege' labels)
	[=] //note// - extended page-properties are stored in the ACLS table, as for ACL access-control privileges

//null// **Saveacl**(//string// tag, //string// privilege, //string// list=""''"") -- save selected page-privilege/property in ACLS table
	##tag## - page-tag for requested privilege/property
	##privilege## - name of privilege/property to be stored
	##list## - access-control list ([=] or extended page-property) to be stored

[*] //null// **Deleteacl**(//string/array// tag, //string/array// privilege, //string/array// list=""''"") -- delete page-properties for specified tag(s) and/or property-type(s) and/or content
	##tag## - page-tag or 0-indexed array of page-tags
	##privilege## - extended page-property name or 0-indexed array of page-property names
	##list## - property content or 0-indexed array of content-values
	//note// - this function is mainly used internally to keep the ACLS table free of empty or unused extended page-property records, and also by the added ##'delete'## method to delete a complete page

[*] //string// **Mysqlquotestr**(//string/array// value) - return a string suitable for use in a Mysql 'IN' clause
	##value## - string or array of string to which the PHP ##mysql_escape_string()## function is to be applied
	//note// - for consistency with other code, the string returned does //not// include the outermost '..' characters needed in the final Mysql query

[*] //bool// **Hasrights**(//string// rights) -- return true if current user has any of the specified access-rights
	##rights## - comma-separated list user-ids and/or user-groups (see ##Hasaccess()## function)
	//note// - returns false if current user is not logged on (use less resource-intensive ##GetUser()## function to test for logged-on status)

//bool// **Hasaccess**(//string// privilege, //string// tag=""''"", //string// user=""''"") -- return true if the specified user has the specified access-rights to the specified page
	##privilege## - type of access-right to be tested - either ##'read'##, ##'write'## or ##'comment'##
	##tag## - page-tag on which to test for rights (defaults to current page)
	##user## - user-id if user for access-rights test (defaults to current user)
	always returns true if the user-id matches the page-'owner' (see ##Userisowner()## function) or [=] belongs to a system-administrator group (see ##Userisadmin()## function)
	returns true if the user-id matches any of the wildcard keys ##'*'## any-user, or ##'§'## (or [=] ##'$'##) any logged-on user, or any of the specific user-ids (or, [=] recursively, any user-groups), listed in the ##list## parameter for this tag and privilege in the ACLS table or, if no record is found in ACLS, in the respective ##'default_read_acl'##, ##'default_write_acl'## or ##'default_comment_acl'## config-parameter
	returns false if the user-id does not match, is explicitly blocked by a ##'!'## prefix, or the list is present and empty

[*] //bool// **Testaccess**(//string// list, //string// user, //bool// is_registered) -- provides recursive support for access-rights checks
	//note// - this is effectively a private support-function for ##Userisadmin()##, ##Hasrights()## and ##Hasaccess()##

[*] //array// **Loadgroups**() -- return array of names of all user-groups
	array is 0-indexed list of user-groups

==== XML support ==

//null// **Writerecentchangesxml**() - generate RSS 0.92-spec XML file summarising recent changes
	//note// - generates/overwrites a single hardwired ##recentchanges_//name//.xml## file in the ##/xml## directory, where ##//name//## is derived from the sites ##'wakka_name'##' config-parameter

==== Miscellaneous (for backward compatibility to PHP 4.0.6) ==
//Note// - these functions provide basic emulation of the 'superglobals' ##$_SESSION## etc available only after PHP 4.1.0

[*] //null// **Setv**(//string// vtype=""''"", //string// vkey=""''"", //string// vidx=""''"", //mixed// val=""''"") -- set 'superglobal' parameter
	##vtype## - name of 'superglobal' parameter - function forces a ##die()## if this is not present
	##vkey## - name of member of 'superglobal' - function forces a ##die()## if this is not present
	##vidx## - optional array-key if ##vkey## is an array
	##val## - value to store in 'superglobal'

[*] //mixed// **Getv**(//string// vtype=""''"", //string// vkey=""''"", //string// vidx=""''"") -- return 'superglobal' value of parameter
	##vtype## - name of 'superglobal' parameter - function forces a ##die()## if this is not present
	##vkey## - name of member of 'superglobal' - function forces a ##die()## if this is not present
	##vidx## - optional array-key if ##vkey## is an array

==== Templates ==

This new [*] section provides support for templates, to separate visual code (HTML/CSS) from logic code (PHP).

Templates are standard HTML files, or just PHP strings, with substitution-markers in the form ##{//marker//}##, and optional blocks to extract and replace - recursively if required - in the form ##//content//##. Blocks and substitution markers may be nested as required. For a simple example, see the ##Menu_out()## function in the //Menu// section below.

Handles and markers are case-independent.

The following additional config-parameters are used:
	- ##'tpl_unknowns'## - substitution of 'undefined' marker-blocks (see ##Tpl_getundefined()## function): either ##'keep'## (leave unchanged - default), ##'remove'## (delete from the output) or ##'comment'## (replace with a #### HTML comment)
	- ##'tpl_halt_on_error'## - action on error: either ##'yes'## (halt on error - default), ##'report'## (insert a warning-message in the output stream) or ##'ignore'## (ignore the error and try to continue)
	- ##'tpl_path'## - directory-path for templates: default to ##./templates##

//Credits// - this section is based on Kristian Koehntopp's Template library for ""PHPLib""

[*] //bool// **Tpl_setfile**(//string/array// handle, //string// filename=""''"") -- associate a filename with a matching template-handle (or filenames with handles)
	##handle## - template-handle or, if array, array of handle=>filename pairs
	##filename## - name of template file (should be blank if ##handle## is an array)
	returns ##false## if any filename is blank or any file is not found
	//note// - this only assigns filenames to handles - file-loading does not take place until a ##tpl_setblock()##, ##tpl_subst()## (usually via ##tpl_parse()##) or ##tpl_getundefined()## function is called

[*] //bool// **Tpl_setblock**(//string// parent, //string// handle, //string// name=""''"") -- extract a named block from the template-handle
	##parent## - template-handle of parent-block (will attempt to load associated file if not yet loaded)
	##handle## - template-handle in which to store extracted block
	##name## - optional handle-name to insert in parent-block in place of extracted block (if not present, ##handle## value will be used as name)
	markers for blocks to extract take the form ##//content//##, where //handle// is the name to use here as the value of the ##handle## parameter, and //content// is the block to be associated with //handle//
	when the function completes, the extracted block is replaced in the parent-block with ##{//handle//}## or ##{//name//}##
	the ##name## parameter is used when the content will be constructed repeatedly, such as in a table-output loop, and the resultant content finally copied back to ##handle## - see ##Tpl_parse()## function

[*] //null// **Tpl_setvar**(//string/array// handle, //string// value=""''"") -- assign content for a template-handle or a set of template-handles
	##handle## - template-handle or, if array, array of handle=>value pairs
	##value## - content to assign (should be blank if ##handle## is an array)

[*] //string// **Tpl_subst**(//string/array// handle) -- return result of substituting content for the respective markers in the block(s) specified by //handle//
	##handle## - template-handle, or 0-indexed array of template-handles, for block(s) in which to perform substitution
	will perform substitution for any markers found in ##handle##'s block(s)
	//note// - the key difference between this function and ##Tpl_parse()## is that this does //not// change the content stored in ##handle##'s block(s) - the result of the substitution is returned but not stored by this function

[*] //string// **Tpl_parse**(//string// target, //string/array// handle, //bool// append=false) -- return result of performing marker/content substitution in //handle//, also storing the result in //target//
	##target## - destination-handle to store the result
	##handle## - source-handle for content in which marker-substitution will take place
	##append## - if true, append the result to any existing content held by ##target##
	//note// - ##append## is typically used when markers in content pointed to by ##handle## are to be substituted with new content repeatedly in a loop, such as when processing a recordset from a query; the content held by ##handle## is //not// changed in the substitution, but the content of target is
	//note// - if ##append## is true, ##handle## must be a string, not an array

[*] //string// **Tpl_getvar**(//string// handle) -- return content held by //handle//

[*] //array// **Tpl_getvars**(//array// handles=""''"") -- return array of content-strings content held by //handles//, or all content
	##handles## -- 0-indexed array of handles for which to return content (if empty, returns content of all current handles)
	returns handle-indexed array of strings

[*] //array// **Tpl_getundefined**() -- return array of apparent handles in template for which no content has been defined
	returns handle-indexed array of apparent-handle names
	returns false if no 'undefineds' are found
	//note// - 'undefineds' will occur if the text contains any arbitrary ##{..}## blocks

[*] //string// **Tpl_out**(//string// handle) -- return content held by //handle// after processing of any 'unknowns'
	//note// - 'unknowns' (i.e. un-substituted apparent markers) will occur if the text contains any arbitrary ##{..}## blocks: if any part of your template includes code or other probable occurrences of ##{..}## (such as providing a summary of Wakka formatting on a template for an edit-form), you should ensure that the ##'tpl_unknowns'## config-parameter is set to ##'keep'##

[*] //null// **Tpl_print**(//string// handle) -- print content held by //handle// after processing of any 'unknowns'
	//note// - this is just a shorthand for ##print($this->tpl_out($handle))##

==== Menus ==

This new [*] section supports automated generation of code for various types of menus, using the Template section above. For example, using the default parameters, the final menu-output call
	##$out=$this->menu_out('{NAME}=new Array("{TEXT}","{LINK}","////",{COUNT},{HEIGHT},{WIDTH});'."\n",""''"",true);##
generates Javascript code compatible with Ger Versluis' well-known ##""HVMenu""## package for nested popout menus (see http://www.dynamicdrive.com ).

Template parameters for each menu-item are:
	- ##{NAME}## - assigned name, or ""HVMenu""-compatible default of ##Menu## followed by the auto-generated item-handle
	- ##{TEXT}## - text for the link, or copy of the original link (e.g. Wakka tag) as default
	- ##{LINK}## - URL for the menu-item, including any Wakka method defined as default in the menu-initialisation
	- ##{COUNT}## - auto-generated count of any 'child'-items in a sub-menu attached to this item
	- ##{HEIGHT}## - common height-value for all items in this menu or sub-menu (may be different for each sub-menu)
	- ##{WIDTH}## - common width-value for all items in this menu or sub-menu, as for ##{HEIGHT}##

A minimum menu using an item-string ##$menu## returned by ##Maketree()## (see ##Menu_item()## function) and using the template-string above (as ##$tpl##) would be as follows:
	##$this->menu_init();  ""//""initialise menu using standard defaults 	
	$menuhandle=$this->menu_set(20,120);  ""//""set typical default cell-sizes for ""HVMenu""
	$itemhandle=$this->menu_item($menu);  ""//""generate menu-items from Maketree list
	$out=$this->menu_out($tpl,""''"");  ""//""generate single-layer menu without recursion##

The handles ##menuhandle## and ##lastitemhandle## returned from the ##Menu_set()## and ##Menu_item()## functions respectively are not used in this simple example, but would be used to keep track of nesting in multi-layered menus.

[*] //null// **Menu_init**(//string// method=""''"", //string// fsep='|', //string// rsep="~") -- initialise the menu-data store
	##method## - Wakka 'method' to be used for all output links

	##fsep## - field-separator for multi-link strings (see ##Menu_item()## function)
	##rsep## - record-separator for multi-link strings

[*] //string// **Menu_set**(//string// height, //string// width, //string// parent=""''"", //string// name=""''"") -- return handle for new sub-menu attached to the //parent// item
	##height## - height for all entries in this sub-menu (required for ""HVMenu"", not required for some other menu-types - will be substituted for any ##{HEIGHT}## marker in the output-template)
	##width## - width for all entries in this sub-menu (required for ""HVMenu"", not required for some other menu-types - will be substituted for any ##{WIDTH}## marker in the output-template)
	##parent## - handle for parent-item to which this menu will be attached; if blank, most-recent menu-item will be used as parent
	##name## - name to be used as menu-descriptor (not used at present, reserved for future use)
	returns a menu-id in the form ##//i//_//j//_//k//...##, representing the parent item (see ##Menu_item()## function), or the string ##:root:## for the root-level menu

[*] //string// **Menu_item**(//string// text, //string// link=""''"", //string// parent=""''"", //string// name=""''"") -- return handle for new menu-item(s) attached to the //parent// menu
	##text## - text to be shown for the menu-item (but note special ase described below)
	##link## - tag or other URL content for the link, to be output through the ##Href()## function
	##parent## - handle for parent-menu to which this item will be attached; if blank, most-recent menu will be used as parent
	##name## - content for the ##{NAME}## output-parameter; if absent, default of ##Menu## followed by the auto-generated item-handle will be used
	returns an item-id in the form ##//i//_//j//_//k//..## (e.g. ##2_5_3##), where //i//, //j//, //k// and so on are the respective item-numbers of the items in this menu-tree, with the last number representing this item
	//special case// - if ##link## is blank and ##text## contains at least one match to the ##fsep## field-separator string defined in the menu-initialisation, the string is 'unpacked' as a set of menu-item records, separated by the ##rsep## record-separator, and with each record in the form //link// //fsep// //text// (e.g. ##""HomePage|Home page""##); this is compatible with the output from the ##Maketree()## function

[*] //string// **Menu_out**(//string// tpl, //string// id=":root:", //bool// recurse=false) -- return the result of processing the menu-data with the specified template
	##tpl## - template-string (as described at the start of this section)
	##id## - menu-id of the sub-menu to which this template will be applied; if blank, applies (or starts) at the root-level menu
	##recurse## - if false, applies the template to this menu only; if true, applies the template recursively to this template and all of its 'children'

[*] //array// **Menu_get**(//string// id=""''"", //bool// type) -- return stored content for a menu or menu-item
	##id## - menu-id or item-id, dependent on ##type##; if empty, return the menu-data array for the root-level menu
	##type## - if false, return menu-data array (default); if true, return item-data array
	//note// - for further details, see the code; this is effectively a private function but may be useful in some cases where direct manipulation is needed

[*] //int// **Menu_count**(//string// id=""''"") -- return the item-count for the selected menu
	##id## - menu-id; defaults to ##:root:##, for the root-level menu
	//note// - for compatibility with ""HVMenu"", output the Javascript string
		"var NoOffFirstLineMenus=".$this->menu_count();

==== Run (and support for Run) ==

[*] //null// **Page_init**() -- ensure that absolute defaults are applied to any undefined essential config-parameters
	//note// - see the code for details of these config-parameters - mostly for the amended ##Link()## function

//null// **Maintenance**() -- do basic database housekeeping ([=] //deprecated//, does nothing)
	//note// - in the original Wakka 0.1.2 code, this does housekeeping for outdated page-versions and referrers; whilst it's necessary that this be done regularly, it's //not// necessary to do so on every page-view; in this Wakka variant housekeeping for outdated page-versions is handled more appropriately by ##Savepage()##, and for referrers by ##Logreferrer()##

//null// **Run**(//string// tag, //string// method=""''"") -- generate the output page for the specified tag and method
	##tag## - page-tag for page-content to be output
	##method## - processing method for page-content (see ##Method()## function), defaults to ##'show'## method
	[=] a number of minor but significant changes:
		- loads config-parameters in cascaded form, overlaying the basic ##wakkaconfig## parameters (already defined by this point) with config-parameters for a subsite (if any) specified by a ##'section'## extended page-property for the current-page, and then overlaying any parameters defined for the section (if any); this provides flexibility to allow directories, menus, templates, layouts, headers, footers all to change on a per-section and per-subsite basis (and also on a per-page basis, in some cases)
		- default show, edit and print methods can also change on a per-subsite/section basis, as defined by optional ##'show_method'##, ##'edit_method'## and ##'print_method'## config-parameters respectively
		- the horrible 'method ending in ##.xml##' kludge for outputting null-header pages is bypassed by a cleaner process of setting header- and footer-action to the string ##'null'## (though the original process is still supported for backward compatibility)
		- although still output in the same header/main/footer sequence, the main content is processed //before// the header, allowing page-content code to select the output header and footer dependent on run-time context

==== Extended page-properties ===

Although in principle any number of properties could be defined, the following are some typical examples which I've used in my (TomG's) own code, and which I edit through a modified ##'edit'## page-method:

	- ##headline## - substitute for page-tag as page-headline (defaults to Wakka page-tag)
	- ##shortname## - shorter (usually) version of headline for use in menus and breadcrumb-trails (defaults to Wakka page-tag)
	- ##title## - content for HTML  tag (default is ##'meta_keywords'## config-parameter)
	- ##description## - content for HTML  tag (default is ##'meta_description'## config-parameter)
	- ##parent## - tag of 'parent' in a parent/child trail, e.g. for a menu or breadcrumb-trail (default is blank, i.e. a standard Wakka 'everything is at root-level' page)
	- ##section## - specifies the member of the ##sections## config-parameter array to use to overlay the standard config-parameters; this in turn may include a ##'site'## config-parameter to select the respective member from the ##sites## config-parameter array

The optional ##sites## and ##sections## arrays in the config-parameters contain arrays of overlays for higher-level config-parameters; if absent, the result is exactly the same as the Wakka 0.1.2 standard configuration-file. In principle the ##sites## and ##sections## arrays could contain overlays for //any// config-parameters, though in practice they will not affect the start-up parameters (Mysql configuration) and should not change key parameters such as ##'base_url'##, ##'table_prefix'## and ##'rewrite_mode'## (though there's nothing to stop them from doing so should the need arise!)