Template Tags
All PHP commands used in the MetaPhilter system are called from Tag object methods. This allows index creation to be easy, swift, and very clean. The following is documentation on the Tag object class and the default tags used in the MetaPhilter community weblog templates.
The first sections of this chapter are about the PHP involved in the coding of Tag objects. If you're only interested in the existing tags, jump to the Default Template Tags section.
Tag Basics
Every tag used in the MetaPhilter templates is defined as a class object in 'tags.php'. Each tag is derived from the "Tag" class. The default constructor of the Tag class takes a reference to its containing Tag object as its first parameter. The second parameter is an associative array of the tag's attributes (e.g. in
<MPLinks category="development" lastn="10">
,category
andlastn
are keys in the array with their values as "development" and "10" respectively). The third and final parameter is a string of the element's contents. The values passed to the constructor are then assigned to the object's member variables$container
,$attributes
, and$contents
.Tags should be included in templates like HTML elements. Example:
<TagName attribute="value"> <NestedTag> Some text. </NestedTag> <EmptyTag another="something else"/> </TagName>
Tag attribute values can be encased in either double-quotes or single-quotes. The value is parsed for template tags just as any other character data in the template. Therefore, it is possible to nest template tags inside attribute values.
If your nested template tags contain other attribute values, you should switch the quoting of the nested attribute values.
For instance, the following example will not parse because the nested attribute value is encased with the same quoting as its parent:
<MPLinkComments id="<MPFormData select="commentID"/>"> ... </MPLinkComments>
However, if you switch the quoting of the inner attribute value, it will work as intended:
<MPLinkComments id="<MPFormData select='commentID'/>"> ... </MPLinkComments>
Creating new index templates is simply achieved by including the MetaPhilter PHP script in the first line of the template:
<?php include '../mp/metaphilter.php'; ?> <YourTag> Your site content. Regular HTML unchanged: <ul> <li><a href="http://url.com">One</a></li> <li><a href="http://url.com">Two</a></li> <li><a href="http://url.com">Three</a></li> </ul> </YourTag>
If you notice the
<?php ?>
line being displayed in the parsed output, make sure that it occurs on the first line of your template.Tag Methods
The following is a short description of each of the default Tag object methods.
loadTags(string $parent)
This function returns an array of the names of all defined classes derived from class parent. This method is called from the global scope and its results are loaded in global variables for use in the default Tag constructor. The constructor loads a reference to the list into the
$template_tags
member variable. The$template_tags
member variable is an array consisting of the tags that are parseable when the object'sparseContents()
method is called. In the global scope, all classes derived from the "Tag" class object are loaded.ancestor(string $name [, ...])
Returns a reference to the closest ancestor of the object of class name. If multiple class names are given as arguments, the first encountered matching ancestor will be returned.
parseContents([bool $entities])
Returns the iteratively parsed contents of the object's
$contents
member variable. The parseable elements of this object are defined as an array in its$template_tags
member variable.parseContents()
creates new tags as found in the$template_tags
member variable, links their parent to the$this
pointer of the current object, and parses its$attributes
as they occur in the template. This function takes an optional boolean parameter that determines whether or not to parse HTML entities as their representative characters. So far, this is only used when converting user submitted data to legal HTML elements. The allowed HTML elements for user submitted data are defined as theUSER_LEGAL_HTML
constant.output()
Calls the member method
parseContents()
and returns the parsed result. You may choose not to return the parsed contents of the tag in the case that it is a control structure or implemented as an empty tag.
It is useful to create new constructor methods when a Tag derived class has children that could potentially rely on information provided by their parent. It is important to call the default constructor in the event that you need to declare a new one. Not doing so will result in an unparseable
$contents
member variable.Common Tag Object Implementations
Example
A tag that takes a color value as an attribute and has child tags rendering HTML elements using the value of that attribute:
class CommonColor extends Tag { var $color; function &CommonColor(&$container,$attributes,$contents) { $this->Tag($container,$attributes,$contents); if (!strlen($this->color=$this->attributes['color'])) $this->color=DEFAULT_COLOR; } //No special functionality needed for the output() method } class ColoredDiv extends Tag { function &output() { //Return nothing if this tag is not in //the context of a 'CommonColor' tag: if (!($parent=$this->ancestor("CommonColor"))) return; return<<<HTML <div style="background: $parent->color; margin: auto; width: 400px;"> My background color is $parent->color! </div> HTML; } }
Using these in a template is as easy as...
<CommonColor color="silver"> <ColoredDiv/> </CommonColor>
...and will produce this visible HTML:
My background color is silver!ColoredDiv can be inserted as an empty tag because its
output()
method does not return the result of aparseContents()
call.Example
An element that calls
parseContents()
multiple times in itsoutput()
method to produce a countdown:class Countdown extends Tag { var $warnings; var $warning; function &Countdown(&$container,$attributes,$contents) { $this->Tag($container,$attributes,$contents); if (!($fuse=$this->attributes['fuse'])) $fuse=DEFAULT_FUSE; while ($fuse--) $this->warnings[]="$fuse ticks remaining...<br/>"; } function &output() { while ($this->warning=array_shift($this->warnings)) $output.=$this->parseContents(); return $output; } } class Tick extends Tag { function &output() { if (!($countdown=&$this->ancestor('Countdown'))) return; if ($countdown->warning{0}==0) return "KABOOM!"; return $countdown->warning; } }
Implementing these tags like so...
<Countdown fuse="5"> <Tick/> </Countdown>
...would produce an output similar to:
4 ticks remaining... 3 ticks remaining... 2 ticks remaining... 1 ticks remaining... KABOOM!
Plugins
It is suggested that you don't edit the default template tags. If you want to create news tags, or use tags developed by another author, create a 'plugins' directory (if one does not exist yet) inside your MetaPhilter system path and place the plugin there. MetaPhilter will automatically include them when defining the template classes.
Here's an example of some arbitrary plugin file:
<?php /* MPLastUser Plugin Used to return the name of the most recently added user to your MetaPhilter database. */ class MPLastUser extends Tag { function &output() { if ($result=mysql_query("SELECT username FROM 1_users ORDER BY added DESC LIMIT 0,1")) return mysql_result($result,0,0); } } ?>
And let's say the author of this plugin just saves it as 'MPLastUser.mp'. To make this tag available in your index templates, you can simply place the 'MPLastUser.mp' plugin in the 'plugins' directory of your MetaPhilter system path. Once located to plugins directory, you can implement it as you would any of the other template tag.
Context Notation
As of MePhi .9, you can control the functionality of a tag by controlling its applied context in the MePhi DOM (Note: DOM is a term used very loosely)
To control the context of a tag, include a trailing dollar sign ('$') on the tag name in a template.
So far, a tag can only apply its context to the document level, but in future releases, you'll be able to control a tag's context by elevating its placement up the DOM from its current context.
Example:
<MPLinks> You are <MPUserData$ select="username"/> viewing <MPUserData select="username"/>'s thread. </MPLinks>
This will produce something similar to...
You are Yourself viewing Someone else's thread.
...where the first line always contains the username of the currently logged in user, and the second line contains the username of the user who posted the current link. This is because the trailing $ of the first
MPUserData
elevates its context so that it is not applied inside theMPLinks
tags. WhenMPUserData
is used outside the context of user, link, or comment containers, it will always return data of the currently logged in user.Therefore,
<MPUserData$ select="username"/>
will always return the username of the currently logged in user.